Move invite-state to new persistence + context and replace the notifications with just showing uses in the modal (#1840)
This commit is contained in:
parent
74f8390f1d
commit
e75b2d508b
12 changed files with 137 additions and 259 deletions
56
src/state/invites.tsx
Normal file
56
src/state/invites.tsx
Normal file
|
@ -0,0 +1,56 @@
|
|||
import React from 'react'
|
||||
import * as persisted from '#/state/persisted'
|
||||
|
||||
type StateContext = persisted.Schema['invites']
|
||||
type ApiContext = {
|
||||
setInviteCopied: (code: string) => void
|
||||
}
|
||||
|
||||
const stateContext = React.createContext<StateContext>(
|
||||
persisted.defaults.invites,
|
||||
)
|
||||
const apiContext = React.createContext<ApiContext>({
|
||||
setInviteCopied(_: string) {},
|
||||
})
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const [state, setState] = React.useState(persisted.get('invites'))
|
||||
|
||||
const api = React.useMemo(
|
||||
() => ({
|
||||
setInviteCopied(code: string) {
|
||||
setState(state => {
|
||||
state = {
|
||||
...state,
|
||||
copiedInvites: state.copiedInvites.includes(code)
|
||||
? state.copiedInvites
|
||||
: state.copiedInvites.concat([code]),
|
||||
}
|
||||
persisted.write('invites', state)
|
||||
return state
|
||||
})
|
||||
},
|
||||
}),
|
||||
[setState],
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
return persisted.onUpdate(() => {
|
||||
setState(persisted.get('invites'))
|
||||
})
|
||||
}, [setState])
|
||||
|
||||
return (
|
||||
<stateContext.Provider value={state}>
|
||||
<apiContext.Provider value={api}>{children}</apiContext.Provider>
|
||||
</stateContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useInvitesState() {
|
||||
return React.useContext(stateContext)
|
||||
}
|
||||
|
||||
export function useInvitesAPI() {
|
||||
return React.useContext(apiContext)
|
||||
}
|
|
@ -304,7 +304,7 @@ export class NotificationsFeedModel {
|
|||
}
|
||||
|
||||
get unreadCountLabel(): string {
|
||||
const count = this.unreadCount + this.rootStore.invitedUsers.numNotifs
|
||||
const count = this.unreadCount
|
||||
if (count >= MAX_VISIBLE_NOTIFS) {
|
||||
return `${MAX_VISIBLE_NOTIFS}+`
|
||||
}
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
import {makeAutoObservable, runInAction} from 'mobx'
|
||||
import {ComAtprotoServerDefs, AppBskyActorDefs} from '@atproto/api'
|
||||
import {RootStoreModel} from './root-store'
|
||||
import {isObj, hasProp, isStrArray} from 'lib/type-guards'
|
||||
import {logger} from '#/logger'
|
||||
|
||||
export class InvitedUsers {
|
||||
copiedInvites: string[] = []
|
||||
seenDids: string[] = []
|
||||
profiles: AppBskyActorDefs.ProfileViewDetailed[] = []
|
||||
|
||||
get numNotifs() {
|
||||
return this.profiles.length
|
||||
}
|
||||
|
||||
constructor(public rootStore: RootStoreModel) {
|
||||
makeAutoObservable(
|
||||
this,
|
||||
{rootStore: false, serialize: false, hydrate: false},
|
||||
{autoBind: true},
|
||||
)
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return {seenDids: this.seenDids, copiedInvites: this.copiedInvites}
|
||||
}
|
||||
|
||||
hydrate(v: unknown) {
|
||||
if (isObj(v) && hasProp(v, 'seenDids') && isStrArray(v.seenDids)) {
|
||||
this.seenDids = v.seenDids
|
||||
}
|
||||
if (
|
||||
isObj(v) &&
|
||||
hasProp(v, 'copiedInvites') &&
|
||||
isStrArray(v.copiedInvites)
|
||||
) {
|
||||
this.copiedInvites = v.copiedInvites
|
||||
}
|
||||
}
|
||||
|
||||
async fetch(invites: ComAtprotoServerDefs.InviteCode[]) {
|
||||
// pull the dids of invited users not marked seen
|
||||
const dids = []
|
||||
for (const invite of invites) {
|
||||
for (const use of invite.uses) {
|
||||
if (!this.seenDids.includes(use.usedBy)) {
|
||||
dids.push(use.usedBy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fetch their profiles
|
||||
this.profiles = []
|
||||
if (dids.length) {
|
||||
try {
|
||||
const res = await this.rootStore.agent.app.bsky.actor.getProfiles({
|
||||
actors: dids,
|
||||
})
|
||||
runInAction(() => {
|
||||
// save the ones following -- these are the ones we want to notify the user about
|
||||
this.profiles = res.data.profiles.filter(
|
||||
profile => !profile.viewer?.following,
|
||||
)
|
||||
})
|
||||
this.rootStore.me.follows.hydrateMany(this.profiles)
|
||||
} catch (e) {
|
||||
logger.error('Failed to fetch profiles for invited users', {
|
||||
error: e,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isInviteCopied(invite: string) {
|
||||
return this.copiedInvites.includes(invite)
|
||||
}
|
||||
|
||||
setInviteCopied(invite: string) {
|
||||
if (!this.isInviteCopied(invite)) {
|
||||
this.copiedInvites.push(invite)
|
||||
}
|
||||
}
|
||||
|
||||
markSeen(did: string) {
|
||||
this.seenDids.push(did)
|
||||
this.profiles = this.profiles.filter(profile => profile.did !== did)
|
||||
}
|
||||
}
|
|
@ -193,7 +193,6 @@ export class MeModel {
|
|||
error: e,
|
||||
})
|
||||
}
|
||||
await this.rootStore.invitedUsers.fetch(this.invites)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ import {ProfilesCache} from './cache/profiles-view'
|
|||
import {PostsCache} from './cache/posts'
|
||||
import {LinkMetasCache} from './cache/link-metas'
|
||||
import {MeModel} from './me'
|
||||
import {InvitedUsers} from './invited-users'
|
||||
import {PreferencesModel} from './ui/preferences'
|
||||
import {resetToTab} from '../../Navigation'
|
||||
import {ImageSizesCache} from './cache/image-sizes'
|
||||
|
@ -42,7 +41,6 @@ export class RootStoreModel {
|
|||
shell = new ShellUiModel(this)
|
||||
preferences = new PreferencesModel(this)
|
||||
me = new MeModel(this)
|
||||
invitedUsers = new InvitedUsers(this)
|
||||
handleResolutions = new HandleResolutionsCache()
|
||||
profiles = new ProfilesCache(this)
|
||||
posts = new PostsCache(this)
|
||||
|
@ -68,7 +66,6 @@ export class RootStoreModel {
|
|||
session: this.session.serialize(),
|
||||
me: this.me.serialize(),
|
||||
preferences: this.preferences.serialize(),
|
||||
invitedUsers: this.invitedUsers.serialize(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,9 +86,6 @@ export class RootStoreModel {
|
|||
if (hasProp(v, 'preferences')) {
|
||||
this.preferences.hydrate(v.preferences)
|
||||
}
|
||||
if (hasProp(v, 'invitedUsers')) {
|
||||
this.invitedUsers.hydrate(v.invitedUsers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -97,11 +97,9 @@ export function transform(legacy: LegacySchema): Schema {
|
|||
legacy.preferences.requireAltTextEnabled ||
|
||||
defaults.requireAltTextEnabled,
|
||||
mutedThreads: legacy.mutedThreads.uris || defaults.mutedThreads,
|
||||
invitedUsers: {
|
||||
seenDids: legacy.invitedUsers.seenDids || defaults.invitedUsers.seenDids,
|
||||
invites: {
|
||||
copiedInvites:
|
||||
legacy.invitedUsers.copiedInvites ||
|
||||
defaults.invitedUsers.copiedInvites,
|
||||
legacy.invitedUsers.copiedInvites || defaults.invites.copiedInvites,
|
||||
},
|
||||
onboarding: {
|
||||
step: legacy.onboarding.step || defaults.onboarding.step,
|
||||
|
|
|
@ -29,8 +29,7 @@ export const schema = z.object({
|
|||
}),
|
||||
requireAltTextEnabled: z.boolean(), // should move to server
|
||||
mutedThreads: z.array(z.string()), // should move to server
|
||||
invitedUsers: z.object({
|
||||
seenDids: z.array(z.string()),
|
||||
invites: z.object({
|
||||
copiedInvites: z.array(z.string()),
|
||||
}),
|
||||
onboarding: z.object({
|
||||
|
@ -58,8 +57,7 @@ export const defaults: Schema = {
|
|||
},
|
||||
requireAltTextEnabled: false,
|
||||
mutedThreads: [],
|
||||
invitedUsers: {
|
||||
seenDids: [],
|
||||
invites: {
|
||||
copiedInvites: [],
|
||||
},
|
||||
onboarding: {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue