Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

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

Merge branch 'main' of github.com:bluesky-social/social-app into main

+177 -80
+1 -1
lingui.config.js
··· 1 1 /** @type {import('@lingui/conf').LinguiConfig} */ 2 2 module.exports = { 3 - locales: ['en', 'cs', 'fr', 'hi', 'es'], 3 + locales: ['en', 'hi'], 4 4 catalogs: [ 5 5 { 6 6 path: '<rootDir>/src/locale/locales/{locale}/messages',
-3
src/locale/i18n.ts
··· 7 7 8 8 export const locales = { 9 9 en: 'English', 10 - cs: 'Česky', 11 - fr: 'Français', 12 10 hi: 'हिंदी', 13 - es: 'Español', 14 11 } 15 12 export const defaultLocale = 'en' 16 13
-3
src/locale/i18n.web.ts
··· 5 5 6 6 export const locales = { 7 7 en: 'English', 8 - cs: 'Česky', 9 - fr: 'Français', 10 8 hi: 'हिंदी', 11 - es: 'Español', 12 9 } 13 10 export const defaultLocale = 'en' 14 11
+22 -4
src/locale/locales/en/messages.po
··· 827 827 msgstr "" 828 828 829 829 #: src/view/screens/Feeds.tsx:475 830 + #: src/view/screens/Profile.tsx:164 830 831 #: src/view/shell/bottom-bar/BottomBar.tsx:168 831 832 #: src/view/shell/desktop/LeftNav.tsx:341 832 833 #: src/view/shell/Drawer.tsx:463 ··· 1132 1133 msgid "Liked by" 1133 1134 msgstr "" 1134 1135 1136 + #: src/view/screens/Profile.tsx:163 1137 + msgid "Likes" 1138 + msgstr "" 1139 + 1135 1140 #: src/view/screens/Moderation.tsx:203 1136 1141 #~ msgid "Limit the visibility of my account" 1137 1142 #~ msgstr "" ··· 1148 1153 msgid "List Name" 1149 1154 msgstr "" 1150 1155 1156 + #: src/view/screens/Profile.tsx:165 1151 1157 #: src/view/shell/desktop/LeftNav.tsx:381 1152 1158 #: src/view/shell/Drawer.tsx:479 1153 1159 #: src/view/shell/Drawer.tsx:480 ··· 1191 1197 msgid "Make sure this is where you intend to go!" 1192 1198 msgstr "" 1193 1199 1200 + #: src/view/screens/Profile.tsx:162 1201 + msgid "Media" 1202 + msgstr "" 1203 + 1194 1204 #: src/view/screens/Search/Search.tsx:503 1195 1205 msgid "Menu" 1196 1206 msgstr "" ··· 1534 1544 msgid "Post not found" 1535 1545 msgstr "" 1536 1546 1547 + #: src/view/screens/Profile.tsx:160 1548 + msgid "Posts" 1549 + msgstr "" 1550 + 1537 1551 #: src/view/com/modals/LinkWarning.tsx:44 1538 1552 msgid "Potentially Misleading Link" 1539 1553 msgstr "" ··· 1647 1661 #: src/view/com/modals/ListAddRemoveUsers.tsx:199 1648 1662 #: src/view/com/modals/UserAddRemoveLists.tsx:136 1649 1663 msgid "Removed from list" 1664 + msgstr "" 1665 + 1666 + #: src/view/screens/Profile.tsx:161 1667 + msgid "Replies" 1650 1668 msgstr "" 1651 1669 1652 1670 #: src/view/screens/PreferencesHomeFeed.tsx:135 ··· 1951 1969 msgid "Sign into" 1952 1970 msgstr "" 1953 1971 1954 - #: src/view/com/modals/SwitchAccount.tsx:60 1955 - #: src/view/com/modals/SwitchAccount.tsx:63 1972 + #: src/view/com/modals/SwitchAccount.tsx:64 1973 + #: src/view/com/modals/SwitchAccount.tsx:67 1956 1974 msgid "Sign out" 1957 1975 msgstr "" 1958 1976 ··· 2020 2038 msgid "Support" 2021 2039 msgstr "" 2022 2040 2023 - #: src/view/com/modals/SwitchAccount.tsx:111 2041 + #: src/view/com/modals/SwitchAccount.tsx:115 2024 2042 msgid "Switch Account" 2025 2043 msgstr "" 2026 2044 ··· 2421 2439 msgid "Your posts, likes, and blocks are public. Mutes are private." 2422 2440 msgstr "" 2423 2441 2424 - #: src/view/com/modals/SwitchAccount.tsx:78 2442 + #: src/view/com/modals/SwitchAccount.tsx:82 2425 2443 msgid "Your profile" 2426 2444 msgstr "" 2427 2445
+23 -5
src/locale/locales/hi/messages.po
··· 823 823 msgstr "प्रतिक्रिया" 824 824 825 825 #: src/view/screens/Feeds.tsx:475 826 + #: src/view/screens/Profile.tsx:164 826 827 #: src/view/shell/bottom-bar/BottomBar.tsx:168 827 828 #: src/view/shell/desktop/LeftNav.tsx:341 828 829 #: src/view/shell/Drawer.tsx:463 ··· 1124 1125 msgid "Liked by" 1125 1126 msgstr "इन यूजर ने लाइक किया है" 1126 1127 1128 + #: src/view/screens/Profile.tsx:163 1129 + msgid "Likes" 1130 + msgstr "" 1131 + 1127 1132 #: src/view/screens/Moderation.tsx:203 1128 1133 #~ msgid "Limit the visibility of my account" 1129 1134 #~ msgstr "" ··· 1140 1145 msgid "List Name" 1141 1146 msgstr "सूची का नाम" 1142 1147 1148 + #: src/view/screens/Profile.tsx:165 1143 1149 #: src/view/shell/desktop/LeftNav.tsx:381 1144 1150 #: src/view/shell/Drawer.tsx:479 1145 1151 #: src/view/shell/Drawer.tsx:480 ··· 1183 1189 msgid "Make sure this is where you intend to go!" 1184 1190 msgstr "यह सुनिश्चित करने के लिए कि आप कहाँ जाना चाहते हैं!" 1185 1191 1192 + #: src/view/screens/Profile.tsx:162 1193 + msgid "Media" 1194 + msgstr "" 1195 + 1186 1196 #: src/view/screens/Search/Search.tsx:503 1187 1197 msgid "Menu" 1188 1198 msgstr "मेनू" ··· 1525 1535 #: src/view/com/post-thread/PostThread.tsx:427 1526 1536 msgid "Post not found" 1527 1537 msgstr "पोस्ट नहीं मिला" 1538 + 1539 + #: src/view/screens/Profile.tsx:160 1540 + msgid "Posts" 1541 + msgstr "" 1528 1542 1529 1543 #: src/view/com/modals/LinkWarning.tsx:44 1530 1544 msgid "Potentially Misleading Link" ··· 1641 1655 msgid "Removed from list" 1642 1656 msgstr "" 1643 1657 1658 + #: src/view/screens/Profile.tsx:161 1659 + msgid "Replies" 1660 + msgstr "" 1661 + 1644 1662 #: src/view/screens/PreferencesHomeFeed.tsx:135 1645 1663 msgid "Reply Filters" 1646 1664 msgstr "फिल्टर" ··· 1943 1961 msgid "Sign into" 1944 1962 msgstr "साइन इन करें" 1945 1963 1946 - #: src/view/com/modals/SwitchAccount.tsx:60 1947 - #: src/view/com/modals/SwitchAccount.tsx:63 1964 + #: src/view/com/modals/SwitchAccount.tsx:64 1965 + #: src/view/com/modals/SwitchAccount.tsx:67 1948 1966 msgid "Sign out" 1949 1967 msgstr "साइन आउट" 1950 1968 ··· 1993 2011 1994 2012 #: src/view/com/modals/AppealLabel.tsx:101 1995 2013 msgid "Submit" 1996 - msgstr ">>>>>>> cb8a33b6 (Fix translations)" 2014 + msgstr "" 1997 2015 1998 2016 #: src/view/screens/ProfileList.tsx:597 1999 2017 msgid "Subscribe" ··· 2012 2030 msgid "Support" 2013 2031 msgstr "सहायता" 2014 2032 2015 - #: src/view/com/modals/SwitchAccount.tsx:111 2033 + #: src/view/com/modals/SwitchAccount.tsx:115 2016 2034 msgid "Switch Account" 2017 2035 msgstr "खाते बदलें" 2018 2036 ··· 2413 2431 msgid "Your posts, likes, and blocks are public. Mutes are private." 2414 2432 msgstr "आपकी पोस्ट, पसंद और ब्लॉक सार्वजनिक हैं। म्यूट निजी हैं।।" 2415 2433 2416 - #: src/view/com/modals/SwitchAccount.tsx:78 2434 + #: src/view/com/modals/SwitchAccount.tsx:82 2417 2435 msgid "Your profile" 2418 2436 msgstr "आपकी प्रोफ़ाइल" 2419 2437
+118 -55
src/state/queries/post-feed.ts
··· 1 - import {useCallback, useEffect} from 'react' 1 + import React, {useCallback, useEffect} from 'react' 2 2 import { 3 3 AppBskyFeedDefs, 4 4 AppBskyFeedPost, ··· 97 97 const feedTuners = useFeedTuners(feedDesc) 98 98 const moderationOpts = useModerationOpts() 99 99 const enabled = opts?.enabled !== false && Boolean(moderationOpts) 100 + const lastRun = React.useRef<{ 101 + data: InfiniteData<FeedPageUnselected> 102 + args: typeof selectArgs 103 + result: InfiniteData<FeedPage> 104 + } | null>(null) 105 + 106 + // Make sure this doesn't invalidate unless really needed. 107 + const selectArgs = React.useMemo( 108 + () => ({ 109 + feedTuners, 110 + disableTuner: params?.disableTuner, 111 + moderationOpts, 112 + ignoreFilterFor: opts?.ignoreFilterFor, 113 + }), 114 + [feedTuners, params?.disableTuner, moderationOpts, opts?.ignoreFilterFor], 115 + ) 100 116 101 117 const query = useInfiniteQuery< 102 118 FeedPageUnselected, ··· 147 163 : undefined, 148 164 select: useCallback( 149 165 (data: InfiniteData<FeedPageUnselected, RQPageParam>) => { 150 - const tuner = params?.disableTuner 166 + // If the selection depends on some data, that data should 167 + // be included in the selectArgs object and read here. 168 + const {feedTuners, disableTuner, moderationOpts, ignoreFilterFor} = 169 + selectArgs 170 + 171 + const tuner = disableTuner 151 172 ? new NoopFeedTuner() 152 173 : new FeedTuner(feedTuners) 153 - return { 174 + 175 + // Keep track of the last run and whether we can reuse 176 + // some already selected pages from there. 177 + let reusedPages = [] 178 + if (lastRun.current) { 179 + const { 180 + data: lastData, 181 + args: lastArgs, 182 + result: lastResult, 183 + } = lastRun.current 184 + let canReuse = true 185 + for (let key in selectArgs) { 186 + if (selectArgs.hasOwnProperty(key)) { 187 + if ((selectArgs as any)[key] !== (lastArgs as any)[key]) { 188 + // Can't do reuse anything if any input has changed. 189 + canReuse = false 190 + break 191 + } 192 + } 193 + } 194 + if (canReuse) { 195 + for (let i = 0; i < data.pages.length; i++) { 196 + if (data.pages[i] && lastData.pages[i] === data.pages[i]) { 197 + reusedPages.push(lastResult.pages[i]) 198 + // Keep the tuner in sync so that the end result is deterministic. 199 + tuner.tune(lastData.pages[i].feed) 200 + continue 201 + } 202 + // Stop as soon as pages stop matching up. 203 + break 204 + } 205 + } 206 + } 207 + 208 + const result = { 154 209 pageParams: data.pageParams, 155 - pages: data.pages.map(page => ({ 156 - api: page.api, 157 - tuner, 158 - cursor: page.cursor, 159 - slices: tuner 160 - .tune(page.feed) 161 - .map(slice => { 162 - const moderations = slice.items.map(item => 163 - moderatePost(item.post, moderationOpts!), 164 - ) 210 + pages: [ 211 + ...reusedPages, 212 + ...data.pages.slice(reusedPages.length).map(page => ({ 213 + api: page.api, 214 + tuner, 215 + cursor: page.cursor, 216 + slices: tuner 217 + .tune(page.feed) 218 + .map(slice => { 219 + const moderations = slice.items.map(item => 220 + moderatePost(item.post, moderationOpts!), 221 + ) 165 222 166 - // apply moderation filter 167 - for (let i = 0; i < slice.items.length; i++) { 168 - if ( 169 - moderations[i]?.content.filter && 170 - slice.items[i].post.author.did !== opts?.ignoreFilterFor 171 - ) { 172 - return undefined 223 + // apply moderation filter 224 + for (let i = 0; i < slice.items.length; i++) { 225 + if ( 226 + moderations[i]?.content.filter && 227 + slice.items[i].post.author.did !== ignoreFilterFor 228 + ) { 229 + return undefined 230 + } 173 231 } 174 - } 175 232 176 - return { 177 - _reactKey: slice._reactKey, 178 - rootUri: slice.rootItem.post.uri, 179 - isThread: 180 - slice.items.length > 1 && 181 - slice.items.every( 182 - item => 183 - item.post.author.did === slice.items[0].post.author.did, 184 - ), 185 - items: slice.items 186 - .map((item, i) => { 187 - if ( 188 - AppBskyFeedPost.isRecord(item.post.record) && 189 - AppBskyFeedPost.validateRecord(item.post.record).success 190 - ) { 191 - return { 192 - _reactKey: `${slice._reactKey}-${i}`, 193 - uri: item.post.uri, 194 - post: item.post, 195 - record: item.post.record, 196 - reason: 197 - i === 0 && slice.source 198 - ? slice.source 199 - : item.reason, 200 - moderation: moderations[i], 233 + return { 234 + _reactKey: slice._reactKey, 235 + rootUri: slice.rootItem.post.uri, 236 + isThread: 237 + slice.items.length > 1 && 238 + slice.items.every( 239 + item => 240 + item.post.author.did === 241 + slice.items[0].post.author.did, 242 + ), 243 + items: slice.items 244 + .map((item, i) => { 245 + if ( 246 + AppBskyFeedPost.isRecord(item.post.record) && 247 + AppBskyFeedPost.validateRecord(item.post.record) 248 + .success 249 + ) { 250 + return { 251 + _reactKey: `${slice._reactKey}-${i}`, 252 + uri: item.post.uri, 253 + post: item.post, 254 + record: item.post.record, 255 + reason: 256 + i === 0 && slice.source 257 + ? slice.source 258 + : item.reason, 259 + moderation: moderations[i], 260 + } 201 261 } 202 - } 203 - return undefined 204 - }) 205 - .filter(Boolean) as FeedPostSliceItem[], 206 - } 207 - }) 208 - .filter(Boolean) as FeedPostSlice[], 209 - })), 262 + return undefined 263 + }) 264 + .filter(Boolean) as FeedPostSliceItem[], 265 + } 266 + }) 267 + .filter(Boolean) as FeedPostSlice[], 268 + })), 269 + ], 210 270 } 271 + // Save for memoization. 272 + lastRun.current = {data, result, args: selectArgs} 273 + return result 211 274 }, 212 - [feedTuners, params?.disableTuner, moderationOpts, opts?.ignoreFilterFor], 275 + [selectArgs /* Don't change. Everything needs to go into selectArgs. */], 213 276 ), 214 277 }) 215 278
+6 -2
src/view/com/modals/SwitchAccount.tsx
··· 20 20 import {useLingui} from '@lingui/react' 21 21 import {useSession, useSessionApi, SessionAccount} from '#/state/session' 22 22 import {useProfileQuery} from '#/state/queries/profile' 23 + import {useCloseAllActiveElements} from '#/state/util' 23 24 24 25 export const snapPoints = ['40%', '90%'] 25 26 ··· 32 33 const {data: profile} = useProfileQuery({did: account.did}) 33 34 const isCurrentAccount = account.did === currentAccount?.did 34 35 const {onPressSwitchAccount} = useAccountSwitcher() 36 + const closeAllActiveElements = useCloseAllActiveElements() 35 37 36 38 const onPressSignout = React.useCallback(() => { 37 39 track('Settings:SignOutButtonClicked') 38 - logout() 39 - }, [track, logout]) 40 + closeAllActiveElements() 41 + // needs to be in timeout or the modal re-opens 42 + setTimeout(() => logout(), 0) 43 + }, [track, logout, closeAllActiveElements]) 40 44 41 45 const contents = ( 42 46 <View style={[pal.view, styles.linkCard]}>
+7 -7
src/view/screens/Profile.tsx
··· 157 157 const showListsTab = hasSession && (isMe || extraInfoQuery.data?.hasLists) 158 158 const sectionTitles = useMemo<string[]>(() => { 159 159 return [ 160 - 'Posts', 161 - showRepliesTab ? 'Replies' : undefined, 162 - 'Media', 163 - showLikesTab ? 'Likes' : undefined, 164 - showFeedsTab ? 'Feeds' : undefined, 165 - showListsTab ? 'Lists' : undefined, 160 + _(msg`Posts`), 161 + showRepliesTab ? _(msg`Replies`) : undefined, 162 + _(msg`Media`), 163 + showLikesTab ? _(msg`Likes`) : undefined, 164 + showFeedsTab ? _(msg`Feeds`) : undefined, 165 + showListsTab ? _(msg`Lists`) : undefined, 166 166 ].filter(Boolean) as string[] 167 - }, [showRepliesTab, showLikesTab, showFeedsTab, showListsTab]) 167 + }, [showRepliesTab, showLikesTab, showFeedsTab, showListsTab, _]) 168 168 169 169 let nextIndex = 0 170 170 const postsIndex = nextIndex++