Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

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

at 3814f6b46054e082a4e28f04b257a9af4b3e7fd9 165 lines 5.2 kB view raw
1import React from 'react' 2 3import {type AppLanguage} from '#/locale/languages' 4import * as persisted from '#/state/persisted' 5 6type SetStateCb = ( 7 s: persisted.Schema['languagePrefs'], 8) => persisted.Schema['languagePrefs'] 9type StateContext = persisted.Schema['languagePrefs'] 10type ApiContext = { 11 setPrimaryLanguage: (code2: string) => void 12 setPostLanguage: (commaSeparatedLangCodes: string) => void 13 setContentLanguage: (code2: string) => void 14 toggleContentLanguage: (code2: string) => void 15 togglePostLanguage: (code2: string) => void 16 savePostLanguageToHistory: () => void 17 setAppLanguage: (code2: AppLanguage) => void 18} 19 20const stateContext = React.createContext<StateContext>( 21 persisted.defaults.languagePrefs, 22) 23stateContext.displayName = 'LanguagePrefsStateContext' 24const apiContext = React.createContext<ApiContext>({ 25 setPrimaryLanguage: (_: string) => {}, 26 setPostLanguage: (_: string) => {}, 27 setContentLanguage: (_: string) => {}, 28 toggleContentLanguage: (_: string) => {}, 29 togglePostLanguage: (_: string) => {}, 30 savePostLanguageToHistory: () => {}, 31 setAppLanguage: (_: AppLanguage) => {}, 32}) 33apiContext.displayName = 'LanguagePrefsApiContext' 34 35export function Provider({children}: React.PropsWithChildren<{}>) { 36 const [state, setState] = React.useState(persisted.get('languagePrefs')) 37 38 const setStateWrapped = React.useCallback( 39 (fn: SetStateCb) => { 40 const s = fn(persisted.get('languagePrefs')) 41 setState(s) 42 persisted.write('languagePrefs', s) 43 }, 44 [setState], 45 ) 46 47 React.useEffect(() => { 48 return persisted.onUpdate('languagePrefs', nextLanguagePrefs => { 49 setState(nextLanguagePrefs) 50 }) 51 }, [setStateWrapped]) 52 53 const api = React.useMemo( 54 () => ({ 55 setPrimaryLanguage(code2: string) { 56 setStateWrapped(s => ({...s, primaryLanguage: code2})) 57 }, 58 setPostLanguage(commaSeparatedLangCodes: string) { 59 setStateWrapped(s => ({...s, postLanguage: commaSeparatedLangCodes})) 60 }, 61 setContentLanguage(code2: string) { 62 setStateWrapped(s => ({...s, contentLanguages: [code2]})) 63 }, 64 toggleContentLanguage(code2: string) { 65 setStateWrapped(s => { 66 const exists = s.contentLanguages.includes(code2) 67 const next = exists 68 ? s.contentLanguages.filter(lang => lang !== code2) 69 : s.contentLanguages.concat(code2) 70 return { 71 ...s, 72 contentLanguages: next, 73 } 74 }) 75 }, 76 togglePostLanguage(code2: string) { 77 setStateWrapped(s => { 78 const exists = hasPostLanguage(state.postLanguage, code2) 79 let next = s.postLanguage 80 81 if (exists) { 82 next = toPostLanguages(s.postLanguage) 83 .filter(lang => lang !== code2) 84 .join(',') 85 } else { 86 // sort alphabetically for deterministic comparison in context menu 87 next = toPostLanguages(s.postLanguage) 88 .concat([code2]) 89 .sort((a, b) => a.localeCompare(b)) 90 .join(',') 91 } 92 93 return { 94 ...s, 95 postLanguage: next, 96 } 97 }) 98 }, 99 /** 100 * Saves whatever language codes are currently selected into a history array, 101 * which is then used to populate the language selector menu. 102 */ 103 savePostLanguageToHistory() { 104 // filter out duplicate `this.postLanguage` if exists, and prepend 105 // value to start of array 106 setStateWrapped(s => ({ 107 ...s, 108 postLanguageHistory: [s.postLanguage] 109 .concat( 110 s.postLanguageHistory.filter( 111 commaSeparatedLangCodes => 112 commaSeparatedLangCodes !== s.postLanguage, 113 ), 114 ) 115 .slice(0, 6), 116 })) 117 }, 118 setAppLanguage(code2: AppLanguage) { 119 setStateWrapped(s => ({...s, appLanguage: code2})) 120 }, 121 }), 122 [state, setStateWrapped], 123 ) 124 125 return ( 126 <stateContext.Provider value={state}> 127 <apiContext.Provider value={api}>{children}</apiContext.Provider> 128 </stateContext.Provider> 129 ) 130} 131 132export function useLanguagePrefs() { 133 return React.useContext(stateContext) 134} 135 136export function useLanguagePrefsApi() { 137 return React.useContext(apiContext) 138} 139 140export function getContentLanguages() { 141 return persisted.get('languagePrefs').contentLanguages 142} 143 144/** 145 * Be careful with this. It's used for the PWI home screen so that users can 146 * select a UI language and have it apply to the fetched Discover feed. 147 * 148 * We only support BCP-47 two-letter codes here, hence the split. 149 */ 150export function getAppLanguageAsContentLanguage() { 151 return persisted.get('languagePrefs').appLanguage.split('-')[0] 152} 153 154export function toPostLanguages(postLanguage: string): string[] { 155 // filter out empty strings if exist 156 return postLanguage.split(',').filter(Boolean) 157} 158 159export function fromPostLanguages(languages: string[]): string { 160 return languages.filter(Boolean).join(',') 161} 162 163export function hasPostLanguage(postLanguage: string, code2: string): boolean { 164 return toPostLanguages(postLanguage).includes(code2) 165}