diff --git a/html/src/app.js b/html/src/app.js index 71281157..2c325f0b 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -19239,6 +19239,65 @@ speechSynthesis.getVoices(); }); }; + // App: Previous Instance Info Dialog + + $app.data.previousInstanceInfoDialogTable = { + data: [], + filters: [ + { + prop: 'displayName', + value: '' + } + ], + tableProps: { + stripe: true, + size: 'mini', + defaultSort: { + prop: 'created_at', + order: 'descending' + } + }, + pageSize: 10, + paginationProps: { + small: true, + layout: 'sizes,prev,pager,next,total', + pageSizes: [10, 25, 50, 100] + } + }; + + $app.data.previousInstanceInfoDialog = { + visible: false, + loading: false, + forceUpdate: 0, + $location: {} + }; + + $app.methods.showPreviousInstanceInfoDialog = function (instanceId) { + this.$nextTick(() => + adjustDialogZ(this.$refs.previousInstanceInfoDialog.$el) + ); + var D = this.previousInstanceInfoDialog; + D.$location = API.parseLocation(instanceId); + D.visible = true; + D.loading = true; + this.refreshPreviousInstanceInfoTable(); + }; + + $app.methods.refreshPreviousInstanceInfoTable = function () { + var D = this.previousInstanceInfoDialog; + database.getPlayersFromInstance(D.$location.tag).then((data) => { + var array = []; + for (var entry of Array.from(data.values())) { + entry.timer = timeToText(entry.time); + array.push(entry); + } + array.sort(compareByCreatedAt); + this.previousInstanceInfoDialogTable.data = array; + D.loading = false; + workerTimers.setTimeout(() => D.forceUpdate++, 150); + }); + }; + $app.data.dtHour12 = configRepository.getBool('VRCX_dtHour12'); $app.data.dtIsoFormat = configRepository.getBool('VRCX_dtIsoFormat'); $app.methods.setDatetimeFormat = async function () { diff --git a/html/src/index.pug b/html/src/index.pug index 1466ee6e..ca7b3248 100644 --- a/html/src/index.pug +++ b/html/src/index.pug @@ -2230,15 +2230,19 @@ html el-button(v-if="VRCXUpdateDialog.updatePending" type="primary" size="small" @click="restartVRCX") Install //- dialog: launch - el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="launchDialog" :visible.sync="launchDialog.visible" title="Launch" width="400px") + el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="launchDialog" :visible.sync="launchDialog.visible" title="Launch" width="450px") div #[span(v-text="launchDialog.shortUrl" style="word-break:break-all;font-size:12px")] el-tooltip(placement="top" content="Copy to clipboard" :disabled="hideTooltips") el-button(v-if="launchDialog.shortUrl" @click="copyInstanceUrl(launchDialog.shortUrl)" size="mini" icon="el-icon-s-order" style="margin-left:5px" circle) div(style="margin-top:10px") #[span(v-text="launchDialog.url" style="word-break:break-all;font-size:12px")] el-tooltip(placement="top" content="Copy to clipboard" :disabled="hideTooltips") el-button(@click="copyInstanceUrl(launchDialog.url)" size="mini" icon="el-icon-s-order" style="margin-left:5px" circle) + div(style="margin-top:10px") #[span(v-text="launchDialog.location" style="word-break:break-all;font-size:12px")] + el-tooltip(placement="top" content="Copy to clipboard" :disabled="hideTooltips") + el-button(@click="copyInstanceUrl(launchDialog.location)" size="mini" icon="el-icon-s-order" style="margin-left:5px" circle) template(#footer) el-checkbox(v-model="launchDialog.desktop" style="float:left;margin-top:5px") Start as Desktop (No VR) + el-button(size="small" @click="showPreviousInstanceInfoDialog(launchDialog.location)") Info el-button(size="small" @click="showInviteDialog(launchDialog.location)" :disabled="checkCanInvite(launchDialog.location)") Invite el-button(type="primary" size="small" @click="launchGame(locationToLaunchArg(launchDialog.location))") Launch @@ -2845,9 +2849,10 @@ html el-table-column(label="Time" prop="time" width="90" sortable) template(v-once #default="scope") span(v-text="scope.row.timer") - el-table-column(label="Action" width="60" align="right") + el-table-column(label="Action" width="90" align="right") template(v-once #default="scope") el-button(type="text" icon="el-icon-info" size="mini" @click="showLaunchDialog(scope.row.location)") + el-button(type="text" icon="el-icon-tickets" size="mini" @click="showPreviousInstanceInfoDialog(scope.row.location)") el-button(type="text" icon="el-icon-close" size="mini" @click="confirmDeleteGameLogUserInstance(scope.row)") //- dialog Table: Previous Instances World @@ -2876,10 +2881,32 @@ html el-table-column(label="Time" prop="time" width="90" sortable) template(v-once #default="scope") span(v-text="scope.row.timer") - el-table-column(label="Action" width="60" align="right") + el-table-column(label="Action" width="90" align="right") template(v-once #default="scope") + el-button(type="text" icon="el-icon-tickets" size="mini" @click="showPreviousInstanceInfoDialog(scope.row.location)") el-button(type="text" icon="el-icon-close" size="mini" @click="confirmDeleteGameLogWorldInstance(scope.row)") + //- dialog Table: Previous Instance Info + el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="previousInstanceInfoDialog" :visible.sync="previousInstanceInfoDialog.visible" title="Previous Instance Info" width="800px") + location(:location="previousInstanceInfoDialog.$location.tag" style="font-size:14px") + el-input(v-model="previousInstanceInfoDialogTable.filters[0].value" placeholder="Search" style="display:block;width:150px;margin-top:15px") + data-tables(v-if="previousInstanceInfoDialog.visible" v-bind="previousInstanceInfoDialogTable" v-loading="previousInstanceInfoDialog.loading" style="margin-top:10px") + el-table-column(label="Date" prop="created_at" sortable width="110") + template(v-once #default="scope") + el-tooltip(placement="left") + template(#content) + span {{ scope.row.created_at | formatDate('long') }} + span {{ scope.row.created_at | formatDate('short') }} + el-table-column(label="Display Name" prop="displayName" sortable) + template(v-once #default="scope") + span.x-link(v-text="scope.row.displayName" @click="lookupUser(scope.row)") + el-table-column(label="Time" prop="time" width="90" sortable) + template(v-once #default="scope") + span(v-text="scope.row.timer") + el-table-column(label="Count" prop="count" width="90" sortable) + template(v-once #default="scope") + span(v-text="scope.row.count") + //- dialog: open source software notice el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" :visible.sync="ossDialog" title="Open Source Software Notice" width="650px") div(style="height:350px;overflow:hidden scroll;word-break:break-all") diff --git a/html/src/repository/database.js b/html/src/repository/database.js index 3ec74525..568cc201 100644 --- a/html/src/repository/database.js +++ b/html/src/repository/database.js @@ -1360,6 +1360,42 @@ class Database { ); } + async getPlayersFromInstance(location) { + var players = new Map(); + await sqliteService.execute( + (dbRow) => { + var time = 0; + var count = 0; + var created_at = dbRow[0]; + if (dbRow[3]) { + time = dbRow[3]; + } + var ref = players.get(dbRow[1]); + if (typeof ref !== 'undefined') { + time += ref.time; + count = ref.count; + created_at = ref.created_at; + } + if (dbRow[4] === 'OnPlayerJoined') { + count++; + } + var row = { + created_at, + displayName: dbRow[1], + userId: dbRow[2], + time, + count + }; + players.set(row.displayName, row); + }, + `SELECT created_at, display_name, user_id, time, type FROM gamelog_join_leave WHERE location = @location`, + { + '@location': location + } + ); + return players; + } + async getpreviousDisplayNamesByUserId(ref) { var data = new Map(); await sqliteService.execute(