mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-19 14:53:50 +02:00
refactor: favorites tab (#1177)
* refactor: favorites tab * rm useless characters
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div id="aside" class="x-aside-container">
|
||||
<div id="aside" class="x-aside-container" v-show="isSideBarTabShow">
|
||||
<div style="display: flex; align-items: baseline">
|
||||
<el-select
|
||||
value=""
|
||||
@@ -125,6 +125,8 @@
|
||||
hideNicknames: Boolean,
|
||||
isHideFriendsInSameInstance: Boolean,
|
||||
|
||||
isSideBarTabShow: Boolean,
|
||||
|
||||
quickSearchRemoteMethod: Function,
|
||||
quickSearchItems: Array,
|
||||
onlineFriendCount: Number,
|
||||
|
||||
223
src/views/dialogs/AvatarExportDialog.vue
Normal file
223
src/views/dialogs/AvatarExportDialog.vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<el-dialog :visible.sync="isDialogVisible" :title="$t('dialog.avatar_export.header')" width="650px">
|
||||
<el-checkbox-group
|
||||
v-model="exportSelectedOptions"
|
||||
style="margin-bottom: 10px"
|
||||
@change="updateAvatarExportDialog()">
|
||||
<template v-for="option in exportSelectOptions">
|
||||
<el-checkbox :key="option.value" :label="option.label"></el-checkbox>
|
||||
</template>
|
||||
</el-checkbox-group>
|
||||
|
||||
<el-dropdown trigger="click" size="small" @click.native.stop>
|
||||
<el-button size="mini">
|
||||
<span v-if="avatarExportFavoriteGroup">
|
||||
{{ avatarExportFavoriteGroup.displayName }} ({{ avatarExportFavoriteGroup.count }}/{{
|
||||
avatarExportFavoriteGroup.capacity
|
||||
}})
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
<span v-else>
|
||||
All Favorites
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item style="display: block; margin: 10px 0" @click.native="selectAvatarExportGroup(null)">
|
||||
All Favorites
|
||||
</el-dropdown-item>
|
||||
<template v-for="groupAPI in API.favoriteAvatarGroups">
|
||||
<el-dropdown-item
|
||||
:key="groupAPI.name"
|
||||
style="display: block; margin: 10px 0"
|
||||
@click.native="selectAvatarExportGroup(groupAPI)">
|
||||
{{ groupAPI.displayName }} ({{ groupAPI.count }}/{{ groupAPI.capacity }})
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
<el-dropdown trigger="click" size="small" style="margin-left: 10px" @click.native.stop>
|
||||
<el-button size="mini">
|
||||
<span v-if="avatarExportLocalFavoriteGroup">
|
||||
{{ avatarExportLocalFavoriteGroup }} ({{
|
||||
getLocalAvatarFavoriteGroupLength(avatarExportLocalFavoriteGroup)
|
||||
}})
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
<span v-else>
|
||||
Select Group
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
style="display: block; margin: 10px 0"
|
||||
@click.native="selectAvatarExportLocalGroup(null)">
|
||||
None
|
||||
</el-dropdown-item>
|
||||
<template v-for="group in localAvatarFavoriteGroups">
|
||||
<el-dropdown-item
|
||||
:key="group"
|
||||
style="display: block; margin: 10px 0"
|
||||
@click.native="selectAvatarExportLocalGroup(group)">
|
||||
{{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<br />
|
||||
<el-input
|
||||
v-model="avatarExportContent"
|
||||
type="textarea"
|
||||
size="mini"
|
||||
rows="15"
|
||||
resize="none"
|
||||
readonly
|
||||
style="margin-top: 15px"
|
||||
@click.native="handleCopyAvatarExportData"></el-input>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AvatarExportDialog',
|
||||
inject: ['API'],
|
||||
props: {
|
||||
avatarExportDialogVisible: Boolean,
|
||||
favoriteAvatars: Array,
|
||||
localAvatarFavoriteGroups: Array,
|
||||
localAvatarFavorites: Object,
|
||||
localAvatarFavoritesList: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
avatarExportContent: '',
|
||||
avatarExportFavoriteGroup: null,
|
||||
avatarExportLocalFavoriteGroup: null,
|
||||
exportSelectedOptions: ['ID', 'Name'],
|
||||
exportSelectOptions: [
|
||||
{ label: 'ID', value: 'id' },
|
||||
{ label: 'Name', value: 'name' },
|
||||
{ label: 'Author ID', value: 'authorId' },
|
||||
{ label: 'Author Name', value: 'authorName' },
|
||||
{ label: 'Thumbnail', value: 'thumbnailImageUrl' }
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isDialogVisible: {
|
||||
get() {
|
||||
return this.avatarExportDialogVisible;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:avatar-export-dialog-visible', value);
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
avatarExportDialogVisible(visible) {
|
||||
if (visible) {
|
||||
this.showAvatarExportDialog();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showAvatarExportDialog() {
|
||||
this.avatarExportFavoriteGroup = null;
|
||||
this.avatarExportLocalFavoriteGroup = null;
|
||||
this.updateAvatarExportDialog();
|
||||
},
|
||||
handleCopyAvatarExportData(event) {
|
||||
if (event.target.tagName === 'TEXTAREA') {
|
||||
event.target.select();
|
||||
}
|
||||
navigator.clipboard
|
||||
.writeText(this.avatarExportContent)
|
||||
.then(() => {
|
||||
this.$message({
|
||||
message: 'Copied successfully!',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Copy failed:', err);
|
||||
this.$message.error('Copy failed!');
|
||||
});
|
||||
},
|
||||
updateAvatarExportDialog() {
|
||||
const formatter = function (str) {
|
||||
if (/[\x00-\x1f,"]/.test(str) === true) {
|
||||
return `"${str.replace(/"/g, '""')}"`;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
const propsForQuery = this.exportSelectOptions
|
||||
.filter((option) => this.exportSelectedOptions.includes(option.label))
|
||||
.map((option) => option.value);
|
||||
|
||||
function resText(ref) {
|
||||
let resArr = [];
|
||||
propsForQuery.forEach((e) => {
|
||||
resArr.push(formatter(ref?.[e]));
|
||||
});
|
||||
return resArr.join(',');
|
||||
}
|
||||
|
||||
const lines = [this.exportSelectedOptions.join(',')];
|
||||
|
||||
if (this.avatarExportFavoriteGroup) {
|
||||
this.API.favoriteAvatarGroups.forEach((group) => {
|
||||
if (!this.avatarExportFavoriteGroup || this.avatarExportFavoriteGroup === group) {
|
||||
this.favoriteAvatars.forEach((ref) => {
|
||||
if (group.key === ref.groupKey) {
|
||||
lines.push(resText(ref.ref));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (this.avatarExportLocalFavoriteGroup) {
|
||||
const favoriteGroup = this.localAvatarFavorites[this.avatarExportLocalFavoriteGroup];
|
||||
if (!favoriteGroup) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < favoriteGroup.length; ++i) {
|
||||
const ref = favoriteGroup[i];
|
||||
lines.push(resText(ref));
|
||||
}
|
||||
} else {
|
||||
// export all
|
||||
this.favoriteAvatars.forEach((ref) => {
|
||||
lines.push(resText(ref.ref));
|
||||
});
|
||||
for (let i = 0; i < this.localAvatarFavoritesList.length; ++i) {
|
||||
const avatarId = this.localAvatarFavoritesList[i];
|
||||
const ref = this.API.cachedAvatars.get(avatarId);
|
||||
if (typeof ref !== 'undefined') {
|
||||
lines.push(resText(ref));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.avatarExportContent = lines.join('\n');
|
||||
},
|
||||
selectAvatarExportGroup(group) {
|
||||
this.avatarExportFavoriteGroup = group;
|
||||
this.avatarExportLocalFavoriteGroup = null;
|
||||
this.updateAvatarExportDialog();
|
||||
},
|
||||
selectAvatarExportLocalGroup(group) {
|
||||
this.avatarExportLocalFavoriteGroup = group;
|
||||
this.avatarExportFavoriteGroup = null;
|
||||
this.updateAvatarExportDialog();
|
||||
},
|
||||
getLocalAvatarFavoriteGroupLength(group) {
|
||||
const favoriteGroup = this.localAvatarFavorites[group];
|
||||
if (!favoriteGroup) {
|
||||
return 0;
|
||||
}
|
||||
return favoriteGroup.length;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
127
src/views/dialogs/FriendExportDialog.vue
Normal file
127
src/views/dialogs/FriendExportDialog.vue
Normal file
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:visible.sync="isDialogVisible"
|
||||
class="x-dialog"
|
||||
:title="$t('dialog.friend_export.header')"
|
||||
width="650px"
|
||||
destroy-on-close>
|
||||
<el-dropdown trigger="click" size="small" @click.native.stop>
|
||||
<el-button size="mini">
|
||||
<span v-if="friendExportFavoriteGroup">
|
||||
{{ friendExportFavoriteGroup.displayName }} ({{ friendExportFavoriteGroup.count }}/{{
|
||||
friendExportFavoriteGroup.capacity
|
||||
}})
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
<span v-else>All Favorites <i class="el-icon-arrow-down el-icon--right"></i></span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item style="display: block; margin: 10px 0" @click.native="selectFriendExportGroup(null)">
|
||||
All Favorites
|
||||
</el-dropdown-item>
|
||||
<template v-for="groupAPI in API.favoriteFriendGroups">
|
||||
<el-dropdown-item
|
||||
:key="groupAPI.name"
|
||||
style="display: block; margin: 10px 0"
|
||||
@click.native="selectFriendExportGroup(groupAPI)">
|
||||
{{ groupAPI.displayName }} ({{ groupAPI.count }}/{{ groupAPI.capacity }})
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<br />
|
||||
<el-input
|
||||
v-model="friendExportContent"
|
||||
type="textarea"
|
||||
size="mini"
|
||||
rows="15"
|
||||
resize="none"
|
||||
readonly
|
||||
style="margin-top: 15px"
|
||||
@click.native="handleCopyFriendExportData"></el-input>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FriendExportDialog',
|
||||
inject: ['API'],
|
||||
props: {
|
||||
friendExportDialogVisible: Boolean,
|
||||
favoriteFriends: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
friendExportFavoriteGroup: null,
|
||||
friendExportContent: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isDialogVisible: {
|
||||
get() {
|
||||
return this.friendExportDialogVisible;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:friend-export-dialog-visible', value);
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
friendExportDialogVisible(value) {
|
||||
if (value) {
|
||||
this.showFriendExportDialog();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showFriendExportDialog() {
|
||||
this.friendExportFavoriteGroup = null;
|
||||
this.updateFriendExportDialog();
|
||||
},
|
||||
|
||||
handleCopyFriendExportData(event) {
|
||||
if (event.target.tagName === 'TEXTAREA') {
|
||||
event.target.select();
|
||||
}
|
||||
navigator.clipboard
|
||||
.writeText(this.friendExportContent)
|
||||
.then(() => {
|
||||
this.$message({
|
||||
message: 'Copied successfully!',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Copy failed:', err);
|
||||
this.$message.error('Copy failed!');
|
||||
});
|
||||
},
|
||||
|
||||
updateFriendExportDialog() {
|
||||
const _ = function (str) {
|
||||
if (/[\x00-\x1f,"]/.test(str) === true) {
|
||||
return `"${str.replace(/"/g, '""')}"`;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
const lines = ['UserID,Name'];
|
||||
this.API.favoriteFriendGroups.forEach((group) => {
|
||||
if (!this.friendExportFavoriteGroup || this.friendExportFavoriteGroup === group) {
|
||||
this.favoriteFriends.forEach((ref) => {
|
||||
if (group.key === ref.groupKey) {
|
||||
lines.push(`${_(ref.id)},${_(ref.name)}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
this.friendExportContent = lines.join('\n');
|
||||
},
|
||||
|
||||
selectFriendExportGroup(group) {
|
||||
this.friendExportFavoriteGroup = group;
|
||||
this.updateFriendExportDialog();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -862,7 +862,7 @@
|
||||
memo
|
||||
});
|
||||
} else {
|
||||
database.deleteWorldMemo(id);
|
||||
database.deleteWorldMemo(worldId);
|
||||
}
|
||||
},
|
||||
showPreviousInstancesWorldDialog(world) {
|
||||
|
||||
231
src/views/dialogs/WorldExportDialog.vue
Normal file
231
src/views/dialogs/WorldExportDialog.vue
Normal file
@@ -0,0 +1,231 @@
|
||||
<template>
|
||||
<el-dialog :visible.sync="isDialogVisible" :title="$t('dialog.world_export.header')" width="650px">
|
||||
<el-checkbox-group
|
||||
v-model="exportSelectedOptions"
|
||||
style="margin-bottom: 10px"
|
||||
@change="updateWorldExportDialog">
|
||||
<template v-for="option in exportSelectOptions">
|
||||
<el-checkbox :key="option.value" :label="option.label"></el-checkbox>
|
||||
</template>
|
||||
</el-checkbox-group>
|
||||
|
||||
<el-dropdown trigger="click" size="small" @click.native.stop>
|
||||
<el-button size="mini">
|
||||
<span v-if="worldExportFavoriteGroup">
|
||||
{{ worldExportFavoriteGroup.displayName }} ({{ worldExportFavoriteGroup.count }}/{{
|
||||
worldExportFavoriteGroup.capacity
|
||||
}})
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
<span v-else>
|
||||
All Favorites
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item style="display: block; margin: 10px 0" @click.native="selectWorldExportGroup(null)">
|
||||
None
|
||||
</el-dropdown-item>
|
||||
<template v-for="groupAPI in API.favoriteWorldGroups">
|
||||
<el-dropdown-item
|
||||
:key="groupAPI.name"
|
||||
style="display: block; margin: 10px 0"
|
||||
@click.native="selectWorldExportGroup(groupAPI)">
|
||||
{{ groupAPI.displayName }} ({{ groupAPI.count }}/{{ groupAPI.capacity }})
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
<el-dropdown trigger="click" size="small" style="margin-left: 10px" @click.native.stop>
|
||||
<el-button size="mini">
|
||||
<span v-if="worldExportLocalFavoriteGroup">
|
||||
{{ worldExportLocalFavoriteGroup }} ({{
|
||||
getLocalWorldFavoriteGroupLength(worldExportLocalFavoriteGroup)
|
||||
}})
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
<span v-else>
|
||||
Select Group
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
style="display: block; margin: 10px 0"
|
||||
@click.native="selectWorldExportLocalGroup(null)">
|
||||
None
|
||||
</el-dropdown-item>
|
||||
<template v-for="group in localWorldFavoriteGroups">
|
||||
<el-dropdown-item
|
||||
:key="group"
|
||||
style="display: block; margin: 10px 0"
|
||||
@click.native="selectWorldExportLocalGroup(group)">
|
||||
{{ group }} ({{ localWorldFavorites[group].length }})
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
<br />
|
||||
|
||||
<el-input
|
||||
v-model="worldExportContent"
|
||||
type="textarea"
|
||||
size="mini"
|
||||
rows="15"
|
||||
resize="none"
|
||||
readonly
|
||||
style="margin-top: 15px"
|
||||
@click.native="handleCopyWorldExportData"></el-input>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'WorldExportDialog',
|
||||
inject: ['API'],
|
||||
props: {
|
||||
favoriteWorlds: Array,
|
||||
worldExportDialogVisible: Boolean,
|
||||
localWorldFavorites: Object,
|
||||
localWorldFavoriteGroups: Array,
|
||||
localWorldFavoritesList: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
worldExportContent: '',
|
||||
worldExportFavoriteGroup: null,
|
||||
worldExportLocalFavoriteGroup: null,
|
||||
// Storage of selected filtering options for model and world export
|
||||
exportSelectedOptions: ['ID', 'Name'],
|
||||
exportSelectOptions: [
|
||||
{ label: 'ID', value: 'id' },
|
||||
{ label: 'Name', value: 'name' },
|
||||
{ label: 'Author ID', value: 'authorId' },
|
||||
{ label: 'Author Name', value: 'authorName' },
|
||||
{ label: 'Thumbnail', value: 'thumbnailImageUrl' }
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isDialogVisible: {
|
||||
get() {
|
||||
return this.worldExportDialogVisible;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:world-export-dialog-visible', value);
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
worldExportDialogVisible(value) {
|
||||
if (value) {
|
||||
this.showWorldExportDialog();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showWorldExportDialog() {
|
||||
this.worldExportFavoriteGroup = null;
|
||||
this.worldExportLocalFavoriteGroup = null;
|
||||
this.updateWorldExportDialog();
|
||||
},
|
||||
|
||||
handleCopyWorldExportData(event) {
|
||||
if (event.target.tagName === 'TEXTAREA') {
|
||||
event.target.select();
|
||||
}
|
||||
navigator.clipboard
|
||||
.writeText(this.worldExportContent)
|
||||
.then(() => {
|
||||
this.$message({
|
||||
message: 'Copied successfully!',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Copy failed:', err);
|
||||
this.$message.error('Copy failed!');
|
||||
});
|
||||
},
|
||||
|
||||
updateWorldExportDialog() {
|
||||
const formatter = function (str) {
|
||||
if (/[\x00-\x1f,"]/.test(str) === true) {
|
||||
return `"${str.replace(/"/g, '""')}"`;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
const propsForQuery = this.exportSelectOptions
|
||||
.filter((option) => this.exportSelectedOptions.includes(option.label))
|
||||
.map((option) => option.value);
|
||||
|
||||
function resText(ref) {
|
||||
let resArr = [];
|
||||
propsForQuery.forEach((e) => {
|
||||
resArr.push(formatter(ref?.[e]));
|
||||
});
|
||||
return resArr.join(',');
|
||||
}
|
||||
|
||||
const lines = [this.exportSelectedOptions.join(',')];
|
||||
|
||||
if (this.worldExportFavoriteGroup) {
|
||||
this.API.favoriteWorldGroups.forEach((group) => {
|
||||
if (this.worldExportFavoriteGroup === group) {
|
||||
this.favoriteWorlds.forEach((ref) => {
|
||||
if (group.key === ref.groupKey) {
|
||||
lines.push(resText(ref.ref));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (this.worldExportLocalFavoriteGroup) {
|
||||
const favoriteGroup = this.localWorldFavorites[this.worldExportLocalFavoriteGroup];
|
||||
if (!favoriteGroup) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < favoriteGroup.length; ++i) {
|
||||
const ref = favoriteGroup[i];
|
||||
lines.push(resText(ref));
|
||||
}
|
||||
} else {
|
||||
// export all
|
||||
this.favoriteWorlds.forEach((ref) => {
|
||||
lines.push(resText(ref.ref));
|
||||
});
|
||||
for (let i = 0; i < this.localWorldFavoritesList.length; ++i) {
|
||||
const worldId = this.localWorldFavoritesList[i];
|
||||
const ref = this.API.cachedWorlds.get(worldId);
|
||||
if (typeof ref !== 'undefined') {
|
||||
lines.push(resText(ref));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.worldExportContent = lines.join('\n');
|
||||
},
|
||||
|
||||
selectWorldExportGroup(group) {
|
||||
this.worldExportFavoriteGroup = group;
|
||||
this.worldExportLocalFavoriteGroup = null;
|
||||
this.updateWorldExportDialog();
|
||||
},
|
||||
|
||||
selectWorldExportLocalGroup(group) {
|
||||
this.worldExportLocalFavoriteGroup = group;
|
||||
this.worldExportFavoriteGroup = null;
|
||||
this.updateWorldExportDialog();
|
||||
},
|
||||
getLocalWorldFavoriteGroupLength(group) {
|
||||
const favoriteGroup = this.localWorldFavorites[group];
|
||||
if (!favoriteGroup) {
|
||||
return 0;
|
||||
}
|
||||
return favoriteGroup.length;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
331
src/views/tabs/Favorites.vue
Normal file
331
src/views/tabs/Favorites.vue
Normal file
@@ -0,0 +1,331 @@
|
||||
<template>
|
||||
<div v-show="menuActiveIndex === 'favorite'" class="x-container">
|
||||
<div style="font-size: 13px; position: absolute; display: flex; right: 0; z-index: 1; margin-right: 15px">
|
||||
<div v-if="editFavoritesMode" style="display: inline-block; margin-right: 10px">
|
||||
<el-button size="small" @click="clearBulkFavoriteSelection">{{ $t('view.favorite.clear') }}</el-button>
|
||||
<el-button size="small" @click="bulkCopyFavoriteSelection">{{ $t('view.favorite.copy') }}</el-button>
|
||||
<el-button size="small" @click="showBulkUnfavoriteSelectionConfirm">{{
|
||||
$t('view.favorite.bulk_unfavorite')
|
||||
}}</el-button>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; margin-right: 10px">
|
||||
<span class="name">{{ $t('view.favorite.edit_mode') }}</span>
|
||||
<el-switch v-model="editFavoritesMode" style="margin-left: 5px"></el-switch>
|
||||
</div>
|
||||
<el-tooltip placement="bottom" :content="$t('view.favorite.refresh_tooltip')" :disabled="hideTooltips">
|
||||
<el-button
|
||||
type="default"
|
||||
:loading="API.isFavoriteLoading"
|
||||
@click="
|
||||
API.refreshFavorites();
|
||||
getLocalWorldFavorites();
|
||||
"
|
||||
size="small"
|
||||
icon="el-icon-refresh"
|
||||
circle></el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-tabs type="card" v-loading="API.isFavoriteLoading" style="height: 100%">
|
||||
<el-tab-pane :label="$t('view.favorite.friends.header')" lazy>
|
||||
<favorites-friend-tab
|
||||
:favorite-friends="favoriteFriends"
|
||||
:sort-favorites.sync="isSortByTime"
|
||||
:hide-tooltips="hideTooltips"
|
||||
:grouped-by-group-key-favorite-friends="groupedByGroupKeyFavoriteFriends"
|
||||
:edit-favorites-mode="editFavoritesMode"
|
||||
@show-friend-import-dialog="showFriendImportDialog"
|
||||
@save-sort-favorites-option="saveSortFavoritesOption"
|
||||
@change-favorite-group-name="changeFavoriteGroupName" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('view.favorite.worlds.header')" lazy>
|
||||
<favorites-world-tab
|
||||
@show-world-import-dialog="showWorldImportDialog"
|
||||
@save-sort-favorites-option="saveSortFavoritesOption"
|
||||
@show-world-dialog="showWorldDialog"
|
||||
@change-favorite-group-name="changeFavoriteGroupName"
|
||||
@new-instance-self-invite="newInstanceSelfInvite"
|
||||
@show-favorite-dialog="showFavoriteDialog"
|
||||
@refresh-local-world-favorite="refreshLocalWorldFavorites"
|
||||
@delete-local-world-favorite-group="deleteLocalWorldFavoriteGroup"
|
||||
@remove-local-world-favorite="removeLocalWorldFavorite"
|
||||
@rename-local-world-favorite-group="renameLocalWorldFavoriteGroup"
|
||||
@new-local-world-favorite-group="newLocalWorldFavoriteGroup"
|
||||
:sort-favorites.sync="isSortByTime"
|
||||
:hide-tooltips="hideTooltips"
|
||||
:favorite-worlds="favoriteWorlds"
|
||||
:edit-favorites-mode="editFavoritesMode"
|
||||
:shift-held="shiftHeld"
|
||||
:refresh-local-world-favorites="refreshLocalWorldFavorites"
|
||||
:local-world-favorite-groups="localWorldFavoriteGroups"
|
||||
:local-world-favorites="localWorldFavorites"
|
||||
:local-world-favorites-list="localWorldFavoritesList" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('view.favorite.avatars.header')" lazy>
|
||||
<favorites-avatar-tab
|
||||
:sort-favorites.sync="isSortByTime"
|
||||
:hide-tooltips="hideTooltips"
|
||||
:shift-held="shiftHeld"
|
||||
:edit-favorites-mode="editFavoritesMode"
|
||||
:avatar-history-array="avatarHistoryArray"
|
||||
:refreshing-local-favorites="refreshingLocalFavorites"
|
||||
:local-avatar-favorite-groups="localAvatarFavoriteGroups"
|
||||
:local-avatar-favorites="localAvatarFavorites"
|
||||
:favorite-avatars="favoriteAvatars"
|
||||
:local-avatar-favorites-list="localAvatarFavoritesList"
|
||||
@show-avatar-import-dialog="showAvatarImportDialog"
|
||||
@save-sort-favorites-option="saveSortFavoritesOption"
|
||||
@show-avatar-dialog="showAvatarDialog"
|
||||
@show-favorite-dialog="showFavoriteDialog"
|
||||
@change-favorite-group-name="changeFavoriteGroupName"
|
||||
@remove-local-avatar-favorite="removeLocalAvatarFavorite"
|
||||
@select-avatar-with-confirmation="selectAvatarWithConfirmation"
|
||||
@prompt-clear-avatar-history="promptClearAvatarHistory"
|
||||
@prompt-new-local-avatar-favorite-group="promptNewLocalAvatarFavoriteGroup"
|
||||
@refresh-local-avatar-favorites="refreshLocalAvatarFavorites"
|
||||
@prompt-local-avatar-favorite-group-rename="promptLocalAvatarFavoriteGroupRename"
|
||||
@prompt-local-avatar-favorite-group-delete="promptLocalAvatarFavoriteGroupDelete" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FavoritesFriendTab from '../../components/favorites/FavoritesFriendTab.vue';
|
||||
import FavoritesWorldTab from '../../components/favorites/FavoritesWorldTab.vue';
|
||||
import FavoritesAvatarTab from '../../components/favorites/FavoritesAvatarTab.vue';
|
||||
import { avatarRequest, favoriteRequest, worldRequest } from '../../classes/request';
|
||||
import * as workerTimers from 'worker-timers';
|
||||
|
||||
export default {
|
||||
name: 'FavoritesTab',
|
||||
components: {
|
||||
FavoritesFriendTab,
|
||||
FavoritesWorldTab,
|
||||
FavoritesAvatarTab
|
||||
},
|
||||
inject: ['API'],
|
||||
props: {
|
||||
menuActiveIndex: String,
|
||||
hideTooltips: Boolean,
|
||||
shiftHeld: Boolean,
|
||||
favoriteFriends: Array,
|
||||
sortFavorites: Boolean,
|
||||
groupedByGroupKeyFavoriteFriends: Object,
|
||||
favoriteWorlds: Array,
|
||||
localWorldFavoriteGroups: Array,
|
||||
localWorldFavorites: Object,
|
||||
avatarHistoryArray: Array,
|
||||
localAvatarFavoriteGroups: Array,
|
||||
localAvatarFavorites: Object,
|
||||
favoriteAvatars: Array,
|
||||
localAvatarFavoritesList: Array,
|
||||
localWorldFavoritesList: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editFavoritesMode: false,
|
||||
refreshingLocalFavorites: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isSortByTime: {
|
||||
get() {
|
||||
return this.sortFavorites;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:sort-favorites', value);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showBulkUnfavoriteSelectionConfirm() {
|
||||
const elementsTicked = [];
|
||||
// check favorites type
|
||||
for (const ctx of this.favoriteFriends) {
|
||||
if (ctx.$selected) {
|
||||
elementsTicked.push(ctx.id);
|
||||
}
|
||||
}
|
||||
for (const ctx of this.favoriteWorlds) {
|
||||
if (ctx.$selected) {
|
||||
elementsTicked.push(ctx.id);
|
||||
}
|
||||
}
|
||||
for (const ctx of this.favoriteAvatars) {
|
||||
if (ctx.$selected) {
|
||||
elementsTicked.push(ctx.id);
|
||||
}
|
||||
}
|
||||
if (elementsTicked.length === 0) {
|
||||
return;
|
||||
}
|
||||
this.$confirm(
|
||||
`Are you sure you want to unfavorite ${elementsTicked.length} favorites?
|
||||
This action cannot be undone.`,
|
||||
`Delete ${elementsTicked.length} favorites?`,
|
||||
{
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info',
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
this.bulkUnfavoriteSelection(elementsTicked);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
bulkUnfavoriteSelection(elementsTicked) {
|
||||
for (const id of elementsTicked) {
|
||||
favoriteRequest.deleteFavorite({
|
||||
objectId: id
|
||||
});
|
||||
}
|
||||
this.editFavoritesMode = false;
|
||||
},
|
||||
|
||||
changeFavoriteGroupName(ctx) {
|
||||
this.$prompt(
|
||||
$t('prompt.change_favorite_group_name.description'),
|
||||
$t('prompt.change_favorite_group_name.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
cancelButtonText: $t('prompt.change_favorite_group_name.cancel'),
|
||||
confirmButtonText: $t('prompt.change_favorite_group_name.change'),
|
||||
inputPlaceholder: $t('prompt.change_favorite_group_name.input_placeholder'),
|
||||
inputValue: ctx.displayName,
|
||||
inputPattern: /\S+/,
|
||||
inputErrorMessage: $t('prompt.change_favorite_group_name.input_error'),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm') {
|
||||
favoriteRequest
|
||||
.saveFavoriteGroup({
|
||||
type: ctx.type,
|
||||
group: ctx.name,
|
||||
displayName: instance.inputValue
|
||||
})
|
||||
.then(() => {
|
||||
this.$message({
|
||||
message: $t('prompt.change_favorite_group_name.message.success'),
|
||||
type: 'success'
|
||||
});
|
||||
// load new group name
|
||||
this.API.refreshFavoriteGroups();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
async refreshLocalAvatarFavorites() {
|
||||
if (this.refreshingLocalFavorites) {
|
||||
return;
|
||||
}
|
||||
this.refreshingLocalFavorites = true;
|
||||
for (const avatarId of this.localAvatarFavoritesList) {
|
||||
if (!this.refreshingLocalFavorites) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
await avatarRequest.getAvatar({
|
||||
avatarId
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
await new Promise((resolve) => {
|
||||
workerTimers.setTimeout(resolve, 1000);
|
||||
});
|
||||
}
|
||||
this.refreshingLocalFavorites = false;
|
||||
},
|
||||
async refreshLocalWorldFavorites() {
|
||||
if (this.refreshingLocalFavorites) {
|
||||
return;
|
||||
}
|
||||
this.refreshingLocalFavorites = true;
|
||||
for (const worldId of this.localWorldFavoritesList) {
|
||||
if (!this.refreshingLocalFavorites) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
await worldRequest.getWorld({
|
||||
worldId
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
await new Promise((resolve) => {
|
||||
workerTimers.setTimeout(resolve, 1000);
|
||||
});
|
||||
}
|
||||
this.refreshingLocalFavorites = false;
|
||||
},
|
||||
clearBulkFavoriteSelection() {
|
||||
this.$emit('clear-bulk-favorite-selection');
|
||||
},
|
||||
bulkCopyFavoriteSelection() {
|
||||
this.$emit('bulk-copy-favorite-selection');
|
||||
},
|
||||
getLocalWorldFavorites() {
|
||||
this.$emit('get-local-world-favorites');
|
||||
},
|
||||
showFriendImportDialog() {
|
||||
this.$emit('show-friend-import-dialog');
|
||||
},
|
||||
saveSortFavoritesOption() {
|
||||
this.$emit('save-sort-favorites-option');
|
||||
},
|
||||
showWorldImportDialog() {
|
||||
this.$emit('show-world-import-dialog');
|
||||
},
|
||||
showWorldDialog(tag, shortName) {
|
||||
this.$emit('show-world-dialog', tag, shortName);
|
||||
},
|
||||
newInstanceSelfInvite(worldId) {
|
||||
this.$emit('new-instance-self-invite', worldId);
|
||||
},
|
||||
showFavoriteDialog(type, objectId) {
|
||||
this.$emit('show-favorite-dialog', type, objectId);
|
||||
},
|
||||
deleteLocalWorldFavoriteGroup(group) {
|
||||
this.$emit('delete-local-world-favorite-group', group);
|
||||
},
|
||||
removeLocalWorldFavorite(worldId, group) {
|
||||
this.$emit('remove-local-world-favorite', worldId, group);
|
||||
},
|
||||
showAvatarImportDialog() {
|
||||
this.$emit('show-avatar-import-dialog');
|
||||
},
|
||||
showAvatarDialog(avatarId) {
|
||||
this.$emit('show-avatar-dialog', avatarId);
|
||||
},
|
||||
removeLocalAvatarFavorite(avatarId, group) {
|
||||
this.$emit('remove-local-avatar-favorite', avatarId, group);
|
||||
},
|
||||
selectAvatarWithConfirmation(id) {
|
||||
this.$emit('select-avatar-with-confirmation', id);
|
||||
},
|
||||
promptClearAvatarHistory() {
|
||||
this.$emit('prompt-clear-avatar-history');
|
||||
},
|
||||
promptNewLocalAvatarFavoriteGroup() {
|
||||
this.$emit('prompt-new-local-avatar-favorite-group');
|
||||
},
|
||||
promptLocalAvatarFavoriteGroupRename(group) {
|
||||
this.$emit('prompt-local-avatar-favorite-group-rename', group);
|
||||
},
|
||||
promptLocalAvatarFavoriteGroupDelete(group) {
|
||||
this.$emit('prompt-local-avatar-favorite-group-delete', group);
|
||||
},
|
||||
renameLocalWorldFavoriteGroup(inputValue, group) {
|
||||
this.$emit('rename-local-world-favorite-group', inputValue, group);
|
||||
},
|
||||
newLocalWorldFavoriteGroup(inputValue) {
|
||||
this.$emit('new-local-world-favorite-group', inputValue);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="x-container">
|
||||
<div class="x-container" v-if="menuActiveIndex === 'moderation'">
|
||||
<data-tables
|
||||
:data="tableData.data"
|
||||
:pageSize="tableData.pageSize"
|
||||
@@ -101,6 +101,7 @@
|
||||
name: 'ModerationTab',
|
||||
inject: ['API', 'showUserDialog'],
|
||||
props: {
|
||||
menuActiveIndex: String,
|
||||
tableData: Object,
|
||||
shiftHeld: Boolean,
|
||||
hideTooltips: Boolean
|
||||
|
||||
Reference in New Issue
Block a user