Add UI to remove members from scenes

zio/stable
Paul Frazee 2022-11-11 13:28:23 -06:00
parent 22849fb4fc
commit fe52d19c48
3 changed files with 69 additions and 3 deletions

View File

@ -1,5 +1,7 @@
import {makeAutoObservable} from 'mobx' import {makeAutoObservable, runInAction} from 'mobx'
import * as GetMembers from '../../third-party/api/src/client/types/app/bsky/graph/getMembers' import * as GetMembers from '../../third-party/api/src/client/types/app/bsky/graph/getMembers'
import {APP_BSKY_GRAPH} from '../../third-party/api'
import {AtUri} from '../../third-party/uri'
import {RootStoreModel} from './root-store' import {RootStoreModel} from './root-store'
type Subject = GetMembers.OutputSchema['subject'] type Subject = GetMembers.OutputSchema['subject']
@ -16,7 +18,12 @@ export class MembersViewModel {
params: GetMembers.QueryParams params: GetMembers.QueryParams
// data // data
subject: Subject = {did: '', handle: '', displayName: ''} subject: Subject = {
did: '',
handle: '',
displayName: '',
declaration: {cid: '', actorType: ''},
}
members: MemberItem[] = [] members: MemberItem[] = []
constructor( constructor(
@ -65,6 +72,26 @@ export class MembersViewModel {
// TODO // 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 // state transitions
// = // =
@ -101,6 +128,7 @@ export class MembersViewModel {
this.subject.did = res.data.subject.did this.subject.did = res.data.subject.did
this.subject.handle = res.data.subject.handle this.subject.handle = res.data.subject.handle
this.subject.displayName = res.data.subject.displayName this.subject.displayName = res.data.subject.displayName
this.subject.declaration = res.data.subject.declaration
this.members.length = 0 this.members.length = 0
let counter = 0 let counter = 0
for (const item of res.data.members) { for (const item of res.data.members) {

View File

@ -1,15 +1,19 @@
import React, {useEffect, useState, useMemo} from 'react' import React, {useEffect, useState, useMemo} from 'react'
import {StyleSheet, Text, View} from 'react-native' import {StyleSheet, Text, View} from 'react-native'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {ViewSelector} from '../com/util/ViewSelector' import {ViewSelector} from '../com/util/ViewSelector'
import {ScreenParams} from '../routes' import {ScreenParams} from '../routes'
import {ProfileUiModel, Sections} from '../../state/models/profile-ui' import {ProfileUiModel, Sections} from '../../state/models/profile-ui'
import {MembershipItem} from '../../state/models/memberships-view'
import {useStores} from '../../state' import {useStores} from '../../state'
import {ConfirmModel} from '../../state/models/shell-ui'
import {ProfileHeader} from '../com/profile/ProfileHeader' import {ProfileHeader} from '../com/profile/ProfileHeader'
import {FeedItem} from '../com/posts/FeedItem' import {FeedItem} from '../com/posts/FeedItem'
import {ProfileCard} from '../com/profile/ProfileCard' import {ProfileCard} from '../com/profile/ProfileCard'
import {ErrorScreen} from '../com/util/ErrorScreen' import {ErrorScreen} from '../com/util/ErrorScreen'
import {ErrorMessage} from '../com/util/ErrorMessage' import {ErrorMessage} from '../com/util/ErrorMessage'
import Toast from '../com/util/Toast'
import {s, colors} from '../lib/styles' import {s, colors} from '../lib/styles'
import {UserGroupIcon} from '../lib/icons' import {UserGroupIcon} from '../lib/icons'
@ -65,10 +69,28 @@ export const Profile = observer(({visible, params}: ScreenParams) => {
const onPressTryAgain = () => { const onPressTryAgain = () => {
uiState.setup() uiState.setup()
} }
const onPressRemoveMember = (membership: MembershipItem) => {
store.shell.openModal(
new ConfirmModel(
`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`, {
duration: Toast.durations.LONG,
position: Toast.positions.TOP,
})
},
),
)
}
// rendering // rendering
// = // =
const isSceneCreator =
uiState.isScene && store.me.did === uiState.profile.creator
const renderHeader = () => { const renderHeader = () => {
if (!uiState) { if (!uiState) {
return <View /> return <View />
@ -155,11 +177,26 @@ export const Profile = observer(({visible, params}: ScreenParams) => {
if (uiState.members.hasContent) { if (uiState.members.hasContent) {
items = uiState.members.members.slice() items = uiState.members.members.slice()
renderItem = (item: any) => { renderItem = (item: any) => {
const shouldAdmin = isSceneCreator && item.did !== store.me.did
const renderButton = shouldAdmin
? () => (
<>
<FontAwesomeIcon
icon="user-xmark"
style={[s.mr5]}
size={14}
/>
<Text style={[s.fw400, s.f14]}>Remove</Text>
</>
)
: undefined
return ( return (
<ProfileCard <ProfileCard
did={item.did} did={item.did}
handle={item.handle} handle={item.handle}
displayName={item.displayName} displayName={item.displayName}
renderButton={renderButton}
onPressButton={() => onPressRemoveMember(item)}
/> />
) )
} }

View File

@ -18,9 +18,10 @@ Paul's todo list
- User search - User search
- Use pagination to make sure there are suggestions - Use pagination to make sure there are suggestions
- User profile - User profile
- User
- Invite to scene
- Scene - Scene
> Edit profile > Edit profile
> Remove member
- Reply gating - Reply gating
- Composer - Composer
- View on post - View on post