diff --git a/src/state/models/me.ts b/src/state/models/me.ts index 0ae52db8f7..201ce04c72 100644 --- a/src/state/models/me.ts +++ b/src/state/models/me.ts @@ -1,7 +1,6 @@ import {makeAutoObservable, runInAction} from 'mobx' import {RootStoreModel} from './root-store' import {FeedModel} from './feed-view' -import {MembershipsViewModel} from './memberships-view' import {NotificationsViewModel} from './notifications-view' import {isObj, hasProp} from '../lib/type-guards' @@ -12,7 +11,6 @@ export class MeModel { description: string = '' avatar: string = '' notificationCount: number = 0 - memberships?: MembershipsViewModel mainFeed: FeedModel notifications: NotificationsViewModel @@ -35,7 +33,6 @@ export class MeModel { this.description = '' this.avatar = '' this.notificationCount = 0 - this.memberships = undefined } serialize(): unknown { @@ -99,13 +96,7 @@ export class MeModel { algorithm: 'reverse-chronological', }) this.notifications = new NotificationsViewModel(this.rootStore, {}) - this.memberships = new MembershipsViewModel(this.rootStore, { - actor: this.did, - }) await Promise.all([ - this.memberships?.setup().catch(e => { - this.rootStore.log.error('Failed to setup memberships model', e) - }), this.mainFeed.setup().catch(e => { this.rootStore.log.error('Failed to setup main feed model', e) }), @@ -133,8 +124,4 @@ export class MeModel { } }) } - - async refreshMemberships() { - return this.memberships?.refresh() - } } diff --git a/src/state/models/members-view.ts b/src/state/models/members-view.ts deleted file mode 100644 index 73295f4793..0000000000 --- a/src/state/models/members-view.ts +++ /dev/null @@ -1,149 +0,0 @@ -import {makeAutoObservable, runInAction} from 'mobx' -import { - AppBskyGraphGetMembers as GetMembers, - AppBskyActorRef as ActorRef, - APP_BSKY_GRAPH, -} from '@atproto/api' -import {AtUri} from '../../third-party/uri' -import {RootStoreModel} from './root-store' - -export type MemberItem = GetMembers.Member & { - _reactKey: string -} - -export class MembersViewModel { - // state - isLoading = false - isRefreshing = false - hasLoaded = false - error = '' - params: GetMembers.QueryParams - - // data - subject: ActorRef.WithInfo = { - did: '', - handle: '', - displayName: '', - declaration: {cid: '', actorType: ''}, - avatar: undefined, - } - members: MemberItem[] = [] - - constructor( - public rootStore: RootStoreModel, - params: GetMembers.QueryParams, - ) { - makeAutoObservable( - this, - { - rootStore: false, - params: false, - }, - {autoBind: true}, - ) - this.params = params - } - - get hasContent() { - return this.members.length !== 0 - } - - get hasError() { - return this.error !== '' - } - - get isEmpty() { - return this.hasLoaded && !this.hasContent - } - - isMember(did: string) { - return this.members.find(member => member.did === did) - } - - // public api - // = - - async setup() { - await this._fetch() - } - - async refresh() { - await this._fetch(true) - } - - async loadMore() { - // TODO - } - - async removeMember(did: string) { - const assertsRes = await this.rootStore.api.app.bsky.graph.getAssertions({ - author: this.subject.did, - subject: did, - assertion: APP_BSKY_GRAPH.AssertMember, - }) - if (assertsRes.data.assertions.length < 1) { - throw new Error('Could not find membership record') - } - for (const assert of assertsRes.data.assertions) { - await this.rootStore.api.app.bsky.graph.assertion.delete({ - did: this.subject.did, - rkey: new AtUri(assert.uri).rkey, - }) - } - runInAction(() => { - this.members = this.members.filter(m => m.did !== did) - }) - } - - // state transitions - // = - - private _xLoading(isRefreshing = false) { - this.isLoading = true - this.isRefreshing = isRefreshing - this.error = '' - } - - private _xIdle(err?: any) { - this.isLoading = false - this.isRefreshing = false - this.hasLoaded = true - this.error = err ? err.toString() : '' - if (err) { - this.rootStore.log.error('Failed to fetch members', err) - } - } - - // loader functions - // = - - private async _fetch(isRefreshing = false) { - this._xLoading(isRefreshing) - try { - const res = await this.rootStore.api.app.bsky.graph.getMembers( - this.params, - ) - this._replaceAll(res) - this._xIdle() - } catch (e: any) { - this._xIdle(e) - } - } - - private _replaceAll(res: GetMembers.Response) { - this.subject.did = res.data.subject.did - this.subject.handle = res.data.subject.handle - this.subject.displayName = res.data.subject.displayName - this.subject.declaration = res.data.subject.declaration - this.subject.avatar = res.data.subject.avatar - this.members.length = 0 - let counter = 0 - for (const item of res.data.members) { - this._append({_reactKey: `item-${counter++}`, ...item}) - } - } - - private _append(item: MemberItem) { - this.members.push(item) - } -} diff --git a/src/state/models/memberships-view.ts b/src/state/models/memberships-view.ts deleted file mode 100644 index 661b3f6ae4..0000000000 --- a/src/state/models/memberships-view.ts +++ /dev/null @@ -1,127 +0,0 @@ -import {makeAutoObservable} from 'mobx' -import { - AppBskyGraphGetMemberships as GetMemberships, - AppBskyActorRef as ActorRef, -} from '@atproto/api' -import {RootStoreModel} from './root-store' - -export type MembershipItem = GetMemberships.Membership & { - _reactKey: string -} - -export class MembershipsViewModel { - // state - isLoading = false - isRefreshing = false - hasLoaded = false - error = '' - params: GetMemberships.QueryParams - - // data - subject: ActorRef.WithInfo = { - did: '', - handle: '', - displayName: '', - declaration: {cid: '', actorType: ''}, - avatar: undefined, - } - memberships: MembershipItem[] = [] - - constructor( - public rootStore: RootStoreModel, - params: GetMemberships.QueryParams, - ) { - makeAutoObservable( - this, - { - rootStore: false, - params: false, - }, - {autoBind: true}, - ) - this.params = params - } - - get hasContent() { - return this.memberships.length !== 0 - } - - get hasError() { - return this.error !== '' - } - - get isEmpty() { - return this.hasLoaded && !this.hasContent - } - - isMemberOf(did: string) { - return !!this.memberships.find(m => m.did === did) - } - - // public api - // = - - async setup() { - await this._fetch() - } - - async refresh() { - await this._fetch(true) - } - - async loadMore() { - // TODO - } - - // state transitions - // = - - private _xLoading(isRefreshing = false) { - this.isLoading = true - this.isRefreshing = isRefreshing - this.error = '' - } - - private _xIdle(err?: any) { - this.isLoading = false - this.isRefreshing = false - this.hasLoaded = true - this.error = err ? err.toString() : '' - if (err) { - this.rootStore.log.error('Failed to fetch memberships', err) - } - } - - // loader functions - // = - - private async _fetch(isRefreshing = false) { - this._xLoading(isRefreshing) - try { - const res = await this.rootStore.api.app.bsky.graph.getMemberships( - this.params, - ) - this._replaceAll(res) - this._xIdle() - } catch (e: any) { - this._xIdle(e) - } - } - - private _replaceAll(res: GetMemberships.Response) { - this.subject.did = res.data.subject.did - this.subject.handle = res.data.subject.handle - this.subject.displayName = res.data.subject.displayName - this.subject.declaration = res.data.subject.declaration - this.subject.avatar = res.data.subject.avatar - this.memberships.length = 0 - let counter = 0 - for (const item of res.data.memberships) { - this._append({_reactKey: `item-${counter++}`, ...item}) - } - } - - private _append(item: MembershipItem) { - this.memberships.push(item) - } -} diff --git a/src/state/models/profile-ui.ts b/src/state/models/profile-ui.ts index d2c441a117..55fb25061d 100644 --- a/src/state/models/profile-ui.ts +++ b/src/state/models/profile-ui.ts @@ -1,8 +1,6 @@ import {makeAutoObservable} from 'mobx' import {RootStoreModel} from './root-store' import {ProfileViewModel} from './profile-view' -import {MembersViewModel} from './members-view' -import {MembershipsViewModel} from './memberships-view' import {FeedModel} from './feed-view' export enum Sections { @@ -43,7 +41,7 @@ export class ProfileUiModel { }) } - get currentView(): FeedModel | MembershipsViewModel | MembersViewModel { + get currentView(): FeedModel { if ( this.selectedView === Sections.Posts || this.selectedView === Sections.PostsWithReplies diff --git a/src/state/models/profile-view.ts b/src/state/models/profile-view.ts index a3af7b553f..a1535693c9 100644 --- a/src/state/models/profile-view.ts +++ b/src/state/models/profile-view.ts @@ -16,7 +16,6 @@ export const ACTOR_TYPE_USER = 'app.bsky.system.actorUser' export class ProfileViewMyStateModel { follow?: string - member?: string muted?: boolean constructor() { @@ -46,7 +45,6 @@ export class ProfileViewModel { banner?: string followersCount: number = 0 followsCount: number = 0 - membersCount: number = 0 postsCount: number = 0 myState = new ProfileViewMyStateModel() @@ -211,7 +209,6 @@ export class ProfileViewModel { this.banner = res.data.banner this.followersCount = res.data.followersCount this.followsCount = res.data.followsCount - this.membersCount = res.data.membersCount this.postsCount = res.data.postsCount if (res.data.myState) { Object.assign(this.myState, res.data.myState) diff --git a/src/view/com/profile/ProfileMembers.tsx b/src/view/com/profile/ProfileMembers.tsx deleted file mode 100644 index 7f566c1980..0000000000 --- a/src/view/com/profile/ProfileMembers.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React, {useState, useEffect} from 'react' -import {observer} from 'mobx-react-lite' -import {ActivityIndicator, FlatList, View} from 'react-native' -import {MembersViewModel, MemberItem} from '../../../state/models/members-view' -import {ProfileCard} from './ProfileCard' -import {ErrorMessage} from '../util/error/ErrorMessage' -import {useStores} from '../../../state' - -export const ProfileMembers = observer(function ProfileMembers({ - name, -}: { - name: string -}) { - const store = useStores() - const [view, setView] = useState() - - useEffect(() => { - if (view?.params.actor === name) { - return // no change needed? or trigger refresh? - } - const newView = new MembersViewModel(store, {actor: name}) - setView(newView) - newView - .setup() - .catch(err => store.log.error('Failed to fetch members', err)) - }, [name, view?.params.actor, store]) - - const onRefresh = () => { - view?.refresh() - } - - // loading - // = - if ( - !view || - (view.isLoading && !view.isRefreshing) || - view.params.actor !== name - ) { - return ( - - - - ) - } - - // error - // = - if (view.hasError) { - return ( - - - - ) - } - - // loaded - // = - const renderItem = ({item}: {item: MemberItem}) => ( - - ) - return ( - - item._reactKey} - renderItem={renderItem} - /> - - ) -}) diff --git a/src/view/routes.ts b/src/view/routes.ts index 0a2883e69b..7d5a06acd1 100644 --- a/src/view/routes.ts +++ b/src/view/routes.ts @@ -13,7 +13,6 @@ import {PostRepostedBy} from './screens/PostRepostedBy' import {Profile} from './screens/Profile' import {ProfileFollowers} from './screens/ProfileFollowers' import {ProfileFollows} from './screens/ProfileFollows' -import {ProfileMembers} from './screens/ProfileMembers' import {Settings} from './screens/Settings' import {Debug} from './screens/Debug' import {Log} from './screens/Log' @@ -48,7 +47,6 @@ export const routes: Route[] = [ r('/profile/(?[^/]+)/followers'), ], [ProfileFollows, 'Follows', 'users', r('/profile/(?[^/]+)/follows')], - [ProfileMembers, 'Members', 'users', r('/profile/(?[^/]+)/members')], [ PostThread, 'Post', diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index 7c9fac4028..dd6e076117 100644 --- a/src/view/screens/Notifications.tsx +++ b/src/view/screens/Notifications.tsx @@ -15,7 +15,6 @@ export const Notifications = ({navIdx, visible}: ScreenParams) => { return } store.log.debug('Updating notifications feed') - store.me.refreshMemberships() // needed for the invite notifications store.me.notifications .update() .catch(e => { diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index 6d5f79e3c6..d5fc28afde 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -1,16 +1,13 @@ import React, {useEffect, useState, useMemo} from 'react' import {ActivityIndicator, StyleSheet, View} from 'react-native' import {observer} from 'mobx-react-lite' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {ViewSelector} from '../com/util/ViewSelector' import {ScreenParams} from '../routes' import {ProfileUiModel, Sections} from '../../state/models/profile-ui' -import {MembershipItem} from '../../state/models/memberships-view' import {useStores} from '../../state' import {ConfirmModal} from '../../state/models/shell-ui' import {ProfileHeader} from '../com/profile/ProfileHeader' import {FeedItem} from '../com/posts/FeedItem' -import {ProfileCard} from '../com/profile/ProfileCard' import {PostFeedLoadingPlaceholder} from '../com/util/LoadingPlaceholder' import {ErrorScreen} from '../com/util/error/ErrorScreen' import {ErrorMessage} from '../com/util/error/ErrorMessage' @@ -76,18 +73,6 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => { const onPressTryAgain = () => { uiState.setup() } - const onPressRemoveMember = (membership: MembershipItem) => { - store.shell.openModal( - new ConfirmModal( - `Remove ${membership.displayName || membership.handle}?`, - `You'll be able to invite them again if you change your mind.`, - async () => { - await uiState.members.removeMember(membership.did) - Toast.show(`User removed`) - }, - ), - ) - } // rendering // = diff --git a/src/view/screens/ProfileMembers.tsx b/src/view/screens/ProfileMembers.tsx deleted file mode 100644 index 9d6723fe97..0000000000 --- a/src/view/screens/ProfileMembers.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, {useEffect} from 'react' -import {View} from 'react-native' -import {ViewHeader} from '../com/util/ViewHeader' -import {ProfileMembers as ProfileMembersComponent} from '../com/profile/ProfileMembers' -import {ScreenParams} from '../routes' -import {useStores} from '../../state' - -export const ProfileMembers = ({navIdx, visible, params}: ScreenParams) => { - const store = useStores() - const {name} = params - - useEffect(() => { - if (visible) { - store.nav.setTitle(navIdx, `Members of ${name}`) - store.shell.setMinimalShellMode(false) - } - }, [store, visible, name]) - - return ( - - - - - ) -}