diff --git a/src/components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoChart.vue b/src/components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoChart.vue new file mode 100644 index 00000000..3cb3c9c5 --- /dev/null +++ b/src/components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoChart.vue @@ -0,0 +1,424 @@ + + + diff --git a/src/components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue b/src/components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue index 369fea8e..b8b9ed80 100644 --- a/src/components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue +++ b/src/components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue @@ -5,6 +5,7 @@ + +
+
+
+ + + + + + + + + + + + + +
+
+
+
+ {{ t('view.friends_locations.loading_more') }} +
+ +
+
@@ -35,16 +98,21 @@ import { DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { storeToRefs } from 'pinia'; import { useI18n } from 'vue-i18n'; + import { BarChart3, List } from 'lucide-vue-next'; - import { useGameLogStore, useInstanceStore, useSearchStore, useUserStore, useVrcxStore } from '../../../stores'; + import { useGameLogStore, useInstanceStore, useSearchStore, useVrcxStore } from '../../../stores'; import { compareByCreatedAt, localeIncludes, parseLocation, timeToText } from '../../../shared/utils'; import { DataTableLayout } from '../../ui/data-table'; import { InputGroupField } from '../../../components/ui/input-group'; + import { ToggleGroup, ToggleGroupItem } from '../../../components/ui/toggle-group'; + import { TooltipWrapper } from '../../../components/ui/tooltip'; import { createColumns } from './previousInstancesInfoColumns.jsx'; import { database } from '../../../services/database'; import { useVrcxVueTable } from '../../../lib/table/useVrcxVueTable'; import { lookupUser } from '../../../coordinators/userCoordinator'; + import PreviousInstancesInfoChart from './PreviousInstancesInfoChart.vue'; + const { previousInstancesInfoDialog, previousInstancesInfoState } = storeToRefs(useInstanceStore()); const { gameLogIsFriend, gameLogIsFavorite } = useGameLogStore(); const { t } = useI18n(); @@ -107,7 +175,10 @@ }); const { stringComparer } = storeToRefs(useSearchStore()); - const vrcxStore = useVrcxStore(); + + const viewMode = ref('table'); + const chartData = ref([]); + const chartLoading = ref(false); const displayRows = computed(() => { const q = String(search.value ?? '') @@ -168,6 +239,28 @@ sortBy.value = sorting; }; + function handleViewModeChange(value) { + if (value) { + viewMode.value = value; + if (value === 'chart' && chartData.value.length === 0) { + loadChartData(); + } + } + } + + async function loadChartData() { + chartLoading.value = true; + try { + const data = await database.getPlayerDetailFromInstance(location.value.tag); + chartData.value = data; + } catch (error) { + console.error('Failed to load chart data:', error); + chartData.value = []; + } finally { + chartLoading.value = false; + } + } + watch( () => previousInstancesInfoDialog.value.visible, (value) => { @@ -176,6 +269,9 @@ init(); refreshPreviousInstancesInfoTable(); }); + } else { + viewMode.value = 'table'; + chartData.value = []; } }, { immediate: true } diff --git a/src/localization/en.json b/src/localization/en.json index 3189dea3..e4808dac 100644 --- a/src/localization/en.json +++ b/src/localization/en.json @@ -2024,7 +2024,9 @@ "previous_instances": { "header": "Previous Instances", "info": "Previous Instance Info", - "search_placeholder": "Search" + "search_placeholder": "Search", + "table_view": "Table View", + "chart_view": "Chart View" }, "group_member_moderation": { "header": "Group Member Moderation", diff --git a/src/services/database/gameLog.js b/src/services/database/gameLog.js index e789ca64..4006fb99 100644 --- a/src/services/database/gameLog.js +++ b/src/services/database/gameLog.js @@ -1306,6 +1306,32 @@ const gameLog = { return players; }, + /** + * @param {string} location + * @returns {Promise>} + */ + async getPlayerDetailFromInstance(location) { + const entries = []; + await sqliteService.execute( + (dbRow) => { + entries.push({ + created_at: dbRow[0], + display_name: dbRow[1], + user_id: dbRow[2], + time: dbRow[3] || 0 + }); + }, + `SELECT created_at, display_name, user_id, time + FROM gamelog_join_leave + WHERE location = @location AND type = 'OnPlayerLeft' + ORDER BY created_at ASC`, + { + '@location': location + } + ); + return entries; + }, + async getPreviousDisplayNamesByUserId(ref) { var data = new Map(); await sqliteService.execute(