Upgrade to Vue3 and Element Plus (#1374)

* Update Vue devtools

* upgrade vue pinia element-plus vue-i18n, add vite

* fix: i18n

* global components

* change v-deep

* upgrade vue-lazyload

* data table

* update enlint and safe-dialog

* package.json and vite.config.js

* el-icon

* el-message

* vue 2 -> vue3 migration changes

* $pinia

* dialog

* el-popover slot

* lint

* chore

* slot

* scss

* remote state access

* misc

* jsconfig

* el-button size mini -> small

* :model-value

* ElMessageBox

* datatable

* remove v-lazyload

* template #dropdown

* mini -> small

* css

* byebye hideTooltips

* use sass-embedded

* Update SQLite, remove unneeded libraries

* Fix shift remove local avatar favorites

* Electron arm64

* arm64 support

* bye pug

* f-word vite hah

* misc

* remove safe dialog component

* Add self invite to launch dialog

* Fix errors

* Icons 1

* improve localfavorite loading performance

* improve favorites world item performance

* dialog visibility changes for Element Plus

* clear element plus error

* import performance

* revert App.vue hah

* hah

* Revert "Add self invite to launch dialog"

This reverts commit 4801cfad58.

* Toggle self invite/open in-game

* Self invite on launch dialog

* el-button icon

* el-icon

* fix user dialog tab switching logic

* fix PlayerList

* Formatting changes

* More icons

* Fix friend log table

* loading margin

* fix markdown

* fix world dialog tab switching issue

* Fixes and formatting

* fix: global i18n.t export

* fix favorites world tab not working

* Create instance, displayName

* Remove group members sort by userId

* Fix loading dialog tabs on swtich

* Star

* charts console.warn

* wip: fix charts

* wip: fix charts

* wip: charts composables

* fix favorite item tooltip warning

* Fixes and formatting

* Clean up image dialogs

* Remove unused method

* Fix platform/size border

* Fix platform/size border

* $vr

* fix friendExportDialogVisible binding

* ElMessageBox and Settings

* Login formatting

* Rename VR overlay query

* Fix image popover and userdialog badges

* Formatting

* Big buttons

* Fixes, update Cef

* Fix gameLog table nav buttons jumping around while using nav buttons

* Fix z-index

* vr overlay

* vite input add theme

* defineAsyncComponent

* ISO 639-1

* fix i18n

* clean t

* Formatting, fix calendar, rotate arrows

* Show user status when user is offline

* Fix VR overlay

* fix theme and clean up

* split InstanceActivity

* tweak

* Fix VR overlay formatting

* fix scss var

* AppDebug hahahaha

* Years

* remove reactive

* improve perf

* state hah…

* fix user rendering poblems when user object is not yet loaded

* improve perf

* Update avatar/world image uploader, licenses, remove previous images dialog (old images are now deleted)

* improve perf 1

* Suppress stray errors

* fix traveling location display issue

* Fix empty instance creator

* improve friend list refresh performance

* fix main charts

* fix chart

* Fix darkmode

* Fix avatar dialog tags

---------

Co-authored-by: pa <maplenagisa@gmail.com>
This commit is contained in:
Natsumi
2025-09-12 10:45:24 +12:00
committed by GitHub
parent b233bbc299
commit 3324d0d279
249 changed files with 12948 additions and 19815 deletions
+117 -126
View File
@@ -2,15 +2,15 @@
<div v-show="menuActiveIndex === 'search'" class="x-container">
<div style="margin: 0 0 10px; display: flex; align-items: center">
<el-input
:value="searchText"
:model-value="searchText"
:placeholder="t('view.search.search_placeholder')"
style="flex: 1"
@input="updateSearchText"
@keyup.native.13="search"></el-input>
<el-tooltip placement="bottom" :content="t('view.search.clear_results_tooltip')" :disabled="hideTooltips">
@keyup.enter="search"></el-input>
<el-tooltip placement="bottom" :content="t('view.search.clear_results_tooltip')">
<el-button
type="default"
icon="el-icon-delete"
:icon="Delete"
circle
style="flex: none; margin-left: 10px"
@click="handleClearSearch"></el-button>
@@ -30,37 +30,35 @@
:key="user.id"
class="x-friend-item"
@click="showUserDialog(user.id)">
<template>
<div class="avatar">
<img v-lazy="userImage(user, true)" />
</div>
<div class="detail">
<span class="name" v-text="user.displayName"></span>
<span
v-if="randomUserColours"
class="extra"
:class="user.$trustClass"
v-text="user.$trustLevel"></span>
<span
v-else
class="extra"
:style="{ color: user.$userColour }"
v-text="user.$trustLevel"></span>
</div>
</template>
<div class="avatar">
<img :src="userImage(user, true)" loading="lazy" />
</div>
<div class="detail">
<span class="name" v-text="user.displayName"></span>
<span
v-if="randomUserColours"
class="extra"
:class="user.$trustClass"
v-text="user.$trustLevel"></span>
<span
v-else
class="extra"
:style="{ color: user.$userColour }"
v-text="user.$trustLevel"></span>
</div>
</div>
</div>
<el-button-group v-if="searchUserResults.length" style="margin-top: 15px">
<el-button
:disabled="!searchUserParams.offset"
icon="el-icon-back"
:icon="Back"
size="small"
@click="handleMoreSearchUser(-1)"
>{{ t('view.search.prev_page') }}</el-button
>
<el-button
:disabled="searchUserResults.length < 10"
icon="el-icon-right"
:icon="Right"
size="small"
@click="handleMoreSearchUser(1)"
>{{ t('view.search.next_page') }}</el-button
@@ -77,15 +75,17 @@
style="margin-bottom: 15px"
@command="(row) => searchWorld(row)">
<el-button size="small"
>{{ t('view.search.world.category') }} <i class="el-icon-arrow-down el-icon--right"></i
>{{ t('view.search.world.category') }} <el-icon class="el-icon--right"><ArrowDown /></el-icon
></el-button>
<el-dropdown-menu v-slot="dropdown">
<el-dropdown-item
v-for="row in cachedConfig.dynamicWorldRows"
:key="row.index"
:command="row"
v-text="row.name"></el-dropdown-item>
</el-dropdown-menu>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="row in cachedConfig.dynamicWorldRows"
:key="row.index"
:command="row"
v-text="row.name"></el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-checkbox v-model="searchWorldLabs" style="margin-left: 10px">{{
t('view.search.world.community_lab')
@@ -96,31 +96,29 @@
:key="world.id"
class="x-friend-item"
@click="showWorldDialog(world.id)">
<template>
<div class="avatar">
<img v-lazy="world.thumbnailImageUrl" />
</div>
<div class="detail">
<span class="name" v-text="world.name"></span>
<span v-if="world.occupants" class="extra"
>{{ world.authorName }} ({{ world.occupants }})</span
>
<span v-else class="extra" v-text="world.authorName"></span>
</div>
</template>
<div class="avatar">
<img :src="world.thumbnailImageUrl" loading="lazy" />
</div>
<div class="detail">
<span class="name" v-text="world.name"></span>
<span v-if="world.occupants" class="extra"
>{{ world.authorName }} ({{ world.occupants }})</span
>
<span v-else class="extra" v-text="world.authorName"></span>
</div>
</div>
</div>
<el-button-group v-if="searchWorldResults.length" style="margin-top: 15px">
<el-button
:disabled="!searchWorldParams.offset"
icon="el-icon-back"
:icon="Back"
size="small"
@click="moreSearchWorld(-1)"
>{{ t('view.search.prev_page') }}</el-button
>
<el-button
:disabled="searchWorldResults.length < 10"
icon="el-icon-right"
:icon="Right"
size="small"
@click="moreSearchWorld(1)"
>{{ t('view.search.next_page') }}</el-button
@@ -136,34 +134,33 @@
<el-dropdown
v-if="avatarRemoteDatabaseProviderList.length > 1"
trigger="click"
size="mini"
size="small"
style="margin-right: 5px"
@click.native.stop>
@click.stop>
<el-button size="small"
>{{ t('view.search.avatar.search_provider') }}
<i class="el-icon-arrow-down el-icon--right"></i
<el-icon class="el-icon--right"><ArrowDown /></el-icon
></el-button>
<el-dropdown-menu v-slot="dropdown">
<el-dropdown-item
v-for="provider in avatarRemoteDatabaseProviderList"
:key="provider"
@click.native="setAvatarProvider(provider)">
<i
v-if="provider === avatarRemoteDatabaseProvider"
class="el-icon-check el-icon--left"></i>
{{ provider }}
</el-dropdown-item>
</el-dropdown-menu>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="provider in avatarRemoteDatabaseProviderList"
:key="provider"
@click="setAvatarProvider(provider)">
<el-icon v-if="provider === avatarRemoteDatabaseProvider" class="el-icon--left"
><Check
/></el-icon>
{{ provider }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-tooltip
placement="bottom"
:content="t('view.search.avatar.refresh_tooltip')"
:disabled="hideTooltips">
<el-tooltip placement="bottom" :content="t('view.search.avatar.refresh_tooltip')">
<el-button
type="default"
:loading="userDialog.isAvatarsLoading"
size="mini"
icon="el-icon-refresh"
size="small"
:icon="Refresh"
circle
@click="refreshUserDialogAvatars"></el-button>
</el-tooltip>
@@ -176,7 +173,7 @@
<div style="display: flex; align-items: center">
<el-radio-group
v-model="searchAvatarFilter"
size="mini"
size="small"
style="margin: 5px; display: block"
@change="searchAvatar">
<el-radio label="all">{{ t('view.search.avatar.all') }}</el-radio>
@@ -186,7 +183,7 @@
<el-divider direction="vertical"></el-divider>
<el-radio-group
v-model="searchAvatarFilterRemote"
size="mini"
size="small"
style="margin: 5px; display: block"
@change="searchAvatar">
<el-radio label="all">{{ t('view.search.avatar.all') }}</el-radio>
@@ -201,7 +198,7 @@
<el-radio-group
v-model="searchAvatarSort"
:disabled="searchAvatarFilterRemote !== 'local'"
size="mini"
size="small"
style="margin: 5px; display: block"
@change="searchAvatar">
<el-radio label="name">{{ t('view.search.avatar.sort_name') }}</el-radio>
@@ -215,33 +212,31 @@
:key="avatar.id"
class="x-friend-item"
@click="showAvatarDialog(avatar.id)">
<template>
<div class="avatar">
<img v-if="avatar.thumbnailImageUrl" v-lazy="avatar.thumbnailImageUrl" />
<img v-else-if="avatar.imageUrl" v-lazy="avatar.imageUrl" />
</div>
<div class="detail">
<span class="name" v-text="avatar.name"></span>
<span
v-if="avatar.releaseStatus === 'public'"
class="extra"
style="color: #67c23a"
v-text="avatar.releaseStatus"></span>
<span
v-else-if="avatar.releaseStatus === 'private'"
class="extra"
style="color: #f56c6c"
v-text="avatar.releaseStatus"></span>
<span v-else class="extra" v-text="avatar.releaseStatus"></span>
<span class="extra" v-text="avatar.authorName"></span>
</div>
</template>
<div class="avatar">
<img v-if="avatar.thumbnailImageUrl" :src="avatar.thumbnailImageUrl" loading="lazy" />
<img v-else-if="avatar.imageUrl" :src="avatar.imageUrl" loading="lazy" />
</div>
<div class="detail">
<span class="name" v-text="avatar.name"></span>
<span
v-if="avatar.releaseStatus === 'public'"
class="extra"
style="color: #67c23a"
v-text="avatar.releaseStatus"></span>
<span
v-else-if="avatar.releaseStatus === 'private'"
class="extra"
style="color: #f56c6c"
v-text="avatar.releaseStatus"></span>
<span v-else class="extra" v-text="avatar.releaseStatus"></span>
<span class="extra" v-text="avatar.authorName"></span>
</div>
</div>
</div>
<el-button-group v-if="searchAvatarPage.length" style="margin-top: 15px">
<el-button
:disabled="!searchAvatarPageNum"
icon="el-icon-back"
:icon="Back"
size="small"
@click="moreSearchAvatar(-1)"
>{{ t('view.search.prev_page') }}</el-button
@@ -251,7 +246,7 @@
searchAvatarResults.length < 10 ||
(searchAvatarPageNum + 1) * 10 >= searchAvatarResults.length
"
icon="el-icon-right"
:icon="Right"
size="small"
@click="moreSearchAvatar(1)"
>{{ t('view.search.next_page') }}</el-button
@@ -268,41 +263,39 @@
:key="group.id"
class="x-friend-item"
@click="showGroupDialog(group.id)">
<template>
<div class="avatar">
<img v-lazy="getSmallThumbnailUrl(group.iconUrl)" />
</div>
<div class="detail">
<span class="name">
<span v-text="group.name"></span>
<span style="margin-left: 5px; font-weight: normal">({{ group.memberCount }})</span>
<span
style="
margin-left: 5px;
color: #909399;
font-weight: normal;
font-family: monospace;
font-size: 12px;
"
>{{ group.shortCode }}.{{ group.discriminator }}</span
>
</span>
<span class="extra" v-text="group.description"></span>
</div>
</template>
<div class="avatar">
<img :src="getSmallThumbnailUrl(group.iconUrl)" loading="lazy" />
</div>
<div class="detail">
<span class="name">
<span v-text="group.name"></span>
<span style="margin-left: 5px; font-weight: normal">({{ group.memberCount }})</span>
<span
style="
margin-left: 5px;
color: #909399;
font-weight: normal;
font-family: monospace;
font-size: 12px;
"
>{{ group.shortCode }}.{{ group.discriminator }}</span
>
</span>
<span class="extra" v-text="group.description"></span>
</div>
</div>
</div>
<el-button-group v-if="searchGroupResults.length" style="margin-top: 15px">
<el-button
:disabled="!searchGroupParams.offset"
icon="el-icon-back"
:icon="Back"
size="small"
@click="moreSearchGroup(-1)"
>{{ t('view.search.prev_page') }}</el-button
>
<el-button
:disabled="searchGroupResults.length < 10"
icon="el-icon-right"
:icon="Right"
size="small"
@click="moreSearchGroup(1)"
>{{ t('view.search.next_page') }}</el-button
@@ -314,9 +307,10 @@
</template>
<script setup>
import { Delete, Back, Right, Refresh, ArrowDown, Check } from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia';
import { ref } from 'vue';
import { useI18n } from 'vue-i18n-bridge';
import { useI18n } from 'vue-i18n';
import { groupRequest, worldRequest } from '../../api';
import {
compareByCreatedAt,
@@ -339,18 +333,15 @@
useWorldStore
} from '../../stores';
const { hideTooltips, randomUserColours } = storeToRefs(useAppearanceSettingsStore());
const { randomUserColours } = storeToRefs(useAppearanceSettingsStore());
const { avatarRemoteDatabase } = storeToRefs(useAdvancedSettingsStore());
const { avatarRemoteDatabaseProviderList, avatarRemoteDatabaseProvider } = storeToRefs(useAvatarProviderStore());
const { setAvatarProvider } = useAvatarProviderStore();
const { userDialog } = storeToRefs(useUserStore());
const { showUserDialog, refreshUserDialogAvatars } = useUserStore();
const { showAvatarDialog, lookupAvatars } = useAvatarStore();
const { cachedAvatars } = storeToRefs(useAvatarStore());
const { cachedWorlds } = storeToRefs(useWorldStore());
const { showWorldDialog } = useWorldStore();
const { showAvatarDialog, lookupAvatars, cachedAvatars } = useAvatarStore();
const { cachedWorlds, showWorldDialog } = useWorldStore();
const { showGroupDialog, applyGroup } = useGroupStore();
const { cachedGroups } = storeToRefs(useGroupStore());
const { menuActiveIndex } = storeToRefs(useUiStore());
const { searchText, searchUserResults } = storeToRefs(useSearchStore());
const { clearSearch, moreSearchUser } = useSearchStore();
@@ -523,7 +514,7 @@
.then((args) => {
const map = new Map();
for (const json of args.json) {
const ref = cachedWorlds.value.get(json.id);
const ref = cachedWorlds.get(json.id);
if (typeof ref !== 'undefined') {
map.set(ref.id, ref);
}
@@ -552,7 +543,7 @@
const query = searchText.value;
const queryUpper = query.toUpperCase();
if (!query) {
for (ref of cachedAvatars.value.values()) {
for (ref of cachedAvatars.values()) {
switch (searchAvatarFilter.value) {
case 'all':
avatars.set(ref.id, ref);
@@ -572,7 +563,7 @@
isSearchAvatarLoading.value = false;
} else {
if (searchAvatarFilterRemote.value === 'all' || searchAvatarFilterRemote.value === 'local') {
for (ref of cachedAvatars.value.values()) {
for (ref of cachedAvatars.values()) {
let match = ref.name.toUpperCase().includes(queryUpper);
if (!match && ref.description) {
match = ref.description.toUpperCase().includes(queryUpper);