Implement mutelist creation
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
import {makeAutoObservable} from 'mobx'
|
||||
import {
|
||||
AppBskyGraphGetList as GetList,
|
||||
AppBskyActorDefs,
|
||||
AppBskyGraphDefs,
|
||||
AppBskyGraphList,
|
||||
AppBskyRichtextFacet,
|
||||
RichText,
|
||||
} from '@atproto/api'
|
||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
import {RootStoreModel} from '../root-store'
|
||||
import * as apilib from 'lib/api/index'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
|
||||
export class ListModel {
|
||||
// state
|
||||
isLoading = false
|
||||
isRefreshing = false
|
||||
hasLoaded = false
|
||||
error = ''
|
||||
params: GetList.QueryParams
|
||||
|
||||
// data
|
||||
uri: string
|
||||
creator: AppBskyActorDefs.ProfileView
|
||||
name: string
|
||||
purpose: AppBskyGraphDefs.ListPurpose
|
||||
description?: string
|
||||
descriptionFacets?: AppBskyRichtextFacet.Main[]
|
||||
avatar?: string
|
||||
viewer?: AppBskyGraphDefs.ListViewerState
|
||||
indexedAt?: string
|
||||
|
||||
// added data
|
||||
descriptionRichText?: RichText = new RichText({text: ''})
|
||||
|
||||
static async createModList(
|
||||
rootStore: RootStoreModel,
|
||||
{
|
||||
name,
|
||||
description,
|
||||
avatar,
|
||||
}: {name: string; description: string; avatar: RNImage | undefined},
|
||||
) {
|
||||
const record: AppBskyGraphList.Record = {
|
||||
purpose: 'app.bsky.graph.defs#modlist',
|
||||
name,
|
||||
description,
|
||||
avatar: undefined,
|
||||
createdAt: new Date().toISOString(),
|
||||
}
|
||||
if (avatar) {
|
||||
const blobRes = await apilib.uploadBlob(
|
||||
rootStore,
|
||||
avatar.path,
|
||||
avatar.mime,
|
||||
)
|
||||
record.avatar = blobRes.data.blob
|
||||
}
|
||||
return await rootStore.agent.app.bsky.graph.list.create(
|
||||
{
|
||||
repo: rootStore.me.did,
|
||||
},
|
||||
record,
|
||||
)
|
||||
}
|
||||
|
||||
constructor(public rootStore: RootStoreModel, params: GetList.QueryParams) {
|
||||
makeAutoObservable(
|
||||
this,
|
||||
{
|
||||
rootStore: false,
|
||||
params: false,
|
||||
},
|
||||
{autoBind: true},
|
||||
)
|
||||
this.params = params
|
||||
}
|
||||
|
||||
get hasContent() {
|
||||
return this.uri !== ''
|
||||
}
|
||||
|
||||
get hasError() {
|
||||
return this.error !== ''
|
||||
}
|
||||
|
||||
get isEmpty() {
|
||||
return this.hasLoaded && !this.hasContent
|
||||
}
|
||||
|
||||
// public api
|
||||
// =
|
||||
|
||||
async setup() {
|
||||
await this._load()
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
await this._load(true)
|
||||
}
|
||||
|
||||
// state transitions
|
||||
// =
|
||||
|
||||
_xLoading(isRefreshing = false) {
|
||||
this.isLoading = true
|
||||
this.isRefreshing = isRefreshing
|
||||
this.error = ''
|
||||
}
|
||||
|
||||
_xIdle(err?: any) {
|
||||
this.isLoading = false
|
||||
this.isRefreshing = false
|
||||
this.hasLoaded = true
|
||||
this.error = cleanError(err)
|
||||
if (err) {
|
||||
this.rootStore.log.error('Failed to fetch profile', err)
|
||||
}
|
||||
}
|
||||
|
||||
// loader functions
|
||||
// =
|
||||
|
||||
async _load(isRefreshing = false) {
|
||||
this._xLoading(isRefreshing)
|
||||
try {
|
||||
const res = await this.rootStore.agent.app.bsky.graph.getList(this.params)
|
||||
this._replaceAll(res)
|
||||
this._xIdle()
|
||||
} catch (e: any) {
|
||||
this._xIdle(e)
|
||||
}
|
||||
}
|
||||
|
||||
_replaceAll(res: GetList.Response) {
|
||||
this.uri = res.data.list.uri
|
||||
this.creator = res.data.list.creator
|
||||
this.name = res.data.list.name
|
||||
this.purpose = res.data.list.purpose
|
||||
this.description = res.data.list.description
|
||||
this.descriptionFacets = res.data.list.descriptionFacets
|
||||
this.avatar = res.data.list.avatar
|
||||
this.viewer = res.data.list.viewer
|
||||
this.indexedAt = res.data.list.indexedAt
|
||||
this.descriptionRichText = new RichText({
|
||||
text: this.description || '',
|
||||
facets: this.descriptionFacets,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,6 @@ import {
|
||||
filterProfileLabels,
|
||||
} from 'lib/labeling/helpers'
|
||||
|
||||
export const ACTOR_TYPE_USER = 'app.bsky.system.actorUser'
|
||||
|
||||
export class ProfileViewerModel {
|
||||
muted?: boolean
|
||||
following?: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import {StyleSheet, View} from 'react-native'
|
||||
import {AppBskyGraphDefs, RichText} from '@atproto/api'
|
||||
import {AtUri, AppBskyGraphDefs, RichText} from '@atproto/api'
|
||||
import {Link} from '../util/Link'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {RichText as RichTextCom} from '../util/text/RichText'
|
||||
@@ -8,11 +8,6 @@ import {UserAvatar} from '../util/UserAvatar'
|
||||
import {s} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
||||
import {
|
||||
getProfileViewBasicLabelInfo,
|
||||
getProfileModeration,
|
||||
} from 'lib/labeling/helpers'
|
||||
import {ModerationBehaviorCode} from 'lib/labeling/types'
|
||||
|
||||
export const ListCard = ({
|
||||
testID,
|
||||
@@ -29,6 +24,15 @@ export const ListCard = ({
|
||||
}) => {
|
||||
const pal = usePalette('default')
|
||||
|
||||
const rkey = React.useMemo(() => {
|
||||
try {
|
||||
const urip = new AtUri(list.uri)
|
||||
return urip.rkey
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}, [list])
|
||||
|
||||
const descriptionRichText = React.useMemo(() => {
|
||||
if (list.description) {
|
||||
return new RichText({
|
||||
@@ -48,7 +52,7 @@ export const ListCard = ({
|
||||
noBorder && styles.outerNoBorder,
|
||||
!noBg && pal.view,
|
||||
]}
|
||||
href={`/profile/${'list.author'}/lists/${'list.rkey'}`}
|
||||
href={`/profile/${list.creator.did}/lists/${rkey}`}
|
||||
title={list.name}
|
||||
asAnchor
|
||||
anchorNoUnderline>
|
||||
@@ -65,10 +69,10 @@ export const ListCard = ({
|
||||
{sanitizeDisplayName(list.name)}
|
||||
</Text>
|
||||
<Text type="md" style={[pal.textLight]} numberOfLines={1}>
|
||||
{list.purpose === 'app.bsky.graph.defs#blocklist' && 'Block list'}{' '}
|
||||
by @TODO
|
||||
{list.purpose === 'app.bsky.graph.defs#modlist' && 'Mute list'} by @
|
||||
{list.creator.handle}
|
||||
</Text>
|
||||
{!!list.viewer?.blocked && (
|
||||
{!!list.viewer?.muted && (
|
||||
<View style={s.flexRow}>
|
||||
<View style={[s.mt5, pal.btn, styles.pill]}>
|
||||
<Text type="xs" style={pal.text}>
|
||||
|
||||
@@ -14,6 +14,7 @@ import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {useStores} from 'state/index'
|
||||
import {ListModel} from 'state/models/content/list'
|
||||
import {s, colors, gradients} from 'lib/styles'
|
||||
import {enforceLen} from 'lib/strings/helpers'
|
||||
import {compressIfNeeded} from 'lib/media/manip'
|
||||
@@ -71,9 +72,13 @@ export function Component({onCreate}: {onCreate?: (uri: string) => void}) {
|
||||
setError('')
|
||||
}
|
||||
try {
|
||||
// TODO
|
||||
const res = await ListModel.createModList(store, {
|
||||
name,
|
||||
description,
|
||||
avatar: newAvatar,
|
||||
})
|
||||
Toast.show('Mute-list created')
|
||||
onCreate?.('todo')
|
||||
onCreate?.(res.uri)
|
||||
store.shell.closeModal()
|
||||
} catch (e: any) {
|
||||
if (isNetworkError(e)) {
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import React from 'react'
|
||||
import {StyleSheet} from 'react-native'
|
||||
import {useFocusEffect} from '@react-navigation/native'
|
||||
import {
|
||||
useFocusEffect,
|
||||
useNavigation,
|
||||
StackActions,
|
||||
} from '@react-navigation/native'
|
||||
import {AtUri} from '@atproto/api'
|
||||
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
|
||||
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
|
||||
import {EmptyStateWithButton} from 'view/com/util/EmptyStateWithButton'
|
||||
import {MutelistsEmptyState} from 'view/com/lists/MutelistsEmptyState'
|
||||
import {useStores} from 'state/index'
|
||||
import {ListsListModel} from 'state/models/lists/lists-list'
|
||||
import {ListsList} from 'view/com/lists/ListsList'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {CenteredView} from 'view/com/util/Views'
|
||||
import {ViewHeader} from 'view/com/util/ViewHeader'
|
||||
@@ -20,6 +25,7 @@ type Props = NativeStackScreenProps<
|
||||
export const ModerationMuteListsScreen = withAuthRequired(({route}: Props) => {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
|
||||
const mutelists: ListsListModel = React.useMemo(() => {
|
||||
const list = new ListsListModel(store, 'mutelists')
|
||||
@@ -37,7 +43,13 @@ export const ModerationMuteListsScreen = withAuthRequired(({route}: Props) => {
|
||||
store.shell.openModal({
|
||||
name: 'create-mute-list',
|
||||
onCreate: (uri: string) => {
|
||||
// TODO
|
||||
try {
|
||||
const urip = new AtUri(uri)
|
||||
navigation.navigate('ProfileList', {
|
||||
name: urip.hostname,
|
||||
rkey: urip.rkey,
|
||||
})
|
||||
} catch {}
|
||||
},
|
||||
})
|
||||
}, [store])
|
||||
|
||||
Reference in New Issue
Block a user