@open-mercato/ui 0.6.4-develop.3962.1.70f30e284c → 0.6.4-develop.3976.1.4e72e60206
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -26,6 +26,7 @@ function ConversationShareDialog({ open, onOpenChange, conversationId }) {
|
|
|
26
26
|
const t = useT();
|
|
27
27
|
const currentUserId = useCurrentUserId();
|
|
28
28
|
const [participants, setParticipants] = React.useState([]);
|
|
29
|
+
const [ownerUserId, setOwnerUserId] = React.useState(null);
|
|
29
30
|
const [users, setUsers] = React.useState([]);
|
|
30
31
|
const [canListUsers, setCanListUsers] = React.useState(true);
|
|
31
32
|
const [loading, setLoading] = React.useState(false);
|
|
@@ -43,6 +44,7 @@ function ConversationShareDialog({ open, onOpenChange, conversationId }) {
|
|
|
43
44
|
const res = await apiCall(baseUrl);
|
|
44
45
|
if (!res.ok || !res.result) throw new Error("fetch failed");
|
|
45
46
|
setParticipants(res.result.participants.filter((p) => p.role !== "owner"));
|
|
47
|
+
setOwnerUserId(res.result.ownerUserId ?? null);
|
|
46
48
|
} catch {
|
|
47
49
|
setError(t("common.error", "Something went wrong."));
|
|
48
50
|
} finally {
|
|
@@ -78,8 +80,10 @@ function ConversationShareDialog({ open, onOpenChange, conversationId }) {
|
|
|
78
80
|
[participants]
|
|
79
81
|
);
|
|
80
82
|
const availableUsers = React.useMemo(
|
|
81
|
-
() => users.filter(
|
|
82
|
-
|
|
83
|
+
() => users.filter(
|
|
84
|
+
(u) => !participantIds.has(u.id) && u.id !== currentUserId && u.id !== ownerUserId
|
|
85
|
+
),
|
|
86
|
+
[users, participantIds, currentUserId, ownerUserId]
|
|
83
87
|
);
|
|
84
88
|
const activeUserId = canListUsers ? selectedUserId : textUserId.trim();
|
|
85
89
|
const handleAdd = React.useCallback(async () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/ai/ConversationShareDialog.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { UserPlus, X, Loader2 } from 'lucide-react'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '../primitives/dialog'\nimport { Button } from '../primitives/button'\nimport { Input } from '../primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '../primitives/select'\nimport { apiCall } from '../backend/utils/apiCall'\nimport { useCurrentUserId } from '../backend/utils/useCurrentUserId'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ninterface Participant {\n userId: string\n role: string\n lastReadAt: string | null\n addedAt: string\n}\n\ninterface UserOption {\n id: string\n email: string\n name: string | null\n}\n\ninterface Props {\n open: boolean\n onOpenChange: (next: boolean) => void\n conversationId: string\n}\n\nexport function ConversationShareDialog({ open, onOpenChange, conversationId }: Props) {\n const t = useT()\n const currentUserId = useCurrentUserId()\n const [participants, setParticipants] = React.useState<Participant[]>([])\n const [users, setUsers] = React.useState<UserOption[]>([])\n const [canListUsers, setCanListUsers] = React.useState(true)\n const [loading, setLoading] = React.useState(false)\n const [loadingUsers, setLoadingUsers] = React.useState(false)\n const [selectedUserId, setSelectedUserId] = React.useState('')\n const [textUserId, setTextUserId] = React.useState('')\n const [adding, setAdding] = React.useState(false)\n const [removingId, setRemovingId] = React.useState<string | null>(null)\n const [error, setError] = React.useState<string | null>(null)\n\n const baseUrl = `/api/ai_assistant/ai/conversations/${conversationId}/participants`\n\n const fetchParticipants = React.useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const res = await apiCall<{ participants: Participant[] }>(baseUrl)\n if (!res.ok || !res.result) throw new Error('fetch failed')\n setParticipants(res.result.participants.filter((p) => p.role !== 'owner'))\n } catch {\n setError(t('common.error', 'Something went wrong.'))\n } finally {\n setLoading(false)\n }\n }, [conversationId, t])\n\n const fetchUsers = React.useCallback(async () => {\n setLoadingUsers(true)\n try {\n const res = await apiCall<{ items: UserOption[] }>('/api/auth/users?limit=200')\n if (res.ok && res.result) {\n setUsers(res.result.items)\n setCanListUsers(true)\n } else {\n setCanListUsers(false)\n }\n } catch {\n setCanListUsers(false)\n } finally {\n setLoadingUsers(false)\n }\n }, [])\n\n React.useEffect(() => {\n if (open) {\n setSelectedUserId('')\n setTextUserId('')\n setError(null)\n void Promise.all([fetchParticipants(), fetchUsers()])\n }\n }, [open, fetchParticipants, fetchUsers])\n\n const participantIds = React.useMemo(\n () => new Set(participants.map((p) => p.userId)),\n [participants],\n )\n\n const availableUsers = React.useMemo(\n () => users.filter((u) => !participantIds.has(u.id) && u.id !== currentUserId),\n [users, participantIds, currentUserId],\n )\n\n const activeUserId = canListUsers ? selectedUserId : textUserId.trim()\n\n const handleAdd = React.useCallback(async () => {\n if (!activeUserId) return\n setAdding(true)\n setError(null)\n try {\n const res = await apiCall<{ participant: Participant }>(baseUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ userId: activeUserId, role: 'viewer' }),\n })\n if (!res.ok || !res.result) throw new Error('fetch failed')\n setParticipants((prev) => {\n const next = res.result!.participant\n const exists = prev.find((p) => p.userId === next.userId)\n if (exists) return prev.map((p) => (p.userId === next.userId ? next : p))\n return [...prev, next]\n })\n setSelectedUserId('')\n setTextUserId('')\n } catch {\n setError(t('common.error', 'Something went wrong.'))\n } finally {\n setAdding(false)\n }\n }, [activeUserId, baseUrl, t])\n\n React.useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n if (activeUserId && !adding) void handleAdd()\n }\n }\n if (open) document.addEventListener('keydown', handler)\n return () => document.removeEventListener('keydown', handler)\n }, [open, activeUserId, adding, handleAdd])\n\n const handleRemove = async (userId: string) => {\n setRemovingId(userId)\n setError(null)\n try {\n await apiCall(`${baseUrl}/${userId}`, { method: 'DELETE', headers: { 'content-type': 'application/json' } })\n setParticipants((prev) => prev.filter((p) => p.userId !== userId))\n } catch {\n setError(t('common.error', 'Something went wrong.'))\n } finally {\n setRemovingId(null)\n }\n }\n\n const formatUserLabel = (u: UserOption) => (u.name ? `${u.name} \u2014 ${u.email}` : u.email)\n\n const getUserLabel = (userId: string) => {\n const u = users.find((u) => u.id === userId)\n return u ? formatUserLabel(u) : userId\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>\n {t('ai_assistant.share.dialogTitle', 'Share Conversation')}\n </DialogTitle>\n <DialogDescription>\n {t(\n 'ai_assistant.share.dialogDescription',\n 'Share this conversation with other users. They will get read-only access.',\n )}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <div className=\"flex gap-2\">\n {canListUsers === false ? (\n <Input\n placeholder={t('ai_assistant.share.participantPlaceholder', 'User ID...')}\n value={textUserId}\n onChange={(e) => setTextUserId(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') { e.preventDefault(); void handleAdd() }\n }}\n className=\"flex-1\"\n />\n ) : (\n <Select\n value={selectedUserId}\n onValueChange={setSelectedUserId}\n disabled={loadingUsers || availableUsers.length === 0}\n >\n <SelectTrigger className=\"flex-1\">\n <SelectValue\n placeholder={\n loadingUsers\n ? t('common.loading', 'Loading...')\n : availableUsers.length === 0\n ? t('ai_assistant.share.allUsersAdded', 'All users already added')\n : t('ai_assistant.share.selectUser', 'Select a user...')\n }\n />\n </SelectTrigger>\n <SelectContent>\n {availableUsers.map((u) => (\n <SelectItem key={u.id} value={u.id}>\n {formatUserLabel(u)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <Button\n type=\"button\"\n onClick={() => void handleAdd()}\n disabled={!activeUserId || adding}\n size=\"default\"\n >\n {adding ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <UserPlus className=\"size-4\" />\n )}\n {t('ai_assistant.share.addParticipant', 'Add')}\n </Button>\n </div>\n\n {error ? (\n <p className=\"text-sm text-status-danger-text\">{error}</p>\n ) : null}\n\n <div className=\"space-y-2 min-h-[60px]\">\n {loading ? (\n <div className=\"flex items-center justify-center py-4\">\n <Loader2 className=\"size-5 animate-spin text-muted-foreground\" />\n </div>\n ) : participants.length === 0 ? (\n <p className=\"text-sm text-muted-foreground py-2\">\n {t(\n 'ai_assistant.share.noParticipants',\n 'No participants yet. Add someone to share this conversation.',\n )}\n </p>\n ) : (\n participants.map((p) => (\n <div\n key={p.userId}\n className=\"flex items-center justify-between rounded-lg border border-border px-3 py-2\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm text-foreground\">{getUserLabel(p.userId)}</p>\n <p className=\"text-xs text-muted-foreground capitalize\">{p.role}</p>\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => void handleRemove(p.userId)}\n disabled={removingId === p.userId}\n aria-label={t('ai_assistant.share.removeParticipant', 'Remove')}\n >\n {removingId === p.userId ? (\n <Loader2 className=\"size-3 animate-spin\" />\n ) : (\n <X className=\"size-4\" />\n )}\n </Button>\n </div>\n ))\n )}\n </div>\n </div>\n\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => onOpenChange(false)}>\n {t('common.close', 'Close')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n\nexport default ConversationShareDialog\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { UserPlus, X, Loader2 } from 'lucide-react'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '../primitives/dialog'\nimport { Button } from '../primitives/button'\nimport { Input } from '../primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '../primitives/select'\nimport { apiCall } from '../backend/utils/apiCall'\nimport { useCurrentUserId } from '../backend/utils/useCurrentUserId'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ninterface Participant {\n userId: string\n role: string\n lastReadAt: string | null\n addedAt: string\n}\n\ninterface UserOption {\n id: string\n email: string\n name: string | null\n}\n\ninterface Props {\n open: boolean\n onOpenChange: (next: boolean) => void\n conversationId: string\n}\n\nexport function ConversationShareDialog({ open, onOpenChange, conversationId }: Props) {\n const t = useT()\n const currentUserId = useCurrentUserId()\n const [participants, setParticipants] = React.useState<Participant[]>([])\n const [ownerUserId, setOwnerUserId] = React.useState<string | null>(null)\n const [users, setUsers] = React.useState<UserOption[]>([])\n const [canListUsers, setCanListUsers] = React.useState(true)\n const [loading, setLoading] = React.useState(false)\n const [loadingUsers, setLoadingUsers] = React.useState(false)\n const [selectedUserId, setSelectedUserId] = React.useState('')\n const [textUserId, setTextUserId] = React.useState('')\n const [adding, setAdding] = React.useState(false)\n const [removingId, setRemovingId] = React.useState<string | null>(null)\n const [error, setError] = React.useState<string | null>(null)\n\n const baseUrl = `/api/ai_assistant/ai/conversations/${conversationId}/participants`\n\n const fetchParticipants = React.useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const res = await apiCall<{ participants: Participant[]; ownerUserId?: string | null }>(baseUrl)\n if (!res.ok || !res.result) throw new Error('fetch failed')\n setParticipants(res.result.participants.filter((p) => p.role !== 'owner'))\n setOwnerUserId(res.result.ownerUserId ?? null)\n } catch {\n setError(t('common.error', 'Something went wrong.'))\n } finally {\n setLoading(false)\n }\n }, [conversationId, t])\n\n const fetchUsers = React.useCallback(async () => {\n setLoadingUsers(true)\n try {\n const res = await apiCall<{ items: UserOption[] }>('/api/auth/users?limit=200')\n if (res.ok && res.result) {\n setUsers(res.result.items)\n setCanListUsers(true)\n } else {\n setCanListUsers(false)\n }\n } catch {\n setCanListUsers(false)\n } finally {\n setLoadingUsers(false)\n }\n }, [])\n\n React.useEffect(() => {\n if (open) {\n setSelectedUserId('')\n setTextUserId('')\n setError(null)\n void Promise.all([fetchParticipants(), fetchUsers()])\n }\n }, [open, fetchParticipants, fetchUsers])\n\n const participantIds = React.useMemo(\n () => new Set(participants.map((p) => p.userId)),\n [participants],\n )\n\n const availableUsers = React.useMemo(\n () =>\n users.filter(\n (u) => !participantIds.has(u.id) && u.id !== currentUserId && u.id !== ownerUserId,\n ),\n [users, participantIds, currentUserId, ownerUserId],\n )\n\n const activeUserId = canListUsers ? selectedUserId : textUserId.trim()\n\n const handleAdd = React.useCallback(async () => {\n if (!activeUserId) return\n setAdding(true)\n setError(null)\n try {\n const res = await apiCall<{ participant: Participant }>(baseUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ userId: activeUserId, role: 'viewer' }),\n })\n if (!res.ok || !res.result) throw new Error('fetch failed')\n setParticipants((prev) => {\n const next = res.result!.participant\n const exists = prev.find((p) => p.userId === next.userId)\n if (exists) return prev.map((p) => (p.userId === next.userId ? next : p))\n return [...prev, next]\n })\n setSelectedUserId('')\n setTextUserId('')\n } catch {\n setError(t('common.error', 'Something went wrong.'))\n } finally {\n setAdding(false)\n }\n }, [activeUserId, baseUrl, t])\n\n React.useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n if (activeUserId && !adding) void handleAdd()\n }\n }\n if (open) document.addEventListener('keydown', handler)\n return () => document.removeEventListener('keydown', handler)\n }, [open, activeUserId, adding, handleAdd])\n\n const handleRemove = async (userId: string) => {\n setRemovingId(userId)\n setError(null)\n try {\n await apiCall(`${baseUrl}/${userId}`, { method: 'DELETE', headers: { 'content-type': 'application/json' } })\n setParticipants((prev) => prev.filter((p) => p.userId !== userId))\n } catch {\n setError(t('common.error', 'Something went wrong.'))\n } finally {\n setRemovingId(null)\n }\n }\n\n const formatUserLabel = (u: UserOption) => (u.name ? `${u.name} \u2014 ${u.email}` : u.email)\n\n const getUserLabel = (userId: string) => {\n const u = users.find((u) => u.id === userId)\n return u ? formatUserLabel(u) : userId\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>\n {t('ai_assistant.share.dialogTitle', 'Share Conversation')}\n </DialogTitle>\n <DialogDescription>\n {t(\n 'ai_assistant.share.dialogDescription',\n 'Share this conversation with other users. They will get read-only access.',\n )}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <div className=\"flex gap-2\">\n {canListUsers === false ? (\n <Input\n placeholder={t('ai_assistant.share.participantPlaceholder', 'User ID...')}\n value={textUserId}\n onChange={(e) => setTextUserId(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') { e.preventDefault(); void handleAdd() }\n }}\n className=\"flex-1\"\n />\n ) : (\n <Select\n value={selectedUserId}\n onValueChange={setSelectedUserId}\n disabled={loadingUsers || availableUsers.length === 0}\n >\n <SelectTrigger className=\"flex-1\">\n <SelectValue\n placeholder={\n loadingUsers\n ? t('common.loading', 'Loading...')\n : availableUsers.length === 0\n ? t('ai_assistant.share.allUsersAdded', 'All users already added')\n : t('ai_assistant.share.selectUser', 'Select a user...')\n }\n />\n </SelectTrigger>\n <SelectContent>\n {availableUsers.map((u) => (\n <SelectItem key={u.id} value={u.id}>\n {formatUserLabel(u)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <Button\n type=\"button\"\n onClick={() => void handleAdd()}\n disabled={!activeUserId || adding}\n size=\"default\"\n >\n {adding ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <UserPlus className=\"size-4\" />\n )}\n {t('ai_assistant.share.addParticipant', 'Add')}\n </Button>\n </div>\n\n {error ? (\n <p className=\"text-sm text-status-danger-text\">{error}</p>\n ) : null}\n\n <div className=\"space-y-2 min-h-[60px]\">\n {loading ? (\n <div className=\"flex items-center justify-center py-4\">\n <Loader2 className=\"size-5 animate-spin text-muted-foreground\" />\n </div>\n ) : participants.length === 0 ? (\n <p className=\"text-sm text-muted-foreground py-2\">\n {t(\n 'ai_assistant.share.noParticipants',\n 'No participants yet. Add someone to share this conversation.',\n )}\n </p>\n ) : (\n participants.map((p) => (\n <div\n key={p.userId}\n className=\"flex items-center justify-between rounded-lg border border-border px-3 py-2\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm text-foreground\">{getUserLabel(p.userId)}</p>\n <p className=\"text-xs text-muted-foreground capitalize\">{p.role}</p>\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => void handleRemove(p.userId)}\n disabled={removingId === p.userId}\n aria-label={t('ai_assistant.share.removeParticipant', 'Remove')}\n >\n {removingId === p.userId ? (\n <Loader2 className=\"size-3 animate-spin\" />\n ) : (\n <X className=\"size-4\" />\n )}\n </Button>\n </div>\n ))\n )}\n </div>\n </div>\n\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => onOpenChange(false)}>\n {t('common.close', 'Close')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n\nexport default ConversationShareDialog\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["u"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/ui",
|
|
3
|
-
"version": "0.6.4-develop.
|
|
3
|
+
"version": "0.6.4-develop.3976.1.4e72e60206",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -141,13 +141,13 @@
|
|
|
141
141
|
"remark-gfm": "^4.0.1"
|
|
142
142
|
},
|
|
143
143
|
"peerDependencies": {
|
|
144
|
-
"@open-mercato/shared": "0.6.4-develop.
|
|
144
|
+
"@open-mercato/shared": "0.6.4-develop.3976.1.4e72e60206",
|
|
145
145
|
"react": ">=18.0.0",
|
|
146
146
|
"react-dom": ">=18.0.0",
|
|
147
147
|
"react-is": ">=18.0.0"
|
|
148
148
|
},
|
|
149
149
|
"devDependencies": {
|
|
150
|
-
"@open-mercato/shared": "0.6.4-develop.
|
|
150
|
+
"@open-mercato/shared": "0.6.4-develop.3976.1.4e72e60206",
|
|
151
151
|
"@testing-library/dom": "^10.4.1",
|
|
152
152
|
"@testing-library/jest-dom": "^6.9.1",
|
|
153
153
|
"@testing-library/react": "^16.3.1",
|
|
@@ -46,6 +46,7 @@ export function ConversationShareDialog({ open, onOpenChange, conversationId }:
|
|
|
46
46
|
const t = useT()
|
|
47
47
|
const currentUserId = useCurrentUserId()
|
|
48
48
|
const [participants, setParticipants] = React.useState<Participant[]>([])
|
|
49
|
+
const [ownerUserId, setOwnerUserId] = React.useState<string | null>(null)
|
|
49
50
|
const [users, setUsers] = React.useState<UserOption[]>([])
|
|
50
51
|
const [canListUsers, setCanListUsers] = React.useState(true)
|
|
51
52
|
const [loading, setLoading] = React.useState(false)
|
|
@@ -62,9 +63,10 @@ export function ConversationShareDialog({ open, onOpenChange, conversationId }:
|
|
|
62
63
|
setLoading(true)
|
|
63
64
|
setError(null)
|
|
64
65
|
try {
|
|
65
|
-
const res = await apiCall<{ participants: Participant[] }>(baseUrl)
|
|
66
|
+
const res = await apiCall<{ participants: Participant[]; ownerUserId?: string | null }>(baseUrl)
|
|
66
67
|
if (!res.ok || !res.result) throw new Error('fetch failed')
|
|
67
68
|
setParticipants(res.result.participants.filter((p) => p.role !== 'owner'))
|
|
69
|
+
setOwnerUserId(res.result.ownerUserId ?? null)
|
|
68
70
|
} catch {
|
|
69
71
|
setError(t('common.error', 'Something went wrong.'))
|
|
70
72
|
} finally {
|
|
@@ -104,8 +106,11 @@ export function ConversationShareDialog({ open, onOpenChange, conversationId }:
|
|
|
104
106
|
)
|
|
105
107
|
|
|
106
108
|
const availableUsers = React.useMemo(
|
|
107
|
-
() =>
|
|
108
|
-
|
|
109
|
+
() =>
|
|
110
|
+
users.filter(
|
|
111
|
+
(u) => !participantIds.has(u.id) && u.id !== currentUserId && u.id !== ownerUserId,
|
|
112
|
+
),
|
|
113
|
+
[users, participantIds, currentUserId, ownerUserId],
|
|
109
114
|
)
|
|
110
115
|
|
|
111
116
|
const activeUserId = canListUsers ? selectedUserId : textUserId.trim()
|