added ownership transfer
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
@close="showInviteModal = false" @room-changed="handleRoomChanged" />
|
@close="showInviteModal = false" @room-changed="handleRoomChanged" />
|
||||||
|
|
||||||
<RoomDetailsModal v-if="showDetailsModal && isSocketConnected" :roomUuid="props.uuid"
|
<RoomDetailsModal v-if="showDetailsModal && isSocketConnected" :roomUuid="props.uuid"
|
||||||
:roomName="currentRoom?.name || 'Unknown room'" :isGlobal="currentRoom?.global || false"
|
:roomName="currentRoom?.name || 'Unknown room'" :isGlobal="currentRoom?.global || false" :ownerUuid="currentRoom?.owner_uuid || ''"
|
||||||
@close="showDetailsModal = false" @room-changed="handleRoomChanged" />
|
@close="showDetailsModal = false" @room-changed="handleRoomChanged" />
|
||||||
|
|
||||||
<div v-if="uuid === 'none'" class="no-room">
|
<div v-if="uuid === 'none'" class="no-room">
|
||||||
|
|||||||
@@ -8,10 +8,12 @@
|
|||||||
<input v-model="name" :placeholder="$t('chat-create-name-placeholder')" autofocus />
|
<input v-model="name" :placeholder="$t('chat-create-name-placeholder')" autofocus />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
<input type="checkbox" v-model="global" />
|
<input type="checkbox" v-model="global" />
|
||||||
<span>{{ $t('chat-create-global') }}</span>
|
<span>{{ $t('chat-create-global') }}</span>
|
||||||
</label>
|
</label>
|
||||||
|
-->
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button type="button" @click="emit('close')" class="secondary">
|
<button type="button" @click="emit('close')" class="secondary">
|
||||||
|
|||||||
@@ -1,51 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="backdrop" @click.self="emit('close')">
|
<div class="backdrop" @click.self="emit('close')">
|
||||||
<div class="modal">
|
<div class="modal">
|
||||||
<h2>{{ roomName }}</h2>
|
<h2>{{ roomName }}</h2>
|
||||||
<h3 v-if="!isGlobal">Members:</h3>
|
<h3 v-if="!isGlobal">Members:</h3>
|
||||||
|
|
||||||
<ul v-if="!isGlobal" class="member-list">
|
<ul v-if="!isGlobal" class="member-list">
|
||||||
<li v-for="user in users" :key="user.uuid || user.username" class="member-item">
|
<li v-for="user in users" :key="user.uuid || user.username" class="member-item" :class="{
|
||||||
<img :src="getAvatarUrl(user.uuid)" @error="handleAvatarError" class="avatar" alt="avatar" />
|
'is-owner': user.uuid === ownerUuid,
|
||||||
<span class="member-name">{{ user.username }}</span>
|
'selectable': isTransferringOwnership && user.uuid !== ownerUuid,
|
||||||
</li>
|
'dimmed': isTransferringOwnership && user.uuid === currentUserUuid
|
||||||
</ul>
|
}" @click="handleUserClick(user)">
|
||||||
|
<img :src="getAvatarUrl(user.uuid)" @error="handleAvatarError" class="avatar" alt="avatar" />
|
||||||
|
<span class="member-name">{{ user.username }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<p v-else class="global-tag">
|
<p v-else class="global-tag">
|
||||||
<i class="fa-solid fa-earth"></i>
|
<i class="fa-solid fa-earth"></i>
|
||||||
{{ $t('chat-room-global') }}
|
{{ $t('chat-room-global') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button v-if="!isGlobal && !isOwner" class="btn" @click="requestLeave" :disabled="isLoading">
|
<button v-if="!isGlobal && !isOwner && !isTransferringOwnership" class="btn" @click="requestLeave"
|
||||||
{{ $t('chat-room-actions-leave') }}
|
:disabled="isLoading">
|
||||||
</button>
|
{{ $t('chat-room-actions-leave') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
<button v-if="isOwner" class="btn delete-btn" @click="requestDelete" :disabled="isLoading">
|
<button v-if="isOwner && !isTransferringOwnership" class="btn delete-btn" @click="requestDelete"
|
||||||
{{ $t('chat-room-actions-delete') }}
|
:disabled="isLoading || isTransferringOwnership">
|
||||||
</button>
|
{{ $t('chat-room-actions-delete') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- <button class="btn ownership-btn">{{ $t('chat-room-actions-ownership') }}</button> -->
|
<button v-if="!isGlobal && isOwner" class="btn ownership-btn" :class="{ 'active': isTransferringOwnership }"
|
||||||
</div>
|
@click="toggleTransferMode" :disabled="isLoading">
|
||||||
|
{{ isTransferringOwnership ? $t('shared-cancel') : $t('chat-room-actions-ownership') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button @click="emit('close')" class="secondary">
|
<button @click="emit('close')" class="secondary">
|
||||||
{{ $t('shared-close') }}
|
{{ $t('shared-close') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ConfirmModal v-if="modalState.visible" :title="modalState.title" :message="modalState.message"
|
<ConfirmModal v-if="modalState.visible" :title="modalState.title" :message="modalState.message"
|
||||||
:confirm-label="modalState.confirmLabel" :confirm-button-class="modalState.isDanger ? 'btn-danger' : ''"
|
:confirm-label="modalState.confirmLabel" :confirm-button-class="modalState.isDanger ? 'btn-danger' : ''"
|
||||||
@yes="handleConfirmAction" @no="closeConfirmModal" />
|
@yes="handleConfirmAction" @no="closeConfirmModal" />
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref, reactive } from 'vue';
|
import { computed, onMounted, ref, reactive } from 'vue';
|
||||||
import { UserProfile } from '../types';
|
import { UserProfile } from '../types';
|
||||||
import { deleteRoom, leaveRoom, listMembers } from '../api/rooms';
|
import { deleteRoom, leaveRoom, listMembers, transferOwnership } from '../api/rooms';
|
||||||
import { getAuthData, getAvatarUrl } from '../store.ts';
|
import { getAuthData, getAvatarUrl } from '../store.ts';
|
||||||
import defaultAvatar from '../assets/default-avatar.png';
|
import defaultAvatar from '../assets/default-avatar.png';
|
||||||
import ConfirmModal from './ConfirmModal.vue';
|
import ConfirmModal from './ConfirmModal.vue';
|
||||||
@@ -53,228 +62,296 @@ import { useFluent } from 'fluent-vue';
|
|||||||
|
|
||||||
const { $t } = useFluent()
|
const { $t } = useFluent()
|
||||||
|
|
||||||
const props = defineProps<{ roomUuid: string, roomName: string, isGlobal: boolean }>()
|
const props = defineProps<{ roomUuid: string, roomName: string, isGlobal: boolean, ownerUuid: string }>()
|
||||||
const emit = defineEmits(['close', 'room-changed']);
|
const emit = defineEmits(['close', 'room-changed']);
|
||||||
|
|
||||||
const users = ref<UserProfile[]>([]);
|
const users = ref<UserProfile[]>([]);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const currentUserUuid = ref<string>('');
|
const currentUserUuid = ref<string>('');
|
||||||
|
|
||||||
type ActionType = 'leave' | 'delete' | null;
|
const isTransferringOwnership = ref(false);
|
||||||
|
const targetTransferUser = ref<UserProfile | null>(null);
|
||||||
|
|
||||||
|
type ActionType = 'leave' | 'delete' | 'transfer' | null;
|
||||||
|
|
||||||
const modalState = reactive({
|
const modalState = reactive({
|
||||||
visible: false,
|
visible: false,
|
||||||
type: null as ActionType,
|
type: null as ActionType,
|
||||||
title: '',
|
title: '',
|
||||||
message: '',
|
message: '',
|
||||||
confirmLabel: '',
|
confirmLabel: '',
|
||||||
isDanger: false
|
isDanger: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const requestLeave = () => {
|
const requestLeave = () => {
|
||||||
modalState.type = 'leave';
|
modalState.type = 'leave';
|
||||||
modalState.title = $t('chat-room-actions-leave');
|
modalState.title = $t('chat-room-actions-leave');
|
||||||
modalState.message = $t('chat-room-actions-leave-confirm');
|
modalState.message = $t('chat-room-actions-leave-confirm');
|
||||||
modalState.confirmLabel = $t('shared-leave');
|
modalState.confirmLabel = $t('shared-leave');
|
||||||
modalState.isDanger = false;
|
modalState.isDanger = false;
|
||||||
modalState.visible = true;
|
modalState.visible = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const requestDelete = () => {
|
const requestDelete = () => {
|
||||||
modalState.type = 'delete';
|
modalState.type = 'delete';
|
||||||
modalState.title = $t('chat-room-actions-delete');
|
modalState.title = $t('chat-room-actions-delete');
|
||||||
modalState.message = $t('chat-room-actions-delete-confirm');
|
modalState.message = $t('chat-room-actions-delete-confirm');
|
||||||
modalState.confirmLabel = $t('shared-delete');
|
modalState.confirmLabel = $t('shared-delete');
|
||||||
modalState.isDanger = true;
|
modalState.isDanger = true;
|
||||||
modalState.visible = true;
|
modalState.visible = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleTransferMode = () => {
|
||||||
|
isTransferringOwnership.value = !isTransferringOwnership.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUserClick = (user: UserProfile) => {
|
||||||
|
if (!isTransferringOwnership.value || user.uuid === currentUserUuid.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetTransferUser.value = user;
|
||||||
|
|
||||||
|
modalState.type = 'transfer';
|
||||||
|
modalState.title = $t('chat-room-actions-ownership');
|
||||||
|
modalState.message = $t('chat-room-actions-ownership-confirm', { user: user.username });
|
||||||
|
modalState.confirmLabel = $t('shared-confirm');
|
||||||
|
modalState.isDanger = true;
|
||||||
|
modalState.visible = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeConfirmModal = () => {
|
const closeConfirmModal = () => {
|
||||||
modalState.visible = false;
|
modalState.visible = false;
|
||||||
modalState.type = null;
|
modalState.type = null;
|
||||||
|
targetTransferUser.value = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConfirmAction = async () => {
|
const handleConfirmAction = async () => {
|
||||||
const actionType = modalState.type;
|
const actionType = modalState.type;
|
||||||
|
const userToTransfer = targetTransferUser.value;
|
||||||
|
|
||||||
closeConfirmModal();
|
closeConfirmModal();
|
||||||
|
isTransferringOwnership.value = false;
|
||||||
|
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (actionType === 'leave') {
|
if (actionType === 'leave') {
|
||||||
await leaveRoom(props.roomUuid);
|
await leaveRoom(props.roomUuid);
|
||||||
emit('room-changed');
|
emit('room-changed');
|
||||||
emit('close');
|
emit('close');
|
||||||
} else if (actionType === 'delete') {
|
} else if (actionType === 'delete') {
|
||||||
await deleteRoom(props.roomUuid);
|
await deleteRoom(props.roomUuid);
|
||||||
emit('room-changed');
|
emit('room-changed');
|
||||||
emit('close');
|
emit('close');
|
||||||
}
|
} else if (actionType === 'transfer' && userToTransfer) {
|
||||||
} catch (error) {
|
console.log("ownership")
|
||||||
console.error(`Failed to ${actionType} room:`, error);
|
await transferOwnership(props.roomUuid, userToTransfer.uuid);
|
||||||
} finally {
|
emit('room-changed');
|
||||||
isLoading.value = false;
|
emit('close');
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to ${actionType} room:`, error);
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isOwner = computed(() => {
|
const isOwner = computed(() => {
|
||||||
return true; // Logic placeholder
|
return currentUserUuid.value === props.ownerUuid;
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const auth = await getAuthData();
|
const auth = await getAuthData();
|
||||||
currentUserUuid.value = auth.user?.uuid || 'undefined';
|
currentUserUuid.value = auth.user?.uuid || 'undefined';
|
||||||
|
|
||||||
if (!props.isGlobal) {
|
if (!props.isGlobal) {
|
||||||
users.value = await listMembers(props.roomUuid);
|
users.value = await listMembers(props.roomUuid);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleAvatarError = (event: Event) => {
|
const handleAvatarError = (event: Event) => {
|
||||||
const img = event.target as HTMLImageElement;
|
const img = event.target as HTMLImageElement;
|
||||||
img.src = defaultAvatar;
|
img.src = defaultAvatar;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.backdrop {
|
.backdrop {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(0, 0, 0, 0.6);
|
background: rgba(0, 0, 0, 0.6);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
z-index: 500;
|
z-index: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1.2rem;
|
gap: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.secondary {
|
.secondary {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.member-list {
|
.member-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.member-item {
|
.member-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
border: 2px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.member-name {
|
.member-name {
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-item.is-owner .avatar {
|
||||||
|
border: 2px solid var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-item.is-owner .member-name {
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-item.dimmed {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
filter: grayscale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-item.selectable {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: rgba(var(--accent-rgb, 100, 100, 255), 0.1);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-item.selectable:hover {
|
||||||
|
background-color: var(--accent);
|
||||||
|
color: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-item.selectable:hover .member-name {
|
||||||
|
color: var(--bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.global-tag {
|
.global-tag {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 0.6rem;
|
gap: 0.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 0.6rem;
|
gap: 0.6rem;
|
||||||
margin-top: 22px;
|
margin-top: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
border: 1px solid var(--accent);
|
border: 1px solid var(--accent);
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:hover {
|
.btn:hover {
|
||||||
color: var(--accent)
|
color: var(--accent)
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:hover i {
|
.btn:hover i {
|
||||||
color: var(--accent)
|
color: var(--accent)
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn i {
|
.btn i {
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.delete-btn {
|
.delete-btn {
|
||||||
border: 1px solid var(--error);
|
border: 1px solid var(--error);
|
||||||
}
|
}
|
||||||
|
|
||||||
.delete-btn:hover {
|
.delete-btn:hover {
|
||||||
color: var(--error)
|
color: var(--error)
|
||||||
}
|
}
|
||||||
|
|
||||||
.delete-btn:hover i {
|
.delete-btn:hover i {
|
||||||
color: var(--error)
|
color: var(--error)
|
||||||
}
|
}
|
||||||
|
|
||||||
.ownership-btn {
|
.ownership-btn {
|
||||||
border: 1px solid var(--accent-second);
|
border: 1px solid var(--accent-second);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ownership-btn:hover {
|
.ownership-btn:hover,
|
||||||
color: var(--accent-second)
|
.ownership-btn.active {
|
||||||
|
background-color: var(--accent-second);
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ownership-btn:hover i {
|
.ownership-btn:hover i {
|
||||||
color: var(--accent-second)
|
color: white;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ let unlistenDrag: UnlistenFn;
|
|||||||
let unlistenHover: UnlistenFn;
|
let unlistenHover: UnlistenFn;
|
||||||
let unlistenLeave: UnlistenFn;
|
let unlistenLeave: UnlistenFn;
|
||||||
|
|
||||||
|
function uiLog(message: string) {
|
||||||
|
errorMessage.value += (errorMessage.value ? '\n' : '') + message;
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
unlistenHover = await listen('tauri://drag-enter', () => isDragging.value = true);
|
unlistenHover = await listen('tauri://drag-enter', () => isDragging.value = true);
|
||||||
unlistenLeave = await listen('tauri://drag-leave', () => isDragging.value = false);
|
unlistenLeave = await listen('tauri://drag-leave', () => isDragging.value = false);
|
||||||
@@ -83,21 +87,36 @@ onUnmounted(() => {
|
|||||||
if (previewUrl.value) URL.revokeObjectURL(previewUrl.value);
|
if (previewUrl.value) URL.revokeObjectURL(previewUrl.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
async function pickFile() {
|
async function pickFile() {
|
||||||
|
errorMessage.value = '';
|
||||||
|
// uiLog('[pickFile] called');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const selected = await open({
|
const selected = await open({
|
||||||
multiple: false,
|
multiple: false,
|
||||||
filters: [{ name: 'Image', extensions: ['png', 'jpeg', 'jpg', 'webp'] }]
|
filters: [{ name: 'Image', extensions: ['png', 'jpeg', 'jpg', 'webp'] }]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (selected && typeof selected === 'string') {
|
// uiLog('[pickFile] dialog returned: ' + JSON.stringify(selected));
|
||||||
await setFile(selected);
|
|
||||||
|
if (!selected) {
|
||||||
|
uiLog('No file selected (null)');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
if (typeof selected !== 'string') {
|
||||||
|
uiLog('Unexpected return type');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await setFile(selected);
|
||||||
|
} catch (err: any) {
|
||||||
|
uiLog('Error: ' + (err?.message ?? String(err)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Async function to read file and create blob URL
|
// Async function to read file and create blob URL
|
||||||
async function setFile(path: string) {
|
async function setFile(path: string) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ chat-room-actions-leave-confirm = Are you sure you want to leave this room?
|
|||||||
chat-room-actions-delete = Delete Room
|
chat-room-actions-delete = Delete Room
|
||||||
chat-room-actions-delete-confirm = Are you sure you want to delete this room? This cannot be undone.
|
chat-room-actions-delete-confirm = Are you sure you want to delete this room? This cannot be undone.
|
||||||
chat-room-actions-ownership = Transfer Ownership
|
chat-room-actions-ownership = Transfer Ownership
|
||||||
|
chat-room-actions-ownership-confirm = Do you really wish to transfer this room's ownership to {$user}? You might never get it back.
|
||||||
chat-create-title = Create room
|
chat-create-title = Create room
|
||||||
chat-create-name = Room name
|
chat-create-name = Room name
|
||||||
chat-create-name-placeholder = room name
|
chat-create-name-placeholder = room name
|
||||||
@@ -110,3 +111,4 @@ shared-save = Save
|
|||||||
shared-updating = Updating
|
shared-updating = Updating
|
||||||
shared-delete = Delete
|
shared-delete = Delete
|
||||||
shared-leave = Leave
|
shared-leave = Leave
|
||||||
|
shared-confirm = Confirm
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ chat-room-actions-leave-confirm = Voulez-vous vraiment quitter ce salon?
|
|||||||
chat-room-actions-delete = Supprimer le Salon
|
chat-room-actions-delete = Supprimer le Salon
|
||||||
chat-room-actions-delete-confirm = Voulez-vous vraiment supprimer ce salon? C'est irréversible.
|
chat-room-actions-delete-confirm = Voulez-vous vraiment supprimer ce salon? C'est irréversible.
|
||||||
chat-room-actions-ownership = Transférer la Propriété
|
chat-room-actions-ownership = Transférer la Propriété
|
||||||
|
chat-room-actions-ownership-confirm = Voulez-vous vraiment transférer la propriété du salon à {$user}? Vous pourriez ne jamais la récupérer.
|
||||||
chat-create-title = Créer un salon
|
chat-create-title = Créer un salon
|
||||||
chat-create-name = Nom du salon
|
chat-create-name = Nom du salon
|
||||||
chat-create-name-placeholder = nom du salon
|
chat-create-name-placeholder = nom du salon
|
||||||
@@ -108,3 +109,4 @@ shared-save = Enregistrer
|
|||||||
shared-updating = Mise à jour...
|
shared-updating = Mise à jour...
|
||||||
shared-delete = Supprimer
|
shared-delete = Supprimer
|
||||||
shared-leave = Quitter
|
shared-leave = Quitter
|
||||||
|
shared-confirm = Confirmer
|
||||||
|
|||||||
24
src/main.ts
24
src/main.ts
@@ -8,22 +8,22 @@ import './base.css'
|
|||||||
import { getLocalePreference } from './store.ts'
|
import { getLocalePreference } from './store.ts'
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
await validateToken()
|
await validateToken()
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(fluent)
|
app.use(fluent)
|
||||||
|
|
||||||
const savedLocale = await getLocalePreference();
|
const savedLocale = await getLocalePreference();
|
||||||
const osLocale = navigator.language;
|
const osLocale = navigator.language;
|
||||||
|
|
||||||
if (savedLocale) {
|
if (savedLocale) {
|
||||||
setLanguage(savedLocale);
|
setLanguage(savedLocale);
|
||||||
} else {
|
} else {
|
||||||
setLanguage(osLocale);
|
setLanguage(osLocale);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
}
|
}
|
||||||
|
|
||||||
init()
|
init()
|
||||||
|
|||||||
Reference in New Issue
Block a user