u
This commit is contained in:
1 parent
fc005e0fb4
commit
10d0cadfe3
18 files changed
+591
-133
No files matched your search
@@ -127,7 +127,7 @@ export default function Auth() {
|
||||
// avatar: loginRes.data.avatar,
|
||||
// })
|
||||
|
||||
// router.push('/auth/space')
|
||||
// router.push('/space/manage')
|
||||
// }
|
||||
// }
|
||||
// catch (err) {
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
import type { UserSpace } from '@/types/space'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Text, View } from 'react-native'
|
||||
import { toast } from 'sonner-native'
|
||||
import { LucideIcon } from '@/components/common/ao'
|
||||
import { AInput, AModal } from '@/components/common/ao/index'
|
||||
import {
|
||||
Actionsheet,
|
||||
ActionsheetBackdrop,
|
||||
ActionsheetContent,
|
||||
ActionsheetDragIndicator,
|
||||
ActionsheetDragIndicatorWrapper,
|
||||
ActionsheetIcon,
|
||||
ActionsheetItem,
|
||||
ActionsheetItemText,
|
||||
} from '@/components/ui/actionsheet'
|
||||
import { dissolveSpaceApi } from '@/request/api/space'
|
||||
import { useUserStore } from '@/store/user'
|
||||
|
||||
interface SettingActionsProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
|
||||
space: UserSpace | null
|
||||
|
||||
onActionSuccess?: () => void
|
||||
}
|
||||
|
||||
export default function SettingActions(
|
||||
{
|
||||
isOpen,
|
||||
onClose,
|
||||
space,
|
||||
onActionSuccess
|
||||
}: SettingActionsProps,
|
||||
) {
|
||||
const userStore = useUserStore()
|
||||
|
||||
const [localSpace, setLocalSpace] = useState(space)
|
||||
|
||||
const [showNameModal, setShowNameModal] = useState(false)
|
||||
|
||||
const [showMemberNameModal, setShowMemberNameModal] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setLocalSpace(space)
|
||||
}, [space])
|
||||
return (
|
||||
<>
|
||||
<Actionsheet
|
||||
isOpen={isOpen}
|
||||
onClose={() => {
|
||||
onClose()
|
||||
}}
|
||||
>
|
||||
<ActionsheetBackdrop />
|
||||
<ActionsheetContent
|
||||
style={{
|
||||
paddingBottom: 6,
|
||||
}}
|
||||
>
|
||||
<ActionsheetDragIndicatorWrapper>
|
||||
<ActionsheetDragIndicator />
|
||||
</ActionsheetDragIndicatorWrapper>
|
||||
{
|
||||
space?.role === 0 && (
|
||||
<ActionsheetItem>
|
||||
<ActionsheetIcon
|
||||
as={() => <LucideIcon icon="UserCog" />}
|
||||
/>
|
||||
<ActionsheetItemText>成员管理</ActionsheetItemText>
|
||||
</ActionsheetItem>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
space?.owner_id === userStore.userInfo.id && (
|
||||
<ActionsheetItem onPress={() => {
|
||||
setShowNameModal(true)
|
||||
}}
|
||||
>
|
||||
<ActionsheetIcon
|
||||
as={() => <LucideIcon icon="Pencil" />}
|
||||
/>
|
||||
<ActionsheetItemText>修改空间名称</ActionsheetItemText>
|
||||
</ActionsheetItem>
|
||||
)
|
||||
}
|
||||
|
||||
<ActionsheetItem>
|
||||
<ActionsheetIcon
|
||||
as={() => <LucideIcon icon="Pencil" />}
|
||||
/>
|
||||
<ActionsheetItemText>修改我的空间昵称</ActionsheetItemText>
|
||||
</ActionsheetItem>
|
||||
|
||||
<ActionsheetItem>
|
||||
<ActionsheetIcon
|
||||
as={() => <LucideIcon icon="Star" />}
|
||||
/>
|
||||
<ActionsheetItemText>
|
||||
{space?.is_default === 1 ? '取消' : '设置'}
|
||||
默认
|
||||
</ActionsheetItemText>
|
||||
</ActionsheetItem>
|
||||
|
||||
<ActionsheetItem onPress={() => {
|
||||
if (space?.owner_id === userStore.userInfo.id) {
|
||||
// 解散空间
|
||||
dissolveSpaceApi({
|
||||
space_id: space.space_id,
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
toast.success('空间已解散')
|
||||
onActionSuccess?.
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
else {
|
||||
// 退出空间
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ActionsheetIcon
|
||||
as={() => <LucideIcon icon="LogOut" />}
|
||||
/>
|
||||
<ActionsheetItemText>
|
||||
{space?.owner_id === userStore.userInfo.id ? '解散' : '退出'}
|
||||
空间
|
||||
</ActionsheetItemText>
|
||||
</ActionsheetItem>
|
||||
|
||||
</ActionsheetContent>
|
||||
</Actionsheet>
|
||||
|
||||
<AModal
|
||||
title="修改空间名称"
|
||||
showCloseIcon
|
||||
body={
|
||||
(
|
||||
<View className="gap-2">
|
||||
<Text>空间名称</Text>
|
||||
<AInput
|
||||
value={localSpace?.name || ''}
|
||||
onChangeText={(val) => {
|
||||
setLocalSpace(prev => prev
|
||||
? ({
|
||||
...prev,
|
||||
name: val,
|
||||
})
|
||||
: null)
|
||||
}}
|
||||
className="flex-1"
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
isOpen={showNameModal}
|
||||
onCancel={() => {
|
||||
setShowNameModal(false)
|
||||
setLocalSpace(space)
|
||||
}}
|
||||
onClose={() => {
|
||||
setShowNameModal(false)
|
||||
setLocalSpace(space)
|
||||
}}
|
||||
onConfirm={async () => {
|
||||
// 更新名称
|
||||
if (!localSpace?.name) {
|
||||
toast.warning('请输入空间名称')
|
||||
}
|
||||
}}
|
||||
>
|
||||
</AModal>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
+80
-14
@@ -1,5 +1,5 @@
|
||||
import type { UserSpace } from '@/types/space'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Pressable, Text, View } from 'react-native'
|
||||
import Animated, { useAnimatedRef } from 'react-native-reanimated'
|
||||
import Sortable from 'react-native-sortables'
|
||||
@@ -11,12 +11,18 @@ import CustomHeader from '@/components/custom/Header'
|
||||
import { Badge, BadgeText } from '@/components/ui/badge'
|
||||
import { createSpaceApi, listUserSpacesApi } from '@/request/api/space/index'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import SettingActions from './components/SettingActions'
|
||||
|
||||
export default function AuthSpace() {
|
||||
const userStore = useUserStore()
|
||||
|
||||
// scrollView 相关
|
||||
const scrollableRef = useAnimatedRef<Animated.ScrollView>()
|
||||
const [scrollHeight, setScrollHeight] = useState(0)
|
||||
const ITEM_HEIGHT = 64
|
||||
|
||||
// 空间列表
|
||||
const [spaces, setSpaces] = useState<UserSpace[]>([])
|
||||
const [selectedId, setSelectedId] = useState<number | null>(null)
|
||||
|
||||
// 新建
|
||||
@@ -25,8 +31,11 @@ export default function AuthSpace() {
|
||||
name: '',
|
||||
})
|
||||
|
||||
const [spaces, setSpaces] = useState<UserSpace[]>([])
|
||||
// 设置
|
||||
const [showSettings, setShowSettings] = useState(false)
|
||||
const [settingCurrentSpace, setSettingCurrentSpace] = useState<UserSpace | null>(null)
|
||||
|
||||
// 获取空间列表
|
||||
function listUserSpaces() {
|
||||
listUserSpacesApi().then((res) => {
|
||||
if (res.isSuccess) {
|
||||
@@ -36,12 +45,18 @@ export default function AuthSpace() {
|
||||
}
|
||||
|
||||
const renderItem
|
||||
= ({ item }: { item: UserSpace }) => (
|
||||
= ({ item, index }: { item: UserSpace, index: number }) => (
|
||||
<Pressable
|
||||
key={item.id}
|
||||
onPress={() => setSelectedId(item.id)}
|
||||
key={item.space_id}
|
||||
onPress={() => {
|
||||
setSelectedId(item.space_id)
|
||||
scrollToCenter(index)
|
||||
}}
|
||||
style={{
|
||||
height: ITEM_HEIGHT,
|
||||
}}
|
||||
className={`rounded-md flex-row items-center justify-between px-4 py-3 border
|
||||
${item.id === selectedId ? 'border-gray-700' : 'border-gray-300'}`}
|
||||
${item.space_id === selectedId ? 'border-gray-700' : 'border-gray-300'}`}
|
||||
>
|
||||
<View className="flex-row items-center">
|
||||
<Sortable.Handle>
|
||||
@@ -83,7 +98,7 @@ export default function AuthSpace() {
|
||||
<Text
|
||||
className="text-xs"
|
||||
>
|
||||
{item.user_name}
|
||||
{item.member_name}
|
||||
</Text>
|
||||
</View>
|
||||
<View className="flex-row gap-1">
|
||||
@@ -98,21 +113,55 @@ export default function AuthSpace() {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<LucideIcon
|
||||
icon="Bolt"
|
||||
size={20}
|
||||
className="p-1"
|
||||
/>
|
||||
<View className="flex flex-row items-center gap-3">
|
||||
<View className="w-8 h-8 rounded-full items-center justify-center bg-[#333333]">
|
||||
<LucideIcon
|
||||
icon="UserPlus"
|
||||
size={18}
|
||||
color="#fff"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Pressable
|
||||
className="w-8 h-8 rounded-full items-center justify-center bg-[#333333]"
|
||||
onPress={(e) => {
|
||||
e.stopPropagation()
|
||||
setSettingCurrentSpace(item)
|
||||
setShowSettings(true)
|
||||
}}
|
||||
>
|
||||
<LucideIcon
|
||||
icon="Bolt"
|
||||
size={18}
|
||||
color="#fff"
|
||||
clickable={false}
|
||||
/>
|
||||
</Pressable>
|
||||
</View>
|
||||
</Pressable>
|
||||
)
|
||||
|
||||
const scrollToCenter = (index: number) => {
|
||||
if (!scrollHeight)
|
||||
return
|
||||
|
||||
const itemTop = index * ITEM_HEIGHT
|
||||
|
||||
const scrollY
|
||||
= itemTop - scrollHeight / 2 + ITEM_HEIGHT / 2
|
||||
|
||||
scrollableRef.current?.scrollTo({
|
||||
y: Math.max(0, scrollY),
|
||||
animated: true,
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
listUserSpaces()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<View className="flex-1 bg-white">
|
||||
{/* Header */}
|
||||
<CustomHeader title="空间列表" />
|
||||
|
||||
<View className="flex-1 px-4">
|
||||
@@ -146,6 +195,9 @@ export default function AuthSpace() {
|
||||
className="flex-1"
|
||||
showsVerticalScrollIndicator={false}
|
||||
ref={scrollableRef}
|
||||
onLayout={(e) => {
|
||||
setScrollHeight(e.nativeEvent.layout.height)
|
||||
}}
|
||||
>
|
||||
{spaces.length === 0
|
||||
? (
|
||||
@@ -218,7 +270,7 @@ export default function AuthSpace() {
|
||||
|
||||
await createSpaceApi({
|
||||
name: createForm.name,
|
||||
user_name: userStore.userInfo.nickname,
|
||||
member_name: userStore.userInfo.nickname,
|
||||
sort_number: 0,
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
@@ -227,11 +279,25 @@ export default function AuthSpace() {
|
||||
setCreateForm({
|
||||
name: '',
|
||||
})
|
||||
|
||||
listUserSpaces()
|
||||
}
|
||||
})
|
||||
}}
|
||||
>
|
||||
</AModal>
|
||||
|
||||
<SettingActions
|
||||
isOpen={showSettings}
|
||||
onClose={() => {
|
||||
setShowSettings(false)
|
||||
|
||||
setTimeout(() => {
|
||||
setSettingCurrentSpace(null)
|
||||
}, 300)
|
||||
}}
|
||||
space={settingCurrentSpace}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user