Bluesky app fork with some witchin' additions ๐Ÿ’ซ

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]}>