tangled
alpha
login
or
join now
robinwobin.dev
/
witchsky.app
forked from
jollywhoppers.com/witchsky.app
0
fork
atom
Bluesky app fork with some witchin' additions 💫
0
fork
atom
overview
issues
pulls
pipelines
remove hide follow notification deer tweak
Daniela Henkel
6 months ago
f5611275
d0b6ffd0
+42
-151
8 changed files
expand all
collapse all
unified
split
src
screens
Settings
DeerSettings.tsx
state
persisted
schema.ts
preferences
hide-follow-notifications.tsx
index.tsx
queries
notifications
feed.ts
unread.tsx
util.ts
view
screens
DebugMod.tsx
-26
src/screens/Settings/DeerSettings.tsx
···
38
38
useSetHideFeedsPromoTab,
39
39
} from '#/state/preferences/hide-feeds-promo-tab'
40
40
import {
41
41
-
useHideFollowNotifications,
42
42
-
useSetHideFollowNotifications,
43
43
-
} from '#/state/preferences/hide-follow-notifications'
44
44
-
import {
45
41
useHighQualityImages,
46
42
useSetHighQualityImages,
47
43
} from '#/state/preferences/high-quality-images'
···
70
66
import * as Dialog from '#/components/Dialog'
71
67
import * as Toggle from '#/components/forms/Toggle'
72
68
import {Atom_Stroke2_Corner0_Rounded as DeerIcon} from '#/components/icons/Atom'
73
73
-
import {Bell_Stroke2_Corner0_Rounded as BellIcon} from '#/components/icons/Bell'
74
69
import {Eye_Stroke2_Corner0_Rounded as VisibilityIcon} from '#/components/icons/Eye'
75
70
import {Lab_Stroke2_Corner0_Rounded as BeakerIcon} from '#/components/icons/Lab'
76
71
import {PaintRoller_Stroke2_Corner2_Rounded as PaintRollerIcon} from '#/components/icons/PaintRoller'
···
228
223
229
224
const highQualityImages = useHighQualityImages()
230
225
const setHighQualityImages = useSetHighQualityImages()
231
231
-
232
232
-
const hideFollowNotifications = useHideFollowNotifications()
233
233
-
const setHideFollowNotifications = useSetHideFollowNotifications()
234
226
235
227
const hideFeedsPromoTab = useHideFeedsPromoTab()
236
228
const setHideFeedsPromoTab = useSetHideFeedsPromoTab()
···
509
501
<Trans>
510
502
Hide "Feeds ✨" tab when only one feed is selected
511
503
</Trans>
512
512
-
</Toggle.LabelText>
513
513
-
<Toggle.Platform />
514
514
-
</Toggle.Item>
515
515
-
</SettingsList.Group>
516
516
-
517
517
-
<SettingsList.Group contentContainerStyle={[a.gap_sm]}>
518
518
-
<SettingsList.ItemIcon icon={BellIcon} />
519
519
-
<SettingsList.ItemText>
520
520
-
<Trans>Notification Filters</Trans>
521
521
-
</SettingsList.ItemText>
522
522
-
<Toggle.Item
523
523
-
name="hide_follow_notifications"
524
524
-
label={_(msg`Hide follow notifications`)}
525
525
-
value={hideFollowNotifications ?? false}
526
526
-
onChange={value => setHideFollowNotifications(value)}
527
527
-
style={[a.w_full]}>
528
528
-
<Toggle.LabelText style={[a.flex_1]}>
529
529
-
<Trans>Hide follow notifications</Trans>
530
504
</Toggle.LabelText>
531
505
<Toggle.Platform />
532
506
</Toggle.Item>
-2
src/state/persisted/schema.ts
···
131
131
noAppLabelers: z.boolean().optional(),
132
132
noDiscoverFallback: z.boolean().optional(),
133
133
repostCarouselEnabled: z.boolean().optional(),
134
134
-
hideFollowNotifications: z.boolean().optional(),
135
134
constellationInstance: z.string().optional(),
136
135
showLinkInHandle: z.boolean().optional(),
137
136
hideFeedsPromoTab: z.boolean().optional(),
···
202
201
noAppLabelers: false,
203
202
noDiscoverFallback: false,
204
203
repostCarouselEnabled: false,
205
205
-
hideFollowNotifications: false,
206
204
constellationInstance: 'https://constellation.microcosm.blue/',
207
205
showLinkInHandle: false,
208
206
hideFeedsPromoTab: false,
-52
src/state/preferences/hide-follow-notifications.tsx
···
1
1
-
import React from 'react'
2
2
-
3
3
-
import * as persisted from '#/state/persisted'
4
4
-
5
5
-
type StateContext = persisted.Schema['hideFollowNotifications']
6
6
-
type SetContext = (v: persisted.Schema['hideFollowNotifications']) => void
7
7
-
8
8
-
const stateContext = React.createContext<StateContext>(
9
9
-
persisted.defaults.hideFollowNotifications,
10
10
-
)
11
11
-
const setContext = React.createContext<SetContext>(
12
12
-
(_: persisted.Schema['hideFollowNotifications']) => {},
13
13
-
)
14
14
-
15
15
-
export function Provider({children}: React.PropsWithChildren<{}>) {
16
16
-
const [state, setState] = React.useState(
17
17
-
persisted.get('hideFollowNotifications'),
18
18
-
)
19
19
-
20
20
-
const setStateWrapped = React.useCallback(
21
21
-
(hideFollowNotifications: persisted.Schema['hideFollowNotifications']) => {
22
22
-
setState(hideFollowNotifications)
23
23
-
persisted.write('hideFollowNotifications', hideFollowNotifications)
24
24
-
},
25
25
-
[setState],
26
26
-
)
27
27
-
28
28
-
React.useEffect(() => {
29
29
-
return persisted.onUpdate(
30
30
-
'hideFollowNotifications',
31
31
-
nextHideFollowNotifications => {
32
32
-
setState(nextHideFollowNotifications)
33
33
-
},
34
34
-
)
35
35
-
}, [setStateWrapped])
36
36
-
37
37
-
return (
38
38
-
<stateContext.Provider value={state}>
39
39
-
<setContext.Provider value={setStateWrapped}>
40
40
-
{children}
41
41
-
</setContext.Provider>
42
42
-
</stateContext.Provider>
43
43
-
)
44
44
-
}
45
45
-
46
46
-
export function useHideFollowNotifications() {
47
47
-
return React.useContext(stateContext)
48
48
-
}
49
49
-
50
50
-
export function useSetHideFollowNotifications() {
51
51
-
return React.useContext(setContext)
52
52
-
}
+39
-42
src/state/preferences/index.tsx
···
11
11
import {Provider as GoLinksProvider} from './go-links-enabled'
12
12
import {Provider as HiddenPostsProvider} from './hidden-posts'
13
13
import {Provider as HideFeedsPromoTabProvider} from './hide-feeds-promo-tab'
14
14
-
import {Provider as FollowNotificationsProvider} from './hide-follow-notifications'
15
14
import {Provider as HighQualityImagesProvider} from './high-quality-images'
16
15
import {Provider as InAppBrowserProvider} from './in-app-browser'
17
16
import {Provider as KawaiiProvider} from './kawaii'
···
51
50
<AltTextRequiredProvider>
52
51
<GoLinksProvider>
53
52
<NoAppLabelersProvider>
54
54
-
<FollowNotificationsProvider>
55
55
-
<DirectFetchRecordsProvider>
56
56
-
<ConstellationProvider>
57
57
-
<ConstellationInstanceProvider>
58
58
-
<DeerVerificationProvider>
59
59
-
<NoDiscoverProvider>
60
60
-
<ShowLinkInHandleProvider>
61
61
-
<LargeAltBadgeProvider>
62
62
-
<ExternalEmbedsProvider>
63
63
-
<HiddenPostsProvider>
64
64
-
<HighQualityImagesProvider>
65
65
-
<InAppBrowserProvider>
66
66
-
<DisableHapticsProvider>
67
67
-
<AutoplayProvider>
68
68
-
<UsedStarterPacksProvider>
69
69
-
<SubtitlesProvider>
70
70
-
<TrendingSettingsProvider>
71
71
-
<RepostCarouselProvider>
72
72
-
<KawaiiProvider>
73
73
-
<HideFeedsPromoTabProvider>
74
74
-
{children}
75
75
-
</HideFeedsPromoTabProvider>
76
76
-
</KawaiiProvider>
77
77
-
</RepostCarouselProvider>
78
78
-
</TrendingSettingsProvider>
79
79
-
</SubtitlesProvider>
80
80
-
</UsedStarterPacksProvider>
81
81
-
</AutoplayProvider>
82
82
-
</DisableHapticsProvider>
83
83
-
</InAppBrowserProvider>
84
84
-
</HighQualityImagesProvider>
85
85
-
</HiddenPostsProvider>
86
86
-
</ExternalEmbedsProvider>
87
87
-
</LargeAltBadgeProvider>
88
88
-
</ShowLinkInHandleProvider>
89
89
-
</NoDiscoverProvider>
90
90
-
</DeerVerificationProvider>
91
91
-
</ConstellationInstanceProvider>
92
92
-
</ConstellationProvider>
93
93
-
</DirectFetchRecordsProvider>
94
94
-
</FollowNotificationsProvider>
53
53
+
<DirectFetchRecordsProvider>
54
54
+
<ConstellationProvider>
55
55
+
<ConstellationInstanceProvider>
56
56
+
<DeerVerificationProvider>
57
57
+
<NoDiscoverProvider>
58
58
+
<ShowLinkInHandleProvider>
59
59
+
<LargeAltBadgeProvider>
60
60
+
<ExternalEmbedsProvider>
61
61
+
<HiddenPostsProvider>
62
62
+
<HighQualityImagesProvider>
63
63
+
<InAppBrowserProvider>
64
64
+
<DisableHapticsProvider>
65
65
+
<AutoplayProvider>
66
66
+
<UsedStarterPacksProvider>
67
67
+
<SubtitlesProvider>
68
68
+
<TrendingSettingsProvider>
69
69
+
<RepostCarouselProvider>
70
70
+
<KawaiiProvider>
71
71
+
<HideFeedsPromoTabProvider>
72
72
+
{children}
73
73
+
</HideFeedsPromoTabProvider>
74
74
+
</KawaiiProvider>
75
75
+
</RepostCarouselProvider>
76
76
+
</TrendingSettingsProvider>
77
77
+
</SubtitlesProvider>
78
78
+
</UsedStarterPacksProvider>
79
79
+
</AutoplayProvider>
80
80
+
</DisableHapticsProvider>
81
81
+
</InAppBrowserProvider>
82
82
+
</HighQualityImagesProvider>
83
83
+
</HiddenPostsProvider>
84
84
+
</ExternalEmbedsProvider>
85
85
+
</LargeAltBadgeProvider>
86
86
+
</ShowLinkInHandleProvider>
87
87
+
</NoDiscoverProvider>
88
88
+
</DeerVerificationProvider>
89
89
+
</ConstellationInstanceProvider>
90
90
+
</ConstellationProvider>
91
91
+
</DirectFetchRecordsProvider>
95
92
</NoAppLabelersProvider>
96
93
</GoLinksProvider>
97
94
</AltTextRequiredProvider>
-3
src/state/queries/notifications/feed.ts
···
32
32
useQueryClient,
33
33
} from '@tanstack/react-query'
34
34
35
35
-
import {useHideFollowNotifications} from '#/state/preferences/hide-follow-notifications'
36
35
import {useModerationOpts} from '#/state/preferences/moderation-opts'
37
36
import {STALE} from '#/state/queries'
38
37
import {useAgent} from '#/state/session'
···
64
63
const agent = useAgent()
65
64
const queryClient = useQueryClient()
66
65
const moderationOpts = useModerationOpts()
67
67
-
const hideFollowNotifications = useHideFollowNotifications()
68
66
const unreads = useUnreadNotificationsApi()
69
67
const enabled = opts.enabled !== false
70
68
const filter = opts.filter
···
113
111
cursor: pageParam,
114
112
queryClient,
115
113
moderationOpts,
116
116
-
hideFollowNotifications,
117
114
fetchAdditionalData: true,
118
115
reasons,
119
116
})
+1
-10
src/state/queries/notifications/unread.tsx
···
10
10
import BroadcastChannel from '#/lib/broadcast'
11
11
import {resetBadgeCount} from '#/lib/notifications/notifications'
12
12
import {logger} from '#/logger'
13
13
-
import {useHideFollowNotifications} from '#/state/preferences/hide-follow-notifications'
14
13
import {useAgent, useSession} from '#/state/session'
15
14
import {useModerationOpts} from '../../preferences/moderation-opts'
16
15
import {truncateAndInvalidate} from '../util'
···
50
49
const agent = useAgent()
51
50
const queryClient = useQueryClient()
52
51
const moderationOpts = useModerationOpts()
53
53
-
const hideFollowNotifications = useHideFollowNotifications()
54
52
55
53
const [numUnread, setNumUnread] = React.useState('')
56
54
···
157
155
limit: 40,
158
156
queryClient,
159
157
moderationOpts,
160
160
-
hideFollowNotifications,
161
158
reasons: [],
162
159
163
160
// only fetch subjects when the page is going to be used
···
206
203
}
207
204
},
208
205
}
209
209
-
}, [
210
210
-
setNumUnread,
211
211
-
queryClient,
212
212
-
moderationOpts,
213
213
-
hideFollowNotifications,
214
214
-
agent,
215
215
-
])
206
206
+
}, [setNumUnread, queryClient, moderationOpts, agent])
216
207
checkUnreadRef.current = api.checkUnread
217
208
218
209
return (
+1
-7
src/state/queries/notifications/util.ts
···
43
43
limit,
44
44
queryClient,
45
45
moderationOpts,
46
46
-
hideFollowNotifications,
47
46
fetchAdditionalData,
48
47
reasons,
49
48
}: {
···
52
51
limit: number
53
52
queryClient: QueryClient
54
53
moderationOpts: ModerationOpts | undefined
55
55
-
hideFollowNotifications: boolean | undefined
56
54
fetchAdditionalData: boolean
57
55
reasons: string[]
58
56
}): Promise<{
···
69
67
70
68
// filter out notifs by mod rules
71
69
const notifs = res.data.notifications.filter(
72
72
-
notif => !shouldFilterNotif(notif, moderationOpts, hideFollowNotifications),
70
70
+
notif => !shouldFilterNotif(notif, moderationOpts),
73
71
)
74
72
75
73
// group notifications which are essentially similar (follows, likes on a post)
···
120
118
export function shouldFilterNotif(
121
119
notif: AppBskyNotificationListNotifications.Notification,
122
120
moderationOpts: ModerationOpts | undefined,
123
123
-
hideFollowNotifications: boolean | undefined,
124
121
): boolean {
125
122
const containsImperative = !!notif.author.labels?.some(labelIsHideableOffense)
126
123
if (containsImperative) {
127
127
-
return true
128
128
-
}
129
129
-
if (hideFollowNotifications && notif.reason == 'follow') {
130
124
return true
131
125
}
132
126
if (!moderationOpts) {
+1
-9
src/view/screens/DebugMod.tsx
···
24
24
type CommonNavigatorParams,
25
25
type NativeStackScreenProps,
26
26
} from '#/lib/routes/types'
27
27
-
import {useHideFollowNotifications} from '#/state/preferences/hide-follow-notifications'
28
27
import {useModerationOpts} from '#/state/preferences/moderation-opts'
29
28
import {moderationOptsOverrideContext} from '#/state/preferences/moderation-opts'
30
29
import {type FeedNotification} from '#/state/queries/notifications/types'
···
878
877
moderationOpts: ModerationOpts
879
878
}) {
880
879
const t = useTheme()
881
881
-
const hideFollowNotifications = useHideFollowNotifications()
882
882
-
if (
883
883
-
shouldFilterNotif(
884
884
-
notif.notification,
885
885
-
moderationOpts,
886
886
-
hideFollowNotifications,
887
887
-
)
888
888
-
) {
880
880
+
if (shouldFilterNotif(notif.notification, moderationOpts)) {
889
881
return (
890
882
<P style={[t.atoms.bg_contrast_25, a.px_lg, a.py_md]}>
891
883
Filtered from the feed