Fixed message deletion error and layout changes for the message input box.
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "ConversationHiddenMessage" ADD COLUMN "scope" TEXT NOT NULL DEFAULT 'all';
|
||||||
|
|
||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "ConversationHiddenMessage_organizationId_externalRoomId_messageId_key";
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "ConversationHiddenMessage_organizationId_externalRoomId_messageId_scope_hiddenByUserId_key" ON "ConversationHiddenMessage"("organizationId", "externalRoomId", "messageId", "scope", "hiddenByUserId");
|
||||||
@@ -2097,7 +2097,8 @@ model ConversationRoom {
|
|||||||
@@index([organizationId, kind])
|
@@index([organizationId, kind])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// پیامهای حذفشده برای همه در CRM (سرویس چت خارجی حذف پایدار ندارد)
|
/// پیامهای حذفشده در CRM (سرویس چت خارجی حذف پایدار ندارد)
|
||||||
|
/// scope=all → برای همه؛ scope=self → فقط برای hiddenByUserId
|
||||||
model ConversationHiddenMessage {
|
model ConversationHiddenMessage {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
organizationId String
|
organizationId String
|
||||||
@@ -2105,8 +2106,10 @@ model ConversationHiddenMessage {
|
|||||||
externalRoomId String
|
externalRoomId String
|
||||||
messageId String
|
messageId String
|
||||||
hiddenByUserId String
|
hiddenByUserId String
|
||||||
|
/// all = حذف برای همه | self = حذف فقط برای کاربر
|
||||||
|
scope String @default("all")
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
@@unique([organizationId, externalRoomId, messageId])
|
@@unique([organizationId, externalRoomId, messageId, scope, hiddenByUserId])
|
||||||
@@index([organizationId, externalRoomId])
|
@@index([organizationId, externalRoomId])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ export class ConversationsController {
|
|||||||
user.role,
|
user.role,
|
||||||
dto.externalRoomId,
|
dto.externalRoomId,
|
||||||
dto.messageIds,
|
dto.messageIds,
|
||||||
|
dto.forEveryone !== false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -500,7 +500,8 @@ export class ConversationsService {
|
|||||||
role: string,
|
role: string,
|
||||||
externalRoomId: string,
|
externalRoomId: string,
|
||||||
messageIds: string[],
|
messageIds: string[],
|
||||||
): Promise<{ messageIds: string[] }> {
|
forEveryone = true,
|
||||||
|
): Promise<{ messageIds: string[]; scope: 'all' | 'self' }> {
|
||||||
await this.loadAccess(userId, organizationId, role);
|
await this.loadAccess(userId, organizationId, role);
|
||||||
const roomId = externalRoomId.trim();
|
const roomId = externalRoomId.trim();
|
||||||
if (!roomId) throw new BadRequestException('شناسه اتاق خالی است');
|
if (!roomId) throw new BadRequestException('شناسه اتاق خالی است');
|
||||||
@@ -513,17 +514,19 @@ export class ConversationsService {
|
|||||||
].slice(0, 100);
|
].slice(0, 100);
|
||||||
if (!ids.length) throw new BadRequestException('شناسه پیام خالی است');
|
if (!ids.length) throw new BadRequestException('شناسه پیام خالی است');
|
||||||
|
|
||||||
|
const scope = forEveryone ? 'all' : 'self';
|
||||||
await this.prisma.conversationHiddenMessage.createMany({
|
await this.prisma.conversationHiddenMessage.createMany({
|
||||||
data: ids.map((messageId) => ({
|
data: ids.map((messageId) => ({
|
||||||
organizationId,
|
organizationId,
|
||||||
externalRoomId: roomId,
|
externalRoomId: roomId,
|
||||||
messageId,
|
messageId,
|
||||||
hiddenByUserId: userId,
|
hiddenByUserId: userId,
|
||||||
|
scope,
|
||||||
})),
|
})),
|
||||||
skipDuplicates: true,
|
skipDuplicates: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { messageIds: ids };
|
return { messageIds: ids, scope };
|
||||||
}
|
}
|
||||||
|
|
||||||
async listHiddenMessageIds(
|
async listHiddenMessageIds(
|
||||||
@@ -537,10 +540,14 @@ export class ConversationsService {
|
|||||||
if (!roomId) throw new BadRequestException('شناسه اتاق خالی است');
|
if (!roomId) throw new BadRequestException('شناسه اتاق خالی است');
|
||||||
|
|
||||||
const rows = await this.prisma.conversationHiddenMessage.findMany({
|
const rows = await this.prisma.conversationHiddenMessage.findMany({
|
||||||
where: { organizationId, externalRoomId: roomId },
|
where: {
|
||||||
|
organizationId,
|
||||||
|
externalRoomId: roomId,
|
||||||
|
OR: [{ scope: 'all' }, { scope: 'self', hiddenByUserId: userId }],
|
||||||
|
},
|
||||||
select: { messageId: true },
|
select: { messageId: true },
|
||||||
take: 5000,
|
take: 5000,
|
||||||
});
|
});
|
||||||
return { messageIds: rows.map((r) => r.messageId) };
|
return { messageIds: [...new Set(rows.map((r) => r.messageId))] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
|
IsBoolean,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
IsString,
|
IsString,
|
||||||
MaxLength,
|
MaxLength,
|
||||||
@@ -61,4 +62,9 @@ export class HideConversationMessagesDto {
|
|||||||
@ArrayMaxSize(100)
|
@ArrayMaxSize(100)
|
||||||
@IsString({ each: true })
|
@IsString({ each: true })
|
||||||
messageIds!: string[];
|
messageIds!: string[];
|
||||||
|
|
||||||
|
/** true = حذف برای همه؛ false = فقط برای کاربر جاری */
|
||||||
|
@IsOptional()
|
||||||
|
@IsBoolean()
|
||||||
|
forEveryone?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,11 @@ function isRememberedHidden(roomId: string, messageId: string): boolean {
|
|||||||
return Boolean(hiddenIdsByRoom.get(roomId)?.has(messageId));
|
return Boolean(hiddenIdsByRoom.get(roomId)?.has(messageId));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function persistHiddenOnServer(externalRoomId: string, messageIds: string[]) {
|
async function persistHiddenOnServer(
|
||||||
|
externalRoomId: string,
|
||||||
|
messageIds: string[],
|
||||||
|
forEveryone = true,
|
||||||
|
) {
|
||||||
const ids = [...new Set(messageIds.map((id) => String(id || '').trim()).filter(Boolean))];
|
const ids = [...new Set(messageIds.map((id) => String(id || '').trim()).filter(Boolean))];
|
||||||
if (!externalRoomId || !ids.length) return ids;
|
if (!externalRoomId || !ids.length) return ids;
|
||||||
rememberHidden(externalRoomId, ids);
|
rememberHidden(externalRoomId, ids);
|
||||||
@@ -102,6 +106,7 @@ async function persistHiddenOnServer(externalRoomId: string, messageIds: string[
|
|||||||
await api.post('/conversations/messages/hide', {
|
await api.post('/conversations/messages/hide', {
|
||||||
externalRoomId,
|
externalRoomId,
|
||||||
messageIds: ids,
|
messageIds: ids,
|
||||||
|
forEveryone,
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
// local purge already applied; sync may retry via poll
|
// local purge already applied; sync may retry via poll
|
||||||
@@ -726,7 +731,7 @@ function onSocketMessage(event: MessageEvent) {
|
|||||||
const ids = [hideTarget, purgeId].filter(Boolean);
|
const ids = [hideTarget, purgeId].filter(Boolean);
|
||||||
purgeMessageIds(ids);
|
purgeMessageIds(ids);
|
||||||
rememberHidden(roomId, ids);
|
rememberHidden(roomId, ids);
|
||||||
void persistHiddenOnServer(roomId, ids);
|
void persistHiddenOnServer(roomId, ids, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (purgeId && isDeletedContent(purgeContent)) {
|
if (purgeId && isDeletedContent(purgeContent)) {
|
||||||
@@ -1196,27 +1201,34 @@ export function useOrgChat() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteMessage(roomId: string, messageId: string) {
|
async function deleteMessage(
|
||||||
|
roomId: string,
|
||||||
|
messageId: string,
|
||||||
|
opts?: { forEveryone?: boolean },
|
||||||
|
) {
|
||||||
if (!messageId) return;
|
if (!messageId) return;
|
||||||
await connectSocket();
|
const forEveryone = opts?.forEveryone !== false;
|
||||||
if (!socket || socket.readyState !== WebSocket.OPEN) throw new Error('ws not open');
|
|
||||||
|
|
||||||
// 1) Persist hide in CRM (survives refresh for everyone)
|
// 1) Persist hide in CRM
|
||||||
await persistHiddenOnServer(roomId, [messageId]);
|
await persistHiddenOnServer(roomId, [messageId], forEveryone);
|
||||||
|
|
||||||
// 2) Realtime: send a normal text command (text delivery is reliable on chat WS)
|
// 2) Realtime sync only when deleting for everyone
|
||||||
const controlId = crypto.randomUUID();
|
if (forEveryone) {
|
||||||
rememberHidden(roomId, [controlId]);
|
await connectSocket();
|
||||||
socket.send(
|
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||||
JSON.stringify({
|
const controlId = crypto.randomUUID();
|
||||||
id: controlId,
|
rememberHidden(roomId, [controlId]);
|
||||||
type: 'text',
|
socket.send(
|
||||||
room_id: roomId,
|
JSON.stringify({
|
||||||
content: `${HIDE_CMD_PREFIX}${messageId}`,
|
id: controlId,
|
||||||
}),
|
type: 'text',
|
||||||
);
|
room_id: roomId,
|
||||||
// Hide the control message itself so it never appears in history UI
|
content: `${HIDE_CMD_PREFIX}${messageId}`,
|
||||||
void persistHiddenOnServer(roomId, [controlId]);
|
}),
|
||||||
|
);
|
||||||
|
void persistHiddenOnServer(roomId, [controlId], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const last = [...messages.value].reverse().find((m) => m.roomId === roomId);
|
const last = [...messages.value].reverse().find((m) => m.roomId === roomId);
|
||||||
emitRoomLive({
|
emitRoomLive({
|
||||||
@@ -1227,10 +1239,16 @@ export function useOrgChat() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteMessages(roomId: string, messageIds: string[]) {
|
async function deleteMessages(
|
||||||
const ids = [...new Set(messageIds.filter(Boolean))];
|
roomId: string,
|
||||||
for (const id of ids) {
|
items: Array<{ id: string; forEveryone?: boolean }>,
|
||||||
await deleteMessage(roomId, id);
|
) {
|
||||||
|
const seen = new Set<string>();
|
||||||
|
for (const item of items) {
|
||||||
|
const id = String(item.id || '').trim();
|
||||||
|
if (!id || seen.has(id)) continue;
|
||||||
|
seen.add(id);
|
||||||
|
await deleteMessage(roomId, id, { forEveryone: item.forEveryone !== false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const recording = ref(false);
|
|||||||
const recordSecs = ref(0);
|
const recordSecs = ref(0);
|
||||||
const lightboxUrl = ref('');
|
const lightboxUrl = ref('');
|
||||||
const fileInputEl = ref<HTMLInputElement | null>(null);
|
const fileInputEl = ref<HTMLInputElement | null>(null);
|
||||||
const draftInputEl = ref<HTMLInputElement | null>(null);
|
const draftInputEl = ref<HTMLTextAreaElement | null>(null);
|
||||||
const emojiOpen = ref(false);
|
const emojiOpen = ref(false);
|
||||||
const editingMessageId = ref<string | null>(null);
|
const editingMessageId = ref<string | null>(null);
|
||||||
const msgMenu = ref<{ id: string; x: number; y: number } | null>(null);
|
const msgMenu = ref<{ id: string; x: number; y: number } | null>(null);
|
||||||
@@ -194,11 +194,19 @@ function selectedMessages(): ChatMessage[] {
|
|||||||
|
|
||||||
async function deleteSelected() {
|
async function deleteSelected() {
|
||||||
if (!activeRoom.value || !selectedIds.value.length) return;
|
if (!activeRoom.value || !selectedIds.value.length) return;
|
||||||
const n = selectedIds.value.length;
|
const list = selectedMessages();
|
||||||
if (!window.confirm(n > 1 ? `${n} پیام حذف شوند؟` : 'این پیام حذف شود؟')) return;
|
if (!list.length) return;
|
||||||
const ids = [...selectedIds.value];
|
const mineCount = list.filter((m) => isMineMessage(m)).length;
|
||||||
|
const otherCount = list.length - mineCount;
|
||||||
|
const parts: string[] = [];
|
||||||
|
if (mineCount) parts.push(`${mineCount} پیام خودتان برای همه`);
|
||||||
|
if (otherCount) parts.push(`${otherCount} پیام دریافتی فقط برای شما`);
|
||||||
|
if (!window.confirm(`حذف شود؟\n${parts.join('\n')}`)) return;
|
||||||
try {
|
try {
|
||||||
await deleteMessages(activeRoom.value.externalRoomId, ids);
|
await deleteMessages(
|
||||||
|
activeRoom.value.externalRoomId,
|
||||||
|
list.map((m) => ({ id: m.id, forEveryone: isMineMessage(m) })),
|
||||||
|
);
|
||||||
exitSelectMode();
|
exitSelectMode();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error.value = e instanceof Error ? e.message : 'حذف پیام ناموفق بود.';
|
error.value = e instanceof Error ? e.message : 'حذف پیام ناموفق بود.';
|
||||||
@@ -266,15 +274,49 @@ function startEditMessage(msg: ChatMessage) {
|
|||||||
async function confirmDeleteMessage(msg: ChatMessage) {
|
async function confirmDeleteMessage(msg: ChatMessage) {
|
||||||
msgMenu.value = null;
|
msgMenu.value = null;
|
||||||
if (!activeRoom.value || msg.optimistic || msg.deleted) return;
|
if (!activeRoom.value || msg.optimistic || msg.deleted) return;
|
||||||
if (!window.confirm('این پیام برای همه حذف شود؟')) return;
|
const mine = isMineMessage(msg);
|
||||||
|
const ok = window.confirm(
|
||||||
|
mine
|
||||||
|
? 'این پیام برای همه حذف شود؟'
|
||||||
|
: 'این پیام فقط برای شما حذف شود؟ (برای طرف مقابل باقی میماند)',
|
||||||
|
);
|
||||||
|
if (!ok) return;
|
||||||
try {
|
try {
|
||||||
await deleteMessage(activeRoom.value.externalRoomId, msg.id);
|
await deleteMessage(activeRoom.value.externalRoomId, msg.id, {
|
||||||
|
forEveryone: mine,
|
||||||
|
});
|
||||||
if (editingMessageId.value === msg.id) cancelEdit();
|
if (editingMessageId.value === msg.id) cancelEdit();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error.value = e instanceof Error ? e.message : 'حذف پیام ناموفق بود.';
|
error.value = e instanceof Error ? e.message : 'حذف پیام ناموفق بود.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onComposerKeydown(event: KeyboardEvent) {
|
||||||
|
if (event.key !== 'Enter') return;
|
||||||
|
if (event.ctrlKey || event.metaKey) {
|
||||||
|
// Ctrl/Cmd+Enter → newline
|
||||||
|
event.preventDefault();
|
||||||
|
const el = draftInputEl.value;
|
||||||
|
if (!el) {
|
||||||
|
draft.value += '\n';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const start = el.selectionStart ?? draft.value.length;
|
||||||
|
const end = el.selectionEnd ?? start;
|
||||||
|
const next = draft.value.slice(0, start) + '\n' + draft.value.slice(end);
|
||||||
|
draft.value = next;
|
||||||
|
nextTick(() => {
|
||||||
|
const pos = start + 1;
|
||||||
|
el.focus();
|
||||||
|
el.setSelectionRange(pos, pos);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.shiftKey) return;
|
||||||
|
event.preventDefault();
|
||||||
|
void submitMessage();
|
||||||
|
}
|
||||||
|
|
||||||
function openMsgMenu(msg: ChatMessage, event: MouseEvent | TouchEvent) {
|
function openMsgMenu(msg: ChatMessage, event: MouseEvent | TouchEvent) {
|
||||||
if (msg.deleted || msg.optimistic) return;
|
if (msg.deleted || msg.optimistic) return;
|
||||||
if (selectMode.value) {
|
if (selectMode.value) {
|
||||||
@@ -966,38 +1008,6 @@ onUnmounted(() => {
|
|||||||
accept="image/*,video/*,audio/*,.pdf,.doc,.docx,.xls,.xlsx,.zip,.rar,.txt"
|
accept="image/*,video/*,audio/*,.pdf,.doc,.docx,.xls,.xlsx,.zip,.rar,.txt"
|
||||||
@change="onFilePicked"
|
@change="onFilePicked"
|
||||||
/>
|
/>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="tool-btn attach-btn"
|
|
||||||
title="پیوست"
|
|
||||||
:disabled="sending || recording || Boolean(editingMessageId)"
|
|
||||||
@click="fileInputEl?.click()"
|
|
||||||
>
|
|
||||||
<UiIcon name="plus" :size="20" />
|
|
||||||
</button>
|
|
||||||
<div class="input-shell">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="emoji-toggle"
|
|
||||||
title="ایموجی"
|
|
||||||
:class="{ on: emojiOpen }"
|
|
||||||
:disabled="sending || recording"
|
|
||||||
@click="emojiOpen = !emojiOpen"
|
|
||||||
>
|
|
||||||
😊
|
|
||||||
</button>
|
|
||||||
<input
|
|
||||||
ref="draftInputEl"
|
|
||||||
v-model="draft"
|
|
||||||
type="text"
|
|
||||||
class="composer-input"
|
|
||||||
:placeholder="editingMessageId ? 'ویرایش پیام…' : 'پیام بنویسید…'"
|
|
||||||
autocomplete="off"
|
|
||||||
enterkeyhint="send"
|
|
||||||
:disabled="sending || recording"
|
|
||||||
@focus="emojiOpen = false"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button
|
<button
|
||||||
v-if="showMicInsteadOfSend"
|
v-if="showMicInsteadOfSend"
|
||||||
type="button"
|
type="button"
|
||||||
@@ -1017,6 +1027,39 @@ onUnmounted(() => {
|
|||||||
>
|
>
|
||||||
<UiIcon name="send" :size="18" />
|
<UiIcon name="send" :size="18" />
|
||||||
</button>
|
</button>
|
||||||
|
<div class="input-shell">
|
||||||
|
<textarea
|
||||||
|
ref="draftInputEl"
|
||||||
|
v-model="draft"
|
||||||
|
class="composer-input"
|
||||||
|
rows="1"
|
||||||
|
:placeholder="editingMessageId ? 'ویرایش پیام…' : 'پیام بنویسید…'"
|
||||||
|
autocomplete="off"
|
||||||
|
enterkeyhint="send"
|
||||||
|
:disabled="sending || recording"
|
||||||
|
@focus="emojiOpen = false"
|
||||||
|
@keydown="onComposerKeydown"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="emoji-toggle"
|
||||||
|
title="ایموجی"
|
||||||
|
:class="{ on: emojiOpen }"
|
||||||
|
:disabled="sending || recording"
|
||||||
|
@click="emojiOpen = !emojiOpen"
|
||||||
|
>
|
||||||
|
😊
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="tool-btn attach-btn"
|
||||||
|
title="پیوست"
|
||||||
|
:disabled="sending || recording || Boolean(editingMessageId)"
|
||||||
|
@click="fileInputEl?.click()"
|
||||||
|
>
|
||||||
|
<UiIcon name="plus" :size="20" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@@ -1062,6 +1105,15 @@ onUnmounted(() => {
|
|||||||
<UiIcon name="trash" :size="16" />
|
<UiIcon name="trash" :size="16" />
|
||||||
حذف برای همه
|
حذف برای همه
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
v-else
|
||||||
|
type="button"
|
||||||
|
class="danger"
|
||||||
|
@click="confirmDeleteMessage(menuMessage()!)"
|
||||||
|
>
|
||||||
|
<UiIcon name="trash" :size="16" />
|
||||||
|
حذف فقط برای من
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</section>
|
</section>
|
||||||
@@ -1604,12 +1656,11 @@ onUnmounted(() => {
|
|||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: flex-end;
|
||||||
gap: 0.15rem;
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #e2e8f0;
|
border: 1px solid #e2e8f0;
|
||||||
border-radius: 22px;
|
border-radius: 22px;
|
||||||
padding: 0.15rem 0.35rem 0.15rem 0.55rem;
|
padding: 0.35rem 0.75rem;
|
||||||
min-height: 2.75rem;
|
min-height: 2.75rem;
|
||||||
}
|
}
|
||||||
.composer-input {
|
.composer-input {
|
||||||
@@ -1618,17 +1669,22 @@ onUnmounted(() => {
|
|||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
padding: 0.55rem 0.35rem;
|
padding: 0.35rem 0;
|
||||||
font: inherit;
|
font: inherit;
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
resize: none;
|
||||||
|
max-height: 7.5rem;
|
||||||
|
line-height: 1.45;
|
||||||
|
field-sizing: content;
|
||||||
}
|
}
|
||||||
.emoji-toggle {
|
.emoji-toggle {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
border: none;
|
border: none;
|
||||||
background: transparent;
|
background: #fff;
|
||||||
width: 2.1rem;
|
border: 1px solid #e2e8f0;
|
||||||
height: 2.1rem;
|
width: 2.75rem;
|
||||||
|
height: 2.75rem;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
font-size: 1.15rem;
|
font-size: 1.15rem;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
@@ -1638,6 +1694,7 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
.emoji-toggle.on {
|
.emoji-toggle.on {
|
||||||
background: rgba(13, 148, 136, 0.12);
|
background: rgba(13, 148, 136, 0.12);
|
||||||
|
border-color: rgba(13, 148, 136, 0.35);
|
||||||
}
|
}
|
||||||
.emoji-panel {
|
.emoji-panel {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|||||||
Reference in New Issue
Block a user