Bluesky app fork with some witchin' additions ๐Ÿ’ซ witchsky.app
bluesky fork client
122
fork

Configure Feed

Select the types of activity you want to include in your feed.

Is it "newskie" or "newsky" ๐Ÿค” (#4557)

* add newskie icon

(cherry picked from commit 152e074ee053e076bf644e368047e486a5ad127c)
(cherry picked from commit 8d2326f115c9c9d32aa1c41259bb81936b3868aa)

* add size prop

(cherry picked from commit af09ae2d8f4fedf8a50993e94b76efc44a2ef4ea)
(cherry picked from commit 38dd38451bcce8afcf302ad1180802640857722a)

* add a dialog for newskies to profiles

(cherry picked from commit fe16f55e9c5e8faef540b563662b0c0c9a1d2d77)
(cherry picked from commit c5b9f1b16ace276f422832069db076a5360616fe)

* move newskie to handle

(cherry picked from commit 150f2635b278a92ed67dcec748333b428aacb670)
(cherry picked from commit 1efaaf835380f4e76d2e4b7fe8b727a92731a794)

* use "say hello" in newskie dialog

(cherry picked from commit d9a286cfc823a9e697061de84dd317625741a862)
(cherry picked from commit 018dd1739fee68906dec63e05519f5ca9ae73910)

* tweaks

(cherry picked from commit 070363c947600c48368b01c776ea34fbf422f81e)
(cherry picked from commit c30855d4ff311e31fb6ae357a9d6cd1662b291d5)

* Tweaks

* Re-export newskie icon

* Design tweaks

* Tweaks

* Add source icon

* Remove unused file

* Remove unneeded edits

* Simplify logic

* Update source

* Moderate displayName, fix createdAt type

---------

Co-authored-by: Hailey <me@haileyok.com>

authored by

Eric Bailey
Hailey
and committed by
GitHub
11065174 73c9de3c

+93 -1
+1
assets/icons/newskie.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#FFC404" fill-rule="evenodd" d="M11.183 8.561c0 .544.348.984.892.984.545 0 .893-.44.893-.985V6.985c0-.544-.348-.985-.893-.985-.543 0-.892.44-.892.985v1.576Zm5.94 7.481c0 .539-.438.942-.976.942H8.004c-.538 0-.975-.411-.975-.95 0-2.782 2.264-5.021 5.046-5.021 2.783 0 5.047 2.247 5.047 5.03Zm-.43-4.584a.983.983 0 0 1 0-1.393l1.114-1.114a.985.985 0 0 1 1.393 1.393l-1.114 1.114a.985.985 0 0 1-1.393 0Zm2.897 3.741h1.575c.544 0 .985.349.985.892 0 .544-.44.892-.985.892h-1.67a.872.872 0 0 1-.89-.887c0-.543.44-.897.985-.897Zm-14.045.893c0-.544-.44-.892-.985-.892H2.985c-.544 0-.985.349-.985.892 0 .544.44.892.985.892H4.56c.545 0 .985-.349.985-.892Zm1.913-6.027a.985.985 0 0 1-1.393 1.393L4.95 10.344A.985.985 0 0 1 6.344 8.95l1.114 1.114Z" clip-rule="evenodd"/></svg>
+81
src/components/NewskieDialog.tsx
··· 1 + import React from 'react' 2 + import {View} from 'react-native' 3 + import {AppBskyActorDefs, moderateProfile} from '@atproto/api' 4 + import {msg, Trans} from '@lingui/macro' 5 + import {useLingui} from '@lingui/react' 6 + import {differenceInSeconds} from 'date-fns' 7 + 8 + import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo' 9 + import {useModerationOpts} from '#/state/preferences/moderation-opts' 10 + import {HITSLOP_10} from 'lib/constants' 11 + import {sanitizeDisplayName} from 'lib/strings/display-names' 12 + import {atoms as a} from '#/alf' 13 + import {Button} from '#/components/Button' 14 + import * as Dialog from '#/components/Dialog' 15 + import {useDialogControl} from '#/components/Dialog' 16 + import {Newskie} from '#/components/icons/Newskie' 17 + import {Text} from '#/components/Typography' 18 + 19 + export function NewskieDialog({ 20 + profile, 21 + }: { 22 + profile: AppBskyActorDefs.ProfileViewDetailed 23 + }) { 24 + const {_} = useLingui() 25 + const moderationOpts = useModerationOpts() 26 + const control = useDialogControl() 27 + const profileName = React.useMemo(() => { 28 + const name = profile.displayName || profile.handle 29 + if (!moderationOpts) return name 30 + const moderation = moderateProfile(profile, moderationOpts) 31 + return sanitizeDisplayName(name, moderation.ui('displayName')) 32 + }, [moderationOpts, profile]) 33 + const timeAgo = useGetTimeAgo() 34 + const createdAt = profile.createdAt as string | undefined 35 + const daysOld = React.useMemo(() => { 36 + if (!createdAt) return Infinity 37 + return differenceInSeconds(new Date(), new Date(createdAt)) / 86400 38 + }, [createdAt]) 39 + 40 + if (!createdAt || daysOld > 7) return null 41 + 42 + return ( 43 + <View style={[a.pr_2xs]}> 44 + <Button 45 + label={_( 46 + msg`This user is new here. Press for more info about when they joined.`, 47 + )} 48 + hitSlop={HITSLOP_10} 49 + onPress={control.open}> 50 + {({hovered, pressed}) => ( 51 + <Newskie 52 + size="lg" 53 + fill="#FFC404" 54 + style={{ 55 + opacity: hovered || pressed ? 0.5 : 1, 56 + }} 57 + /> 58 + )} 59 + </Button> 60 + 61 + <Dialog.Outer control={control}> 62 + <Dialog.Handle /> 63 + <Dialog.ScrollableInner 64 + label={_(msg`New user info dialog`)} 65 + style={[{width: 'auto', maxWidth: 400, minWidth: 200}]}> 66 + <View style={[a.gap_sm]}> 67 + <Text style={[a.font_bold, a.text_xl]}> 68 + <Trans>Say hello!</Trans> 69 + </Text> 70 + <Text style={[a.text_md]}> 71 + <Trans> 72 + {profileName} joined Bluesky{' '} 73 + {timeAgo(createdAt, {format: 'long'})} ago 74 + </Trans> 75 + </Text> 76 + </View> 77 + </Dialog.ScrollableInner> 78 + </Dialog.Outer> 79 + </View> 80 + ) 81 + }
+5
src/components/icons/Newskie.tsx
··· 1 + import {createSinglePathSVG} from './TEMPLATE' 2 + 3 + export const Newskie = createSinglePathSVG({ 4 + path: 'M11.183 8.561c0 .544.348.984.892.984.545 0 .893-.44.893-.985V6.985c0-.544-.348-.985-.893-.985-.543 0-.892.44-.892.985v1.576Zm5.94 7.481c0 .539-.438.942-.976.942H8.004c-.538 0-.975-.411-.975-.95 0-2.782 2.264-5.021 5.046-5.021 2.783 0 5.047 2.247 5.047 5.03Zm-.43-4.584a.983.983 0 0 1 0-1.393l1.114-1.114a.985.985 0 0 1 1.393 1.393l-1.114 1.114a.985.985 0 0 1-1.393 0Zm2.897 3.741h1.575c.544 0 .985.349.985.892 0 .544-.44.892-.985.892h-1.67a.872.872 0 0 1-.89-.887c0-.543.44-.897.985-.897Zm-14.045.893c0-.544-.44-.892-.985-.892H2.985c-.544 0-.985.349-.985.892 0 .544.44.892.985.892H4.56c.545 0 .985-.349.985-.892Zm1.913-6.027a.985.985 0 0 1-1.393 1.393L4.95 10.344A.985.985 0 0 1 6.344 8.95l1.114 1.114Z', 5 + })
+6 -1
src/screens/Profile/Header/Handle.tsx
··· 5 5 6 6 import {Shadow} from '#/state/cache/types' 7 7 import {isInvalidHandle} from 'lib/strings/handles' 8 + import {isAndroid} from 'platform/detection' 8 9 import {atoms as a, useTheme, web} from '#/alf' 10 + import {NewskieDialog} from '#/components/NewskieDialog' 9 11 import {Text} from '#/components/Typography' 10 12 11 13 export function ProfileHeaderHandle({ ··· 17 19 const invalidHandle = isInvalidHandle(profile.handle) 18 20 const blockHide = profile.viewer?.blocking || profile.viewer?.blockedBy 19 21 return ( 20 - <View style={[a.flex_row, a.gap_xs, a.align_center]} pointerEvents="none"> 22 + <View 23 + style={[a.flex_row, a.gap_xs, a.align_center]} 24 + pointerEvents={isAndroid ? 'box-only' : 'auto'}> 25 + <NewskieDialog profile={profile} /> 21 26 {profile.viewer?.followedBy && !blockHide ? ( 22 27 <View style={[t.atoms.bg_contrast_25, a.rounded_xs, a.px_sm, a.py_xs]}> 23 28 <Text style={[t.atoms.text, a.text_sm]}>