Add UI to remove members from scenes
parent
22849fb4fc
commit
fe52d19c48
|
@ -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) {
|
||||||
|
|
|
@ -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)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue