Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

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

at cope-settings-sync 130 lines 4.2 kB view raw
1import {useMemo} from 'react' 2import { 3 ageAssuranceRuleIDs as ids, 4 type AppBskyAgeassuranceDefs, 5 type AtpAgent, 6 getAgeAssuranceRegionConfig, 7 type ModerationPrefs, 8} from '@atproto/api' 9 10import {getAge} from '#/lib/strings/time' 11import {restrictChatSettings} from '#/state/queries/messages/restrictChatSettings' 12import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/moderation' 13import { 14 getDidFromAgentSession, 15 getOtherRequiredDataFromCache, 16 useAgeAssuranceDataContext, 17} from '#/ageAssurance/data' 18import {AgeAssuranceAccess} from '#/ageAssurance/types' 19import {type Geolocation, useGeolocation} from '#/geolocation' 20 21export const MIN_ACCESS_AGE = 1 22const FALLBACK_REGION_CONFIG: AppBskyAgeassuranceDefs.ConfigRegion = { 23 countryCode: '*', 24 regionCode: undefined, 25 minAccessAge: MIN_ACCESS_AGE, 26 rules: [ 27 { 28 $type: ids.IfDeclaredOverAge, 29 age: MIN_ACCESS_AGE, 30 access: AgeAssuranceAccess.Full, 31 }, 32 { 33 $type: ids.Default, 34 access: AgeAssuranceAccess.None, 35 }, 36 ], 37} 38 39/** 40 * Get age assurance region config based on geolocation, with fallback to 41 * app defaults if no region config is found. 42 * 43 * See {@link getAgeAssuranceRegionConfig} for the generic option, which can 44 * return undefined if the geolocation does not match any AA region. 45 */ 46export function getAgeAssuranceRegionConfigWithFallback( 47 config: AppBskyAgeassuranceDefs.Config, 48 geolocation: Geolocation, 49): AppBskyAgeassuranceDefs.ConfigRegion { 50 const region = getAgeAssuranceRegionConfig(config, { 51 countryCode: geolocation.countryCode ?? '', 52 regionCode: geolocation.regionCode, 53 }) 54 55 return region || FALLBACK_REGION_CONFIG 56} 57 58/** 59 * Hook to get the age assurance region config based on current geolocation. 60 * Does not fall-back to our app defaults. If no config is found, returns 61 * undefined, which indicates no regional age assurance rules apply. 62 */ 63export function useAgeAssuranceRegionConfig() { 64 const geolocation = useGeolocation() 65 const {config} = useAgeAssuranceDataContext() 66 return useMemo(() => { 67 if (!config) return 68 // use generic helper, we want to potentially return undefined 69 return getAgeAssuranceRegionConfig(config, { 70 countryCode: geolocation.countryCode ?? '', 71 regionCode: geolocation.regionCode, 72 }) 73 }, [config, geolocation]) 74} 75 76/** 77 * Hook to get the age assurance region config based on current geolocation. 78 * Falls back to our app defaults if no region config is found. 79 */ 80export function useAgeAssuranceRegionConfigWithFallback() { 81 return useAgeAssuranceRegionConfig() || FALLBACK_REGION_CONFIG 82} 83 84/** 85 * Some users may have erroneously set their birth date to the current date 86 * if one wasn't set on their account. We previously didn't do validation on 87 * the bday dialog, and it defaulted to the current date. This bug _has_ been 88 * seen in production, so we need to check for it where possible. 89 */ 90export function isLegacyBirthdateBug(birthDate: string) { 91 return ['2025', '2024', '2023'].includes((birthDate || '').slice(0, 4)) 92} 93 94/** 95 * Returns whether the date (converted to an age as a whole integer) is under 96 * the provided minimum age. 97 */ 98export function isUnderAge(birthDate: string, age: number) { 99 return getAge(new Date(birthDate)) < age 100} 101 102export function getBirthdateStringFromAge(age: number) { 103 const today = new Date() 104 return new Date( 105 today.getFullYear() - age, 106 today.getMonth(), 107 today.getDate() - 1, // set to day before to ensure age is reached 108 ).toISOString() 109} 110 111export const makeAgeRestrictedModerationPrefs = ( 112 prefs: ModerationPrefs, 113): ModerationPrefs => ({ 114 ...prefs, 115 adultContentEnabled: false, 116 labels: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES, 117}) 118 119/** 120 * Checks our cache of the actor's chat declaration record, and if it's not 121 * already restricted, restricts it. 122 */ 123export function maybeRestrictChatSettings({agent}: {agent: AtpAgent}) { 124 const did = getDidFromAgentSession(agent) 125 if (!did) return 126 const data = getOtherRequiredDataFromCache({did}) 127 // ...update the chat setting record if allowIncoming is not already 'none'. 128 if (data?.actorDeclaration?.allowIncoming === 'none') return 129 restrictChatSettings({agent, did}) 130}