fix: optimize view of group bottom bar in mobile mode
This commit is contained in:
@@ -6,12 +6,13 @@ import { resolveAssetUrl } from '@/lib/api/assets'
|
||||
import {
|
||||
RefreshCw,
|
||||
Image as ImageIcon,
|
||||
Camera,
|
||||
Mic,
|
||||
Square,
|
||||
Send,
|
||||
Activity,
|
||||
Smile,
|
||||
Plus,
|
||||
X,
|
||||
} from 'lucide-react'
|
||||
import { Button, Badge } from '@/components/common'
|
||||
import { getDeviceMemberRoleLabel } from '@/features/team/constants'
|
||||
@@ -57,6 +58,7 @@ export function GroupFeed({ deviceId }: GroupFeedProps) {
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
const [text, setText] = useState('')
|
||||
const [showMore, setShowMore] = useState(false)
|
||||
const [showEmoji, setShowEmoji] = useState(false)
|
||||
const [sending, setSending] = useState(false)
|
||||
const [recording, setRecording] = useState(false)
|
||||
@@ -69,7 +71,6 @@ export function GroupFeed({ deviceId }: GroupFeedProps) {
|
||||
const recordTimerRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
const bottomRef = useRef<HTMLDivElement>(null)
|
||||
const galleryInputRef = useRef<HTMLInputElement>(null)
|
||||
const cameraInputRef = useRef<HTMLInputElement>(null)
|
||||
const recordSecondsRef = useRef(0)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -210,6 +211,7 @@ export function GroupFeed({ deviceId }: GroupFeedProps) {
|
||||
recordSecondsRef.current = 0
|
||||
recorder.start()
|
||||
setRecording(true)
|
||||
setShowMore(false)
|
||||
|
||||
recordTimerRef.current = setInterval(() => {
|
||||
recordSecondsRef.current += 1
|
||||
@@ -273,16 +275,6 @@ export function GroupFeed({ deviceId }: GroupFeedProps) {
|
||||
<div className="flex items-center justify-between px-4 py-3 border-b border-gray-100 bg-gray-50">
|
||||
<h3 className="text-sm font-semibold text-gray-800">گفتگو</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
icon={Activity}
|
||||
onClick={sendSnapshot}
|
||||
loading={sending}
|
||||
tooltip="ارسال وضعیت سنسورها و رلهها"
|
||||
>
|
||||
وضعیت سنسورها
|
||||
</Button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={refresh}
|
||||
@@ -328,6 +320,16 @@ export function GroupFeed({ deviceId }: GroupFeedProps) {
|
||||
<div ref={bottomRef} />
|
||||
</div>
|
||||
|
||||
{/* More actions panel */}
|
||||
{showMore && (
|
||||
<div className="flex items-center gap-1 px-3 py-2 border-t border-gray-100 bg-white">
|
||||
<MoreAction icon={Smile} label="ایموجی" active={showEmoji} onClick={() => setShowEmoji(v => !v)} />
|
||||
<MoreAction icon={ImageIcon} label="عکس" onClick={() => { setShowMore(false); galleryInputRef.current?.click() }} />
|
||||
<MoreAction icon={Mic} label="ویس" onClick={startRecording} />
|
||||
<MoreAction icon={Activity} label="وضعیت سنسورها" disabled={sending} onClick={sendSnapshot} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Emoji row */}
|
||||
{showEmoji && (
|
||||
<div className="flex flex-wrap gap-1 px-4 py-2 border-t border-gray-100 bg-white">
|
||||
@@ -346,47 +348,25 @@ export function GroupFeed({ deviceId }: GroupFeedProps) {
|
||||
|
||||
{/* Composer */}
|
||||
<div className="border-t border-gray-100 p-3 bg-white">
|
||||
<input
|
||||
ref={galleryInputRef}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className="hidden"
|
||||
onChange={handleGalleryFile}
|
||||
/>
|
||||
|
||||
<div className="flex items-end gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowEmoji(v => !v)}
|
||||
aria-label="ایموجی"
|
||||
className="p-2 rounded-lg text-gray-500 hover:text-gray-700 hover:bg-gray-100 transition-colors"
|
||||
onClick={() => setShowMore(v => !v)}
|
||||
aria-label={showMore ? 'بستن گزینهها' : 'گزینههای بیشتر'}
|
||||
className={cn(
|
||||
'p-2 rounded-lg transition-colors',
|
||||
showMore ? 'bg-green-50 text-green-600' : 'text-gray-500 hover:text-gray-700 hover:bg-gray-100'
|
||||
)}
|
||||
>
|
||||
<Smile className="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
<input
|
||||
ref={galleryInputRef}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className="hidden"
|
||||
onChange={handleGalleryFile}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => galleryInputRef.current?.click()}
|
||||
aria-label="انتخاب عکس از گالری"
|
||||
className="p-2 rounded-lg text-gray-500 hover:text-gray-700 hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
<ImageIcon className="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
<input
|
||||
ref={cameraInputRef}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
capture="environment"
|
||||
className="hidden"
|
||||
onChange={handleGalleryFile}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => cameraInputRef.current?.click()}
|
||||
aria-label="عکس با دوربین"
|
||||
className="p-2 rounded-lg text-gray-500 hover:text-gray-700 hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
<Camera className="w-5 h-5" />
|
||||
{showMore ? <X className="w-5 h-5" /> : <Plus className="w-5 h-5" />}
|
||||
</button>
|
||||
|
||||
<textarea
|
||||
@@ -400,26 +380,17 @@ export function GroupFeed({ deviceId }: GroupFeedProps) {
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={recording ? stopRecording : startRecording}
|
||||
aria-label={recording ? 'توقف ضبط' : 'ضبط ویس'}
|
||||
onClick={recording ? stopRecording : sendText}
|
||||
disabled={!recording && (!text.trim() || sending)}
|
||||
aria-label={recording ? 'توقف ضبط' : 'ارسال'}
|
||||
className={cn(
|
||||
'p-2 rounded-lg transition-colors',
|
||||
recording
|
||||
? 'bg-red-50 text-red-600 animate-pulse'
|
||||
: 'text-gray-500 hover:text-gray-700 hover:bg-gray-100'
|
||||
? 'bg-red-500 text-white'
|
||||
: 'bg-green-600 text-white hover:bg-green-700 disabled:opacity-40'
|
||||
)}
|
||||
>
|
||||
{recording ? <Square className="w-5 h-5" /> : <Mic className="w-5 h-5" />}
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={sendText}
|
||||
disabled={!text.trim() || sending}
|
||||
aria-label="ارسال"
|
||||
className="p-2 rounded-lg bg-green-600 text-white hover:bg-green-700 disabled:opacity-40 transition-colors"
|
||||
>
|
||||
<Send className="w-5 h-5" />
|
||||
{recording ? <Square className="w-5 h-5" /> : <Send className="w-5 h-5" />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -450,6 +421,36 @@ export function GroupFeed({ deviceId }: GroupFeedProps) {
|
||||
)
|
||||
}
|
||||
|
||||
function MoreAction({
|
||||
icon: Icon,
|
||||
label,
|
||||
active,
|
||||
disabled,
|
||||
onClick,
|
||||
}: {
|
||||
icon: React.ComponentType<{ className?: string }>
|
||||
label: string
|
||||
active?: boolean
|
||||
disabled?: boolean
|
||||
onClick: () => void
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
aria-label={label}
|
||||
className={cn(
|
||||
'flex flex-col items-center gap-1 px-2 py-1.5 rounded-lg transition-colors disabled:opacity-40',
|
||||
active ? 'bg-green-50 text-green-600' : 'text-gray-500 hover:text-gray-700 hover:bg-gray-100'
|
||||
)}
|
||||
>
|
||||
<Icon className="w-5 h-5" />
|
||||
<span className="text-[10px] leading-none">{label}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function PostBubble({ post, onImageClick }: { post: DevicePostDto; onImageClick: (src: string) => void }) {
|
||||
const fullName = `${post.authorName} ${post.authorFamily}`.trim() || 'بدون نام'
|
||||
const isSnapshot = post.postType === 3
|
||||
|
||||
Reference in New Issue
Block a user