pukaad-ui-lib 1.460.0 → 1.462.0
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.
- package/dist/module.json +1 -1
- package/dist/runtime/components/card/card-mention/card-mention.d.vue.ts +2 -1
- package/dist/runtime/components/card/card-mention/card-mention.vue +3 -2
- package/dist/runtime/components/card/card-mention/card-mention.vue.d.ts +2 -1
- package/dist/runtime/components/chat/chat-message-bubble.d.vue.ts +3 -1
- package/dist/runtime/components/chat/chat-message-bubble.vue +16 -2
- package/dist/runtime/components/chat/chat-message-bubble.vue.d.ts +3 -1
- package/dist/runtime/components/chat/chat-room.d.vue.ts +6 -1
- package/dist/runtime/components/chat/chat-room.vue +20 -1
- package/dist/runtime/components/chat/chat-room.vue.d.ts +6 -1
- package/dist/runtime/components/draggable.d.vue.ts +26 -3
- package/dist/runtime/components/draggable.vue +67 -20
- package/dist/runtime/components/draggable.vue.d.ts +26 -3
- package/dist/runtime/components/input/input-chat.d.vue.ts +2 -0
- package/dist/runtime/components/input/input-chat.vue +7 -2
- package/dist/runtime/components/input/input-chat.vue.d.ts +2 -0
- package/dist/runtime/components/input/input-comment.d.vue.ts +1 -1
- package/dist/runtime/components/input/input-comment.vue.d.ts +1 -1
- package/dist/runtime/components/input/input-mention.vue +4 -20
- package/dist/runtime/components/modal/modal-chat-share.d.vue.ts +17 -0
- package/dist/runtime/components/modal/modal-chat-share.vue +214 -0
- package/dist/runtime/components/modal/modal-chat-share.vue.d.ts +17 -0
- package/dist/runtime/components/modal/modal-password-confirmed.d.vue.ts +1 -1
- package/dist/runtime/components/modal/modal-password-confirmed.vue.d.ts +1 -1
- package/dist/runtime/components/modal/modal-password-verify.d.vue.ts +1 -1
- package/dist/runtime/components/modal/modal-password-verify.vue.d.ts +1 -1
- package/dist/runtime/composables/store-chat.d.ts +83 -2
- package/dist/runtime/composables/store-chat.js +39 -5
- package/dist/runtime/composables/useChatSession.d.ts +82 -1
- package/dist/runtime/types/chat.d.ts +16 -1
- package/dist/runtime/utils/mention.d.ts +39 -0
- package/dist/runtime/utils/mention.js +52 -0
- package/package.json +1 -1
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Modal v-model="isOpen" title="เลือกข้อมูลที่จะส่ง" class="w-[426px]">
|
|
3
|
+
<div class="flex flex-col gap-[16px]">
|
|
4
|
+
<InputSearch name="chat_share_search" v-model="searchTerm" />
|
|
5
|
+
<ShadTabs v-model="currentTab" class="w-full gap-[8px]">
|
|
6
|
+
<ShadTabsList class="grid grid-cols-3 w-full">
|
|
7
|
+
<ShadTabsTrigger
|
|
8
|
+
v-for="tab in listTabs"
|
|
9
|
+
:key="tab.value"
|
|
10
|
+
:value="tab.value"
|
|
11
|
+
>
|
|
12
|
+
{{ tab.label }}
|
|
13
|
+
</ShadTabsTrigger>
|
|
14
|
+
</ShadTabsList>
|
|
15
|
+
<ShadTabsContent
|
|
16
|
+
v-for="tab in listTabs"
|
|
17
|
+
:key="tab.value"
|
|
18
|
+
:ref="(el) => setPanel(tab.value, el)"
|
|
19
|
+
:value="tab.value"
|
|
20
|
+
class="flex-none h-[400px] overflow-y-auto"
|
|
21
|
+
>
|
|
22
|
+
<div
|
|
23
|
+
v-for="item in states[tab.value].items"
|
|
24
|
+
:key="item.id"
|
|
25
|
+
class="flex gap-[8px] items-center py-[6px] px-[8px]"
|
|
26
|
+
>
|
|
27
|
+
<CardMention :item="item" />
|
|
28
|
+
<Button
|
|
29
|
+
color="primary"
|
|
30
|
+
size="sm"
|
|
31
|
+
class="shrink-0"
|
|
32
|
+
@click="emit('send', item)"
|
|
33
|
+
>
|
|
34
|
+
ส่ง
|
|
35
|
+
</Button>
|
|
36
|
+
</div>
|
|
37
|
+
<div
|
|
38
|
+
v-if="
|
|
39
|
+
!states[tab.value].items.length && !states[tab.value].loading && !hasMore(tab.value)
|
|
40
|
+
"
|
|
41
|
+
class="h-full flex items-center justify-center font-body-medium text-gray"
|
|
42
|
+
>
|
|
43
|
+
ไม่พบข้อมูล
|
|
44
|
+
</div>
|
|
45
|
+
<template v-if="states[tab.value].loading">
|
|
46
|
+
<div
|
|
47
|
+
v-for="n in states[tab.value].items.length ? 2 : 5"
|
|
48
|
+
:key="`skeleton-${n}`"
|
|
49
|
+
class="py-[6px] px-[8px]"
|
|
50
|
+
>
|
|
51
|
+
<CardMentionSkeleton :mention-type="tab.value === 'post' ? 'post' : 'blog'" />
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
</ShadTabsContent>
|
|
55
|
+
</ShadTabs>
|
|
56
|
+
</div>
|
|
57
|
+
</Modal>
|
|
58
|
+
</template>
|
|
59
|
+
|
|
60
|
+
<script setup>
|
|
61
|
+
import { reactive, ref, shallowReactive, watch } from "vue";
|
|
62
|
+
import { useDebounceFn, useInfiniteScroll } from "@vueuse/core";
|
|
63
|
+
import { useApi } from "../../composables/useApi";
|
|
64
|
+
import CardMention, {
|
|
65
|
+
} from "../card/card-mention/card-mention.vue";
|
|
66
|
+
import CardMentionSkeleton from "../card/card-mention/card-mention-skeleton.vue";
|
|
67
|
+
import {
|
|
68
|
+
mapBlog,
|
|
69
|
+
mapPlace,
|
|
70
|
+
mapPost
|
|
71
|
+
} from "../../utils/mention";
|
|
72
|
+
const props = defineProps({
|
|
73
|
+
profileIds: { type: Array, required: true }
|
|
74
|
+
});
|
|
75
|
+
const emit = defineEmits(["send"]);
|
|
76
|
+
const isOpen = defineModel({ type: Boolean, ...{ default: false } });
|
|
77
|
+
const PAGE_SIZE = 10;
|
|
78
|
+
const listTabs = [
|
|
79
|
+
{ value: "post", label: "\u0E40\u0E19\u0E37\u0E49\u0E2D\u0E2B\u0E32" },
|
|
80
|
+
{ value: "place", label: "\u0E2A\u0E16\u0E32\u0E19\u0E17\u0E35\u0E48" },
|
|
81
|
+
{ value: "blog", label: "\u0E1A\u0E25\u0E47\u0E2D\u0E01" }
|
|
82
|
+
];
|
|
83
|
+
const api = useApi({ includeProvince: false });
|
|
84
|
+
const currentTab = ref("post");
|
|
85
|
+
const searchTerm = ref("");
|
|
86
|
+
const createState = () => ({
|
|
87
|
+
items: [],
|
|
88
|
+
sources: Object.fromEntries(
|
|
89
|
+
props.profileIds.map((id) => [id, { page: 1, hasMore: true }])
|
|
90
|
+
),
|
|
91
|
+
loading: false
|
|
92
|
+
});
|
|
93
|
+
const states = reactive({
|
|
94
|
+
post: createState(),
|
|
95
|
+
place: createState(),
|
|
96
|
+
blog: createState()
|
|
97
|
+
});
|
|
98
|
+
const generations = { post: 0, place: 0, blog: 0 };
|
|
99
|
+
const hasMore = (tab) => Object.values(states[tab].sources).some((source) => source.hasMore);
|
|
100
|
+
const requestPage = async (tab, profileId, page, q) => {
|
|
101
|
+
if (tab === "post") {
|
|
102
|
+
const res2 = await api(
|
|
103
|
+
`/profiles/${profileId}/contents`,
|
|
104
|
+
{
|
|
105
|
+
query: {
|
|
106
|
+
page,
|
|
107
|
+
page_size: PAGE_SIZE,
|
|
108
|
+
status: "PUBLISHED",
|
|
109
|
+
query: q || void 0
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
return {
|
|
114
|
+
items: (res2.data ?? []).map(mapPost),
|
|
115
|
+
totalPage: res2.meta?.total_page ?? 0
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (tab === "blog") {
|
|
119
|
+
const res2 = await api(
|
|
120
|
+
`/profiles/${profileId}/blogs`,
|
|
121
|
+
{
|
|
122
|
+
query: {
|
|
123
|
+
page,
|
|
124
|
+
page_size: PAGE_SIZE,
|
|
125
|
+
status: "PUBLISHED",
|
|
126
|
+
query: q || void 0
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
return {
|
|
131
|
+
items: (res2.data ?? []).map(mapBlog),
|
|
132
|
+
totalPage: res2.meta?.total_page ?? 0
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const res = await api(`/profiles/${profileId}/places`, {
|
|
136
|
+
query: { page, page_size: PAGE_SIZE, q: q || void 0 }
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
items: (res.data?.items ?? []).map(mapPlace),
|
|
140
|
+
totalPage: res.data?.pagination?.total_page ?? 0
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
const loadMore = async (tab) => {
|
|
144
|
+
const state = states[tab];
|
|
145
|
+
if (state.loading || !hasMore(tab)) return;
|
|
146
|
+
state.loading = true;
|
|
147
|
+
const generation = generations[tab];
|
|
148
|
+
const q = searchTerm.value.trim();
|
|
149
|
+
const pending = Object.entries(state.sources).filter(
|
|
150
|
+
([, source]) => source.hasMore
|
|
151
|
+
);
|
|
152
|
+
const results = await Promise.allSettled(
|
|
153
|
+
pending.map(
|
|
154
|
+
([profileId, source]) => requestPage(tab, profileId, source.page, q)
|
|
155
|
+
)
|
|
156
|
+
);
|
|
157
|
+
if (generation !== generations[tab]) return;
|
|
158
|
+
const seen = new Set(state.items.map((item) => item.id));
|
|
159
|
+
const merged = [...state.items];
|
|
160
|
+
results.forEach((result, index) => {
|
|
161
|
+
const source = state.sources[pending[index][0]];
|
|
162
|
+
if (result.status === "rejected") {
|
|
163
|
+
source.hasMore = false;
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
source.page++;
|
|
167
|
+
source.hasMore = source.page <= result.value.totalPage;
|
|
168
|
+
for (const item of result.value.items) {
|
|
169
|
+
if (seen.has(item.id)) continue;
|
|
170
|
+
seen.add(item.id);
|
|
171
|
+
merged.push(item);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
state.items = merged;
|
|
175
|
+
state.loading = false;
|
|
176
|
+
};
|
|
177
|
+
const panels = shallowReactive({
|
|
178
|
+
post: null,
|
|
179
|
+
place: null,
|
|
180
|
+
blog: null
|
|
181
|
+
});
|
|
182
|
+
const setPanel = (tab, ref2) => {
|
|
183
|
+
const el = ref2?.$el ?? ref2;
|
|
184
|
+
const panel = el instanceof HTMLElement ? el : null;
|
|
185
|
+
if (panels[tab] !== panel) panels[tab] = panel;
|
|
186
|
+
};
|
|
187
|
+
useInfiniteScroll(
|
|
188
|
+
() => panels[currentTab.value],
|
|
189
|
+
() => loadMore(currentTab.value),
|
|
190
|
+
{
|
|
191
|
+
distance: 80,
|
|
192
|
+
canLoadMore: () => hasMore(currentTab.value)
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
const resetAll = () => {
|
|
196
|
+
for (const { value: tab } of listTabs) {
|
|
197
|
+
generations[tab]++;
|
|
198
|
+
Object.assign(states[tab], createState());
|
|
199
|
+
const panel = panels[tab];
|
|
200
|
+
if (panel) panel.scrollTop = 0;
|
|
201
|
+
}
|
|
202
|
+
void loadMore(currentTab.value);
|
|
203
|
+
};
|
|
204
|
+
watch(searchTerm, useDebounceFn(resetAll, 300));
|
|
205
|
+
watch(currentTab, (tab) => {
|
|
206
|
+
if (!states[tab].items.length) void loadMore(tab);
|
|
207
|
+
});
|
|
208
|
+
watch(
|
|
209
|
+
[isOpen, () => props.profileIds.join(",")],
|
|
210
|
+
([open]) => {
|
|
211
|
+
if (open) resetAll();
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
</script>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type CardMentionItem } from "../card/card-mention/card-mention.vue.js";
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
profileIds: string[];
|
|
4
|
+
};
|
|
5
|
+
type __VLS_ModelProps = {
|
|
6
|
+
modelValue?: boolean;
|
|
7
|
+
};
|
|
8
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
9
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
10
|
+
"update:modelValue": (value: boolean) => any;
|
|
11
|
+
send: (item: CardMentionItem) => any;
|
|
12
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
13
|
+
"onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
|
|
14
|
+
onSend?: ((item: CardMentionItem) => any) | undefined;
|
|
15
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
|
+
declare const _default: typeof __VLS_export;
|
|
17
|
+
export default _default;
|
|
@@ -24,8 +24,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
24
24
|
}) => any) | undefined;
|
|
25
25
|
}>, {
|
|
26
26
|
title: string;
|
|
27
|
-
disabledForgotPassword: boolean;
|
|
28
27
|
confirmText: string;
|
|
28
|
+
disabledForgotPassword: boolean;
|
|
29
29
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
30
30
|
declare const _default: typeof __VLS_export;
|
|
31
31
|
export default _default;
|
|
@@ -24,8 +24,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
24
24
|
}) => any) | undefined;
|
|
25
25
|
}>, {
|
|
26
26
|
title: string;
|
|
27
|
-
disabledForgotPassword: boolean;
|
|
28
27
|
confirmText: string;
|
|
28
|
+
disabledForgotPassword: boolean;
|
|
29
29
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
30
30
|
declare const _default: typeof __VLS_export;
|
|
31
31
|
export default _default;
|
|
@@ -32,8 +32,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
32
32
|
}>, {
|
|
33
33
|
title: string;
|
|
34
34
|
mode: "login" | "secure";
|
|
35
|
-
disabledForgotPassword: boolean;
|
|
36
35
|
confirmText: string;
|
|
36
|
+
disabledForgotPassword: boolean;
|
|
37
37
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
38
38
|
declare const _default: typeof __VLS_export;
|
|
39
39
|
export default _default;
|
|
@@ -32,8 +32,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
32
32
|
}>, {
|
|
33
33
|
title: string;
|
|
34
34
|
mode: "login" | "secure";
|
|
35
|
-
disabledForgotPassword: boolean;
|
|
36
35
|
confirmText: string;
|
|
36
|
+
disabledForgotPassword: boolean;
|
|
37
37
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
38
38
|
declare const _default: typeof __VLS_export;
|
|
39
39
|
export default _default;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChatConversation, ChatMessage } from "../types/chat.js";
|
|
1
|
+
import type { ChatConversation, ChatMessage, ChatSharePreview } from "../types/chat.js";
|
|
2
2
|
export declare const useChatStore: import("pinia").StoreDefinition<"pukaad-chat", Pick<{
|
|
3
3
|
conversations: import("vue").Ref<{
|
|
4
4
|
id: string;
|
|
@@ -31,6 +31,19 @@ export declare const useChatStore: import("pinia").StoreDefinition<"pukaad-chat"
|
|
|
31
31
|
is_deleted: boolean;
|
|
32
32
|
thumbnail_url: string | null;
|
|
33
33
|
} | null | undefined;
|
|
34
|
+
share?: {
|
|
35
|
+
type: import("../types/chat.js").ChatShareType;
|
|
36
|
+
id: string;
|
|
37
|
+
title: string;
|
|
38
|
+
content_type?: string | undefined;
|
|
39
|
+
cover_color?: string | undefined;
|
|
40
|
+
image?: string | undefined;
|
|
41
|
+
thumbnail?: string | undefined;
|
|
42
|
+
place_name?: string | undefined;
|
|
43
|
+
address?: string | undefined;
|
|
44
|
+
author_name?: string | undefined;
|
|
45
|
+
author_avatar?: string | null | undefined;
|
|
46
|
+
} | null | undefined;
|
|
34
47
|
is_deleted: boolean;
|
|
35
48
|
created_at: string;
|
|
36
49
|
status?: import("../types/chat.js").ChatMessageStatus | undefined;
|
|
@@ -74,6 +87,19 @@ export declare const useChatStore: import("pinia").StoreDefinition<"pukaad-chat"
|
|
|
74
87
|
is_deleted: boolean;
|
|
75
88
|
thumbnail_url: string | null;
|
|
76
89
|
} | null | undefined;
|
|
90
|
+
share?: {
|
|
91
|
+
type: import("../types/chat.js").ChatShareType;
|
|
92
|
+
id: string;
|
|
93
|
+
title: string;
|
|
94
|
+
content_type?: string | undefined;
|
|
95
|
+
cover_color?: string | undefined;
|
|
96
|
+
image?: string | undefined;
|
|
97
|
+
thumbnail?: string | undefined;
|
|
98
|
+
place_name?: string | undefined;
|
|
99
|
+
address?: string | undefined;
|
|
100
|
+
author_name?: string | undefined;
|
|
101
|
+
author_avatar?: string | null | undefined;
|
|
102
|
+
} | null | undefined;
|
|
77
103
|
is_deleted: boolean;
|
|
78
104
|
created_at: string;
|
|
79
105
|
status?: import("../types/chat.js").ChatMessageStatus | undefined;
|
|
@@ -107,6 +133,7 @@ export declare const useChatStore: import("pinia").StoreDefinition<"pukaad-chat"
|
|
|
107
133
|
fetchOlderMessages: (conversationId: string) => Promise<void>;
|
|
108
134
|
sendMessage: (conversationId: string, body: string, replyTo?: ChatMessage) => Promise<void>;
|
|
109
135
|
sendMedia: (conversationId: string, files: File[], replyTo?: ChatMessage) => Promise<void>;
|
|
136
|
+
sendShare: (conversationId: string, share: ChatSharePreview) => Promise<void>;
|
|
110
137
|
retryMessage: (conversationId: string, clientId: string) => Promise<void>;
|
|
111
138
|
markRead: (conversationId: string, messageId: string) => Promise<void>;
|
|
112
139
|
isReadByPeer: (conversationId: string, messageId: string) => boolean;
|
|
@@ -153,6 +180,19 @@ export declare const useChatStore: import("pinia").StoreDefinition<"pukaad-chat"
|
|
|
153
180
|
is_deleted: boolean;
|
|
154
181
|
thumbnail_url: string | null;
|
|
155
182
|
} | null | undefined;
|
|
183
|
+
share?: {
|
|
184
|
+
type: import("../types/chat.js").ChatShareType;
|
|
185
|
+
id: string;
|
|
186
|
+
title: string;
|
|
187
|
+
content_type?: string | undefined;
|
|
188
|
+
cover_color?: string | undefined;
|
|
189
|
+
image?: string | undefined;
|
|
190
|
+
thumbnail?: string | undefined;
|
|
191
|
+
place_name?: string | undefined;
|
|
192
|
+
address?: string | undefined;
|
|
193
|
+
author_name?: string | undefined;
|
|
194
|
+
author_avatar?: string | null | undefined;
|
|
195
|
+
} | null | undefined;
|
|
156
196
|
is_deleted: boolean;
|
|
157
197
|
created_at: string;
|
|
158
198
|
status?: import("../types/chat.js").ChatMessageStatus | undefined;
|
|
@@ -196,6 +236,19 @@ export declare const useChatStore: import("pinia").StoreDefinition<"pukaad-chat"
|
|
|
196
236
|
is_deleted: boolean;
|
|
197
237
|
thumbnail_url: string | null;
|
|
198
238
|
} | null | undefined;
|
|
239
|
+
share?: {
|
|
240
|
+
type: import("../types/chat.js").ChatShareType;
|
|
241
|
+
id: string;
|
|
242
|
+
title: string;
|
|
243
|
+
content_type?: string | undefined;
|
|
244
|
+
cover_color?: string | undefined;
|
|
245
|
+
image?: string | undefined;
|
|
246
|
+
thumbnail?: string | undefined;
|
|
247
|
+
place_name?: string | undefined;
|
|
248
|
+
address?: string | undefined;
|
|
249
|
+
author_name?: string | undefined;
|
|
250
|
+
author_avatar?: string | null | undefined;
|
|
251
|
+
} | null | undefined;
|
|
199
252
|
is_deleted: boolean;
|
|
200
253
|
created_at: string;
|
|
201
254
|
status?: import("../types/chat.js").ChatMessageStatus | undefined;
|
|
@@ -229,6 +282,7 @@ export declare const useChatStore: import("pinia").StoreDefinition<"pukaad-chat"
|
|
|
229
282
|
fetchOlderMessages: (conversationId: string) => Promise<void>;
|
|
230
283
|
sendMessage: (conversationId: string, body: string, replyTo?: ChatMessage) => Promise<void>;
|
|
231
284
|
sendMedia: (conversationId: string, files: File[], replyTo?: ChatMessage) => Promise<void>;
|
|
285
|
+
sendShare: (conversationId: string, share: ChatSharePreview) => Promise<void>;
|
|
232
286
|
retryMessage: (conversationId: string, clientId: string) => Promise<void>;
|
|
233
287
|
markRead: (conversationId: string, messageId: string) => Promise<void>;
|
|
234
288
|
isReadByPeer: (conversationId: string, messageId: string) => boolean;
|
|
@@ -275,6 +329,19 @@ export declare const useChatStore: import("pinia").StoreDefinition<"pukaad-chat"
|
|
|
275
329
|
is_deleted: boolean;
|
|
276
330
|
thumbnail_url: string | null;
|
|
277
331
|
} | null | undefined;
|
|
332
|
+
share?: {
|
|
333
|
+
type: import("../types/chat.js").ChatShareType;
|
|
334
|
+
id: string;
|
|
335
|
+
title: string;
|
|
336
|
+
content_type?: string | undefined;
|
|
337
|
+
cover_color?: string | undefined;
|
|
338
|
+
image?: string | undefined;
|
|
339
|
+
thumbnail?: string | undefined;
|
|
340
|
+
place_name?: string | undefined;
|
|
341
|
+
address?: string | undefined;
|
|
342
|
+
author_name?: string | undefined;
|
|
343
|
+
author_avatar?: string | null | undefined;
|
|
344
|
+
} | null | undefined;
|
|
278
345
|
is_deleted: boolean;
|
|
279
346
|
created_at: string;
|
|
280
347
|
status?: import("../types/chat.js").ChatMessageStatus | undefined;
|
|
@@ -318,6 +385,19 @@ export declare const useChatStore: import("pinia").StoreDefinition<"pukaad-chat"
|
|
|
318
385
|
is_deleted: boolean;
|
|
319
386
|
thumbnail_url: string | null;
|
|
320
387
|
} | null | undefined;
|
|
388
|
+
share?: {
|
|
389
|
+
type: import("../types/chat.js").ChatShareType;
|
|
390
|
+
id: string;
|
|
391
|
+
title: string;
|
|
392
|
+
content_type?: string | undefined;
|
|
393
|
+
cover_color?: string | undefined;
|
|
394
|
+
image?: string | undefined;
|
|
395
|
+
thumbnail?: string | undefined;
|
|
396
|
+
place_name?: string | undefined;
|
|
397
|
+
address?: string | undefined;
|
|
398
|
+
author_name?: string | undefined;
|
|
399
|
+
author_avatar?: string | null | undefined;
|
|
400
|
+
} | null | undefined;
|
|
321
401
|
is_deleted: boolean;
|
|
322
402
|
created_at: string;
|
|
323
403
|
status?: import("../types/chat.js").ChatMessageStatus | undefined;
|
|
@@ -351,6 +431,7 @@ export declare const useChatStore: import("pinia").StoreDefinition<"pukaad-chat"
|
|
|
351
431
|
fetchOlderMessages: (conversationId: string) => Promise<void>;
|
|
352
432
|
sendMessage: (conversationId: string, body: string, replyTo?: ChatMessage) => Promise<void>;
|
|
353
433
|
sendMedia: (conversationId: string, files: File[], replyTo?: ChatMessage) => Promise<void>;
|
|
434
|
+
sendShare: (conversationId: string, share: ChatSharePreview) => Promise<void>;
|
|
354
435
|
retryMessage: (conversationId: string, clientId: string) => Promise<void>;
|
|
355
436
|
markRead: (conversationId: string, messageId: string) => Promise<void>;
|
|
356
437
|
isReadByPeer: (conversationId: string, messageId: string) => boolean;
|
|
@@ -365,4 +446,4 @@ export declare const useChatStore: import("pinia").StoreDefinition<"pukaad-chat"
|
|
|
365
446
|
startRealtime: () => void;
|
|
366
447
|
stopRealtime: () => void;
|
|
367
448
|
resync: (activeConversationId?: string) => Promise<void>;
|
|
368
|
-
}, "reset" | "markRead" | "setUnreadFirst" | "setMyProfileId" | "fetchConversations" | "fetchConversation" | "fetchLatestMessages" | "fetchOlderMessages" | "sendMessage" | "sendMedia" | "retryMessage" | "isReadByPeer" | "unreadCountOf" | "startUnreadBadge" | "stopUnreadBadge" | "previewOf" | "sendTyping" | "isPeerTyping" | "deleteMessage" | "startRealtime" | "stopRealtime" | "resync">>;
|
|
449
|
+
}, "reset" | "markRead" | "setUnreadFirst" | "setMyProfileId" | "fetchConversations" | "fetchConversation" | "fetchLatestMessages" | "fetchOlderMessages" | "sendMessage" | "sendMedia" | "sendShare" | "retryMessage" | "isReadByPeer" | "unreadCountOf" | "startUnreadBadge" | "stopUnreadBadge" | "previewOf" | "sendTyping" | "isPeerTyping" | "deleteMessage" | "startRealtime" | "stopRealtime" | "resync">>;
|
|
@@ -117,10 +117,15 @@ export const useChatStore = defineStore("pukaad-chat", () => {
|
|
|
117
117
|
const last = myLastRead[conversationId];
|
|
118
118
|
return !last || BigInt(msg.id) > BigInt(last);
|
|
119
119
|
};
|
|
120
|
+
const shareLabels = {
|
|
121
|
+
post: "\u0E2A\u0E48\u0E07\u0E40\u0E19\u0E37\u0E49\u0E2D\u0E2B\u0E32",
|
|
122
|
+
place: "\u0E2A\u0E48\u0E07\u0E2A\u0E16\u0E32\u0E19\u0E17\u0E35\u0E48",
|
|
123
|
+
blog: "\u0E2A\u0E48\u0E07\u0E1A\u0E25\u0E47\u0E2D\u0E01"
|
|
124
|
+
};
|
|
120
125
|
const previewOf = (conversation) => {
|
|
121
126
|
const msg = conversation.last_message;
|
|
122
127
|
if (!msg) return "";
|
|
123
|
-
const text = msg.is_deleted ? "\u0E02\u0E49\u0E2D\u0E04\u0E27\u0E32\u0E21\u0E16\u0E39\u0E01\u0E25\u0E1A\u0E41\u0E25\u0E49\u0E27" : msg.kind === "image" ? "\u0E2A\u0E48\u0E07\u0E23\u0E39\u0E1B\u0E20\u0E32\u0E1E" : msg.kind === "video" ? "\u0E2A\u0E48\u0E07\u0E27\u0E34\u0E14\u0E35\u0E42\u0E2D" : msg.body.replace(/\s+/g, " ");
|
|
128
|
+
const text = msg.is_deleted ? "\u0E02\u0E49\u0E2D\u0E04\u0E27\u0E32\u0E21\u0E16\u0E39\u0E01\u0E25\u0E1A\u0E41\u0E25\u0E49\u0E27" : msg.kind === "image" ? "\u0E2A\u0E48\u0E07\u0E23\u0E39\u0E1B\u0E20\u0E32\u0E1E" : msg.kind === "video" ? "\u0E2A\u0E48\u0E07\u0E27\u0E34\u0E14\u0E35\u0E42\u0E2D" : msg.kind === "share" ? shareLabels[msg.share?.type ?? "post"] : msg.body.replace(/\s+/g, " ");
|
|
124
129
|
return msg.sender_profile_id === myProfileId.value ? `\u0E04\u0E38\u0E13: ${text}` : text;
|
|
125
130
|
};
|
|
126
131
|
const unreadCountOf = (conversation) => {
|
|
@@ -239,23 +244,29 @@ export const useChatStore = defineStore("pukaad-chat", () => {
|
|
|
239
244
|
};
|
|
240
245
|
const toReplyPreview = (original) => {
|
|
241
246
|
const first = original.attachments[0];
|
|
247
|
+
const share = original.share;
|
|
242
248
|
return {
|
|
243
249
|
id: original.id,
|
|
244
250
|
sender_profile_id: original.sender_profile_id,
|
|
245
251
|
kind: original.kind,
|
|
246
|
-
body: [...original.body].slice(0, 100).join(""),
|
|
252
|
+
body: [...share?.title ?? original.body].slice(0, 100).join(""),
|
|
247
253
|
is_deleted: false,
|
|
248
|
-
thumbnail_url: first ? first.thumbnail_url ?? (first.kind === "image" ? first.url : null) : null
|
|
254
|
+
thumbnail_url: share ? share.image || share.thumbnail || null : first ? first.thumbnail_url ?? (first.kind === "image" ? first.url : null) : null
|
|
249
255
|
};
|
|
250
256
|
};
|
|
251
257
|
const replyIdOf = (conversationId, clientId) => messages[conversationId]?.find((m) => m.client_id === clientId)?.reply_to?.id;
|
|
252
|
-
const postMessage = async (conversationId, clientId, body) => {
|
|
258
|
+
const postMessage = async (conversationId, clientId, body, share) => {
|
|
253
259
|
try {
|
|
254
260
|
const res = await api(
|
|
255
261
|
`/chat/conversations/${conversationId}/messages`,
|
|
256
262
|
{
|
|
257
263
|
method: "POST",
|
|
258
|
-
body: {
|
|
264
|
+
body: {
|
|
265
|
+
client_id: clientId,
|
|
266
|
+
body,
|
|
267
|
+
share: share ? { type: share.type, id: share.id } : void 0,
|
|
268
|
+
reply_to_message_id: replyIdOf(conversationId, clientId)
|
|
269
|
+
},
|
|
259
270
|
timeout: REQUEST_TIMEOUT
|
|
260
271
|
}
|
|
261
272
|
);
|
|
@@ -286,6 +297,23 @@ export const useChatStore = defineStore("pukaad-chat", () => {
|
|
|
286
297
|
messages[conversationId] = [...messages[conversationId] ?? [], local];
|
|
287
298
|
await postMessage(conversationId, clientId, body);
|
|
288
299
|
};
|
|
300
|
+
const sendShare = async (conversationId, share) => {
|
|
301
|
+
const clientId = crypto.randomUUID();
|
|
302
|
+
const local = {
|
|
303
|
+
id: `local-${clientId}`,
|
|
304
|
+
client_id: clientId,
|
|
305
|
+
sender_profile_id: myProfileId.value,
|
|
306
|
+
kind: "share",
|
|
307
|
+
body: "",
|
|
308
|
+
attachments: [],
|
|
309
|
+
share,
|
|
310
|
+
is_deleted: false,
|
|
311
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
312
|
+
status: "sending"
|
|
313
|
+
};
|
|
314
|
+
messages[conversationId] = [...messages[conversationId] ?? [], local];
|
|
315
|
+
await postMessage(conversationId, clientId, "", share);
|
|
316
|
+
};
|
|
289
317
|
const pendingFiles = /* @__PURE__ */ new Map();
|
|
290
318
|
const setProgress = (conversationId, clientId, progress) => {
|
|
291
319
|
const msg = messages[conversationId]?.find((m) => m.client_id === clientId);
|
|
@@ -389,6 +417,10 @@ export const useChatStore = defineStore("pukaad-chat", () => {
|
|
|
389
417
|
if (!msg || msg.status !== "failed" || msg.failure?.retryable === false) return;
|
|
390
418
|
msg.status = "sending";
|
|
391
419
|
msg.failure = void 0;
|
|
420
|
+
if (msg.kind === "share") {
|
|
421
|
+
await postMessage(conversationId, clientId, "", msg.share);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
392
424
|
if (msg.kind !== "text") {
|
|
393
425
|
msg.progress = 0;
|
|
394
426
|
await postMedia(conversationId, clientId);
|
|
@@ -403,6 +435,7 @@ export const useChatStore = defineStore("pukaad-chat", () => {
|
|
|
403
435
|
msg.body = "";
|
|
404
436
|
msg.attachments = [];
|
|
405
437
|
msg.reply_to = null;
|
|
438
|
+
msg.share = null;
|
|
406
439
|
}
|
|
407
440
|
for (const m of messages[conversationId] ?? []) {
|
|
408
441
|
if (m.reply_to?.id === messageId) {
|
|
@@ -564,6 +597,7 @@ export const useChatStore = defineStore("pukaad-chat", () => {
|
|
|
564
597
|
fetchOlderMessages,
|
|
565
598
|
sendMessage,
|
|
566
599
|
sendMedia,
|
|
600
|
+
sendShare,
|
|
567
601
|
retryMessage,
|
|
568
602
|
markRead,
|
|
569
603
|
isReadByPeer,
|