mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-06 22:46:06 +02:00
feat: add tools tab
This commit is contained in:
Generated
+8
@@ -45,6 +45,7 @@
|
|||||||
"pug": "^3.0.3",
|
"pug": "^3.0.3",
|
||||||
"pug-plain-loader": "^1.1.0",
|
"pug-plain-loader": "^1.1.0",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
|
"remixicon": "^4.6.0",
|
||||||
"sass": "^1.90.0",
|
"sass": "^1.90.0",
|
||||||
"sass-loader": "^16.0.5",
|
"sass-loader": "^16.0.5",
|
||||||
"vue": "^2.7.16",
|
"vue": "^2.7.16",
|
||||||
@@ -17224,6 +17225,13 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/remixicon": {
|
||||||
|
"version": "4.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/remixicon/-/remixicon-4.6.0.tgz",
|
||||||
|
"integrity": "sha512-bKM5odjqE1yzVxEZGJE7F79WHhNrJFIKHXR+GG+P1IWXn8AnJZhl8SbIRDJsNAvIqx4VPkNwjuHfc42tutMDpQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/remove-trailing-separator": {
|
"node_modules/remove-trailing-separator": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
|
||||||
|
|||||||
@@ -65,6 +65,7 @@
|
|||||||
"pug": "^3.0.3",
|
"pug": "^3.0.3",
|
||||||
"pug-plain-loader": "^1.1.0",
|
"pug-plain-loader": "^1.1.0",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
|
"remixicon": "^4.6.0",
|
||||||
"sass": "^1.90.0",
|
"sass": "^1.90.0",
|
||||||
"sass-loader": "^16.0.5",
|
"sass-loader": "^16.0.5",
|
||||||
"vue": "^2.7.16",
|
"vue": "^2.7.16",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
import Notification from './views/Notifications/Notification.vue';
|
import Notification from './views/Notifications/Notification.vue';
|
||||||
import FriendList from './views/FriendList/FriendList.vue';
|
import FriendList from './views/FriendList/FriendList.vue';
|
||||||
import Charts from './views/Charts/Charts.vue';
|
import Charts from './views/Charts/Charts.vue';
|
||||||
|
import Tools from './views/Tools/Tools.vue';
|
||||||
import Profile from './views/Profile/Profile.vue';
|
import Profile from './views/Profile/Profile.vue';
|
||||||
import Settings from './views/Settings/Settings.vue';
|
import Settings from './views/Settings/Settings.vue';
|
||||||
|
|
||||||
@@ -59,6 +60,7 @@
|
|||||||
Notification,
|
Notification,
|
||||||
FriendList,
|
FriendList,
|
||||||
Charts,
|
Charts,
|
||||||
|
Tools,
|
||||||
Profile,
|
Profile,
|
||||||
Settings,
|
Settings,
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ doctype html
|
|||||||
|
|
||||||
Charts
|
Charts
|
||||||
|
|
||||||
|
Tools
|
||||||
|
|
||||||
Profile
|
Profile
|
||||||
|
|
||||||
Settings
|
Settings
|
||||||
|
|||||||
Vendored
+2
@@ -3,6 +3,8 @@ import '@fontsource/noto-sans-jp';
|
|||||||
import '@fontsource/noto-sans-sc';
|
import '@fontsource/noto-sans-sc';
|
||||||
import '@fontsource/noto-sans-tc';
|
import '@fontsource/noto-sans-tc';
|
||||||
|
|
||||||
|
import 'remixicon/fonts/remixicon.css';
|
||||||
|
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { PiniaVuePlugin } from 'pinia';
|
import { PiniaVuePlugin } from 'pinia';
|
||||||
import { DataTables } from 'vue-data-tables';
|
import { DataTables } from 'vue-data-tables';
|
||||||
|
|||||||
+26
-12
@@ -42,18 +42,19 @@
|
|||||||
import { useUiStore, useVRCXUpdaterStore } from '../stores';
|
import { useUiStore, useVRCXUpdaterStore } from '../stores';
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ index: 'feed', icon: 'el-icon-news', tooltip: 'nav_tooltip.feed' },
|
{ index: 'feed', icon: 'ri-rss-line', tooltip: 'nav_tooltip.feed' },
|
||||||
{ index: 'gameLog', icon: 'el-icon-s-data', tooltip: 'nav_tooltip.game_log' },
|
{ index: 'gameLog', icon: 'ri-history-line', tooltip: 'nav_tooltip.game_log' },
|
||||||
{ index: 'playerList', icon: 'el-icon-tickets', tooltip: 'nav_tooltip.player_list' },
|
{ index: 'playerList', icon: 'ri-group-3-line', tooltip: 'nav_tooltip.player_list' },
|
||||||
{ index: 'search', icon: 'el-icon-search', tooltip: 'nav_tooltip.search' },
|
{ index: 'search', icon: 'ri-search-line', tooltip: 'nav_tooltip.search' },
|
||||||
{ index: 'favorite', icon: 'el-icon-star-off', tooltip: 'nav_tooltip.favorites' },
|
{ index: 'favorite', icon: 'ri-star-line', tooltip: 'nav_tooltip.favorites' },
|
||||||
{ index: 'friendLog', icon: 'el-icon-notebook-2', tooltip: 'nav_tooltip.friend_log' },
|
{ index: 'friendLog', icon: 'ri-contacts-book-3-line', tooltip: 'nav_tooltip.friend_log' },
|
||||||
{ index: 'moderation', icon: 'el-icon-finished', tooltip: 'nav_tooltip.moderation' },
|
{ index: 'moderation', icon: 'ri-user-forbid-line', tooltip: 'nav_tooltip.moderation' },
|
||||||
{ index: 'notification', icon: 'el-icon-bell', tooltip: 'nav_tooltip.notification' },
|
{ index: 'notification', icon: 'ri-notification-2-line', tooltip: 'nav_tooltip.notification' },
|
||||||
{ index: 'friendList', icon: 'el-icon-s-management', tooltip: 'nav_tooltip.friend_list' },
|
{ index: 'friendList', icon: 'ri-contacts-book-2-line', tooltip: 'nav_tooltip.friend_list' },
|
||||||
{ index: 'charts', icon: 'el-icon-data-analysis', tooltip: 'nav_tooltip.charts' },
|
{ index: 'charts', icon: 'ri-bar-chart-line', tooltip: 'nav_tooltip.charts' },
|
||||||
{ index: 'profile', icon: 'el-icon-user', tooltip: 'nav_tooltip.profile' },
|
{ index: 'tools', icon: 'ri-tools-line', tooltip: 'nav_tooltip.tools' },
|
||||||
{ index: 'settings', icon: 'el-icon-s-tools', tooltip: 'nav_tooltip.settings' }
|
{ index: 'profile', icon: 'ri-user-line', tooltip: 'nav_tooltip.profile' },
|
||||||
|
{ index: 'settings', icon: 'ri-settings-3-line', tooltip: 'nav_tooltip.settings' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const VRCXUpdaterStore = useVRCXUpdaterStore();
|
const VRCXUpdaterStore = useVRCXUpdaterStore();
|
||||||
@@ -63,3 +64,16 @@
|
|||||||
const { menuActiveIndex, notifiedMenus } = storeToRefs(uiStore);
|
const { menuActiveIndex, notifiedMenus } = storeToRefs(uiStore);
|
||||||
const { selectMenu } = uiStore;
|
const { selectMenu } = uiStore;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.el-menu-item i[class*='ri-'] {
|
||||||
|
font-size: 19px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1890,13 +1890,12 @@
|
|||||||
const { friendLogTable } = storeToRefs(useFriendStore());
|
const { friendLogTable } = storeToRefs(useFriendStore());
|
||||||
const { getFriendRequest, handleFriendDelete } = useFriendStore();
|
const { getFriendRequest, handleFriendDelete } = useFriendStore();
|
||||||
const { previousImagesDialogVisible, previousImagesTable } = storeToRefs(useGalleryStore());
|
const { previousImagesDialogVisible, previousImagesTable } = storeToRefs(useGalleryStore());
|
||||||
const { clearInviteImageUpload, showGalleryDialog, checkPreviousImageAvailable, showFullscreenImageDialog } =
|
const { clearInviteImageUpload, checkPreviousImageAvailable, showFullscreenImageDialog } = useGalleryStore();
|
||||||
useGalleryStore();
|
|
||||||
const { isGameRunning } = storeToRefs(useGameStore());
|
const { isGameRunning } = storeToRefs(useGameStore());
|
||||||
const { logout } = useAuthStore();
|
const { logout } = useAuthStore();
|
||||||
const { cachedConfig } = storeToRefs(useAuthStore());
|
const { cachedConfig } = storeToRefs(useAuthStore());
|
||||||
const { applyPlayerModeration, handlePlayerModerationDelete } = useModerationStore();
|
const { applyPlayerModeration, handlePlayerModerationDelete } = useModerationStore();
|
||||||
const { shiftHeld } = storeToRefs(useUiStore());
|
const { shiftHeld, menuActiveIndex } = storeToRefs(useUiStore());
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => userDialog.value.loading,
|
() => userDialog.value.loading,
|
||||||
@@ -2296,7 +2295,9 @@
|
|||||||
} else if (command === 'Previous Instances') {
|
} else if (command === 'Previous Instances') {
|
||||||
showPreviousInstancesUserDialog(D.ref);
|
showPreviousInstancesUserDialog(D.ref);
|
||||||
} else if (command === 'Manage Gallery') {
|
} else if (command === 'Manage Gallery') {
|
||||||
showGalleryDialog();
|
// redirect to tools tab
|
||||||
|
userDialog.value.visible = false;
|
||||||
|
menuActiveIndex.value = 'tools';
|
||||||
} else if (command === 'Invite To Group') {
|
} else if (command === 'Invite To Group') {
|
||||||
showInviteGroupDialog('', D.id);
|
showInviteGroupDialog('', D.id);
|
||||||
// } else if (command === 'Send Boop') {
|
// } else if (command === 'Send Boop') {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"notification": "Notification",
|
"notification": "Notification",
|
||||||
"friend_list": "Friend List",
|
"friend_list": "Friend List",
|
||||||
"charts": "Charts",
|
"charts": "Charts",
|
||||||
|
"tools": "Tools",
|
||||||
"profile": "Profile",
|
"profile": "Profile",
|
||||||
"settings": "Settings"
|
"settings": "Settings"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -529,7 +529,6 @@
|
|||||||
inviteRequestMessageTable,
|
inviteRequestMessageTable,
|
||||||
inviteRequestResponseMessageTable
|
inviteRequestResponseMessageTable
|
||||||
} = storeToRefs(useInviteStore());
|
} = storeToRefs(useInviteStore());
|
||||||
const { showGalleryDialog } = useGalleryStore();
|
|
||||||
const { menuActiveIndex } = storeToRefs(useUiStore());
|
const { menuActiveIndex } = storeToRefs(useUiStore());
|
||||||
const { directAccessWorld } = useSearchStore();
|
const { directAccessWorld } = useSearchStore();
|
||||||
const { logout } = useAuthStore();
|
const { logout } = useAuthStore();
|
||||||
@@ -550,6 +549,11 @@
|
|||||||
|
|
||||||
const visits = ref(0);
|
const visits = ref(0);
|
||||||
|
|
||||||
|
// redirect to tools tab
|
||||||
|
function showGalleryDialog() {
|
||||||
|
menuActiveIndex.value = 'tools';
|
||||||
|
}
|
||||||
|
|
||||||
function getVisits() {
|
function getVisits() {
|
||||||
miscRequest.getVisits().then((args) => {
|
miscRequest.getVisits().then((args) => {
|
||||||
visits.value = args.json;
|
visits.value = args.json;
|
||||||
|
|||||||
@@ -628,6 +628,7 @@
|
|||||||
:label="t('view.settings.appearance.user_dialog.vrcx_memos')"
|
:label="t('view.settings.appearance.user_dialog.vrcx_memos')"
|
||||||
:value="!hideUserMemos"
|
:value="!hideUserMemos"
|
||||||
@change="setHideUserMemos" />
|
@change="setHideUserMemos" />
|
||||||
|
<!-- redirect to tools tab -->
|
||||||
<div class="options-container-item">
|
<div class="options-container-item">
|
||||||
<span class="name">{{
|
<span class="name">{{
|
||||||
t('view.settings.appearance.user_dialog.export_vrcx_memos_into_vrchat_notes')
|
t('view.settings.appearance.user_dialog.export_vrcx_memos_into_vrchat_notes')
|
||||||
@@ -641,6 +642,7 @@
|
|||||||
>{{ t('view.settings.appearance.user_dialog.export_notes') }}</el-button
|
>{{ t('view.settings.appearance.user_dialog.export_notes') }}</el-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- redirect to tools tab end -->
|
||||||
</div>
|
</div>
|
||||||
<!--//- Appearance | Friend Log-->
|
<!--//- Appearance | Friend Log-->
|
||||||
<div class="options-container">
|
<div class="options-container">
|
||||||
@@ -1264,6 +1266,7 @@
|
|||||||
|
|
||||||
<!--//- "Pictures" Tab-->
|
<!--//- "Pictures" Tab-->
|
||||||
<el-tab-pane lazy :label="t('view.settings.category.pictures')">
|
<el-tab-pane lazy :label="t('view.settings.category.pictures')">
|
||||||
|
<!-- redirect to tools tab -->
|
||||||
<div class="options-container" style="margin-top: 0">
|
<div class="options-container" style="margin-top: 0">
|
||||||
<span class="header">{{ t('view.settings.category.pictures') }}</span>
|
<span class="header">{{ t('view.settings.category.pictures') }}</span>
|
||||||
<div class="options-container-item" style="margin-top: 15px">
|
<div class="options-container-item" style="margin-top: 15px">
|
||||||
@@ -1274,6 +1277,7 @@
|
|||||||
</el-button-group>
|
</el-button-group>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- redirect to tools tab end -->
|
||||||
|
|
||||||
<div class="options-container">
|
<div class="options-container">
|
||||||
<span class="header">{{ t('view.settings.pictures.pictures.open_folder') }}</span>
|
<span class="header">{{ t('view.settings.pictures.pictures.open_folder') }}</span>
|
||||||
@@ -1845,9 +1849,7 @@
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<OpenSourceSoftwareNoticeDialog :ossDialog.sync="ossDialog" />
|
<OpenSourceSoftwareNoticeDialog :ossDialog.sync="ossDialog" />
|
||||||
<NoteExportDialog :isNoteExportDialogVisible.sync="isNoteExportDialogVisible" />
|
|
||||||
<NotificationPositionDialog :isNotificationPositionDialogVisible.sync="isNotificationPositionDialogVisible" />
|
<NotificationPositionDialog :isNotificationPositionDialogVisible.sync="isNotificationPositionDialogVisible" />
|
||||||
<ScreenshotMetadataDialog :screenshotMetadataDialog="screenshotMetadataDialog" />
|
|
||||||
<RegistryBackupDialog />
|
<RegistryBackupDialog />
|
||||||
<YouTubeApiDialog :isYouTubeApiDialogVisible.sync="isYouTubeApiDialogVisible" />
|
<YouTubeApiDialog :isYouTubeApiDialogVisible.sync="isYouTubeApiDialogVisible" />
|
||||||
<FeedFiltersDialog :feedFiltersDialogMode.sync="feedFiltersDialogMode" />
|
<FeedFiltersDialog :feedFiltersDialogMode.sync="feedFiltersDialogMode" />
|
||||||
@@ -1886,9 +1888,7 @@
|
|||||||
} from '../../stores';
|
} from '../../stores';
|
||||||
import { photonEventTableTypeFilterList } from '../../shared/constants';
|
import { photonEventTableTypeFilterList } from '../../shared/constants';
|
||||||
import OpenSourceSoftwareNoticeDialog from './dialogs/OpenSourceSoftwareNoticeDialog.vue';
|
import OpenSourceSoftwareNoticeDialog from './dialogs/OpenSourceSoftwareNoticeDialog.vue';
|
||||||
import NoteExportDialog from './dialogs/NoteExportDialog.vue';
|
|
||||||
import NotificationPositionDialog from './dialogs/NotificationPositionDialog.vue';
|
import NotificationPositionDialog from './dialogs/NotificationPositionDialog.vue';
|
||||||
import ScreenshotMetadataDialog from './dialogs/ScreenshotMetadataDialog.vue';
|
|
||||||
import RegistryBackupDialog from './dialogs/RegistryBackupDialog.vue';
|
import RegistryBackupDialog from './dialogs/RegistryBackupDialog.vue';
|
||||||
import YouTubeApiDialog from './dialogs/YouTubeApiDialog.vue';
|
import YouTubeApiDialog from './dialogs/YouTubeApiDialog.vue';
|
||||||
import ChangelogDialog from './dialogs/ChangelogDialog.vue';
|
import ChangelogDialog from './dialogs/ChangelogDialog.vue';
|
||||||
@@ -2179,20 +2179,10 @@
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const ossDialog = ref(false);
|
const ossDialog = ref(false);
|
||||||
const isNoteExportDialogVisible = ref(false);
|
|
||||||
const feedFiltersDialogMode = ref('');
|
const feedFiltersDialogMode = ref('');
|
||||||
const isNotificationPositionDialogVisible = ref(false);
|
const isNotificationPositionDialogVisible = ref(false);
|
||||||
|
|
||||||
const isYouTubeApiDialogVisible = ref(false);
|
const isYouTubeApiDialogVisible = ref(false);
|
||||||
const screenshotMetadataDialog = ref({
|
|
||||||
visible: false,
|
|
||||||
loading: false,
|
|
||||||
search: '',
|
|
||||||
searchType: 'Player Name',
|
|
||||||
searchTypes: ['Player Name', 'Player ID', 'World Name', 'World ID'],
|
|
||||||
metadata: {},
|
|
||||||
isUploading: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const zoomLevel = ref(100);
|
const zoomLevel = ref(100);
|
||||||
|
|
||||||
@@ -2224,16 +2214,18 @@
|
|||||||
feedFiltersDialogMode.value = 'wrist';
|
feedFiltersDialogMode.value = 'wrist';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// redirect to tools tab
|
||||||
function showNoteExportDialog() {
|
function showNoteExportDialog() {
|
||||||
isNoteExportDialogVisible.value = true;
|
menuActiveIndex.value = 'tools';
|
||||||
}
|
}
|
||||||
|
|
||||||
function showNotificationPositionDialog() {
|
function showNotificationPositionDialog() {
|
||||||
isNotificationPositionDialogVisible.value = true;
|
isNotificationPositionDialogVisible.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// redirect to tools tab
|
||||||
function showScreenshotMetadataDialog() {
|
function showScreenshotMetadataDialog() {
|
||||||
screenshotMetadataDialog.value.visible = true;
|
menuActiveIndex.value = 'tools';
|
||||||
}
|
}
|
||||||
|
|
||||||
function openVrcxAppDataFolder() {
|
function openVrcxAppDataFolder() {
|
||||||
|
|||||||
@@ -78,11 +78,9 @@
|
|||||||
({{ groupInstances.length }})
|
({{ groupInstances.length }})
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<el-button class="group-calendar-button" icon="el-icon-date" circle @click="openGroupCalendarDialog" />
|
|
||||||
<GroupsSidebar :group-instances="groupInstances" :group-order="inGameGroupOrder" />
|
<GroupsSidebar :group-instances="groupInstances" :group-order="inGameGroupOrder" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<GroupCalendarDialog :visible="isGroupCalendarDialogVisible" @close="isGroupCalendarDialogVisible = false" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -100,7 +98,6 @@
|
|||||||
} from '../../stores';
|
} from '../../stores';
|
||||||
import FriendsSidebar from './components/FriendsSidebar.vue';
|
import FriendsSidebar from './components/FriendsSidebar.vue';
|
||||||
import GroupsSidebar from './components/GroupsSidebar.vue';
|
import GroupsSidebar from './components/GroupsSidebar.vue';
|
||||||
import GroupCalendarDialog from '../../components/dialogs/GroupDialog/GroupCalendarDialog.vue';
|
|
||||||
|
|
||||||
const { friends, isRefreshFriendsLoading, onlineFriendCount } = storeToRefs(useFriendStore());
|
const { friends, isRefreshFriendsLoading, onlineFriendCount } = storeToRefs(useFriendStore());
|
||||||
const { refreshFriendsList, confirmDeleteFriend } = useFriendStore();
|
const { refreshFriendsList, confirmDeleteFriend } = useFriendStore();
|
||||||
@@ -110,15 +107,9 @@
|
|||||||
const { quickSearchItems } = storeToRefs(useSearchStore());
|
const { quickSearchItems } = storeToRefs(useSearchStore());
|
||||||
const { inGameGroupOrder, groupInstances } = storeToRefs(useGroupStore());
|
const { inGameGroupOrder, groupInstances } = storeToRefs(useGroupStore());
|
||||||
|
|
||||||
const isGroupCalendarDialogVisible = ref(false);
|
|
||||||
|
|
||||||
const isSideBarTabShow = computed(() => {
|
const isSideBarTabShow = computed(() => {
|
||||||
return !(menuActiveIndex.value === 'friendList' || menuActiveIndex.value === 'charts');
|
return !(menuActiveIndex.value === 'friendList' || menuActiveIndex.value === 'charts');
|
||||||
});
|
});
|
||||||
|
|
||||||
function openGroupCalendarDialog() {
|
|
||||||
isGroupCalendarDialogVisible.value = true;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -0,0 +1,242 @@
|
|||||||
|
<template>
|
||||||
|
<div id="chart" class="x-container" v-show="menuActiveIndex === 'tools'">
|
||||||
|
<div class="options-container" style="margin-top: 0">
|
||||||
|
<span class="header">Tools</span>
|
||||||
|
|
||||||
|
<div class="tool-categories">
|
||||||
|
<div class="tool-category">
|
||||||
|
<div class="category-header" @click="toggleCategory('group')">
|
||||||
|
<i class="el-icon-arrow-right" :class="{ rotate: !categoryCollapsed['group'] }"></i>
|
||||||
|
<span class="category-title">Group</span>
|
||||||
|
</div>
|
||||||
|
<div class="tools-grid" v-show="!categoryCollapsed['group']">
|
||||||
|
<el-card :body-style="{ padding: '0px' }" class="tool-card">
|
||||||
|
<div class="tool-content" @click="showGroupCalendarDialog">
|
||||||
|
<div class="tool-icon">
|
||||||
|
<i class="ri-calendar-event-line"></i>
|
||||||
|
</div>
|
||||||
|
<div class="tool-info">
|
||||||
|
<div class="tool-name">Calendar</div>
|
||||||
|
<div class="tool-description">Group Events Calendar</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tool-category">
|
||||||
|
<div class="category-header" @click="toggleCategory('image')">
|
||||||
|
<i class="el-icon-arrow-right" :class="{ rotate: !categoryCollapsed['image'] }"></i>
|
||||||
|
<span class="category-title">Image</span>
|
||||||
|
</div>
|
||||||
|
<div class="tools-grid" v-show="!categoryCollapsed['image']">
|
||||||
|
<el-card :body-style="{ padding: '0px' }" class="tool-card">
|
||||||
|
<div class="tool-content" @click="showScreenshotMetadataDialog">
|
||||||
|
<div class="tool-icon">
|
||||||
|
<i class="ri-screenshot-2-line"></i>
|
||||||
|
</div>
|
||||||
|
<div class="tool-info">
|
||||||
|
<div class="tool-name">Screenshot Management</div>
|
||||||
|
<div class="tool-description">Manage screenshots and view metadata</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
<el-card :body-style="{ padding: '0px' }" class="tool-card">
|
||||||
|
<div class="tool-content" @click="showGalleryDialog">
|
||||||
|
<div class="tool-icon">
|
||||||
|
<i class="ri-multi-image-line"></i>
|
||||||
|
</div>
|
||||||
|
<div class="tool-info">
|
||||||
|
<div class="tool-name">VRC+ Images & Inventory Management</div>
|
||||||
|
<div class="tool-description">Manage VRC+ Images & Inventory</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tool-category">
|
||||||
|
<div class="category-header" @click="toggleCategory('user')">
|
||||||
|
<i class="el-icon-arrow-right" :class="{ rotate: !categoryCollapsed['user'] }"></i>
|
||||||
|
<span class="category-title">User</span>
|
||||||
|
</div>
|
||||||
|
<div class="tools-grid" v-show="!categoryCollapsed['user']">
|
||||||
|
<el-card :body-style="{ padding: '0px' }" class="tool-card">
|
||||||
|
<div class="tool-content" @click="showNoteExportDialog">
|
||||||
|
<div class="tool-icon">
|
||||||
|
<i class="ri-user-shared-line"></i>
|
||||||
|
</div>
|
||||||
|
<div class="tool-info">
|
||||||
|
<div class="tool-name">Export User Notes</div>
|
||||||
|
<div class="tool-description">Export VRCX user memos to VRChat notes</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<GroupCalendarDialog :visible="isGroupCalendarDialogVisible" @close="isGroupCalendarDialogVisible = false" />
|
||||||
|
<ScreenshotMetadataDialog
|
||||||
|
:isScreenshotMetadataDialogVisible="isScreenshotMetadataDialogVisible"
|
||||||
|
@close="isScreenshotMetadataDialogVisible = false" />
|
||||||
|
<NoteExportDialog
|
||||||
|
:isNoteExportDialogVisible="isNoteExportDialogVisible"
|
||||||
|
@close="isNoteExportDialogVisible = false" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useI18n } from 'vue-i18n-bridge';
|
||||||
|
import { useUiStore, useGalleryStore } from '../../stores';
|
||||||
|
import GroupCalendarDialog from './dialogs/GroupCalendarDialog.vue';
|
||||||
|
import ScreenshotMetadataDialog from './dialogs/ScreenshotMetadataDialog.vue';
|
||||||
|
import NoteExportDialog from './dialogs/NoteExportDialog.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const uiStore = useUiStore();
|
||||||
|
const { showGalleryDialog } = useGalleryStore();
|
||||||
|
const { menuActiveIndex } = storeToRefs(uiStore);
|
||||||
|
|
||||||
|
const categoryCollapsed = ref({
|
||||||
|
group: false,
|
||||||
|
image: false,
|
||||||
|
user: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const isGroupCalendarDialogVisible = ref(false);
|
||||||
|
const isScreenshotMetadataDialogVisible = ref(false);
|
||||||
|
const isNoteExportDialogVisible = ref(false);
|
||||||
|
|
||||||
|
const showGroupCalendarDialog = () => {
|
||||||
|
isGroupCalendarDialogVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const showScreenshotMetadataDialog = () => {
|
||||||
|
isScreenshotMetadataDialogVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const showNoteExportDialog = () => {
|
||||||
|
isNoteExportDialogVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleCategory = (category) => {
|
||||||
|
categoryCollapsed.value[category] = !categoryCollapsed.value[category];
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tool-categories {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-category {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.category-header {
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
background-color: var(--el-color-primary-light-9);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--el-color-primary-light-8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon-arrow-right {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 8px;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-card {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-card__body {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px 16px;
|
||||||
|
|
||||||
|
.tool-icon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: var(--el-color-primary-light-9);
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-right: 20px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 28px;
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-info {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.tool-name {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-description {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-card {
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 100%;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rotate {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+1
@@ -65,6 +65,7 @@
|
|||||||
mode: {
|
mode: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
// @ts-ignore
|
||||||
validator: (value) => ['timeline', 'grid'].includes(value)
|
validator: (value) => ['timeline', 'grid'].includes(value)
|
||||||
},
|
},
|
||||||
isFollowing: {
|
isFollowing: {
|
||||||
+100
-58
@@ -49,7 +49,7 @@
|
|||||||
:class="{
|
:class="{
|
||||||
'has-events': filteredCalendar[formatDateKey(date)]?.length
|
'has-events': filteredCalendar[formatDateKey(date)]?.length
|
||||||
}">
|
}">
|
||||||
{{ dayjs(date).format('D') }}
|
{{ dayjs(date).local().format('D') }}
|
||||||
<div
|
<div
|
||||||
v-if="filteredCalendar[formatDateKey(date)]?.length"
|
v-if="filteredCalendar[formatDateKey(date)]?.length"
|
||||||
class="calendar-event-badge"
|
class="calendar-event-badge"
|
||||||
@@ -80,10 +80,13 @@
|
|||||||
<div class="groups-grid" v-loading="isLoading">
|
<div class="groups-grid" v-loading="isLoading">
|
||||||
<div v-if="filteredGroupEvents.length" class="groups-container">
|
<div v-if="filteredGroupEvents.length" class="groups-container">
|
||||||
<div v-for="group in filteredGroupEvents" :key="group.groupId" class="group-row">
|
<div v-for="group in filteredGroupEvents" :key="group.groupId" class="group-row">
|
||||||
<div class="group-header" @click="showGroupDialog(group.groupId)">
|
<div class="group-header" @click="toggleGroup(group.groupId)">
|
||||||
|
<i
|
||||||
|
class="el-icon-arrow-right"
|
||||||
|
:class="{ rotate: !groupCollapsed[group.groupId] }"></i>
|
||||||
{{ group.groupName }}
|
{{ group.groupName }}
|
||||||
</div>
|
</div>
|
||||||
<div class="events-row">
|
<div class="events-row" v-show="!groupCollapsed[group.groupId]">
|
||||||
<GroupCalendarEventCard
|
<GroupCalendarEventCard
|
||||||
v-for="event in group.events"
|
v-for="event in group.events"
|
||||||
:key="event.id"
|
:key="event.id"
|
||||||
@@ -111,11 +114,10 @@
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { groupRequest } from '../../../api';
|
import { groupRequest } from '../../../api';
|
||||||
import { useGroupStore } from '../../../stores';
|
import { useGroupStore } from '../../../stores';
|
||||||
import GroupCalendarEventCard from './GroupCalendarEventCard.vue';
|
import GroupCalendarEventCard from '../components/GroupCalendarEventCard.vue';
|
||||||
import { replaceBioSymbols } from '../../../shared/utils';
|
import { replaceBioSymbols } from '../../../shared/utils';
|
||||||
|
|
||||||
const { cachedGroups } = storeToRefs(useGroupStore());
|
const { cachedGroups } = storeToRefs(useGroupStore());
|
||||||
const { showGroupDialog } = useGroupStore();
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -132,13 +134,15 @@
|
|||||||
const followingCalendar = ref([]);
|
const followingCalendar = ref([]);
|
||||||
const selectedDay = ref(new Date());
|
const selectedDay = ref(new Date());
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const viewMode = ref('timeline'); // 'timeline' | 'grid'
|
const viewMode = ref('timeline');
|
||||||
const searchQuery = ref('');
|
const searchQuery = ref('');
|
||||||
|
const groupCollapsed = ref({});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.visible,
|
() => props.visible,
|
||||||
async (newVisible) => {
|
async (newVisible) => {
|
||||||
if (newVisible) {
|
if (newVisible) {
|
||||||
|
selectedDay.value = new Date();
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
await Promise.all([getCalendarData(), getFollowingCalendarData()])
|
await Promise.all([getCalendarData(), getFollowingCalendarData()])
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -172,6 +176,74 @@
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const groupedByGroupEvents = computed(() => {
|
||||||
|
const currentMonth = dayjs(selectedDay.value).month();
|
||||||
|
const currentYear = dayjs(selectedDay.value).year();
|
||||||
|
|
||||||
|
const currentMonthEvents = calendar.value.filter((event) => {
|
||||||
|
const eventDate = dayjs(event.startsAt);
|
||||||
|
return eventDate.month() === currentMonth && eventDate.year() === currentYear;
|
||||||
|
});
|
||||||
|
|
||||||
|
const groupMap = new Map();
|
||||||
|
currentMonthEvents.forEach((event) => {
|
||||||
|
const groupId = event.ownerId;
|
||||||
|
if (!groupMap.has(groupId)) {
|
||||||
|
groupMap.set(groupId, []);
|
||||||
|
}
|
||||||
|
groupMap.get(groupId).push(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
Array.from(groupMap.values()).forEach((events) => {
|
||||||
|
events.sort((a, b) => (dayjs(a.startsAt).isBefore(dayjs(b.startsAt)) ? -1 : 1));
|
||||||
|
});
|
||||||
|
|
||||||
|
return Array.from(groupMap.entries()).map(([groupId, events]) => ({
|
||||||
|
groupId,
|
||||||
|
groupName: getGroupName(events[0]),
|
||||||
|
events: events
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
const filteredGroupEvents = computed(() => {
|
||||||
|
const hasSearch = searchQuery.value.trim();
|
||||||
|
return !hasSearch
|
||||||
|
? groupedByGroupEvents.value
|
||||||
|
: groupedByGroupEvents.value.filter((group) => {
|
||||||
|
if (group.groupName.toLowerCase().includes(searchQuery.value.toLowerCase())) return true;
|
||||||
|
|
||||||
|
return group.events.some(
|
||||||
|
(event) =>
|
||||||
|
event.title?.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
|
||||||
|
event.description?.toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
[filteredGroupEvents, searchQuery],
|
||||||
|
([groups, search]) => {
|
||||||
|
const newCollapsed = { ...groupCollapsed.value };
|
||||||
|
let hasChanged = false;
|
||||||
|
const hasSearch = search.trim();
|
||||||
|
|
||||||
|
groups.forEach((group) => {
|
||||||
|
if (!(group.groupId in newCollapsed)) {
|
||||||
|
newCollapsed[group.groupId] = false;
|
||||||
|
hasChanged = true;
|
||||||
|
} else if (hasSearch) {
|
||||||
|
newCollapsed[group.groupId] = false;
|
||||||
|
hasChanged = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasChanged) {
|
||||||
|
groupCollapsed.value = newCollapsed;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
const filteredCalendar = computed(() => {
|
const filteredCalendar = computed(() => {
|
||||||
const result = {};
|
const result = {};
|
||||||
calendar.value.forEach((item) => {
|
calendar.value.forEach((item) => {
|
||||||
@@ -231,49 +303,6 @@
|
|||||||
.sort((a, b) => dayjs(a.startsAt).diff(dayjs(b.startsAt)));
|
.sort((a, b) => dayjs(a.startsAt).diff(dayjs(b.startsAt)));
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupedByGroupEvents = computed(() => {
|
|
||||||
const currentMonth = dayjs(selectedDay.value).month();
|
|
||||||
const currentYear = dayjs(selectedDay.value).year();
|
|
||||||
|
|
||||||
const currentMonthEvents = calendar.value.filter((event) => {
|
|
||||||
const eventDate = dayjs(event.startsAt);
|
|
||||||
return eventDate.month() === currentMonth && eventDate.year() === currentYear;
|
|
||||||
});
|
|
||||||
|
|
||||||
const groupMap = new Map();
|
|
||||||
currentMonthEvents.forEach((event) => {
|
|
||||||
const groupId = event.ownerId;
|
|
||||||
if (!groupMap.has(groupId)) {
|
|
||||||
groupMap.set(groupId, []);
|
|
||||||
}
|
|
||||||
groupMap.get(groupId).push(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
Array.from(groupMap.values()).forEach((events) => {
|
|
||||||
events.sort((a, b) => (dayjs(a.startsAt).isBefore(dayjs(b.startsAt)) ? -1 : 1));
|
|
||||||
});
|
|
||||||
|
|
||||||
return Array.from(groupMap.entries()).map(([groupId, events]) => ({
|
|
||||||
groupId,
|
|
||||||
groupName: getGroupName(events[0]),
|
|
||||||
events: events
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
const filteredGroupEvents = computed(() => {
|
|
||||||
if (!searchQuery.value.trim()) return groupedByGroupEvents.value;
|
|
||||||
|
|
||||||
const query = searchQuery.value.toLowerCase();
|
|
||||||
return groupedByGroupEvents.value.filter((group) => {
|
|
||||||
if (group.groupName.toLowerCase().includes(query)) return true;
|
|
||||||
|
|
||||||
return group.events.some(
|
|
||||||
(event) =>
|
|
||||||
event.title?.toLowerCase().includes(query) || event.description?.toLowerCase().includes(query)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const formatDateKey = (date) => dayjs(date).format('YYYY-MM-DD');
|
const formatDateKey = (date) => dayjs(date).format('YYYY-MM-DD');
|
||||||
|
|
||||||
function getGroupName(event) {
|
function getGroupName(event) {
|
||||||
@@ -315,6 +344,13 @@
|
|||||||
viewMode.value = viewMode.value === 'timeline' ? 'grid' : 'timeline';
|
viewMode.value = viewMode.value === 'timeline' ? 'grid' : 'timeline';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleGroup(groupId) {
|
||||||
|
groupCollapsed.value = {
|
||||||
|
...groupCollapsed.value,
|
||||||
|
[groupId]: !groupCollapsed.value[groupId]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function closeDialog() {
|
function closeDialog() {
|
||||||
emit('close');
|
emit('close');
|
||||||
}
|
}
|
||||||
@@ -447,7 +483,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
.search-container {
|
.search-container {
|
||||||
padding: 16px 20px;
|
padding: 2px 20px 12px 20px;
|
||||||
border-bottom: 1px solid #ebeef5;
|
border-bottom: 1px solid #ebeef5;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
@@ -463,22 +499,24 @@
|
|||||||
.groups-container {
|
.groups-container {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
.group-row {
|
.group-row {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 18px;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
.group-header {
|
.group-header {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: var(--el-text-color-primary);
|
color: var(--el-text-color-primary);
|
||||||
padding: 8px 12px 12px 12px;
|
padding: 4px 12px 10px 12px;
|
||||||
margin-bottom: 16px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s ease;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin: 0 -12px 16px -12px;
|
margin: 0 -12px 10px -12px;
|
||||||
&:hover {
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.el-icon-arrow-right {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 8px;
|
||||||
|
transition: transform 0.3s;
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
background-color: var(--el-color-primary-light-9);
|
|
||||||
transform: translateX(4px);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.events-row {
|
.events-row {
|
||||||
@@ -499,4 +537,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rotate {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
+2
-2
@@ -139,7 +139,7 @@
|
|||||||
errors.value = '';
|
errors.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits(['update:isNoteExportDialogVisible']);
|
const emit = defineEmits(['close']);
|
||||||
|
|
||||||
function updateNoteExportDialog() {
|
function updateNoteExportDialog() {
|
||||||
const data = [];
|
const data = [];
|
||||||
@@ -196,6 +196,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function closeDialog() {
|
function closeDialog() {
|
||||||
emit('update:isNoteExportDialogVisible', false);
|
emit('close');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
+34
-16
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<safe-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible.sync="screenshotMetadataDialog.visible"
|
:visible="isScreenshotMetadataDialogVisible"
|
||||||
:title="t('dialog.screenshot_metadata.header')"
|
:title="t('dialog.screenshot_metadata.header')"
|
||||||
width="1050px"
|
width="1050px"
|
||||||
top="10vh">
|
top="10vh"
|
||||||
|
@close="closeDialog">
|
||||||
<div
|
<div
|
||||||
v-if="screenshotMetadataDialog.visible"
|
|
||||||
v-loading="screenshotMetadataDialog.loading"
|
v-loading="screenshotMetadataDialog.loading"
|
||||||
style="-webkit-app-region: drag"
|
style="-webkit-app-region: drag"
|
||||||
@dragover.prevent
|
@dragover.prevent
|
||||||
@@ -170,7 +170,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { getCurrentInstance, ref, watch } from 'vue';
|
import { getCurrentInstance, reactive, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n-bridge';
|
||||||
import { vrcPlusImageRequest } from '../../../api';
|
import { vrcPlusImageRequest } from '../../../api';
|
||||||
import { useGalleryStore, useUserStore, useVrcxStore } from '../../../stores';
|
import { useGalleryStore, useUserStore, useVrcxStore } from '../../../stores';
|
||||||
@@ -191,17 +191,19 @@
|
|||||||
const { fullscreenImageDialog } = storeToRefs(useGalleryStore());
|
const { fullscreenImageDialog } = storeToRefs(useGalleryStore());
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
screenshotMetadataDialog: {
|
isScreenshotMetadataDialogVisible: {
|
||||||
type: Object,
|
type: Boolean,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['close']);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.screenshotMetadataDialog.visible,
|
() => props.isScreenshotMetadataDialogVisible,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
if (!props.screenshotMetadataDialog.metadata.filePath) {
|
if (!screenshotMetadataDialog.metadata.filePath) {
|
||||||
getAndDisplayLastScreenshot();
|
getAndDisplayLastScreenshot();
|
||||||
}
|
}
|
||||||
window.addEventListener('keyup', handleComponentKeyup);
|
window.addEventListener('keyup', handleComponentKeyup);
|
||||||
@@ -211,16 +213,32 @@
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const screenshotMetadataDialog = reactive({
|
||||||
|
// visible: false,
|
||||||
|
loading: false,
|
||||||
|
search: '',
|
||||||
|
searchType: 'Player Name',
|
||||||
|
searchTypes: ['Player Name', 'Player ID', 'World Name', 'World ID'],
|
||||||
|
searchIndex: null,
|
||||||
|
searchResults: null,
|
||||||
|
metadata: {},
|
||||||
|
isUploading: false
|
||||||
|
});
|
||||||
|
|
||||||
const screenshotMetadataSearchInputs = ref(0);
|
const screenshotMetadataSearchInputs = ref(0);
|
||||||
const screenshotMetadataCarouselRef = ref(null);
|
const screenshotMetadataCarouselRef = ref(null);
|
||||||
|
|
||||||
const handleComponentKeyup = (event) => {
|
const handleComponentKeyup = (event) => {
|
||||||
const carouselNavigation = { ArrowLeft: 0, ArrowRight: 2 }[event.key];
|
const carouselNavigation = { ArrowLeft: 0, ArrowRight: 2 }[event.key];
|
||||||
if (typeof carouselNavigation !== 'undefined' && props.screenshotMetadataDialog?.visible) {
|
if (typeof carouselNavigation !== 'undefined' && props.isScreenshotMetadataDialogVisible) {
|
||||||
screenshotMetadataCarouselChange(carouselNavigation);
|
screenshotMetadataCarouselChange(carouselNavigation);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function closeDialog() {
|
||||||
|
emit('close');
|
||||||
|
}
|
||||||
|
|
||||||
function handleDrop(event) {
|
function handleDrop(event) {
|
||||||
if (currentlyDroppingFile.value === null) {
|
if (currentlyDroppingFile.value === null) {
|
||||||
return;
|
return;
|
||||||
@@ -302,12 +320,12 @@
|
|||||||
message: t('message.screenshot_metadata.deleted'),
|
message: t('message.screenshot_metadata.deleted'),
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
const D = props.screenshotMetadataDialog;
|
const D = screenshotMetadataDialog;
|
||||||
getAndDisplayScreenshot(D.metadata.filePath, true);
|
getAndDisplayScreenshot(D.metadata.filePath, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function uploadScreenshotToGallery() {
|
function uploadScreenshotToGallery() {
|
||||||
const D = props.screenshotMetadataDialog;
|
const D = screenshotMetadataDialog;
|
||||||
if (D.metadata.fileSizeBytes > 10000000) {
|
if (D.metadata.fileSizeBytes > 10000000) {
|
||||||
$message({
|
$message({
|
||||||
message: t('message.file.too_large'),
|
message: t('message.file.too_large'),
|
||||||
@@ -342,7 +360,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
function screenshotMetadataSearch() {
|
function screenshotMetadataSearch() {
|
||||||
const D = props.screenshotMetadataDialog;
|
const D = screenshotMetadataDialog;
|
||||||
|
|
||||||
// Don't search if user is still typing
|
// Don't search if user is still typing
|
||||||
screenshotMetadataSearchInputs.value++;
|
screenshotMetadataSearchInputs.value++;
|
||||||
@@ -390,7 +408,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function screenshotMetadataCarouselChange(index) {
|
function screenshotMetadataCarouselChange(index) {
|
||||||
const D = props.screenshotMetadataDialog;
|
const D = screenshotMetadataDialog;
|
||||||
const searchIndex = D.searchIndex;
|
const searchIndex = D.searchIndex;
|
||||||
|
|
||||||
if (searchIndex !== null) {
|
if (searchIndex !== null) {
|
||||||
@@ -422,7 +440,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function screenshotMetadataResetSearch() {
|
function screenshotMetadataResetSearch() {
|
||||||
const D = props.screenshotMetadataDialog;
|
const D = screenshotMetadataDialog;
|
||||||
|
|
||||||
D.search = '';
|
D.search = '';
|
||||||
D.searchIndex = null;
|
D.searchIndex = null;
|
||||||
@@ -430,7 +448,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function screenshotMetadataCarouselChangeSearch(index) {
|
function screenshotMetadataCarouselChangeSearch(index) {
|
||||||
const D = props.screenshotMetadataDialog;
|
const D = screenshotMetadataDialog;
|
||||||
let searchIndex = D.searchIndex;
|
let searchIndex = D.searchIndex;
|
||||||
const filesArr = D.searchResults;
|
const filesArr = D.searchResults;
|
||||||
|
|
||||||
@@ -480,7 +498,7 @@
|
|||||||
async function displayScreenshotMetadata(json, needsCarouselFiles = true) {
|
async function displayScreenshotMetadata(json, needsCarouselFiles = true) {
|
||||||
let time;
|
let time;
|
||||||
let date;
|
let date;
|
||||||
const D = props.screenshotMetadataDialog;
|
const D = screenshotMetadataDialog;
|
||||||
D.metadata.author = {};
|
D.metadata.author = {};
|
||||||
D.metadata.world = {};
|
D.metadata.world = {};
|
||||||
D.metadata.players = [];
|
D.metadata.players = [];
|
||||||
Reference in New Issue
Block a user