Add scene creator
This commit is contained in:
parent
93b64cf474
commit
e7536289cb
18 changed files with 281 additions and 23 deletions
|
@ -8,7 +8,7 @@ import Toast from '../util/Toast'
|
|||
import ProgressCircle from '../util/ProgressCircle'
|
||||
import {useStores} from '../../../state'
|
||||
import * as apilib from '../../../state/lib/api'
|
||||
import {ComposerOpts} from '../../../state/models/shell'
|
||||
import {ComposerOpts} from '../../../state/models/shell-ui'
|
||||
import {s, colors, gradients} from '../../lib/styles'
|
||||
|
||||
const MAX_TEXT_LENGTH = 256
|
||||
|
|
210
src/view/com/modals/CreateScene.tsx
Normal file
210
src/view/com/modals/CreateScene.tsx
Normal file
|
@ -0,0 +1,210 @@
|
|||
import React, {useState} from 'react'
|
||||
import Toast from '../util/Toast'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {ErrorMessage} from '../util/ErrorMessage'
|
||||
import {useStores} from '../../../state'
|
||||
import {s, colors, gradients} from '../../lib/styles'
|
||||
import {makeValidHandle, createFullHandle} from '../../lib/strings'
|
||||
import {AppBskyActorCreateScene} from '../../../third-party/api/index'
|
||||
|
||||
export const snapPoints = ['70%']
|
||||
|
||||
export function Component({}: {}) {
|
||||
const store = useStores()
|
||||
const [error, setError] = useState<string>('')
|
||||
const [isProcessing, setIsProcessing] = useState<boolean>(false)
|
||||
const [handle, setHandle] = useState<string>('')
|
||||
const [displayName, setDisplayName] = useState<string>('')
|
||||
const [description, setDescription] = useState<string>('')
|
||||
const onPressSave = async () => {
|
||||
setIsProcessing(true)
|
||||
if (error) {
|
||||
setError('')
|
||||
}
|
||||
try {
|
||||
if (!store.me.did) {
|
||||
return
|
||||
}
|
||||
const desc = await store.api.com.atproto.server.getAccountsConfig()
|
||||
const fullHandle = createFullHandle(
|
||||
handle,
|
||||
desc.data.availableUserDomains[0],
|
||||
)
|
||||
// create scene actor
|
||||
const createSceneRes = await store.api.app.bsky.actor.createScene({
|
||||
handle: fullHandle,
|
||||
})
|
||||
// set the scene profile
|
||||
// TODO
|
||||
// follow the scene
|
||||
await store.api.app.bsky.graph.follow
|
||||
.create(
|
||||
{
|
||||
did: store.me.did,
|
||||
},
|
||||
{
|
||||
subject: {
|
||||
did: createSceneRes.data.did,
|
||||
declarationCid: createSceneRes.data.declarationCid,
|
||||
},
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
)
|
||||
.catch(e => console.error(e)) // an error here is not critical
|
||||
Toast.show('Scene created', {
|
||||
position: Toast.positions.TOP,
|
||||
})
|
||||
store.shell.closeModal()
|
||||
store.nav.navigate(`/profile/${fullHandle}`)
|
||||
} catch (e: any) {
|
||||
if (e instanceof AppBskyActorCreateScene.InvalidHandleError) {
|
||||
setError(
|
||||
'The handle can only contain letters, numbers, and dashes, and must start with a letter.',
|
||||
)
|
||||
} else if (e instanceof AppBskyActorCreateScene.HandleNotAvailableError) {
|
||||
setError(`The handle "${handle}" is not available.`)
|
||||
} else {
|
||||
console.error(e)
|
||||
setError(
|
||||
'Failed to create the scene. Check your internet connection and try again.',
|
||||
)
|
||||
}
|
||||
setIsProcessing(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={s.flex1}>
|
||||
<Text style={styles.title}>Create a scene</Text>
|
||||
<Text style={styles.description}>
|
||||
Scenes are invite-only groups which aggregate what's popular with
|
||||
members.
|
||||
</Text>
|
||||
<View style={styles.inner}>
|
||||
<View style={styles.group}>
|
||||
<Text style={styles.label}>Scene Handle</Text>
|
||||
<TextInput
|
||||
style={styles.textInput}
|
||||
placeholder="e.g. alices-friends"
|
||||
value={handle}
|
||||
onChangeText={str => setHandle(makeValidHandle(str))}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.group}>
|
||||
<Text style={styles.label}>Scene Display Name</Text>
|
||||
<TextInput
|
||||
style={styles.textInput}
|
||||
placeholder="e.g. Alice's Friends"
|
||||
value={displayName}
|
||||
onChangeText={setDisplayName}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.group}>
|
||||
<Text style={styles.label}>Scene Description</Text>
|
||||
<TextInput
|
||||
style={[styles.textArea]}
|
||||
placeholder="e.g. Artists, dog-lovers, and memelords."
|
||||
multiline
|
||||
value={description}
|
||||
onChangeText={setDescription}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.errorContainer}>
|
||||
{error !== '' && (
|
||||
<View style={s.mb10}>
|
||||
<ErrorMessage message={error} numberOfLines={3} />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
{handle.length >= 2 && !isProcessing ? (
|
||||
<TouchableOpacity style={s.mt10} onPress={onPressSave}>
|
||||
<LinearGradient
|
||||
colors={[gradients.primary.start, gradients.primary.end]}
|
||||
start={{x: 0, y: 0}}
|
||||
end={{x: 1, y: 1}}
|
||||
style={[styles.btn]}>
|
||||
<Text style={[s.white, s.bold, s.f18]}>Create Scene</Text>
|
||||
</LinearGradient>
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
<View style={s.mt10}>
|
||||
<View style={[styles.btn]}>
|
||||
{isProcessing ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<Text style={[s.gray4, s.bold, s.f18]}>Create Scene</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
title: {
|
||||
textAlign: 'center',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 24,
|
||||
marginBottom: 12,
|
||||
},
|
||||
description: {
|
||||
textAlign: 'center',
|
||||
fontSize: 17,
|
||||
paddingHorizontal: 22,
|
||||
color: colors.gray5,
|
||||
marginBottom: 10,
|
||||
},
|
||||
inner: {
|
||||
padding: 14,
|
||||
},
|
||||
group: {
|
||||
marginBottom: 10,
|
||||
},
|
||||
label: {
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
paddingHorizontal: 4,
|
||||
paddingBottom: 4,
|
||||
},
|
||||
textInput: {
|
||||
borderWidth: 1,
|
||||
borderColor: colors.gray3,
|
||||
borderRadius: 6,
|
||||
paddingHorizontal: 14,
|
||||
paddingVertical: 10,
|
||||
fontSize: 16,
|
||||
},
|
||||
textArea: {
|
||||
borderWidth: 1,
|
||||
borderColor: colors.gray3,
|
||||
borderRadius: 6,
|
||||
paddingHorizontal: 12,
|
||||
paddingTop: 10,
|
||||
fontSize: 16,
|
||||
height: 100,
|
||||
textAlignVertical: 'top',
|
||||
},
|
||||
errorContainer: {
|
||||
height: 80,
|
||||
},
|
||||
btn: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
borderRadius: 32,
|
||||
padding: 14,
|
||||
marginBottom: 10,
|
||||
backgroundColor: colors.gray1,
|
||||
},
|
||||
})
|
|
@ -68,7 +68,7 @@ export function Component({profileView}: {profileView: ProfileViewModel}) {
|
|||
/>
|
||||
</View>
|
||||
<View style={styles.group}>
|
||||
<Text style={styles.label}>Biography</Text>
|
||||
<Text style={styles.label}>Description</Text>
|
||||
<TextInput
|
||||
style={[styles.textArea]}
|
||||
placeholder="e.g. Artist, dog-lover, and memelord."
|
||||
|
|
|
@ -5,11 +5,12 @@ import BottomSheet from '@gorhom/bottom-sheet'
|
|||
import {useStores} from '../../../state'
|
||||
import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop'
|
||||
|
||||
import * as models from '../../../state/models/shell'
|
||||
import * as models from '../../../state/models/shell-ui'
|
||||
|
||||
import * as LinkActionsModal from './LinkActions'
|
||||
import * as SharePostModal from './SharePost.native'
|
||||
import * as EditProfile from './EditProfile'
|
||||
import * as CreateScene from './CreateScene'
|
||||
|
||||
const CLOSED_SNAPPOINTS = ['10%']
|
||||
|
||||
|
@ -57,6 +58,9 @@ export const Modal = observer(function Modal() {
|
|||
{...(store.shell.activeModal as models.EditProfileModel)}
|
||||
/>
|
||||
)
|
||||
} else if (store.shell.activeModal?.name === 'create-scene') {
|
||||
snapPoints = CreateScene.snapPoints
|
||||
element = <CreateScene.Component />
|
||||
} else {
|
||||
element = <View />
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
PostThreadViewPostModel,
|
||||
} from '../../../state/models/post-thread-view'
|
||||
import {useStores} from '../../../state'
|
||||
import {SharePostModel} from '../../../state/models/shell'
|
||||
import {SharePostModel} from '../../../state/models/shell-ui'
|
||||
import {PostThreadItem} from './PostThreadItem'
|
||||
|
||||
export const PostThread = observer(function PostThread({uri}: {uri: string}) {
|
||||
|
|
|
@ -5,7 +5,7 @@ import {AtUri} from '../../../third-party/uri'
|
|||
import * as PostType from '../../../third-party/api/src/client/types/app/bsky/feed/post'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {FeedItemModel} from '../../../state/models/feed-view'
|
||||
import {SharePostModel} from '../../../state/models/shell'
|
||||
import {SharePostModel} from '../../../state/models/shell-ui'
|
||||
import {Link} from '../util/Link'
|
||||
import {PostDropdownBtn} from '../util/DropdownBtn'
|
||||
import {UserInfoText} from '../util/UserInfoText'
|
||||
|
|
|
@ -11,7 +11,7 @@ import LinearGradient from 'react-native-linear-gradient'
|
|||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {ProfileViewModel} from '../../../state/models/profile-view'
|
||||
import {useStores} from '../../../state'
|
||||
import {EditProfileModel} from '../../../state/models/shell'
|
||||
import {EditProfileModel} from '../../../state/models/shell-ui'
|
||||
import {pluralize} from '../../lib/strings'
|
||||
import {s, colors} from '../../lib/styles'
|
||||
import {getGradient} from '../../lib/asset-gen'
|
||||
|
|
|
@ -13,7 +13,7 @@ import RootSiblings from 'react-native-root-siblings'
|
|||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {colors} from '../../lib/styles'
|
||||
import {useStores} from '../../../state'
|
||||
import {SharePostModel} from '../../../state/models/shell'
|
||||
import {SharePostModel} from '../../../state/models/shell-ui'
|
||||
|
||||
export interface DropdownItem {
|
||||
icon?: IconProp
|
||||
|
|
|
@ -5,9 +5,11 @@ import {colors} from '../../lib/styles'
|
|||
|
||||
export function ErrorMessage({
|
||||
message,
|
||||
numberOfLines,
|
||||
onPressTryAgain,
|
||||
}: {
|
||||
message: string
|
||||
numberOfLines?: number
|
||||
onPressTryAgain?: () => void
|
||||
}) {
|
||||
return (
|
||||
|
@ -19,7 +21,9 @@ export function ErrorMessage({
|
|||
size={16}
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.message}>{message}</Text>
|
||||
<Text style={styles.message} numberOfLines={numberOfLines}>
|
||||
{message}
|
||||
</Text>
|
||||
{onPressTryAgain && (
|
||||
<TouchableOpacity style={styles.btn} onPress={onPressTryAgain}>
|
||||
<FontAwesomeIcon
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||
import {observer} from 'mobx-react-lite'
|
||||
import {StyleProp, Text, TouchableOpacity, ViewStyle} from 'react-native'
|
||||
import {useStores} from '../../../state'
|
||||
import {LinkActionsModel} from '../../../state/models/shell'
|
||||
import {LinkActionsModel} from '../../../state/models/shell-ui'
|
||||
|
||||
export const Link = observer(function Link({
|
||||
style,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue