refactor: app.js (#1291)

* refactor: frontend

* Fix avatar gallery sort

* Update .NET dependencies

* Update npm dependencies

electron v37.1.0

* bulkRefreshFriends

* fix dark theme

* Remove crowdin

* Fix config.json dialog not updating

* VRCX log file fixes & add Cef log

* Remove SharedVariable, fix startup

* Revert init theme change

* Logging date not working? Fix WinformThemer designer error

* Add Cef request hander, no more escaping main page

* clean

* fix

* fix

* clean

* uh

* Apply thememode at startup, fixes random user colours

* Split database into files

* Instance info remove empty lines

* Open external VRC links with VRCX

* Electron fixes

* fix userdialog style

* ohhhh

* fix store

* fix store

* fix: load all group members after kicking a user

* fix: world dialog favorite button style

* fix: Clear VRCX Cache Timer input value

* clean

* Fix VR overlay

* Fix VR overlay 2

* Fix Discord discord rich presence for RPC worlds

* Clean up age verified user tags

* Fix playerList being occupied after program reload

* no `this`

* Fix login stuck loading

* writable: false

* Hide dialogs on logout

* add flush sync option

* rm LOGIN event

* rm LOGOUT event

* remove duplicate event listeners

* remove duplicate event listeners

* clean

* remove duplicate event listeners

* clean

* fix theme style

* fix t

* clearable

* clean

* fix ipcEvent

* Small changes

* Popcorn Palace support

* Remove checkActiveFriends

* Clean up

* Fix dragEnterCef

* Block API requests when not logged in

* Clear state on login & logout

* Fix worldDialog instances not updating

* use <script setup>

* Fix avatar change event, CheckGameRunning at startup

* Fix image dragging

* fix

* Remove PWI

* fix updateLoop

* add webpack-dev-server to dev environment

* rm unnecessary chunks

* use <script setup>

* webpack-dev-server changes

* use <script setup>

* use <script setup>

* Fix UGC text size

* Split login event

* t

* use <script setup>

* fix

* Update .gitignore and enable checkJs in jsconfig

* fix i18n t

* use <script setup>

* use <script setup>

* clean

* global types

* fix

* use checkJs for debugging

* Add watchState for login watchers

* fix .vue template

* type fixes

* rm Vue.filter

* Cef v138.0.170, VC++ 2022

* Settings fixes

* Remove 'USER:CURRENT'

* clean up 2FA callbacks

* remove userApply

* rm i18n import

* notification handling to use notification store methods

* refactor favorite handling to use favorite store methods and clean up event emissions

* refactor moderation handling to use dedicated functions for player moderation events

* refactor friend handling to use dedicated functions for friend events

* Fix program startup, move lang init

* Fix friend state

* Fix status change error

* Fix user notes diff

* fix

* rm group event

* rm auth event

* rm avatar event

* clean

* clean

* getUser

* getFriends

* getFavoriteWorlds, getFavoriteAvatars

* AvatarGalleryUpload btn style & package.json update

* Fix friend requests

* Apply user

* Apply world

* Fix note diff

* Fix VR overlay

* Fixes

* Update build scripts

* Apply avatar

* Apply instance

* Apply group

* update hidden VRC+ badge

* Fix sameInstance "private"

* fix 502/504 API errors

* fix 502/504 API errors

* clean

* Fix friend in same instance on orange showing twice in friends list

* Add back in broken friend state repair methods

* add types

---------

Co-authored-by: Natsumi <cmcooper123@hotmail.com>
This commit is contained in:
pa
2025-07-14 12:00:08 +09:00
committed by GitHub
parent 952fd77ed5
commit f4f78bb5ec
323 changed files with 47745 additions and 43326 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,6 @@
<el-input
v-for="(provider, index) in avatarRemoteDatabaseProviderList"
:key="index"
v-model="avatarRemoteDatabaseProviderList[index]"
:value="provider"
size="small"
style="margin-top: 5px"
@@ -25,34 +24,25 @@
</template>
<script setup>
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n-bridge';
import { useAvatarProviderStore } from '../../../stores';
const { t } = useI18n();
const avatarProviderStore = useAvatarProviderStore();
const { avatarRemoteDatabaseProviderList } = storeToRefs(avatarProviderStore);
const { saveAvatarProviderList, removeAvatarProvider } = avatarProviderStore;
defineProps({
avatarRemoteDatabaseProviderList: {
type: Array,
required: true
},
isAvatarProviderDialogVisible: {
type: Boolean,
required: true
}
});
const emit = defineEmits([
'update:isAvatarProviderDialogVisible',
'update:avatarRemoteDatabaseProviderList',
'saveAvatarProviderList',
'removeAvatarProvider'
]);
function saveAvatarProviderList() {
emit('saveAvatarProviderList');
}
function removeAvatarProvider(provider) {
emit('removeAvatarProvider', provider);
}
const emit = defineEmits(['update:isAvatarProviderDialogVisible']);
function closeDialog() {
emit('update:isAvatarProviderDialogVisible', false);

View File

@@ -5,10 +5,11 @@
:title="t('dialog.change_log.header')"
width="800px"
top="5vh"
append-to-body
@close="closeDialog">
<div v-if="changeLogDialog.visible" class="changelog-dialog">
<div v-loading="!changeLogDialog.changeLog" class="changelog-dialog">
<h2 v-text="changeLogDialog.buildName"></h2>
<span>
<span v-show="changeLogDialog.buildName">
{{ t('dialog.change_log.description') }}
<a class="x-link" @click="openExternalLink('https://www.patreon.com/Natsumi_VRCX')">Patreon</a>,
<a class="x-link" @click="openExternalLink('https://ko-fi.com/natsumi_sama')">Ko-fi</a>.
@@ -33,22 +34,21 @@
</template>
<script setup>
import { inject } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n-bridge';
import { openExternalLink } from '../../../shared/utils';
import { useVRCXUpdaterStore } from '../../../stores';
const VueMarkdown = () => import('vue-markdown');
const VRCXUpdaterStore = useVRCXUpdaterStore();
const { changeLogDialog } = storeToRefs(VRCXUpdaterStore);
const { t } = useI18n();
const openExternalLink = inject('openExternalLink');
const props = defineProps({
changeLogDialog: {
type: Object,
required: true
}
});
const emit = defineEmits(['update:changeLogDialog']);
function closeDialog() {
emit('update:changeLogDialog', { ...props.changeLogDialog, visible: false });
changeLogDialog.value.visible = false;
}
</script>

View File

@@ -28,7 +28,7 @@
</el-radio-group>
</div>
<template v-if="props.photonLoggingEnabled">
<template v-if="photonLoggingEnabled">
<br />
<div class="toggle-item">
<span class="toggle-name">Photon Event Logging</span>
@@ -59,38 +59,25 @@
</template>
<script setup>
import { storeToRefs } from 'pinia';
import { computed } from 'vue';
import { useI18n } from 'vue-i18n-bridge';
import configRepository from '../../../service/config';
import { feedFiltersOptions } from '../../../composables/setting/constants/feedFiltersOptions';
import { feedFiltersOptions, sharedFeedFiltersDefaults } from '../../../shared/constants';
import { useNotificationsSettingsStore, usePhotonStore, useSharedFeedStore } from '../../../stores';
const { t } = useI18n();
const { photonLoggingEnabled } = storeToRefs(usePhotonStore());
const { notyFeedFiltersOptions, wristFeedFiltersOptions, photonFeedFiltersOptions } = feedFiltersOptions();
const { sharedFeedFilters } = storeToRefs(useNotificationsSettingsStore());
const { updateSharedFeed } = useSharedFeedStore();
const props = defineProps({
feedFiltersDialogMode: {
type: String,
required: true,
default: ''
},
photonLoggingEnabled: {
type: Boolean,
default: false
},
sharedFeedFilters: {
type: Object,
default: () => ({
noty: {},
wrist: {}
})
},
sharedFeedFiltersDefaults: {
type: Object,
default: () => ({
noty: {},
wrist: {}
})
}
});
@@ -100,8 +87,8 @@
const currentSharedFeedFilters = computed(() => {
return props.feedFiltersDialogMode === 'noty'
? props.sharedFeedFilters['noty']
: props.sharedFeedFilters['wrist'];
? sharedFeedFilters.value['noty']
: sharedFeedFilters.value['wrist'];
});
const dialogTitle = computed(() => {
@@ -116,23 +103,23 @@
return props.feedFiltersDialogMode === 'noty' ? resetNotyFeedFilters : resetWristFeedFilters;
});
const emit = defineEmits(['update:feedFiltersDialogMode', 'updateSharedFeed']);
const emit = defineEmits(['update:feedFiltersDialogMode']);
function saveSharedFeedFilters() {
configRepository.setString('sharedFeedFilters', JSON.stringify(props.sharedFeedFilters));
emit('updateSharedFeed', true);
configRepository.setString('sharedFeedFilters', JSON.stringify(sharedFeedFilters.value));
updateSharedFeed(true);
}
function resetNotyFeedFilters() {
props.sharedFeedFilters.noty = {
...props.sharedFeedFiltersDefaults.noty
sharedFeedFilters.value.noty = {
...sharedFeedFiltersDefaults.noty
};
saveSharedFeedFilters();
}
async function resetWristFeedFilters() {
props.sharedFeedFilters.wrist = {
...props.sharedFeedFiltersDefaults.wrist
sharedFeedFilters.value.wrist = {
...sharedFeedFiltersDefaults.wrist
};
saveSharedFeedFilters();
}

View File

@@ -56,33 +56,28 @@
</template>
<script setup>
import { ref, inject, getCurrentInstance } from 'vue';
import { storeToRefs } from 'pinia';
import { computed, getCurrentInstance, ref } from 'vue';
import { useI18n } from 'vue-i18n-bridge';
import configRepository from '../../../service/config';
const openExternalLink = inject('openExternalLink');
const isLinux = inject('isLinux');
import { openExternalLink } from '../../../shared/utils';
import { useLaunchStore } from '../../../stores';
const { t } = useI18n();
const instance = getCurrentInstance();
const $message = instance.proxy.$message;
defineProps({
isLaunchOptionsDialogVisible: {
type: Boolean,
default: false,
required: true
}
});
const emit = defineEmits(['update:isLaunchOptionsDialogVisible']);
const launchStore = useLaunchStore();
const { isLaunchOptionsDialogVisible } = storeToRefs(launchStore);
const launchOptionsDialog = ref({
launchArguments: '',
vrcLaunchPathOverride: ''
});
const isLinux = computed(() => LINUX);
function init() {
configRepository
.getString('launchArguments')
@@ -125,6 +120,6 @@
}
function closeDialog() {
emit('update:isLaunchOptionsDialogVisible');
isLaunchOptionsDialogVisible.value = false;
}
</script>

View File

@@ -88,26 +88,23 @@
</template>
<script setup>
import { ref, watch, inject } from 'vue';
import { storeToRefs } from 'pinia';
import { ref, watch } from 'vue';
import { useI18n } from 'vue-i18n-bridge';
import utils from '../../../classes/utils';
import * as workerTimers from 'worker-timers';
import { miscRequest } from '../../../api';
import { removeFromArray, userImage, userImageFull } from '../../../shared/utils';
import { useFriendStore, useGalleryStore, useUserStore } from '../../../stores';
const { t } = useI18n();
const userImage = inject('userImage');
const userImageFull = inject('userImageFull');
const showUserDialog = inject('showUserDialog');
const showFullscreenImageDialog = inject('showFullscreenImageDialog');
const { friends } = storeToRefs(useFriendStore());
const { showUserDialog } = useUserStore();
const { showFullscreenImageDialog } = useGalleryStore();
const props = defineProps({
isNoteExportDialogVisible: {
type: Boolean
},
friends: {
type: Map,
default: () => new Map()
}
});
@@ -146,7 +143,7 @@
function updateNoteExportDialog() {
const data = [];
props.friends.forEach((ctx) => {
friends.value.forEach((ctx) => {
const newMemo = ctx.memo.replace(/[\r\n]/g, ' ');
if (ctx.memo && ctx.ref && ctx.ref.note !== newMemo.slice(0, 256)) {
data.push({
@@ -174,7 +171,7 @@
targetUserId: ctx.id,
note: ctx.memo.slice(0, 256)
});
utils.removeFromArray(noteExportTable.value.data, ctx);
removeFromArray(noteExportTable.value.data, ctx);
progress.value++;
await new Promise((resolve) => {
workerTimers.setTimeout(resolve, 5000);
@@ -195,7 +192,7 @@
}
function removeFromNoteExportTable(ref) {
utils.removeFromArray(noteExportTable.value.data, ref);
removeFromArray(noteExportTable.value.data, ref);
}
function closeDialog() {

View File

@@ -46,27 +46,25 @@
</template>
<script setup>
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n-bridge';
import { useNotificationsSettingsStore } from '../../../stores';
const { t } = useI18n();
const notificationsSettingsStore = useNotificationsSettingsStore();
const { notificationPosition } = storeToRefs(notificationsSettingsStore);
const { changeNotificationPosition } = notificationsSettingsStore;
defineProps({
isNotificationPositionDialogVisible: {
type: Boolean,
default: false
},
notificationPosition: {
type: String,
default: 'topRight'
}
});
const emit = defineEmits(['update:isNotificationPositionDialogVisible', 'changeNotificationPosition']);
const emit = defineEmits(['update:isNotificationPositionDialogVisible']);
function closeDialog() {
emit('update:isNotificationPositionDialogVisible', false);
}
function changeNotificationPosition(value) {
emit('changeNotificationPosition', value);
}
</script>

View File

@@ -20,7 +20,7 @@
<script setup>
import { useI18n } from 'vue-i18n-bridge';
import { openSourceSoftwareLicenses } from '../../../composables/setting/constants/openSourceSoftwareLicenses';
import { openSourceSoftwareLicenses } from '../../../shared/constants';
const { t } = useI18n();

View File

@@ -32,7 +32,7 @@
enablePrimaryPasswordDialog.password.length === 0 ||
enablePrimaryPasswordDialog.password !== enablePrimaryPasswordDialog.rePassword
"
@click="setPrimaryPassword">
@click="handleSetPrimaryPassword()">
{{ t('dialog.primary_password.ok') }}
</el-button>
</template>
@@ -40,20 +40,18 @@
</template>
<script setup>
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n-bridge';
import { useAuthStore } from '../../../stores';
const { t } = useI18n();
const props = defineProps({
enablePrimaryPasswordDialog: {
type: Object,
required: true
}
});
const authStore = useAuthStore();
const { enablePrimaryPasswordDialog } = storeToRefs(authStore);
const { setPrimaryPassword } = authStore;
const emit = defineEmits(['setPrimaryPassword']);
function setPrimaryPassword() {
emit('setPrimaryPassword', props.enablePrimaryPasswordDialog.password);
props.enablePrimaryPasswordDialog.visible = false;
function handleSetPrimaryPassword() {
setPrimaryPassword(enablePrimaryPasswordDialog.value.password);
enablePrimaryPasswordDialog.value.visible = false;
}
</script>

View File

@@ -9,13 +9,13 @@
<div style="margin-top: 10px">
<div style="display: flex; align-items: center; justify-content: space-between; font-size: 12px">
<span class="name" style="margin-right: 24px">{{ t('dialog.registry_backup.auto_backup') }}</span>
<el-switch v-model="vrcRegistryAutoBackup" @change="saveVrcRegistryAutoBackup"></el-switch>
<el-switch v-model="vrcRegistryAutoBackup" @change="setVrcRegistryAutoBackup"></el-switch>
</div>
<data-tables v-bind="registryBackupTable" style="margin-top: 10px">
<el-table-column :label="t('dialog.registry_backup.name')" prop="name"></el-table-column>
<el-table-column :label="t('dialog.registry_backup.date')" prop="date">
<template #default="scope">
<span>{{ scope.row.date | formatDate('long') }}</span>
<span>{{ formatDateFilter(scope.row.date, 'long') }}</span>
</template>
</el-table-column>
<el-table-column :label="t('dialog.registry_backup.action')" width="90" align="right">
@@ -71,32 +71,25 @@
</template>
<script setup>
import { storeToRefs } from 'pinia';
import { getCurrentInstance, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n-bridge';
import utils from '../../../classes/utils';
import { downloadAndSaveJson } from '../../../composables/shared/utils';
import configRepository from '../../../service/config';
import { downloadAndSaveJson, removeFromArray, formatDateFilter } from '../../../shared/utils';
import { useAppearanceSettingsStore, useVrcxStore, useAdvancedSettingsStore } from '../../../stores';
const { hideTooltips } = storeToRefs(useAppearanceSettingsStore());
const { backupVrcRegistry } = useVrcxStore();
const { isRegistryBackupDialogVisible } = storeToRefs(useVrcxStore());
const { vrcRegistryAutoBackup } = storeToRefs(useAdvancedSettingsStore());
const { setVrcRegistryAutoBackup } = useAdvancedSettingsStore();
const { t } = useI18n();
const instance = getCurrentInstance();
const { $confirm, $message, $prompt } = instance.proxy;
const props = defineProps({
isRegistryBackupDialogVisible: {
type: Boolean
},
hideTooltips: {
type: Boolean,
default: false
},
backupVrcRegistry: {
type: Function
}
});
const emit = defineEmits(['update:isRegistryBackupDialogVisible']);
const registryBackupTable = ref({
data: [],
tableProps: {
@@ -110,10 +103,8 @@
layout: 'table'
});
const vrcRegistryAutoBackup = ref(false);
watch(
() => props.isRegistryBackupDialogVisible,
() => isRegistryBackupDialogVisible.value,
(newVal) => {
if (newVal) {
updateRegistryBackupDialog();
@@ -121,23 +112,11 @@
}
);
setVrcRegistryAutoBackup();
function setVrcRegistryAutoBackup() {
configRepository.getBool('VRCX_vrcRegistryAutoBackup', true).then((value) => {
vrcRegistryAutoBackup.value = value;
});
}
async function updateRegistryBackupDialog() {
let backupsJson = await configRepository.getString('VRCX_VRChatRegistryBackups');
const backupsJson = await configRepository.getString('VRCX_VRChatRegistryBackups');
registryBackupTable.value.data = JSON.parse(backupsJson || '[]');
}
async function saveVrcRegistryAutoBackup() {
await configRepository.setBool('VRCX_vrcRegistryAutoBackup', vrcRegistryAutoBackup.value);
}
function restoreVrcRegistryBackup(row) {
$confirm('Continue? Restore Backup', 'Confirm', {
confirmButtonText: 'Confirm',
@@ -172,7 +151,7 @@
async function deleteVrcRegistryBackup(row) {
const backups = registryBackupTable.value.data;
utils.removeFromArray(backups, row);
removeFromArray(backups, row);
await configRepository.setString('VRCX_VRChatRegistryBackups', JSON.stringify(backups));
await updateRegistryBackupDialog();
}
@@ -197,7 +176,7 @@
}
async function handleBackupVrcRegistry(name) {
await props.backupVrcRegistry(name);
await backupVrcRegistry(name);
await updateRegistryBackupDialog();
}
@@ -295,6 +274,6 @@
}
function closeDialog() {
emit('update:isRegistryBackupDialogVisible', false);
isRegistryBackupDialogVisible.value = false;
}
</script>

View File

@@ -36,7 +36,7 @@
>{{ t('dialog.screenshot_metadata.open_folder') }}</el-button
>
<el-button
v-if="API.currentUser.$isVRCPlus && screenshotMetadataDialog.metadata.filePath"
v-if="currentUser.$isVRCPlus && screenshotMetadataDialog.metadata.filePath"
size="small"
icon="el-icon-upload2"
@click="uploadScreenshotToGallery"
@@ -81,7 +81,7 @@
<br />
</template>
<span v-if="screenshotMetadataDialog.metadata.dateTime" style="margin-right: 5px">{{
screenshotMetadataDialog.metadata.dateTime | formatDate('long')
formatDateFilter(screenshotMetadataDialog.metadata.dateTime, 'long')
}}</span>
<span
v-if="screenshotMetadataDialog.metadata.fileResolution"
@@ -91,11 +91,11 @@
screenshotMetadataDialog.metadata.fileSize
}}</el-tag>
<br />
<location
<Location
v-if="screenshotMetadataDialog.metadata.world"
:location="screenshotMetadataDialog.metadata.world.instanceId"
:hint="screenshotMetadataDialog.metadata.world.name" />
<display-name
<DisplayName
v-if="screenshotMetadataDialog.metadata.author"
:userid="screenshotMetadataDialog.metadata.author.id"
:hint="screenshotMetadataDialog.metadata.author.displayName"
@@ -162,36 +162,34 @@
</template>
<script setup>
import { ref, inject, getCurrentInstance, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { getCurrentInstance, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n-bridge';
import { vrcPlusImageRequest } from '../../../api';
import Location from '../../../components/Location.vue';
import { useGalleryStore, useUserStore, useVrcxStore } from '../../../stores';
import { formatDateFilter } from '../../../shared/utils';
const API = inject('API');
const showFullscreenImageDialog = inject('showFullscreenImageDialog');
const { showFullscreenImageDialog, handleGalleryImageAdd } = useGalleryStore();
const { currentlyDroppingFile } = storeToRefs(useVrcxStore());
const { currentUser } = storeToRefs(useUserStore());
const { t } = useI18n();
const instance = getCurrentInstance();
const $message = instance.proxy.$message;
const userStore = useUserStore();
const { lookupUser } = userStore;
const { fullscreenImageDialog } = storeToRefs(useGalleryStore());
const props = defineProps({
screenshotMetadataDialog: {
type: Object,
required: true
},
currentlyDroppingFile: {
type: String,
default: null
},
fullscreenImageDialog: {
type: Object,
default: null
}
});
const emit = defineEmits(['lookupUser']);
watch(
() => props.screenshotMetadataDialog.visible,
(newVal) => {
@@ -217,13 +215,13 @@
};
function handleDrop(event) {
if (props.currentlyDroppingFile === null) {
if (currentlyDroppingFile.value === null) {
return;
}
console.log('Dropped file into viewer: ', props.currentlyDroppingFile);
console.log('Dropped file into viewer: ', currentlyDroppingFile.value);
screenshotMetadataResetSearch();
getAndDisplayScreenshot(props.currentlyDroppingFile);
getAndDisplayScreenshot(currentlyDroppingFile.value);
event.preventDefault();
}
@@ -296,9 +294,7 @@
vrcPlusImageRequest
.uploadGalleryImage(base64Body)
.then((args) => {
// about uploadGalleryImage -> emit 'GALLERYIMAGE:ADD'
// no need to add to the gallery logic here
// because it refreshes when you open the gallery
handleGalleryImageAdd(args);
$message({
message: t('message.gallery.uploaded'),
type: 'success'
@@ -393,13 +389,10 @@
screenshotMetadataCarouselRef.value.setActiveItem(1);
}
if (props.fullscreenImageDialog.visible) {
if (fullscreenImageDialog.value.visible) {
// TODO
}
}
function lookupUser(user) {
emit('lookupUser', user);
}
function screenshotMetadataResetSearch() {
const D = props.screenshotMetadataDialog;
@@ -505,7 +498,7 @@
D.metadata.dateTime = Date.parse(metadata.creationDate);
}
if (props.fullscreenImageDialog?.visible) {
if (fullscreenImageDialog.value.visible) {
showFullscreenImageDialog(D.metadata.filePath);
}
}

View File

@@ -60,6 +60,7 @@
:type="item.type ? item.type : 'text'"
:min="item.min"
:max="item.max"
@input="refreshDialogValues"
style="flex: 1; margin-top: 5px"
><el-button
v-if="item.folderBrowser"
@@ -186,13 +187,18 @@
</template>
<script setup>
import { computed, getCurrentInstance, inject, ref, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { computed, getCurrentInstance, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n-bridge';
import {
VRChatCameraResolutions,
VRChatScreenshotResolutions
} from '../../../composables/setting/constants/vrchatResolutions';
import { getVRChatResolution } from '../../../composables/setting/utils';
import { VRChatCameraResolutions, VRChatScreenshotResolutions } from '../../../shared/constants';
import { getVRChatResolution, openExternalLink } from '../../../shared/utils';
import { useAdvancedSettingsStore, useAppearanceSettingsStore, useGameStore } from '../../../stores';
const { hideTooltips } = storeToRefs(useAppearanceSettingsStore());
const { VRChatUsedCacheSize, VRChatTotalCacheSize, VRChatCacheSizeLoading } = storeToRefs(useGameStore());
const { sweepVRChatCache, getVRChatCacheSize } = useGameStore();
const { folderSelectorDialog } = useAdvancedSettingsStore();
const { isVRChatConfigDialogVisible } = storeToRefs(useAdvancedSettingsStore());
const { t } = useI18n();
@@ -200,37 +206,6 @@
const $confirm = instance.proxy.$confirm;
const $message = instance.proxy.$message;
const openExternalLink = inject('openExternalLink');
const props = defineProps({
isVRChatConfigDialogVisible: {
type: Boolean,
required: true
},
VRChatUsedCacheSize: {
type: [String, Number],
required: true
},
VRChatTotalCacheSize: {
type: [String, Number],
required: true
},
VRChatCacheSizeLoading: {
type: Boolean,
required: true
},
folderSelectorDialog: {
type: Function,
required: true
},
hideTooltips: {
type: Boolean,
default: false
}
});
const emit = defineEmits(['update:isVRChatConfigDialogVisible', 'getVRChatCacheSize', 'sweepVRChatCache']);
const VRChatConfigFile = ref({});
// it's a object
const VRChatConfigList = ref({
@@ -281,7 +256,7 @@
const loading = ref(false);
watch(
() => props.isVRChatConfigDialogVisible,
() => isVRChatConfigDialogVisible.value,
async (newValue) => {
if (newValue) {
loading.value = true;
@@ -292,13 +267,9 @@
);
const totalCacheSize = computed(() => {
return VRChatConfigFile.value.cache_size || props.VRChatTotalCacheSize;
return VRChatConfigFile.value.cache_size || VRChatTotalCacheSize.value;
});
function getVRChatCacheSize() {
emit('getVRChatCacheSize');
}
function showDeleteAllVRChatCacheConfirm() {
$confirm(`Continue? Delete all VRChat cache`, 'Confirm', {
confirmButtonText: 'Confirm',
@@ -317,15 +288,12 @@
getVRChatCacheSize();
}
function sweepVRChatCache() {
emit('sweepVRChatCache');
}
async function openConfigFolderBrowser(value) {
const oldPath = VRChatConfigFile.value[value];
const newPath = await props.folderSelectorDialog(oldPath);
const newPath = await folderSelectorDialog(oldPath);
if (newPath) {
VRChatConfigFile.value[value] = newPath;
refreshDialogValues();
}
}
@@ -420,6 +388,6 @@
}
function closeDialog() {
emit('update:isVRChatConfigDialogVisible', false);
isVRChatConfigDialogVisible.value = false;
}
</script>

View File

@@ -8,13 +8,12 @@
<div style="font-size: 12px">{{ t('dialog.youtube_api.description') }} <br /></div>
<el-input
:value="youTubeApiKey"
v-model="youTubeApiKey"
type="textarea"
:placeholder="t('dialog.youtube_api.placeholder')"
maxlength="39"
show-word-limit
style="display: block; margin-top: 10px"
@input="updateYouTubeApiKey">
style="display: block; margin-top: 10px">
</el-input>
<template #footer>
@@ -33,52 +32,51 @@
</template>
<script setup>
import { inject, getCurrentInstance } from 'vue';
import configRepository from '../../../service/config';
import { storeToRefs } from 'pinia';
import { getCurrentInstance } from 'vue';
import { useI18n } from 'vue-i18n-bridge';
import { openExternalLink } from '../../../shared/utils';
import { useAdvancedSettingsStore } from '../../../stores';
const advancedSettingsStore = useAdvancedSettingsStore();
const { youTubeApiKey } = storeToRefs(advancedSettingsStore);
const { lookupYouTubeVideo, setYouTubeApiKey } = advancedSettingsStore;
const { t } = useI18n();
const instance = getCurrentInstance();
const $message = instance.proxy.$message;
const openExternalLink = inject('openExternalLink');
const props = defineProps({
isYouTubeApiDialogVisible: {
type: Boolean,
default: false
},
lookupYouTubeVideo: {
type: Function,
default: () => {}
},
youTubeApiKey: {
type: String,
default: ''
}
});
const emit = defineEmits(['update:isYouTubeApiDialogVisible', 'update:youTubeApiKey']);
const emit = defineEmits(['update:isYouTubeApiDialogVisible']);
async function testYouTubeApiKey() {
if (!props.youTubeApiKey) {
const previousKey = youTubeApiKey.value;
if (!youTubeApiKey.value) {
$message({
message: 'YouTube API key removed',
type: 'success'
});
await configRepository.setString('VRCX_youtubeAPIKey', '');
closeDialog();
return;
}
const data = await props.lookupYouTubeVideo('dQw4w9WgXcQ');
const data = await lookupYouTubeVideo('dQw4w9WgXcQ');
if (!data) {
updateYouTubeApiKey('');
setYouTubeApiKey(previousKey);
$message({
message: 'Invalid YouTube API key',
type: 'error'
});
} else {
await configRepository.setString('VRCX_youtubeAPIKey', props.youTubeApiKey);
setYouTubeApiKey(youTubeApiKey.value);
$message({
message: 'YouTube API key valid!',
type: 'success'
@@ -87,10 +85,6 @@
}
}
function updateYouTubeApiKey(value) {
emit('update:youTubeApiKey', value);
}
function closeDialog() {
emit('update:isYouTubeApiDialogVisible', false);
}