From 62e350cc9109979ea6bd66823ad7f401b5e99b84 Mon Sep 17 00:00:00 2001 From: Natsumi Date: Sun, 25 Jun 2023 02:32:29 +1200 Subject: [PATCH] Emoji manager --- Program.cs | 2 +- html/src/app.js | 119 +++++++++++++++++++++++++- html/src/index.pug | 14 +++ html/src/localization/strings/en.json | 5 +- html/src/mixins/tabs/playerList.pug | 2 +- html/src/repository/database.js | 15 +++- 6 files changed, 150 insertions(+), 7 deletions(-) diff --git a/Program.cs b/Program.cs index 9ffad8d3..8dcf1760 100644 --- a/Program.cs +++ b/Program.cs @@ -56,7 +56,7 @@ namespace VRCX //Layout = "${longdate} [${level:uppercase=true}] ${logger} - ${message} ${exception:format=tostring}", // Layout with padding between the level/logger and message so that the message always starts at the same column Layout = "${longdate} [${level:uppercase=true:padding=-5}] ${logger:padding=-20} - ${message} ${exception:format=tostring}", - ArchiveFileName = Path.Combine(AppDataDirectory, "VRCX.{#}.log"), + ArchiveFileName = Path.Combine(AppDataDirectory, "logs", "VRCX.{#}.log"), ArchiveNumbering = ArchiveNumberingMode.DateAndSequence, ArchiveEvery = FileArchivePeriod.Day, MaxArchiveFiles = 4, diff --git a/html/src/app.js b/html/src/app.js index 5ca16ad6..858aaa01 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -2607,6 +2607,9 @@ speechSynthesis.getVoices(); }); API.$on('INSTANCE', function (args) { + if (!args.json?.id) { + return; + } if ( $app.userDialog.visible && $app.userDialog.ref.$location.tag === args.json.id @@ -13702,6 +13705,7 @@ speechSynthesis.getVoices(); }, layout: 'table' }; + $app.data.emojiTable = []; $app.data.VRCPlusIconsTable = []; $app.data.galleryTable = []; $app.data.inviteMessageTable = { @@ -22301,7 +22305,8 @@ speechSynthesis.getVoices(); }); }; - // gallery + // #endregion + // #region | Gallery $app.data.galleryDialog = {}; $app.data.galleryDialogVisible = false; @@ -22454,6 +22459,116 @@ speechSynthesis.getVoices(); } }); + // #endregion + // #region | Emoji + + API.$on('LOGIN', function () { + $app.emojiTable = []; + }); + + $app.methods.refreshEmojiTable = function () { + this.galleryDialogIconsLoading = true; + var params = { + n: 100, + tag: 'emoji' + }; + API.getFileList(params); + }; + + API.$on('FILES:LIST', function (args) { + if (args.params.tag === 'emoji') { + $app.emojiTable = args.json.reverse(); + $app.galleryDialogIconsLoading = false; + } + }); + + $app.methods.deleteEmoji = function (fileId) { + API.deleteFile(fileId).then((args) => { + API.$emit('EMOJI:DELETE', args); + return args; + }); + }; + + API.$on('EMOJI:DELETE', function (args) { + var array = $app.emojiTable; + var { length } = array; + for (var i = 0; i < length; ++i) { + if (args.fileId === array[i].id) { + array.splice(i, 1); + break; + } + } + }); + + $app.methods.onFileChangeEmoji = function (e) { + var clearFile = function () { + if (document.querySelector('#EmojiUploadButton')) { + document.querySelector('#EmojiUploadButton').value = ''; + } + }; + var files = e.target.files || e.dataTransfer.files; + if (!files.length) { + return; + } + if (files[0].size >= 10000000) { + // 10MB + $app.$message({ + message: 'File size too large', + type: 'error' + }); + clearFile(); + return; + } + if (!files[0].type.match(/image.*/)) { + $app.$message({ + message: "File isn't an image", + type: 'error' + }); + clearFile(); + return; + } + var r = new FileReader(); + r.onload = function () { + var base64Body = btoa(r.result); + API.uploadEmoji(base64Body).then((args) => { + $app.$message({ + message: 'Emoji uploaded', + type: 'success' + }); + return args; + }); + }; + r.readAsBinaryString(files[0]); + clearFile(); + }; + + $app.methods.displayEmojiUpload = function () { + document.getElementById('EmojiUploadButton').click(); + }; + + API.uploadEmoji = function (params) { + return this.call('emoji', { + uploadImage: true, + imageData: params + }).then((json) => { + var args = { + json, + params + }; + this.$emit('EMOJI:ADD', args); + return args; + }); + }; + + API.$on('EMOJI:ADD', function (args) { + if (Object.keys($app.emojiTable).length !== 0) { + $app.emojiTable.push(args.json); + } + }); + + // #endregion + // #region Misc + $app.methods.replaceBioSymbols = function (text) { if (!text) { return ''; @@ -23815,7 +23930,7 @@ speechSynthesis.getVoices(); ); $app.methods.updateDatabaseVersion = async function () { - var databaseVersion = 5; + var databaseVersion = 6; if (this.databaseVersion !== databaseVersion) { if (this.databaseVersion) { var msgBox = this.$message({ diff --git a/html/src/index.pug b/html/src/index.pug index 557a0e80..47934f95 100644 --- a/html/src/index.pug +++ b/html/src/index.pug @@ -2101,6 +2101,20 @@ html div(style="float:right;margin-top:5px") el-button(type="default" @click="downloadAndSaveImage(image.versions[image.versions.length - 1].file.url)" size="mini" icon="el-icon-download" circle) el-button(type="default" @click="deleteVRCPlusIcon(image.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogIconsLoading") + span(slot="label") {{ $t('dialog.gallery_icons.emoji') }} + span(style="color:#909399;font-size:12px;margin-left:5px") {{ emojiTable.length }}/64 + input(type="file" accept="image/*" @change="onFileChangeEmoji" id="EmojiUploadButton" style="display:none") + el-button-group + el-button(type="default" size="small" @click="refreshEmojiTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }} + el-button(type="default" size="small" @click="displayEmojiUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }} + br + .x-friend-item(v-if="image.versions && image.versions.length > 0" v-for="image in emojiTable" :key="image.id" style="display:inline-block;margin-top:10px;width:unset;cursor:default") + .vrcplus-icon(v-if="image.versions[image.versions.length - 1].file.url") + img.avatar(v-lazy="image.versions[image.versions.length - 1].file.url") + div(style="float:right;margin-top:5px") + el-button(type="default" @click="downloadAndSaveImage(image.versions[image.versions.length - 1].file.url)" size="mini" icon="el-icon-download" circle) + el-button(type="default" @click="deleteEmoji(image.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") //- dialog Table: Previous Instances User el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="previousInstancesUserDialog" :visible.sync="previousInstancesUserDialog.visible" :title="$t('dialog.previous_instances.header')" width="1000px") diff --git a/html/src/localization/strings/en.json b/html/src/localization/strings/en.json index 9861bc11..6fff5556 100644 --- a/html/src/localization/strings/en.json +++ b/html/src/localization/strings/en.json @@ -516,7 +516,7 @@ "request_invite": "Request Invite", "request_invite_with_message": "Request Invite With Message", "invite_to_group": "Invite To Group", - "manage_gallery_icon": "Manage Gallery/Icons", + "manage_gallery_icon": "Manage Gallery/Icons/Emoji", "accept_friend_request": "Accept Friend Request", "decline_friend_request": "Decline Friend Request", "cancel_friend_request": "Cancel Friend Request", @@ -1087,10 +1087,11 @@ "send": "Send" }, "gallery_icons": { - "header": "Gallery and Icons", + "header": "Gallery, Icons and Emoji", "description": "Recommended image size 1200x900px", "gallery": "Gallery", "icons": "Icons", + "emoji": "Emoji", "refresh": "Refresh", "upload": "Upload", "clear": "Clear" diff --git a/html/src/mixins/tabs/playerList.pug b/html/src/mixins/tabs/playerList.pug index f7d791cc..96e03156 100644 --- a/html/src/mixins/tabs/playerList.pug +++ b/html/src/mixins/tabs/playerList.pug @@ -26,7 +26,7 @@ mixin playerListTab() br location-world(:locationobject="currentInstanceLocation" :currentuserid="API.currentUser.id") instance-info(:location="currentInstanceLocation.tag" :instance="currentInstanceWorld.instance" :friendcount="lastLocation.friendList.size" :updateelement="updateInstanceInfo") - |  #[timer(v-if="lastLocation.date" :epoch="lastLocation.date")] + | ― #[timer(v-if="lastLocation.date" :epoch="lastLocation.date")] div(style="margin-top:5px") span(v-show="currentInstanceWorld.ref.name !== currentInstanceWorld.ref.description" v-text="currentInstanceWorld.ref.description" style="font-size:12px;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2") div(style="display:flex;flex-direction:column;margin-left:20px") diff --git a/html/src/repository/database.js b/html/src/repository/database.js index 3ef3fd17..7458cd81 100644 --- a/html/src/repository/database.js +++ b/html/src/repository/database.js @@ -45,7 +45,7 @@ class Database { async initTables() { await sqliteService.executeNonQuery( - `CREATE TABLE IF NOT EXISTS gamelog_location (id INTEGER PRIMARY KEY, created_at TEXT, location TEXT, world_id TEXT, world_name TEXT, time INTEGER, groupName TEXT, UNIQUE(created_at, location))` + `CREATE TABLE IF NOT EXISTS gamelog_location (id INTEGER PRIMARY KEY, created_at TEXT, location TEXT, world_id TEXT, world_name TEXT, time INTEGER, group_name TEXT, UNIQUE(created_at, location))` ); await sqliteService.executeNonQuery( `CREATE TABLE IF NOT EXISTS gamelog_join_leave (id INTEGER PRIMARY KEY, created_at TEXT, type TEXT, display_name TEXT, location TEXT, user_id TEXT, time INTEGER, UNIQUE(created_at, type, display_name))` @@ -2275,6 +2275,19 @@ class Database { ); } } + // Fix gamelog_location column typo + try { + await sqliteService.executeNonQuery( + `SELECT groupName FROM gamelog_location LIMIT 1` + ); + await sqliteService.executeNonQuery( + `ALTER TABLE gamelog_location DROP COLUMN groupName` + ); + } catch (e) { + if (e.indexOf('no such column') === -1) { + throw e; + } + } } }