mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-20 15:23:50 +02:00
215 lines
7.8 KiB
Vue
215 lines
7.8 KiB
Vue
<template>
|
|
<div style="display: flex; align-items: center; justify-content: space-between">
|
|
<div style="display: flex; align-items: center">
|
|
<Button
|
|
class="rounded-full"
|
|
variant="ghost"
|
|
size="icon-sm"
|
|
:disabled="userDialog.isWorldsLoading"
|
|
@click="refreshUserDialogWorlds()">
|
|
<Spinner v-if="userDialog.isWorldsLoading" />
|
|
<RefreshCw v-else />
|
|
</Button>
|
|
<span style="margin-left: 6px">{{
|
|
t('dialog.user.worlds.total_count', { count: userDialog.worlds.length })
|
|
}}</span>
|
|
</div>
|
|
<div style="display: flex; align-items: center">
|
|
<span class="mr-1">{{ t('dialog.user.worlds.sort_by') }}</span>
|
|
<Select
|
|
:model-value="userDialogWorldSortingKey"
|
|
:disabled="userDialog.isWorldsLoading"
|
|
@update:modelValue="setUserDialogWorldSortingByKey">
|
|
<SelectTrigger size="sm" @click.stop>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem
|
|
v-for="(item, key) in userDialogWorldSortingOptions"
|
|
:key="String(key)"
|
|
:value="String(key)">
|
|
{{ t(item.name) }}
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
<span class="ml-2 mr-1">{{ t('dialog.user.worlds.order_by') }}</span>
|
|
<Select
|
|
:model-value="userDialogWorldOrderKey"
|
|
:disabled="userDialog.isWorldsLoading"
|
|
@update:modelValue="setUserDialogWorldOrderByKey">
|
|
<SelectTrigger size="sm" @click.stop>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem
|
|
v-for="(item, key) in userDialogWorldOrderOptions"
|
|
:key="String(key)"
|
|
:value="String(key)">
|
|
{{ t(item.name) }}
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
<div class="flex flex-wrap items-start" style="margin-top: 8px; min-height: 60px">
|
|
<template v-if="userDialog.worlds.length">
|
|
<div
|
|
v-for="world in userDialog.worlds"
|
|
:key="world.id"
|
|
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
|
@click="showWorldDialog(world.id)">
|
|
<div class="relative inline-block flex-none size-9 mr-2.5">
|
|
<img class="size-full rounded-full object-cover" :src="world.thumbnailImageUrl" loading="lazy" />
|
|
</div>
|
|
<div class="flex-1 overflow-hidden">
|
|
<span class="block truncate font-medium leading-[18px]" v-text="world.name"></span>
|
|
<span v-if="world.occupants" class="block truncate text-xs">({{ world.occupants }})</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<div
|
|
v-else-if="!userDialog.isWorldsLoading"
|
|
style="display: flex; justify-content: center; align-items: center; min-height: 120px; width: 100%">
|
|
<DataTableEmpty type="nodata" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
|
import { Button } from '@/components/ui/button';
|
|
import { DataTableEmpty } from '@/components/ui/data-table';
|
|
import { RefreshCw } from 'lucide-vue-next';
|
|
import { Spinner } from '@/components/ui/spinner';
|
|
import { ref } from 'vue';
|
|
import { storeToRefs } from 'pinia';
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
import { useUserStore, useWorldStore } from '../../../stores';
|
|
import { userDialogWorldOrderOptions, userDialogWorldSortingOptions } from '../../../shared/constants/';
|
|
import { useOptionKeySelect } from '../../../composables/useOptionKeySelect';
|
|
import { worldRequest } from '../../../api';
|
|
|
|
const { t } = useI18n();
|
|
|
|
const userStore = useUserStore();
|
|
const { userDialog, currentUser } = storeToRefs(userStore);
|
|
const { cachedWorlds, showWorldDialog } = useWorldStore();
|
|
|
|
const userDialogWorldsRequestId = ref(0);
|
|
|
|
/**
|
|
*
|
|
* @param userId
|
|
*/
|
|
function setUserDialogWorlds(userId) {
|
|
const worlds = [];
|
|
for (const ref of cachedWorlds.values()) {
|
|
if (ref.authorId === userId) {
|
|
worlds.push(ref);
|
|
}
|
|
}
|
|
userDialog.value.worlds = worlds;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function refreshUserDialogWorlds() {
|
|
const D = userDialog.value;
|
|
if (D.isWorldsLoading) {
|
|
return;
|
|
}
|
|
const requestId = ++userDialogWorldsRequestId.value;
|
|
D.isWorldsLoading = true;
|
|
const params = {
|
|
n: 50,
|
|
offset: 0,
|
|
sort: userDialog.value.worldSorting.value,
|
|
order: userDialog.value.worldOrder.value,
|
|
// user: 'friends',
|
|
userId: D.id,
|
|
releaseStatus: 'public'
|
|
};
|
|
if (params.userId === currentUser.value.id) {
|
|
params.user = 'me';
|
|
params.releaseStatus = 'all';
|
|
}
|
|
const worlds = [];
|
|
const worldIds = new Set();
|
|
(async () => {
|
|
try {
|
|
let offset = 0;
|
|
while (true) {
|
|
const args = await worldRequest.getCachedWorlds({
|
|
...params,
|
|
offset
|
|
});
|
|
if (requestId !== userDialogWorldsRequestId.value || D.id !== params.userId) {
|
|
return;
|
|
}
|
|
for (const world of args.json) {
|
|
if (!worldIds.has(world.id)) {
|
|
worldIds.add(world.id);
|
|
worlds.push(world);
|
|
}
|
|
}
|
|
if (args.json.length < params.n) {
|
|
break;
|
|
}
|
|
offset += params.n;
|
|
}
|
|
if (requestId === userDialogWorldsRequestId.value && D.id === params.userId) {
|
|
userDialog.value.worlds = worlds;
|
|
}
|
|
} finally {
|
|
if (requestId === userDialogWorldsRequestId.value) {
|
|
D.isWorldsLoading = false;
|
|
}
|
|
}
|
|
})().catch((err) => {
|
|
console.error('refreshUserDialogWorlds failed', err);
|
|
});
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param sortOrder
|
|
*/
|
|
async function setUserDialogWorldSorting(sortOrder) {
|
|
const D = userDialog.value;
|
|
if (D.worldSorting.value === sortOrder.value) {
|
|
return;
|
|
}
|
|
D.worldSorting = sortOrder;
|
|
refreshUserDialogWorlds();
|
|
}
|
|
|
|
const { selectedKey: userDialogWorldSortingKey, selectByKey: setUserDialogWorldSortingByKey } = useOptionKeySelect(
|
|
userDialogWorldSortingOptions,
|
|
() => userDialog.value.worldSorting,
|
|
setUserDialogWorldSorting
|
|
);
|
|
|
|
/**
|
|
*
|
|
* @param order
|
|
*/
|
|
async function setUserDialogWorldOrder(order) {
|
|
const D = userDialog.value;
|
|
if (D.worldOrder.value === order.value) {
|
|
return;
|
|
}
|
|
D.worldOrder = order;
|
|
refreshUserDialogWorlds();
|
|
}
|
|
|
|
const { selectedKey: userDialogWorldOrderKey, selectByKey: setUserDialogWorldOrderByKey } = useOptionKeySelect(
|
|
userDialogWorldOrderOptions,
|
|
() => userDialog.value.worldOrder,
|
|
setUserDialogWorldOrder
|
|
);
|
|
|
|
defineExpose({ setUserDialogWorlds, refreshUserDialogWorlds });
|
|
</script>
|