change screenshot metadata dialog to route view

This commit is contained in:
pa
2026-01-13 12:09:30 +09:00
committed by Natsumi
parent aab248a3af
commit 4fbae859f4
3 changed files with 56 additions and 76 deletions

View File

@@ -20,6 +20,7 @@ import Search from './../views/Search/Search.vue';
import Settings from './../views/Settings/Settings.vue'; import Settings from './../views/Settings/Settings.vue';
import Tools from './../views/Tools/Tools.vue'; import Tools from './../views/Tools/Tools.vue';
import Gallery from './../views/Tools/Gallery.vue'; import Gallery from './../views/Tools/Gallery.vue';
import ScreenshotMetadata from './../views/Tools/ScreenshotMetadata.vue';
const routes = [ const routes = [
{ {
@@ -90,6 +91,12 @@ const routes = [
component: Gallery, component: Gallery,
meta: { navKey: 'tools' } meta: { navKey: 'tools' }
}, },
{
path: 'tools/screenshot-metadata',
name: 'screenshot-metadata',
component: ScreenshotMetadata,
meta: { navKey: 'tools' }
},
{ path: 'settings', name: 'settings', component: Settings } { path: 'settings', name: 'settings', component: Settings }
] ]
} }

View File

@@ -1,16 +1,12 @@
<template> <template>
<el-dialog <div class="screenshot-metadata-page x-container">
class="x-dialog" <div class="screenshot-metadata-page__header">
:model-value="isScreenshotMetadataDialogVisible" <Button variant="ghost" class="screenshot-metadata-page__back" @click="goBack">
:title="t('dialog.screenshot_metadata.header')" {{ t('nav_tooltip.tools') }}
width="1050px" </Button>
@close="closeDialog"> <span class="header">{{ t('dialog.screenshot_metadata.header') }}</span>
<div </div>
v-loading="screenshotMetadataDialog.loading" <div v-loading="screenshotMetadataDialog.loading" @dragover.prevent @dragenter.prevent @drop="handleDrop">
style="-webkit-app-region: drag"
@dragover.prevent
@dragenter.prevent
@drop="handleDrop">
<span style="margin-left: 5px; color: var(--el-text-color-secondary); font-family: monospace">{{ <span style="margin-left: 5px; color: var(--el-text-color-secondary); font-family: monospace">{{
t('dialog.screenshot_metadata.drag') t('dialog.screenshot_metadata.drag')
}}</span> }}</span>
@@ -55,13 +51,11 @@
<br /> <br />
<div class="flex items-center"> <div class="flex items-center">
<!-- Search bar input -->
<InputGroupSearch <InputGroupSearch
v-model="screenshotMetadataDialog.search" v-model="screenshotMetadataDialog.search"
placeholder="Search" placeholder="Search"
style="width: 200px" style="width: 200px"
@input="screenshotMetadataSearch" /> @input="screenshotMetadataSearch" />
<!-- Search type dropdown -->
<Select :model-value="screenshotMetadataDialog.searchType" @update:modelValue="handleSearchTypeChange"> <Select :model-value="screenshotMetadataDialog.searchType" @update:modelValue="handleSearchTypeChange">
<SelectTrigger size="sm" style="width: 150px; margin-left: 10px"> <SelectTrigger size="sm" style="width: 150px; margin-left: 10px">
<SelectValue placeholder="Search Type" /> <SelectValue placeholder="Search Type" />
@@ -75,7 +69,6 @@
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
<!-- Search index/total label -->
<template v-if="screenshotMetadataDialog.searchIndex !== null"> <template v-if="screenshotMetadataDialog.searchIndex !== null">
<span style="white-space: pre-wrap; font-size: 12px; margin-left: 10px">{{ <span style="white-space: pre-wrap; font-size: 12px; margin-left: 10px">{{
screenshotMetadataDialog.searchIndex + 1 + '/' + screenshotMetadataDialog.searchResults.length screenshotMetadataDialog.searchIndex + 1 + '/' + screenshotMetadataDialog.searchResults.length
@@ -110,7 +103,7 @@
:hint="screenshotMetadataDialog.metadata.author.displayName" :hint="screenshotMetadataDialog.metadata.author.displayName"
style="color: var(--el-text-color-secondary); font-family: monospace" /> style="color: var(--el-text-color-secondary); font-family: monospace" />
<br /> <br />
<div class="my-2 w-[95%] ml-6.5"> <div class="my-2 w-[90%] ml-17">
<Carousel :opts="{ loop: false }" @init-api="handleScreenshotMetadataCarouselInit"> <Carousel :opts="{ loop: false }" @init-api="handleScreenshotMetadataCarouselInit">
<CarouselContent class="h-150"> <CarouselContent class="h-150">
<CarouselItem> <CarouselItem>
@@ -158,60 +151,36 @@
<br /> <br />
</span> </span>
</div> </div>
</el-dialog> </div>
</template> </template>
<script setup> <script setup>
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/components/ui/carousel'; import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/components/ui/carousel';
import { reactive, ref, watch } from 'vue'; import { onBeforeUnmount, onMounted, reactive, ref } from 'vue';
import { useGalleryStore, useUserStore, useVrcxStore } from '@/stores';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { InputGroupSearch } from '@/components/ui/input-group'; import { InputGroupSearch } from '@/components/ui/input-group';
import { formatDateFilter } from '@/shared/utils';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { toast } from 'vue-sonner'; import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import { vrcPlusImageRequest } from '@/api';
import { useGalleryStore, useUserStore, useVrcxStore } from '../../../stores'; const router = useRouter();
import { Badge } from '../../../components/ui/badge'; const { t } = useI18n();
import { formatDateFilter } from '../../../shared/utils';
import { vrcPlusImageRequest } from '../../../api';
const { showFullscreenImageDialog, handleGalleryImageAdd } = useGalleryStore(); const { showFullscreenImageDialog, handleGalleryImageAdd } = useGalleryStore();
const { currentlyDroppingFile } = storeToRefs(useVrcxStore()); const { currentlyDroppingFile } = storeToRefs(useVrcxStore());
const { isLocalUserVrcPlusSupporter } = storeToRefs(useUserStore()); const { isLocalUserVrcPlusSupporter } = storeToRefs(useUserStore());
const { fullscreenImageDialog } = storeToRefs(useGalleryStore());
const { t } = useI18n();
const userStore = useUserStore(); const userStore = useUserStore();
const { lookupUser } = userStore; const { lookupUser } = userStore;
const { fullscreenImageDialog } = storeToRefs(useGalleryStore());
const props = defineProps({
isScreenshotMetadataDialogVisible: {
type: Boolean,
required: true
}
});
const emit = defineEmits(['close']);
watch(
() => props.isScreenshotMetadataDialogVisible,
(newVal) => {
if (newVal) {
if (!screenshotMetadataDialog.metadata.filePath) {
getAndDisplayLastScreenshot();
}
window.addEventListener('keyup', handleComponentKeyup);
} else {
window.removeEventListener('keyup', handleComponentKeyup);
}
}
);
const screenshotMetadataDialog = reactive({ const screenshotMetadataDialog = reactive({
// visible: false,
loading: false, loading: false,
search: '', search: '',
searchType: 'Player Name', searchType: 'Player Name',
@@ -226,9 +195,20 @@
const screenshotMetadataCarouselApi = ref(null); const screenshotMetadataCarouselApi = ref(null);
const ignoreCarouselSelect = ref(false); const ignoreCarouselSelect = ref(false);
onMounted(() => {
if (!screenshotMetadataDialog.metadata.filePath) {
getAndDisplayLastScreenshot();
}
window.addEventListener('keyup', handleComponentKeyup);
});
onBeforeUnmount(() => {
window.removeEventListener('keyup', handleComponentKeyup);
});
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.isScreenshotMetadataDialogVisible) { if (typeof carouselNavigation !== 'undefined') {
if (screenshotMetadataCarouselApi.value) { if (screenshotMetadataCarouselApi.value) {
if (event.key === 'ArrowLeft') { if (event.key === 'ArrowLeft') {
screenshotMetadataCarouselApi.value.scrollPrev(); screenshotMetadataCarouselApi.value.scrollPrev();
@@ -241,8 +221,8 @@
} }
}; };
function closeDialog() { function goBack() {
emit('close'); router.push({ name: 'tools' });
} }
function handleDrop(event) { function handleDrop(event) {
@@ -261,7 +241,7 @@
let filePath = ''; let filePath = '';
if (LINUX) { if (LINUX) {
filePath = await window.electron.openFileDialog(); // PNG filter is applied in main.js filePath = await window.electron.openFileDialog();
} else { } else {
filePath = await AppApi.OpenFileSelectorDialog( filePath = await AppApi.OpenFileSelectorDialog(
await AppApi.GetVRChatPhotosLocation(), await AppApi.GetVRChatPhotosLocation(),
@@ -347,7 +327,6 @@
function screenshotMetadataSearch() { function screenshotMetadataSearch() {
const D = screenshotMetadataDialog; const D = screenshotMetadataDialog;
// Don't search if user is still typing
screenshotMetadataSearchInputs.value++; screenshotMetadataSearchInputs.value++;
let current = screenshotMetadataSearchInputs.value; let current = screenshotMetadataSearchInputs.value;
setTimeout(() => { setTimeout(() => {
@@ -359,13 +338,12 @@
if (D.search === '') { if (D.search === '') {
screenshotMetadataResetSearch(); screenshotMetadataResetSearch();
if (D.metadata.filePath !== null) { if (D.metadata.filePath !== null) {
// Re-retrieve the current screenshot metadata and get previous/next files for regular carousel directory navigation
getAndDisplayScreenshot(D.metadata.filePath, true); getAndDisplayScreenshot(D.metadata.filePath, true);
} }
return; return;
} }
const searchType = D.searchTypes.indexOf(D.searchType); // Matches the search type enum in .NET const searchType = D.searchTypes.indexOf(D.searchType);
D.loading = true; D.loading = true;
AppApi.FindScreenshotsBySearch(D.search, searchType) AppApi.FindScreenshotsBySearch(D.search, searchType)
.then((json) => { .then((json) => {
@@ -383,7 +361,6 @@
D.searchIndex = 0; D.searchIndex = 0;
D.searchResults = results; D.searchResults = results;
// console.log("Search results", results)
getAndDisplayScreenshot(results[0], false); getAndDisplayScreenshot(results[0], false);
}) })
.finally(() => { .finally(() => {
@@ -530,15 +507,12 @@
return; return;
} }
// Get extra data for display dialog like resolution, file size, etc
D.loading = true; D.loading = true;
const extraData = await AppApi.GetExtraScreenshotData(metadata.sourceFile, needsCarouselFiles); const extraData = await AppApi.GetExtraScreenshotData(metadata.sourceFile, needsCarouselFiles);
D.loading = false; D.loading = false;
const extraDataObj = JSON.parse(extraData); const extraDataObj = JSON.parse(extraData);
Object.assign(metadata, extraDataObj); Object.assign(metadata, extraDataObj);
// console.log("Displaying screenshot metadata", json, "extra data", extraDataObj, "path", json.filePath)
D.metadata = metadata; D.metadata = metadata;
const regex = metadata.fileName?.match( const regex = metadata.fileName?.match(
@@ -546,19 +520,13 @@
); );
if (regex) { if (regex) {
if (typeof regex[2] !== 'undefined' && regex[4].length === 4) { if (typeof regex[2] !== 'undefined' && regex[4].length === 4) {
// old format
// VRChat_3840x2160_2022-02-02_03-21-39.771
date = `${regex[4]}-${regex[5]}-${regex[6]}`; date = `${regex[4]}-${regex[5]}-${regex[6]}`;
time = `${regex[7]}:${regex[8]}:${regex[9]}`; time = `${regex[7]}:${regex[8]}:${regex[9]}`;
D.metadata.dateTime = Date.parse(`${date} ${time}`); D.metadata.dateTime = Date.parse(`${date} ${time}`);
// D.metadata.resolution = `${regex[2]}x${regex[3]}`;
} else if (typeof regex[11] !== 'undefined' && regex[11].length === 4) { } else if (typeof regex[11] !== 'undefined' && regex[11].length === 4) {
// new format
// VRChat_2023-02-16_10-39-25.274_3840x2160
date = `${regex[11]}-${regex[12]}-${regex[13]}`; date = `${regex[11]}-${regex[12]}-${regex[13]}`;
time = `${regex[14]}:${regex[15]}:${regex[16]}`; time = `${regex[14]}:${regex[15]}:${regex[16]}`;
D.metadata.dateTime = Date.parse(`${date} ${time}`); D.metadata.dateTime = Date.parse(`${date} ${time}`);
// D.metadata.resolution = `${regex[18]}x${regex[19]}`;
} }
} }
if (metadata.timestamp) { if (metadata.timestamp) {
@@ -573,3 +541,12 @@
} }
} }
</script> </script>
<style scoped>
.screenshot-metadata-page__header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
}
</style>

View File

@@ -35,7 +35,7 @@
</div> </div>
<div class="tools-grid" v-show="!categoryCollapsed['image']"> <div class="tools-grid" v-show="!categoryCollapsed['image']">
<el-card :body-style="{ padding: '0px' }" class="tool-card"> <el-card :body-style="{ padding: '0px' }" class="tool-card">
<div class="tool-content" @click="showScreenshotMetadataDialog"> <div class="tool-content" @click="showScreenshotMetadataPage">
<div class="tool-icon"> <div class="tool-icon">
<i class="ri-screenshot-2-line"></i> <i class="ri-screenshot-2-line"></i>
</div> </div>
@@ -185,9 +185,6 @@
<GroupCalendarDialog <GroupCalendarDialog
:visible="isGroupCalendarDialogVisible" :visible="isGroupCalendarDialogVisible"
@close="isGroupCalendarDialogVisible = false" /> @close="isGroupCalendarDialogVisible = false" />
<ScreenshotMetadataDialog
:isScreenshotMetadataDialogVisible="isScreenshotMetadataDialogVisible"
@close="isScreenshotMetadataDialogVisible = false" />
<NoteExportDialog <NoteExportDialog
:isNoteExportDialogVisible="isNoteExportDialogVisible" :isNoteExportDialogVisible="isNoteExportDialogVisible"
@close="isNoteExportDialogVisible = false" /> @close="isNoteExportDialogVisible = false" />
@@ -207,16 +204,15 @@
<script setup> <script setup>
import { computed, defineAsyncComponent, ref } from 'vue'; import { computed, defineAsyncComponent, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { ArrowRight } from '@element-plus/icons-vue'; import { ArrowRight } from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { toast } from 'vue-sonner'; import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { useFriendStore, useGalleryStore } from '../../stores'; import { useFriendStore, useGalleryStore } from '../../stores';
const GroupCalendarDialog = defineAsyncComponent(() => import('./dialogs/GroupCalendarDialog.vue')); const GroupCalendarDialog = defineAsyncComponent(() => import('./dialogs/GroupCalendarDialog.vue'));
const ScreenshotMetadataDialog = defineAsyncComponent(() => import('./dialogs/ScreenshotMetadataDialog.vue'));
const NoteExportDialog = defineAsyncComponent(() => import('./dialogs/NoteExportDialog.vue')); const NoteExportDialog = defineAsyncComponent(() => import('./dialogs/NoteExportDialog.vue'));
const EditInviteMessageDialog = defineAsyncComponent(() => import('./dialogs/EditInviteMessagesDialog.vue')); const EditInviteMessageDialog = defineAsyncComponent(() => import('./dialogs/EditInviteMessagesDialog.vue'));
const ExportDiscordNamesDialog = defineAsyncComponent(() => import('./dialogs/ExportDiscordNamesDialog.vue')); const ExportDiscordNamesDialog = defineAsyncComponent(() => import('./dialogs/ExportDiscordNamesDialog.vue'));
@@ -224,6 +220,7 @@
const ExportAvatarsListDialog = defineAsyncComponent(() => import('./dialogs/ExportAvatarsListDialog.vue')); const ExportAvatarsListDialog = defineAsyncComponent(() => import('./dialogs/ExportAvatarsListDialog.vue'));
const { t } = useI18n(); const { t } = useI18n();
const router = useRouter();
const { showGalleryPage } = useGalleryStore(); const { showGalleryPage } = useGalleryStore();
const { friends } = storeToRefs(useFriendStore()); const { friends } = storeToRefs(useFriendStore());
@@ -236,7 +233,6 @@
}); });
const isGroupCalendarDialogVisible = ref(false); const isGroupCalendarDialogVisible = ref(false);
const isScreenshotMetadataDialogVisible = ref(false);
const isNoteExportDialogVisible = ref(false); const isNoteExportDialogVisible = ref(false);
const isExportDiscordNamesDialogVisible = ref(false); const isExportDiscordNamesDialogVisible = ref(false);
const isExportFriendsListDialogVisible = ref(false); const isExportFriendsListDialogVisible = ref(false);
@@ -252,8 +248,8 @@
isGroupCalendarDialogVisible.value = true; isGroupCalendarDialogVisible.value = true;
}; };
const showScreenshotMetadataDialog = () => { const showScreenshotMetadataPage = () => {
isScreenshotMetadataDialogVisible.value = true; router.push({ name: 'screenshot-metadata' });
}; };
const showNoteExportDialog = () => { const showNoteExportDialog = () => {