mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-01 12:43:46 +02:00
refactor: app.js (#1291)
* refactor: frontend * Fix avatar gallery sort * Update .NET dependencies * Update npm dependencies electron v37.1.0 * bulkRefreshFriends * fix dark theme * Remove crowdin * Fix config.json dialog not updating * VRCX log file fixes & add Cef log * Remove SharedVariable, fix startup * Revert init theme change * Logging date not working? Fix WinformThemer designer error * Add Cef request hander, no more escaping main page * clean * fix * fix * clean * uh * Apply thememode at startup, fixes random user colours * Split database into files * Instance info remove empty lines * Open external VRC links with VRCX * Electron fixes * fix userdialog style * ohhhh * fix store * fix store * fix: load all group members after kicking a user * fix: world dialog favorite button style * fix: Clear VRCX Cache Timer input value * clean * Fix VR overlay * Fix VR overlay 2 * Fix Discord discord rich presence for RPC worlds * Clean up age verified user tags * Fix playerList being occupied after program reload * no `this` * Fix login stuck loading * writable: false * Hide dialogs on logout * add flush sync option * rm LOGIN event * rm LOGOUT event * remove duplicate event listeners * remove duplicate event listeners * clean * remove duplicate event listeners * clean * fix theme style * fix t * clearable * clean * fix ipcEvent * Small changes * Popcorn Palace support * Remove checkActiveFriends * Clean up * Fix dragEnterCef * Block API requests when not logged in * Clear state on login & logout * Fix worldDialog instances not updating * use <script setup> * Fix avatar change event, CheckGameRunning at startup * Fix image dragging * fix * Remove PWI * fix updateLoop * add webpack-dev-server to dev environment * rm unnecessary chunks * use <script setup> * webpack-dev-server changes * use <script setup> * use <script setup> * Fix UGC text size * Split login event * t * use <script setup> * fix * Update .gitignore and enable checkJs in jsconfig * fix i18n t * use <script setup> * use <script setup> * clean * global types * fix * use checkJs for debugging * Add watchState for login watchers * fix .vue template * type fixes * rm Vue.filter * Cef v138.0.170, VC++ 2022 * Settings fixes * Remove 'USER:CURRENT' * clean up 2FA callbacks * remove userApply * rm i18n import * notification handling to use notification store methods * refactor favorite handling to use favorite store methods and clean up event emissions * refactor moderation handling to use dedicated functions for player moderation events * refactor friend handling to use dedicated functions for friend events * Fix program startup, move lang init * Fix friend state * Fix status change error * Fix user notes diff * fix * rm group event * rm auth event * rm avatar event * clean * clean * getUser * getFriends * getFavoriteWorlds, getFavoriteAvatars * AvatarGalleryUpload btn style & package.json update * Fix friend requests * Apply user * Apply world * Fix note diff * Fix VR overlay * Fixes * Update build scripts * Apply avatar * Apply instance * Apply group * update hidden VRC+ badge * Fix sameInstance "private" * fix 502/504 API errors * fix 502/504 API errors * clean * Fix friend in same instance on orange showing twice in friends list * Add back in broken friend state repair methods * add types --------- Co-authored-by: Natsumi <cmcooper123@hotmail.com>
This commit is contained in:
@@ -1,28 +1,28 @@
|
||||
<template>
|
||||
<div v-if="menuActiveIndex === 'profile'" class="x-container">
|
||||
<div v-show="menuActiveIndex === 'profile'" class="x-container">
|
||||
<div class="options-container" style="margin-top: 0">
|
||||
<span class="header">{{ t('view.profile.profile.header') }}</span>
|
||||
<div class="x-friend-list" style="margin-top: 10px">
|
||||
<div class="x-friend-item" @click="showUserDialog(API.currentUser.id)">
|
||||
<div class="x-friend-item" @click="showUserDialog(currentUser.id)">
|
||||
<div class="avatar">
|
||||
<img v-lazy="userImage(API.currentUser, true)" />
|
||||
<img v-lazy="userImage(currentUser, true)" />
|
||||
</div>
|
||||
<div class="detail">
|
||||
<span class="name" v-text="API.currentUser.displayName"></span>
|
||||
<span class="extra" v-text="API.currentUser.username"></span>
|
||||
<span class="name" v-text="currentUser.displayName"></span>
|
||||
<span class="extra" v-text="currentUser.username"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="x-friend-item" style="cursor: default">
|
||||
<div class="detail">
|
||||
<span class="name">{{ t('view.profile.profile.last_activity') }}</span>
|
||||
<span class="extra">{{ API.currentUser.last_activity | formatDate('long') }}</span>
|
||||
<span class="extra">{{ formatDateFilter(currentUser.last_activity, 'long') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="x-friend-item" style="cursor: default">
|
||||
<div class="detail">
|
||||
<span class="name">{{ t('view.profile.profile.two_factor') }}</span>
|
||||
<span class="extra">{{
|
||||
API.currentUser.twoFactorAuthEnabled
|
||||
currentUser.twoFactorAuthEnabled
|
||||
? t('view.profile.profile.two_factor_enabled')
|
||||
: t('view.profile.profile.two_factor_disabled')
|
||||
}}</span>
|
||||
@@ -101,12 +101,12 @@
|
||||
icon="el-icon-refresh"
|
||||
circle
|
||||
style="margin-left: 5px"
|
||||
@click="API.getConfig()"></el-button>
|
||||
@click="getConfig"></el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="x-friend-list" style="margin-top: 10px">
|
||||
<div
|
||||
v-for="(link, item) in API.cachedConfig.downloadUrls"
|
||||
v-for="(link, item) in cachedConfig.downloadUrls"
|
||||
:key="item"
|
||||
class="x-friend-item"
|
||||
placement="top">
|
||||
@@ -150,7 +150,7 @@
|
||||
style="margin-left: 5px"
|
||||
@click="
|
||||
inviteMessageTable.visible = true;
|
||||
refreshInviteMessageTable('message');
|
||||
refreshInviteMessageTableData('message');
|
||||
"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip placement="top" :content="t('view.profile.clear_results_tooltip')" :disabled="hideTooltips">
|
||||
@@ -204,7 +204,7 @@
|
||||
style="margin-left: 5px"
|
||||
@click="
|
||||
inviteResponseMessageTable.visible = true;
|
||||
refreshInviteMessageTable('response');
|
||||
refreshInviteMessageTableData('response');
|
||||
"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip placement="top" :content="t('view.profile.clear_results_tooltip')" :disabled="hideTooltips">
|
||||
@@ -261,7 +261,7 @@
|
||||
style="margin-left: 5px"
|
||||
@click="
|
||||
inviteRequestMessageTable.visible = true;
|
||||
refreshInviteMessageTable('request');
|
||||
refreshInviteMessageTableData('request');
|
||||
"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip placement="top" :content="t('view.profile.clear_results_tooltip')" :disabled="hideTooltips">
|
||||
@@ -318,7 +318,7 @@
|
||||
style="margin-left: 5px"
|
||||
@click="
|
||||
inviteRequestResponseMessageTable.visible = true;
|
||||
refreshInviteMessageTable('requestResponse');
|
||||
refreshInviteMessageTableData('requestResponse');
|
||||
"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip placement="top" :content="t('view.profile.clear_results_tooltip')" :disabled="hideTooltips">
|
||||
@@ -371,7 +371,7 @@
|
||||
prop="updated_at"
|
||||
sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.updated_at | formatDate('long') }}</span>
|
||||
<span>{{ formatDateFilter(scope.row.updated_at, 'long') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -489,92 +489,56 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ProfileTab'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script setup>
|
||||
import { inject, ref, getCurrentInstance } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref, getCurrentInstance } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { inviteMessagesRequest, miscRequest, userRequest } from '../../api';
|
||||
import utils from '../../classes/utils';
|
||||
import { parseAvatarUrl } from '../../composables/avatar/utils';
|
||||
import { authRequest, miscRequest, userRequest } from '../../api';
|
||||
import {
|
||||
parseAvatarUrl,
|
||||
buildTreeData,
|
||||
openExternalLink,
|
||||
userImage,
|
||||
parseUserUrl,
|
||||
formatDateFilter
|
||||
} from '../../shared/utils';
|
||||
import { useAuthStore } from '../../stores';
|
||||
import DiscordNamesDialog from './dialogs/DiscordNamesDialog.vue';
|
||||
import ExportFriendsListDialog from './dialogs/ExportFriendsListDialog.vue';
|
||||
import ExportAvatarsListDialog from './dialogs/ExportAvatarsListDialog.vue';
|
||||
import {
|
||||
useAppearanceSettingsStore,
|
||||
useSearchStore,
|
||||
useFriendStore,
|
||||
useUserStore,
|
||||
useAvatarStore,
|
||||
useInviteStore,
|
||||
useGalleryStore,
|
||||
useUiStore
|
||||
} from '../../stores';
|
||||
|
||||
const { friends } = storeToRefs(useFriendStore());
|
||||
const { hideTooltips } = storeToRefs(useAppearanceSettingsStore());
|
||||
const { pastDisplayNameTable, currentUser } = storeToRefs(useUserStore());
|
||||
const { showUserDialog, lookupUser, getCurrentUser } = useUserStore();
|
||||
const { showAvatarDialog } = useAvatarStore();
|
||||
const { showEditInviteMessageDialog, refreshInviteMessageTableData } = useInviteStore();
|
||||
const {
|
||||
inviteMessageTable,
|
||||
inviteResponseMessageTable,
|
||||
inviteRequestMessageTable,
|
||||
inviteRequestResponseMessageTable
|
||||
} = storeToRefs(useInviteStore());
|
||||
const { showGalleryDialog } = useGalleryStore();
|
||||
const { menuActiveIndex } = storeToRefs(useUiStore());
|
||||
const { directAccessWorld } = useSearchStore();
|
||||
const { logout } = useAuthStore();
|
||||
const { cachedConfig } = storeToRefs(useAuthStore());
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { $prompt, $message } = getCurrentInstance().proxy;
|
||||
|
||||
const API = inject('API');
|
||||
const userImage = inject('userImage');
|
||||
const showUserDialog = inject('showUserDialog');
|
||||
const showAvatarDialog = inject('showAvatarDialog');
|
||||
const showGalleryDialog = inject('showGalleryDialog');
|
||||
const openExternalLink = inject('openExternalLink');
|
||||
|
||||
const props = defineProps({
|
||||
menuActiveIndex: {
|
||||
type: String,
|
||||
default: 'profile'
|
||||
},
|
||||
hideTooltips: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
inviteMessageTable: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
visible: false,
|
||||
data: []
|
||||
})
|
||||
},
|
||||
inviteResponseMessageTable: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
visible: false,
|
||||
data: []
|
||||
})
|
||||
},
|
||||
inviteRequestMessageTable: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
visible: false,
|
||||
data: []
|
||||
})
|
||||
},
|
||||
inviteRequestResponseMessageTable: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
visible: false,
|
||||
data: []
|
||||
})
|
||||
},
|
||||
pastDisplayNameTable: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
visible: false,
|
||||
data: []
|
||||
})
|
||||
},
|
||||
friends: {
|
||||
type: Map,
|
||||
default: () => new Map()
|
||||
},
|
||||
directAccessWorld: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
parseUserUrl: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
});
|
||||
const emit = defineEmits(['logout', 'lookupUser', 'showEditInviteMessageDialog']);
|
||||
|
||||
const vrchatCredit = ref(null);
|
||||
const configTreeData = ref([]);
|
||||
const currentUserTreeData = ref([]);
|
||||
@@ -588,18 +552,13 @@
|
||||
|
||||
function getVisits() {
|
||||
miscRequest.getVisits().then((args) => {
|
||||
// API.$on('VISITS')
|
||||
visits.value = args.json;
|
||||
});
|
||||
}
|
||||
|
||||
function getVRChatCredits() {
|
||||
// API.$on('VRCCREDITS')
|
||||
miscRequest.getVRChatCredits().then((args) => (vrchatCredit.value = args.json?.balance));
|
||||
}
|
||||
function logout() {
|
||||
emit('logout');
|
||||
}
|
||||
|
||||
function showDiscordNamesDialog() {
|
||||
discordNamesDialogVisible.value = true;
|
||||
@@ -621,7 +580,7 @@
|
||||
inputErrorMessage: t('prompt.direct_access_username.input_error'),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm' && instance.inputValue) {
|
||||
emit('lookupUser', {
|
||||
lookupUser({
|
||||
displayName: instance.inputValue
|
||||
});
|
||||
}
|
||||
@@ -639,7 +598,7 @@
|
||||
if (action === 'confirm' && instance.inputValue) {
|
||||
const testUrl = instance.inputValue.substring(0, 15);
|
||||
if (testUrl === 'https://vrchat.') {
|
||||
const userId = this.parseUserUrl(instance.inputValue);
|
||||
const userId = parseUserUrl(instance.inputValue);
|
||||
if (userId) {
|
||||
showUserDialog(userId);
|
||||
} else {
|
||||
@@ -664,7 +623,7 @@
|
||||
inputErrorMessage: t('prompt.direct_access_world_id.input_error'),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm' && instance.inputValue) {
|
||||
if (!props.directAccessWorld(instance.inputValue)) {
|
||||
if (!directAccessWorld(instance.inputValue)) {
|
||||
$message({
|
||||
message: t('prompt.direct_access_world_id.message.error'),
|
||||
type: 'error'
|
||||
@@ -685,7 +644,7 @@
|
||||
if (action === 'confirm' && instance.inputValue) {
|
||||
const testUrl = instance.inputValue.substring(0, 15);
|
||||
if (testUrl === 'https://vrchat.') {
|
||||
const avatarId = props.parseAvatarUrl(instance.inputValue);
|
||||
const avatarId = parseAvatarUrl(instance.inputValue);
|
||||
if (avatarId) {
|
||||
showAvatarDialog(avatarId);
|
||||
} else {
|
||||
@@ -701,25 +660,21 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
function showEditInviteMessageDialog(messageType, inviteMessage) {
|
||||
emit('showEditInviteMessageDialog', messageType, inviteMessage);
|
||||
}
|
||||
function refreshInviteMessageTable(messageType) {
|
||||
inviteMessagesRequest.refreshInviteMessageTableData(messageType);
|
||||
async function getConfig() {
|
||||
await authRequest.getConfig();
|
||||
}
|
||||
async function refreshConfigTreeData() {
|
||||
await API.getConfig();
|
||||
configTreeData.value = utils.buildTreeData(API.cachedConfig);
|
||||
await getConfig();
|
||||
configTreeData.value = buildTreeData(cachedConfig.value);
|
||||
}
|
||||
async function refreshCurrentUserTreeData() {
|
||||
await API.getCurrentUser();
|
||||
currentUserTreeData.value = utils.buildTreeData(API.currentUser);
|
||||
await getCurrentUser();
|
||||
currentUserTreeData.value = buildTreeData(currentUser.value);
|
||||
}
|
||||
function getCurrentUserFeedback() {
|
||||
userRequest.getUserFeedback({ userId: API.currentUser.id }).then((args) => {
|
||||
// API.$on('USER:FEEDBACK')
|
||||
if (args.params.userId === API.currentUser.id) {
|
||||
currentUserFeedbackData.value = utils.buildTreeData(args.json);
|
||||
userRequest.getUserFeedback({ userId: currentUser.value.id }).then((args) => {
|
||||
if (args.params.userId === currentUser.value.id) {
|
||||
currentUserFeedbackData.value = buildTreeData(args.json);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,12 +20,13 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, inject } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
|
||||
const API = inject('API');
|
||||
import { useUserStore } from '../../../stores';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const props = defineProps({
|
||||
discordNamesDialogVisible: {
|
||||
@@ -52,7 +53,7 @@
|
||||
const discordNamesContent = ref('');
|
||||
|
||||
function showDiscordNamesContent() {
|
||||
const { friends } = API.currentUser;
|
||||
const { friends } = currentUser.value;
|
||||
if (Array.isArray(friends) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -27,29 +27,23 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, inject, getCurrentInstance } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getCurrentInstance, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { inviteMessagesRequest } from '../../../api';
|
||||
import { useInviteStore } from '../../../stores';
|
||||
|
||||
const { t } = useI18n();
|
||||
const instance = getCurrentInstance();
|
||||
const $message = instance.proxy.$message;
|
||||
const API = inject('API');
|
||||
|
||||
const props = defineProps({
|
||||
editInviteMessageDialog: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
visible: false,
|
||||
newMessage: ''
|
||||
})
|
||||
}
|
||||
});
|
||||
const inviteStore = useInviteStore();
|
||||
const { editInviteMessageDialog } = storeToRefs(inviteStore);
|
||||
|
||||
const message = ref('');
|
||||
|
||||
watch(
|
||||
() => props.editInviteMessageDialog,
|
||||
() => editInviteMessageDialog.value,
|
||||
(newVal) => {
|
||||
if (newVal && newVal.visible) {
|
||||
message.value = newVal.newMessage;
|
||||
@@ -58,10 +52,8 @@
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const emit = defineEmits(['update:editInviteMessageDialog']);
|
||||
|
||||
function saveEditInviteMessage() {
|
||||
const D = props.editInviteMessageDialog;
|
||||
const D = editInviteMessageDialog.value;
|
||||
D.visible = false;
|
||||
if (D.inviteMessage.message !== message.value) {
|
||||
const slot = D.inviteMessage.slot;
|
||||
@@ -75,7 +67,6 @@
|
||||
throw err;
|
||||
})
|
||||
.then((args) => {
|
||||
API.$emit(`INVITE:${messageType.toUpperCase()}`, args);
|
||||
if (args.json[slot].message === D.inviteMessage.message) {
|
||||
$message({
|
||||
message: "VRChat API didn't update message, try again",
|
||||
@@ -91,6 +82,6 @@
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
emit('update:editInviteMessageDialog', { ...props.editInviteMessageDialog, visible: false });
|
||||
editInviteMessageDialog.value.visible = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<safe-dialog :visible.sync="isVisible" :title="$t('dialog.export_own_avatars.header')" width="650px">
|
||||
<safe-dialog :visible.sync="isVisible" :title="t('dialog.export_own_avatars.header')" width="650px">
|
||||
<el-input
|
||||
v-model="exportAvatarsListCsv"
|
||||
v-loading="loading"
|
||||
@@ -13,87 +13,94 @@
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { avatarRequest } from '../../../api';
|
||||
import { processBulk } from '../../../service/request';
|
||||
import { useAvatarStore, useUserStore } from '../../../stores';
|
||||
|
||||
export default {
|
||||
name: 'ExportAvatarsListDialog',
|
||||
inject: ['API'],
|
||||
props: {
|
||||
isExportAvatarsListDialogVisible: Boolean
|
||||
const { t } = useI18n();
|
||||
|
||||
const { cachedAvatars } = storeToRefs(useAvatarStore());
|
||||
const { applyAvatar } = useAvatarStore();
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const props = defineProps({
|
||||
isExportAvatarsListDialogVisible: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const exportAvatarsListCsv = ref('');
|
||||
const loading = ref(false);
|
||||
|
||||
const isVisible = computed({
|
||||
get() {
|
||||
return props.isExportAvatarsListDialogVisible;
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
exportAvatarsListCsv: '',
|
||||
loading: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isVisible: {
|
||||
get() {
|
||||
return this.isExportAvatarsListDialogVisible;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:is-export-avatars-list-dialog-visible', value);
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isExportAvatarsListDialogVisible(value) {
|
||||
if (value) {
|
||||
this.initExportAvatarsListDialog();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initExportAvatarsListDialog() {
|
||||
this.loading = true;
|
||||
for (const ref of this.API.cachedAvatars.values()) {
|
||||
if (ref.authorId === this.API.currentUser.id) {
|
||||
this.API.cachedAvatars.delete(ref.id);
|
||||
}
|
||||
}
|
||||
const params = {
|
||||
n: 50,
|
||||
offset: 0,
|
||||
sort: 'updated',
|
||||
order: 'descending',
|
||||
releaseStatus: 'all',
|
||||
user: 'me'
|
||||
};
|
||||
const map = new Map();
|
||||
this.API.bulk({
|
||||
fn: avatarRequest.getAvatars,
|
||||
N: -1,
|
||||
params,
|
||||
handle: (args) => {
|
||||
for (const json of args.json) {
|
||||
const $ref = this.API.cachedAvatars.get(json.id);
|
||||
if (typeof $ref !== 'undefined') {
|
||||
map.set($ref.id, $ref);
|
||||
}
|
||||
}
|
||||
},
|
||||
done: () => {
|
||||
const avatars = Array.from(map.values());
|
||||
if (Array.isArray(avatars) === false) {
|
||||
return;
|
||||
}
|
||||
const lines = ['AvatarID,AvatarName'];
|
||||
const _ = function (str) {
|
||||
if (/[\x00-\x1f,"]/.test(str) === true) {
|
||||
return `"${str.replace(/"/g, '""')}"`;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
for (const avatar of avatars) {
|
||||
lines.push(`${_(avatar.id)},${_(avatar.name)}`);
|
||||
}
|
||||
this.exportAvatarsListCsv = lines.join('\n');
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
set(value) {
|
||||
emit('update:isExportAvatarsListDialogVisible', value);
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:isExportAvatarsListDialogVisible']);
|
||||
|
||||
watch(
|
||||
() => props.isExportAvatarsListDialogVisible,
|
||||
(value) => {
|
||||
if (value) {
|
||||
initExportAvatarsListDialog();
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
function initExportAvatarsListDialog() {
|
||||
loading.value = true;
|
||||
for (const ref of cachedAvatars.value.values()) {
|
||||
if (ref.authorId === currentUser.value.id) {
|
||||
cachedAvatars.value.delete(ref.id);
|
||||
}
|
||||
}
|
||||
const params = {
|
||||
n: 50,
|
||||
offset: 0,
|
||||
sort: 'updated',
|
||||
order: 'descending',
|
||||
releaseStatus: 'all',
|
||||
user: 'me'
|
||||
};
|
||||
const map = new Map();
|
||||
processBulk({
|
||||
fn: avatarRequest.getAvatars,
|
||||
N: -1,
|
||||
params,
|
||||
handle: (args) => {
|
||||
for (const json of args.json) {
|
||||
const ref = applyAvatar(json);
|
||||
map.set(ref.id, ref);
|
||||
}
|
||||
},
|
||||
done: () => {
|
||||
const avatars = Array.from(map.values());
|
||||
if (Array.isArray(avatars) === false) {
|
||||
return;
|
||||
}
|
||||
const lines = ['AvatarID,AvatarName'];
|
||||
const _ = function (str) {
|
||||
if (/[\x00-\x1f,"]/.test(str) === true) {
|
||||
return `"${str.replace(/"/g, '""')}"`;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
for (const avatar of avatars) {
|
||||
lines.push(`${_(avatar.id)},${_(avatar.name)}`);
|
||||
}
|
||||
exportAvatarsListCsv.value = lines.join('\n');
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<safe-dialog :title="$t('dialog.export_friends_list.header')" :visible.sync="isVisible" width="650px">
|
||||
<safe-dialog :title="t('dialog.export_friends_list.header')" :visible.sync="isVisible" width="650px">
|
||||
<el-tabs type="card">
|
||||
<el-tab-pane :label="$t('dialog.export_friends_list.csv')">
|
||||
<el-tab-pane :label="t('dialog.export_friends_list.csv')">
|
||||
<el-input
|
||||
v-model="exportFriendsListCsv"
|
||||
type="textarea"
|
||||
@@ -12,7 +12,7 @@
|
||||
style="margin-top: 15px"
|
||||
@click.native="$event.target.tagName === 'TEXTAREA' && $event.target.select()" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('dialog.export_friends_list.json')">
|
||||
<el-tab-pane :label="t('dialog.export_friends_list.json')">
|
||||
<el-input
|
||||
v-model="exportFriendsListJson"
|
||||
type="textarea"
|
||||
@@ -27,61 +27,71 @@
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ExportFriendsListDialog',
|
||||
inject: ['API'],
|
||||
props: {
|
||||
friends: Map,
|
||||
isExportFriendsListDialogVisible: Boolean
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import { useUserStore } from '../../../stores';
|
||||
|
||||
const props = defineProps({
|
||||
friends: {
|
||||
type: Map,
|
||||
required: true
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
exportFriendsListCsv: '',
|
||||
exportFriendsListJson: ''
|
||||
};
|
||||
isExportFriendsListDialogVisible: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
const emit = defineEmits(['update:isExportFriendsListDialogVisible']);
|
||||
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const exportFriendsListCsv = ref('');
|
||||
const exportFriendsListJson = ref('');
|
||||
|
||||
const isVisible = computed({
|
||||
get() {
|
||||
return props.isExportFriendsListDialogVisible;
|
||||
},
|
||||
computed: {
|
||||
isVisible: {
|
||||
get() {
|
||||
return this.isExportFriendsListDialogVisible;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:is-export-friends-list-dialog-visible', value);
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isExportFriendsListDialogVisible(value) {
|
||||
if (value) {
|
||||
this.initExportFriendsListDialog();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initExportFriendsListDialog() {
|
||||
const { friends } = this.API.currentUser;
|
||||
if (Array.isArray(friends) === false) {
|
||||
return;
|
||||
}
|
||||
const lines = ['UserID,DisplayName,Memo'];
|
||||
const _ = function (str) {
|
||||
if (/[\x00-\x1f,"]/.test(str) === true) {
|
||||
return `"${str.replace(/"/g, '""')}"`;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
const friendsList = [];
|
||||
for (const userId of friends) {
|
||||
const ref = this.friends.get(userId);
|
||||
const name = (typeof ref !== 'undefined' && ref.name) || '';
|
||||
const memo = (typeof ref !== 'undefined' && ref.memo.replace(/\n/g, ' ')) || '';
|
||||
lines.push(`${_(userId)},${_(name)},${_(memo)}`);
|
||||
friendsList.push(userId);
|
||||
}
|
||||
this.exportFriendsListJson = JSON.stringify({ friends: friendsList }, null, 4);
|
||||
this.exportFriendsListCsv = lines.join('\n');
|
||||
set(value) {
|
||||
emit('update:isExportFriendsListDialogVisible', value);
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.isExportFriendsListDialogVisible,
|
||||
(value) => {
|
||||
if (value) {
|
||||
initExportFriendsListDialog();
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
function initExportFriendsListDialog() {
|
||||
const { friends } = currentUser.value;
|
||||
if (Array.isArray(friends) === false) {
|
||||
return;
|
||||
}
|
||||
const lines = ['UserID,DisplayName,Memo'];
|
||||
const _ = function (str) {
|
||||
if (/[\x00-\x1f,"]/.test(str) === true) {
|
||||
return `"${str.replace(/"/g, '""')}"`;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
const friendsList = [];
|
||||
for (const userId of friends) {
|
||||
const ref = props.friends.get(userId);
|
||||
const name = (typeof ref !== 'undefined' && ref.name) || '';
|
||||
const memo = (typeof ref !== 'undefined' && ref.memo.replace(/\n/g, ' ')) || '';
|
||||
lines.push(`${_(userId)},${_(name)},${_(memo)}`);
|
||||
friendsList.push(userId);
|
||||
}
|
||||
exportFriendsListJson.value = JSON.stringify({ friends: friendsList }, null, 4);
|
||||
exportFriendsListCsv.value = lines.join('\n');
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user