Bluesky app fork with some witchin' additions 💫 witchsky.app
bluesky fork client
117
fork

Configure Feed

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

feat: toggles for 'via' visibility & inclusion

+191 -21
+5 -2
src/lib/api/index.ts
··· 56 56 replyTo?: string 57 57 onStateChange?: (state: string) => void 58 58 langs?: string[] 59 + omitViaField?: boolean 59 60 } 60 61 61 62 type FeatureFlags = { ··· 130 131 const rt = await rtPromise 131 132 const embed = await embedPromise 132 133 const reply = await replyPromise 133 - const record: AppBskyFeedPost.Record & {via: string} = { 134 - via, 134 + const record: AppBskyFeedPost.Record & {via?: string} = { 135 135 // IMPORTANT: $type has to exist, CID is calculated with the `$type` field 136 136 // present and will produce the wrong CID if you omit it. 137 137 $type: 'app.bsky.feed.post', ··· 142 142 embed, 143 143 langs, 144 144 labels, 145 + } 146 + if (!opts.omitViaField) { 147 + record.via = via 145 148 } 146 149 writes.push({ 147 150 $type: 'com.atproto.repo.applyWrites#create',
+3 -1
src/screens/PostThread/components/ThreadItemAnchor.tsx
··· 27 27 import {useDisableRepostsMetrics} from '#/state/preferences/disable-reposts-metrics' 28 28 import {useDisableSavesMetrics} from '#/state/preferences/disable-saves-metrics' 29 29 import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons' 30 + import {useShowViaClient} from '#/state/preferences/show-via-client' 30 31 import {type ThreadItem} from '#/state/queries/usePostThread/types' 31 32 import {useSession} from '#/state/session' 32 33 import {type OnPostSuccessData} from '#/state/shell/composer' ··· 578 579 }) { 579 580 const t = useTheme() 580 581 const {i18n} = useLingui() 582 + const showViaClient = useShowViaClient() 581 583 const isRootPost = !('reply' in post.record) 582 584 const via = post.record.via as string | undefined 583 585 ··· 588 590 <Text style={[a.text_sm, t.atoms.text_contrast_medium]}> 589 591 {niceDate(i18n, post.indexedAt, 'dot separated')} 590 592 </Text> 591 - {via ? ( 593 + {showViaClient && via ? ( 592 594 <Text 593 595 numberOfLines={1} 594 596 style={[a.text_sm, t.atoms.text_contrast_medium, {maxWidth: 160}]}>
+21
src/screens/Settings/RunesSettings/DisplaySettings.tsx
··· 18 18 useRepostCarouselEnabled, 19 19 useSetRepostCarouselEnabled, 20 20 } from '#/state/preferences/repost-carousel-enabled' 21 + import { 22 + useSetShowViaClient, 23 + useShowViaClient, 24 + } from '#/state/preferences/show-via-client' 21 25 import * as SettingsList from '#/screens/Settings/components/SettingsList' 22 26 import {atoms as a} from '#/alf' 23 27 import {Admonition} from '#/components/Admonition' ··· 27 31 import {Image_Stroke2_Corner0_Rounded as ImageIcon} from '#/components/icons/Image' 28 32 import {Pencil_Stroke2_Corner0_Rounded as PencilIcon} from '#/components/icons/Pencil' 29 33 import {Repost_Stroke2_Corner3_Rounded as RepostIcon} from '#/components/icons/Repost' 34 + import {Window_Stroke2_Corner2_Rounded as WindowIcon} from '#/components/icons/Window' 30 35 import {Text} from '#/components/Typography' 31 36 import {IS_WEB} from '#/env' 32 37 import {RunesScreenLayout} from './components/RunesScreenLayout' ··· 39 44 40 45 const highQualityImages = useHighQualityImages() 41 46 const setHighQualityImages = useSetHighQualityImages() 47 + 48 + const showViaClient = useShowViaClient() 49 + const setShowViaClient = useSetShowViaClient() 42 50 43 51 const setPostReplacementDialogControl = Dialog.useDialogControl() 44 52 ··· 78 86 </Trans> 79 87 </Admonition> 80 88 </SettingsList.Item> 89 + <Toggle.Item 90 + name="show_via_client" 91 + label={l`Show client used to post`} 92 + value={showViaClient} 93 + onChange={value => setShowViaClient(value)}> 94 + <SettingsList.Item> 95 + <SettingsList.ItemIcon icon={WindowIcon} /> 96 + <SettingsList.ItemText> 97 + <Trans>Show client used to post</Trans> 98 + </SettingsList.ItemText> 99 + <Toggle.Platform /> 100 + </SettingsList.Item> 101 + </Toggle.Item> 81 102 <SettingsList.Item> 82 103 <SettingsList.ItemIcon icon={PencilIcon} /> 83 104 <SettingsList.ItemText>
+21
src/screens/Settings/RunesSettings/ExtraSettings.tsx
··· 16 16 useDiscoverContextEnabled, 17 17 useSetDiscoverContextEnabled, 18 18 } from '#/state/preferences/discover-context-enabled' 19 + import { 20 + useOmitViaField, 21 + useSetOmitViaField, 22 + } from '#/state/preferences/omit-via-field' 19 23 import * as SettingsList from '#/screens/Settings/components/SettingsList' 20 24 import {atoms as a} from '#/alf' 21 25 import {Admonition} from '#/components/Admonition' 22 26 import * as Toggle from '#/components/forms/Toggle' 23 27 import {BellRinging_Stroke2_Corner0_Rounded as BellRingingIcon} from '#/components/icons/BellRinging' 28 + import {Explosion_Stroke2_Corner0_Rounded as ExplosionIcon} from '#/components/icons/Explosion' 24 29 import {Eye_Stroke2_Corner0_Rounded as VisibilityIcon} from '#/components/icons/Eye' 25 30 import {LikeRepost_Stroke2_Corner2_Rounded as LikeRepostIcon} from '#/components/icons/Heart2' 26 31 import {Lab_Stroke2_Corner0_Rounded as BeakerIcon} from '#/components/icons/Lab' ··· 40 45 41 46 const discoverContextEnabled = useDiscoverContextEnabled() 42 47 const setDiscoverContextEnabled = useSetDiscoverContextEnabled() 48 + 49 + const omitViaField = useOmitViaField() 50 + const setOmitViaField = useSetOmitViaField() 43 51 44 52 return ( 45 53 <RunesScreenLayout titleText={l`Extra`}> ··· 102 110 <SettingsList.ItemIcon icon={BeakerIcon} /> 103 111 <SettingsList.ItemText> 104 112 <Trans>Show debug context for posts in Discover feed</Trans> 113 + </SettingsList.ItemText> 114 + <Toggle.Platform /> 115 + </SettingsList.Item> 116 + </Toggle.Item> 117 + <Toggle.Item 118 + name="omit_via_field" 119 + label={l`Don't include the 'via' field in own posts`} 120 + value={omitViaField} 121 + onChange={value => setOmitViaField(value)}> 122 + <SettingsList.Item> 123 + <SettingsList.ItemIcon icon={ExplosionIcon} /> 124 + <SettingsList.ItemText> 125 + <Trans>Don't include the 'via' field in own posts</Trans> 105 126 </SettingsList.ItemText> 106 127 <Toggle.Platform /> 107 128 </SettingsList.Item>
+5
src/state/persisted/schema.ts
··· 191 191 enableSquareAvatars: z.boolean().optional(), 192 192 enableSquareButtons: z.boolean().optional(), 193 193 disableVerifyEmailReminder: z.boolean().optional(), 194 + showViaClient: z.boolean().optional(), 194 195 deerVerification: z 195 196 .object({ 196 197 enabled: z.boolean(), ··· 237 238 trendingVideoDisabled: z.boolean().optional(), 238 239 239 240 autoLikeOnRepost: z.boolean().optional(), 241 + omitViaField: z.boolean().optional(), 240 242 }) 241 243 export type Schema = z.infer<typeof schema> 242 244 ··· 246 248 colorScheme: 'witchsky', 247 249 hue: 0, 248 250 material3Accent: '#ee6300', 251 + material3Style: 'TONAL_SPOT', 249 252 session: { 250 253 accounts: [], 251 254 currentAccount: undefined, ··· 317 320 enableSquareAvatars: true, 318 321 enableSquareButtons: true, 319 322 disableVerifyEmailReminder: false, 323 + showViaClient: true, 320 324 deerVerification: { 321 325 enabled: false, 322 326 // https://witchsky.app/profile/did:plc:p2cp5gopk7mgjegy6wadk3ep/post/3lndyqyyr4k2k ··· 370 374 }, 371 375 372 376 autoLikeOnRepost: false, 377 + omitViaField: false, 373 378 } 374 379 375 380 export function tryParse(rawData: string): Schema | undefined {
+24 -17
src/state/preferences/index.tsx
··· 40 40 import {Provider as LargeAltBadgeProvider} from './large-alt-badge' 41 41 import {Provider as NoAppLabelersProvider} from './no-app-labelers' 42 42 import {Provider as NoDiscoverProvider} from './no-discover-fallback' 43 + import {Provider as OmitViaFieldProvider} from './omit-via-field' 43 44 import {Provider as OpenRouterProvider} from './openrouter' 44 45 import {Provider as PdsLabelProvider} from './pds-label' 45 46 import {Provider as PlcDirectoryProvider} from './plc-directory' ··· 48 49 import {Provider as ShowFollowsYouBadgeProvider} from './show-follows-you-badge' 49 50 import {Provider as ShowLinkInHandleProvider} from './show-link-in-handle' 50 51 import {Provider as ShowLinkInHandleOnlyOnWorkingLinksProvider} from './show-link-in-handle-only-on-working-links' 52 + import {Provider as ShowViaClientProvider} from './show-via-client' 51 53 import {Provider as SubtitlesProvider} from './subtitles' 52 54 import {Provider as TranslationServicePreferenceProvider} from './translation-service-preference' 53 55 import {Provider as TrendingSettingsProvider} from './trending' ··· 86 88 export {useImageCdnHost, useSetImageCdnHost} from './image-cdn-host' 87 89 export {useLabelDefinitions} from './label-defs' 88 90 export {useLanguagePrefs, useLanguagePrefsApi} from './languages' 91 + export {useOmitViaField, useSetOmitViaField} from './omit-via-field' 89 92 export { 90 93 useOpenRouterApiKey, 91 94 useOpenRouterConfigured, ··· 152 155 <HideUnreplyablePostsProvider> 153 156 <EnableSquareAvatarsProvider> 154 157 <EnableSquareButtonsProvider> 155 - <PostNameReplacementProvider> 156 - <DisableVerifyEmailReminderProvider> 157 - <TranslationServicePreferenceProvider> 158 - <OpenRouterProvider> 159 - <DisableComposerPromptProvider> 160 - <DisableTopOfFeedButtonProvider> 161 - <DiscoverContextEnabledProvider> 162 - { 163 - children 164 - } 165 - </DiscoverContextEnabledProvider> 166 - </DisableTopOfFeedButtonProvider> 167 - </DisableComposerPromptProvider> 168 - </OpenRouterProvider> 169 - </TranslationServicePreferenceProvider> 170 - </DisableVerifyEmailReminderProvider> 171 - </PostNameReplacementProvider> 158 + <ShowViaClientProvider> 159 + <PostNameReplacementProvider> 160 + <DisableVerifyEmailReminderProvider> 161 + <TranslationServicePreferenceProvider> 162 + <OpenRouterProvider> 163 + <DisableComposerPromptProvider> 164 + <DisableTopOfFeedButtonProvider> 165 + <DiscoverContextEnabledProvider> 166 + <OmitViaFieldProvider> 167 + { 168 + children 169 + } 170 + </OmitViaFieldProvider> 171 + </DiscoverContextEnabledProvider> 172 + </DisableTopOfFeedButtonProvider> 173 + </DisableComposerPromptProvider> 174 + </OpenRouterProvider> 175 + </TranslationServicePreferenceProvider> 176 + </DisableVerifyEmailReminderProvider> 177 + </PostNameReplacementProvider> 178 + </ShowViaClientProvider> 172 179 </EnableSquareButtonsProvider> 173 180 </EnableSquareAvatarsProvider> 174 181 </HideUnreplyablePostsProvider>
+54
src/state/preferences/omit-via-field.tsx
··· 1 + import { 2 + createContext, 3 + type PropsWithChildren, 4 + useCallback, 5 + useContext, 6 + useEffect, 7 + useState, 8 + } from 'react' 9 + 10 + import * as persisted from '#/state/persisted' 11 + 12 + type StateContext = persisted.Schema['omitViaField'] 13 + type SetContext = (v: persisted.Schema['omitViaField']) => void 14 + 15 + const stateContext = createContext<StateContext>( 16 + persisted.defaults.omitViaField, 17 + ) 18 + const setContext = createContext<SetContext>( 19 + (_: persisted.Schema['omitViaField']) => {}, 20 + ) 21 + 22 + export function Provider({children}: PropsWithChildren<{}>) { 23 + const [state, setState] = useState(persisted.get('omitViaField')) 24 + 25 + const setStateWrapped = useCallback( 26 + (value: persisted.Schema['omitViaField']) => { 27 + setState(value) 28 + persisted.write('omitViaField', value) 29 + }, 30 + [setState], 31 + ) 32 + 33 + useEffect(() => { 34 + return persisted.onUpdate('omitViaField', next => { 35 + setState(next) 36 + }) 37 + }, [setStateWrapped]) 38 + 39 + return ( 40 + <stateContext.Provider value={state}> 41 + <setContext.Provider value={setStateWrapped}> 42 + {children} 43 + </setContext.Provider> 44 + </stateContext.Provider> 45 + ) 46 + } 47 + 48 + export function useOmitViaField() { 49 + return useContext(stateContext) 50 + } 51 + 52 + export function useSetOmitViaField() { 53 + return useContext(setContext) 54 + }
+54
src/state/preferences/show-via-client.tsx
··· 1 + import { 2 + createContext, 3 + type PropsWithChildren, 4 + useCallback, 5 + useContext, 6 + useEffect, 7 + useState, 8 + } from 'react' 9 + 10 + import * as persisted from '#/state/persisted' 11 + 12 + type StateContext = persisted.Schema['showViaClient'] 13 + type SetContext = (v: persisted.Schema['showViaClient']) => void 14 + 15 + const stateContext = createContext<StateContext>( 16 + persisted.defaults.showViaClient, 17 + ) 18 + const setContext = createContext<SetContext>( 19 + (_: persisted.Schema['showViaClient']) => {}, 20 + ) 21 + 22 + export function Provider({children}: PropsWithChildren<{}>) { 23 + const [state, setState] = useState(persisted.get('showViaClient')) 24 + 25 + const setStateWrapped = useCallback( 26 + (value: persisted.Schema['showViaClient']) => { 27 + setState(value) 28 + persisted.write('showViaClient', value) 29 + }, 30 + [setState], 31 + ) 32 + 33 + useEffect(() => { 34 + return persisted.onUpdate('showViaClient', next => { 35 + setState(next) 36 + }) 37 + }, [setStateWrapped]) 38 + 39 + return ( 40 + <stateContext.Provider value={state}> 41 + <setContext.Provider value={setStateWrapped}> 42 + {children} 43 + </setContext.Provider> 44 + </stateContext.Provider> 45 + ) 46 + } 47 + 48 + export function useShowViaClient() { 49 + return useContext(stateContext) 50 + } 51 + 52 + export function useSetShowViaClient() { 53 + return useContext(setContext) 54 + }
+4 -1
src/view/com/composer/Composer.tsx
··· 99 99 useLanguagePrefs, 100 100 useLanguagePrefsApi, 101 101 } from '#/state/preferences/languages' 102 + import {useOmitViaField} from '#/state/preferences/omit-via-field' 102 103 import { 103 104 useOpenRouterApiKey, 104 105 useOpenRouterConfigured, ··· 215 216 const {closeComposer} = useComposerControls() 216 217 const {t: l, i18n} = useLingui() 217 218 const requireAltTextEnabled = useRequireAltTextEnabled() 219 + const omitViaField = useOmitViaField() 218 220 const langPrefs = useLanguagePrefs() 219 221 const setLangPrefs = useLanguagePrefsApi() 220 222 const textInputRef = useRef<TextInputRef>(null) ··· 913 915 replyTo: replyTo?.uri, 914 916 onStateChange: setPublishingStage, 915 917 langs: currentLanguages, 918 + omitViaField, 916 919 }, 917 920 { 918 921 highResolutionImages: ax.features.enabled( ··· 1925 1928 'playlistUri' in video && ( 1926 1929 <View style={[a.relative, a.mt_lg]}> 1927 1930 <VideoEmbedRedraft 1928 - blobRef={video.pendingPublish?.blobRef as any} 1931 + blobRef={video.pendingPublish?.blobRef} 1929 1932 playlistUri={video.playlistUri} 1930 1933 aspectRatio={video.redraftDimensions} 1931 1934 onRemove={clearVideo}