This commit is contained in:
oneao committed 2025-12-02 17:30:50 +08:00
1 parent 0ab2796dd4
commit f7b0c6ad2f
26 files changed
+1893 -354

No files matched your search

@@ -0,0 +1,58 @@
import type { AFormRef } from '@/components/common/ao'
import { useRef, useState } from 'react'
import { Text, View } from 'react-native'
import { AButton, ADivider, AForm, AFormItem, AInput } from '@/components/common/ao'
import { Link, LinkText } from '@/components/ui/link'
interface JoinSpaceProps {
onBack?: () => void
}
export default function JoinSpace({
onBack,
}: JoinSpaceProps) {
const formRef = useRef<AFormRef>(null)
const [form, setForm] = useState({
code: '',
})
return (
<View className="px-4 items-center">
<AForm ref={formRef} form={form}>
<AFormItem
name="username"
label="邀请码"
>
<AInput placeholder="请输入邀请码"></AInput>
</AFormItem>
</AForm>
<AButton className="w-full">加入空间</AButton>
<View
className="flex-row items-center w-full"
style={{
marginTop: 16,
marginBottom: 16,
}}
>
<View className="flex-1">
<ADivider />
</View>
<Text className="mx-3 text-gray-500 text-sm">或</Text>
<View className="flex-1">
<ADivider />
</View>
</View>
<AButton variant="outline" className="w-full">扫码加入</AButton>
<Link
className="mt-4"
onPress={() => {
onBack?.()
}}
>
<LinkText>返回</LinkText>
</Link>
</View>
)
}
@@ -0,0 +1,37 @@
import { Text, View } from 'react-native'
import { AButton, ADivider, LucideIcon } from '@/components/common/ao'
interface SpaceHomeProps {
onCreate?: () => void
onJoin?: () => void
}
export default function SpaceHome({
onCreate,
onJoin,
}: SpaceHomeProps) {
return (
<View className="p-4">
<View className="flex-col gap-4 justify-center items-center">
<LucideIcon icon="Earth" size={48} strokeWidth={1.5} />
<Text className="font-semibold text-xl">创建共享空间</Text>
<Text className="text-base text-center">创建一个专属空间,邀请朋友加入,共享文件、聊天和协作</Text>
<AButton
className="w-full"
onPress={() => onCreate?.()}
>
创建我的空间
</AButton>
</View>
<ADivider text="或" />
<AButton
variant="outline"
onPress={() => onJoin?.()}
>
加入已有空间
</AButton>
</View>
)
}
@@ -0,0 +1,176 @@
import * as Clipboard from 'expo-clipboard'
import { useState } from 'react'
import { FlatList, Pressable, TouchableOpacity, View } from 'react-native'
import QRCode from 'react-native-qrcode-svg'
import { AButton, ADivider, LucideIcon } from '@/components/common/ao'
import {
Accordion,
AccordionContent,
AccordionHeader,
AccordionIcon,
AccordionItem,
AccordionTitleText,
AccordionTrigger,
} from '@/components/ui/accordion'
import {
Avatar,
AvatarBadge,
AvatarFallbackText,
} from '@/components/ui/avatar'
import { Badge, BadgeText } from '@/components/ui/badge'
import { Heading } from '@/components/ui/heading'
import { HStack } from '@/components/ui/hstack'
import { ChevronDownIcon, ChevronUpIcon } from '@/components/ui/icon'
import { Menu, MenuItem, MenuItemLabel } from '@/components/ui/menu'
import { Text } from '@/components/ui/text'
import { VStack } from '@/components/ui/vstack'
import { toast } from '@/utils/toast'
export default function SpaceInviteAndMembers() {
const [accordion, setAccordion] = useState(['a'])
const members = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 3, name: '王五' },
]
async function copyInviteCode() {
await Clipboard.setStringAsync('hello world')
toast.show({
title: '复制成功',
})
}
return (
<View className="px-4 flex-1">
<Accordion
size="md"
variant="unfilled"
type="multiple"
isCollapsible={true}
isDisabled={false}
value={accordion}
onValueChange={(value) => {
setAccordion(value)
}}
>
<AccordionItem value="a">
<AccordionHeader>
<AccordionTrigger
style={{
paddingLeft: 0,
paddingRight: 0,
}}
>
{({ isExpanded }: { isExpanded: boolean }) => {
return (
<>
<AccordionTitleText className="font-semibold text-lg">
我的邀请码
</AccordionTitleText>
{isExpanded
? (
<AccordionIcon as={ChevronUpIcon} className="ml-3" />
)
: (
<AccordionIcon as={ChevronDownIcon} className="ml-3" />
)}
</>
)
}}
</AccordionTrigger>
</AccordionHeader>
<AccordionContent style={{
marginBottom: 0,
paddingBottom: 0,
}}
>
<TouchableOpacity
className="flex-row items-center justify-center"
onPress={() => {
copyInviteCode()
}}
>
<Text className="font-bold text-3xl tracking-[4px] mr-3">123456</Text>
<LucideIcon icon="Copy" size={16} color="blue" clickable={false} />
</TouchableOpacity>
<View className="items-center justify-center">
<ADivider direction="horizontal" text="扫描二维码" />
<QRCode
size={120}
value="http://awesome.link.qr"
/>
</View>
</AccordionContent>
</AccordionItem>
</Accordion>
<View className="mt-4 flex-1">
<View className="flex flex-row items-center justify-between">
<Text className="font-semibold text-lg">空间成员</Text>
<Badge size="lg" variant="solid" action="muted">
<BadgeText>3 人</BadgeText>
</Badge>
</View>
<View>
<FlatList
data={members}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => (
<TouchableOpacity>
<HStack space="md" className="my-2 items-center">
<Avatar className="bg-indigo-600">
<AvatarFallbackText className="text-white text-lg">
{item.name}
</AvatarFallbackText>
<AvatarBadge />
</Avatar>
<VStack>
<HStack space="sm">
<Heading size="sm">{item.name}</Heading>
<Badge size="sm" variant="solid" action="muted">
<BadgeText>管理员</BadgeText>
</Badge>
</HStack>
<Text size="sm">Marketing Coordinator</Text>
</VStack>
<View className="ml-auto">
<Menu
placement="top"
offset={5}
trigger={triggerProps => (
<Pressable
{...triggerProps}
style={{
padding: 8,
justifyContent: 'center',
alignItems: 'center',
}}
>
<LucideIcon icon="EllipsisVertical" size={20} clickable={false} />
</Pressable>
)}
>
<MenuItem key="Add account" textValue="Add account">
<MenuItemLabel size="sm">添加为管理员</MenuItemLabel>
</MenuItem>
<MenuItem key="Community" textValue="Community">
<MenuItemLabel size="sm">移出空间</MenuItemLabel>
</MenuItem>
</Menu>
</View>
</HStack>
</TouchableOpacity>
)}
/>
</View>
<AButton size="sm" className="mb-8 mt-4">退出该空间</AButton>
</View>
</View>
)
}
@@ -0,0 +1,21 @@
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'
interface StepFadeProps {
visible: boolean
children: React.ReactNode
}
export default function StepFade({ visible, children }: StepFadeProps) {
if (!visible)
return null
return (
<Animated.View
entering={FadeIn.duration(200)}
exiting={FadeOut.duration(200)}
style={{ flex: 1 }}
>
{children}
</Animated.View>
)
}
@@ -1,241 +1,41 @@
import type { AFormRef } from '@/components/common/ao'
import * as React from 'react'
import { useRef, useState } from 'react'
import { FlatList, Pressable, TouchableOpacity, View } from 'react-native'
import QRCode from 'react-native-qrcode-svg'
import { AButton, ADivider, AForm, AFormItem, AInput, LucideIcon } from '@/components/common/ao'
import { useState } from 'react'
import { View } from 'react-native'
import CustomHeader from '@/components/custom/Header'
import {
Accordion,
AccordionContent,
AccordionHeader,
AccordionIcon,
AccordionItem,
AccordionTitleText,
AccordionTrigger,
} from '@/components/ui/accordion'
import {
Avatar,
AvatarBadge,
AvatarFallbackText,
AvatarImage,
} from '@/components/ui/avatar'
import { Badge, BadgeText } from '@/components/ui/badge'
import { Heading } from '@/components/ui/heading'
import { HStack } from '@/components/ui/hstack'
import { ChevronDownIcon, ChevronUpIcon } from '@/components/ui/icon'
import { Link, LinkText } from '@/components/ui/link'
import { Menu, MenuItem, MenuItemLabel } from '@/components/ui/menu'
import { Text } from '@/components/ui/text'
import { VStack } from '@/components/ui/vstack'
import JoinSpace from './components/JoinSpace'
import SpaceHome from './components/SpaceHome'
import SpaceInviteAndMembers from './components/SpaceInviteAndMembers'
import StepFade from './components/StepFade'
export default function PagerWithTab() {
const formRef = useRef<AFormRef>(null)
const [form, setForm] = useState({
code: '',
})
const [accordion, setAccordion] = useState(['a'])
function EnterSapce() {
return (
<View className="px-4 items-center">
<AForm ref={formRef} form={form}>
<AFormItem
name="username"
label="邀请码"
>
<AInput placeholder="请输入邀请码"></AInput>
</AFormItem>
</AForm>
<AButton className="w-full">加入空间</AButton>
<View
className="flex-row items-center w-full"
style={{
marginTop: 16,
marginBottom: 16,
}}
>
<View className="flex-1">
<ADivider />
</View>
<Text className="mx-3 text-gray-500 text-sm">或</Text>
<View className="flex-1">
<ADivider />
</View>
</View>
<AButton variant="outline" className="w-full">扫码加入</AButton>
<Link className="mt-4">
<LinkText>返回</LinkText>
</Link>
</View>
)
}
const members = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 3, name: '王五' },
]
const [step, setStep] = useState<'join' | 'invite' | 'home'>('home')
return (
<View className="flex-1">
<CustomHeader title="共享空间" />
<View className="flex-1">
{/* <View className="p-4">
<View className="flex-col gap-4 justify-center items-center">
<LucideIcon icon="Earth" size={48} strokeWidth={1.5} />
<Text className="font-semibold text-xl">创建共享空间</Text>
<Text className="text-base text-center">创建一个专属空间,邀请朋友加入,共享文件、聊天和协作</Text>
<AButton className="w-full">创建我的空间</AButton>
</View>
<View
className="flex-row items-center w-full"
style={{
marginTop: 16,
marginBottom: 16,
<StepFade visible={step === 'home'}>
<SpaceHome
onCreate={() => {
setStep('invite')
}}
>
<View className="flex-1">
<Divider />
</View>
<Text className="mx-3 text-gray-500 text-sm">或</Text>
<View className="flex-1">
<Divider />
</View>
</View>
<AButton variant="outline">加入已有空间</AButton>
</View> */}
{/* <View className="px-4 flex-1">
<Accordion
size="md"
variant="unfilled"
type="multiple"
isCollapsible={true}
isDisabled={false}
value={accordion}
onValueChange={(value) => {
setAccordion(value)
onJoin={() => {
setStep('join')
}}
>
<AccordionItem value="a">
<AccordionHeader>
<AccordionTrigger
style={{
paddingLeft: 0,
paddingRight: 0,
}}
>
{({ isExpanded }: { isExpanded: boolean }) => {
return (
<>
<AccordionTitleText className="font-semibold text-lg">
我的邀请码
</AccordionTitleText>
{isExpanded
? (
<AccordionIcon as={ChevronUpIcon} className="ml-3" />
)
: (
<AccordionIcon as={ChevronDownIcon} className="ml-3" />
)}
</>
)
}}
</AccordionTrigger>
</AccordionHeader>
<AccordionContent style={{
marginBottom: 0,
paddingBottom: 0,
}}
>
<TouchableOpacity className="flex-row items-center justify-center ">
<Text className="font-bold text-3xl tracking-[4px] mr-3">123456</Text>
<LucideIcon icon="Copy" size={16} color="blue" clickable={false} />
</TouchableOpacity>
/>
</StepFade>
<View className="items-center justify-center">
<ADivider direction="horizontal" text="扫描二维码" />
<QRCode
size={120}
value="http://awesome.link.qr"
/>
</View>
</AccordionContent>
</AccordionItem>
</Accordion>
<View className="mt-4 flex-1">
<View className="flex flex-row items-center justify-between">
<Text className="font-semibold text-lg">空间成员</Text>
<Badge size="lg" variant="solid" action="muted">
<BadgeText>3 人</BadgeText>
</Badge>
</View>
<StepFade visible={step === 'join'}>
<JoinSpace
onBack={() => {
setStep('home')
}}
/>
</StepFade>
<View>
<FlatList
data={members}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => (
<TouchableOpacity>
<HStack space="md" className="my-2 items-center">
<Avatar className="bg-indigo-600">
<AvatarFallbackText className="text-white text-lg">
{item.name}
</AvatarFallbackText>
<AvatarBadge />
</Avatar>
<VStack>
<HStack space="sm">
<Heading size="sm">{item.name}</Heading>
<Badge size="sm" variant="solid" action="muted">
<BadgeText>管理员</BadgeText>
</Badge>
</HStack>
<Text size="sm">Marketing Coordinator</Text>
</VStack>
<View className="ml-auto">
<Menu
placement="top"
offset={5}
trigger={triggerProps => (
<Pressable
{...triggerProps}
style={{
padding: 8,
justifyContent: 'center',
alignItems: 'center',
}}
>
<LucideIcon icon="EllipsisVertical" size={20} clickable={false} />
</Pressable>
)}
>
<MenuItem key="Add account" textValue="Add account">
<MenuItemLabel size="sm">添加为管理员</MenuItemLabel>
</MenuItem>
<MenuItem key="Community" textValue="Community">
<MenuItemLabel size="sm">移出空间</MenuItemLabel>
</MenuItem>
</Menu>
</View>
</HStack>
</TouchableOpacity>
)}
/>
</View>
<AButton size="sm" className="mb-8 mt-4">退出该空间</AButton>
</View>
</View> */}
<EnterSapce />
<StepFade visible={step === 'invite'}>
<SpaceInviteAndMembers />
</StepFade>
</View>
</View>
)
@@ -32,8 +32,8 @@ function isWhiteListed(url: string) {
}
const alovaInstance = createAlova({
baseURL: 'http://192.168.5.6:8086/app',
// baseURL: 'http://192.168.1.136:8086/app',
// baseURL: 'http://192.168.5.6:8086/app',
baseURL: 'http://192.168.1.136:8086/app',
requestAdapter: axiosRequestAdapter(),
timeout: 5000,
cacheFor: null,