mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-17 13:53:52 +02:00
1637 lines
51 KiB
JavaScript
1637 lines
51 KiB
JavaScript
import { computed, reactive, ref, shallowReactive, watch } from 'vue';
|
|
import { defineStore } from 'pinia';
|
|
import { toast } from 'vue-sonner';
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
import {
|
|
compareByName,
|
|
removeFromArray,
|
|
replaceReactiveObject
|
|
} from '../shared/utils';
|
|
import { avatarRequest, favoriteRequest } from '../api';
|
|
import { database } from '../service/database';
|
|
import { processBulk } from '../service/request';
|
|
import { useAppearanceSettingsStore } from './settings/appearance';
|
|
import { useAvatarStore } from './avatar';
|
|
import { useFriendStore } from './friend';
|
|
import { useGeneralSettingsStore } from './settings/general';
|
|
import { useUserStore } from './user';
|
|
import { useWorldStore } from './world';
|
|
import { watchState } from '../service/watchState';
|
|
|
|
export const useFavoriteStore = defineStore('Favorite', () => {
|
|
const appearanceSettingsStore = useAppearanceSettingsStore();
|
|
const friendStore = useFriendStore();
|
|
const generalSettingsStore = useGeneralSettingsStore();
|
|
const avatarStore = useAvatarStore();
|
|
const worldStore = useWorldStore();
|
|
const userStore = useUserStore();
|
|
|
|
const { t } = useI18n();
|
|
|
|
const state = reactive({
|
|
favoriteObjects: new Map(),
|
|
favoriteFriends_: [],
|
|
favoriteWorlds_: [],
|
|
favoriteAvatars_: []
|
|
});
|
|
|
|
const cachedFavorites = reactive(new Map());
|
|
const cachedFavoritesByObjectId = reactive(new Map());
|
|
|
|
const cachedFavoriteGroups = ref({});
|
|
|
|
const isFavoriteGroupLoading = ref(false);
|
|
|
|
const favoriteFriendGroups = ref([]);
|
|
|
|
const favoriteWorldGroups = ref([]);
|
|
const favoriteAvatarGroups = ref([]);
|
|
|
|
const favoriteLimits = ref({
|
|
maxFavoriteGroups: {
|
|
avatar: 6,
|
|
friend: 3,
|
|
vrcPlusWorld: 4,
|
|
world: 4
|
|
},
|
|
maxFavoritesPerGroup: {
|
|
avatar: 50,
|
|
friend: 150,
|
|
vrcPlusWorld: 100,
|
|
world: 100
|
|
}
|
|
});
|
|
|
|
const isFavoriteLoading = ref(false);
|
|
|
|
const friendImportDialogInput = ref('');
|
|
|
|
const worldImportDialogInput = ref('');
|
|
|
|
const avatarImportDialogInput = ref('');
|
|
|
|
const worldImportDialogVisible = ref(false);
|
|
|
|
const avatarImportDialogVisible = ref(false);
|
|
|
|
const friendImportDialogVisible = ref(false);
|
|
|
|
const localWorldFavorites = reactive({});
|
|
|
|
const localAvatarFavorites = reactive({});
|
|
|
|
const selectedFavoriteFriends = ref([]);
|
|
const selectedFavoriteWorlds = ref([]);
|
|
const selectedFavoriteAvatars = ref([]);
|
|
|
|
const favoriteDialog = ref({
|
|
visible: false,
|
|
loading: false,
|
|
type: '',
|
|
objectId: '',
|
|
currentGroup: {}
|
|
});
|
|
|
|
const favoritesSortOrder = ref([]);
|
|
|
|
const favoriteFriends = computed(() => {
|
|
if (appearanceSettingsStore.sortFavorites) {
|
|
return state.favoriteFriends_.sort(compareByFavoriteSortOrder);
|
|
}
|
|
return state.favoriteFriends_.sort(compareByName);
|
|
});
|
|
|
|
const favoriteWorlds = computed(() => {
|
|
if (appearanceSettingsStore.sortFavorites) {
|
|
return state.favoriteWorlds_.sort(compareByFavoriteSortOrder);
|
|
}
|
|
return state.favoriteWorlds_.sort(compareByName);
|
|
});
|
|
|
|
const favoriteAvatars = computed(() => {
|
|
if (appearanceSettingsStore.sortFavorites) {
|
|
return state.favoriteAvatars_.sort(compareByFavoriteSortOrder);
|
|
}
|
|
return state.favoriteAvatars_.sort(compareByName);
|
|
});
|
|
|
|
watch(
|
|
favoriteFriends,
|
|
(list) => {
|
|
syncFavoriteSelection(list, selectedFavoriteFriends);
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
|
|
watch(
|
|
favoriteWorlds,
|
|
(list) => {
|
|
syncFavoriteSelection(list, selectedFavoriteWorlds);
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
|
|
watch(
|
|
favoriteAvatars,
|
|
(list) => {
|
|
syncFavoriteSelection(list, selectedFavoriteAvatars);
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
|
|
const localAvatarFavoriteGroups = computed(() =>
|
|
Object.keys(localAvatarFavorites).sort()
|
|
);
|
|
|
|
const localWorldFavoriteGroups = computed(() =>
|
|
Object.keys(localWorldFavorites).sort()
|
|
);
|
|
|
|
const localWorldFavoritesList = computed(() =>
|
|
Object.values(localWorldFavorites)
|
|
.flat()
|
|
.map((fav) => fav.id)
|
|
);
|
|
|
|
const localAvatarFavoritesList = computed(() =>
|
|
Object.values(localAvatarFavorites)
|
|
.flat()
|
|
.map((fav) => fav.id)
|
|
);
|
|
|
|
const groupedByGroupKeyFavoriteFriends = computed(() => {
|
|
const groupedByGroupKeyFavoriteFriends = {};
|
|
favoriteFriends.value.forEach((friend) => {
|
|
if (friend.groupKey) {
|
|
if (!groupedByGroupKeyFavoriteFriends[friend.groupKey]) {
|
|
groupedByGroupKeyFavoriteFriends[friend.groupKey] = [];
|
|
}
|
|
groupedByGroupKeyFavoriteFriends[friend.groupKey].push(friend);
|
|
}
|
|
});
|
|
return groupedByGroupKeyFavoriteFriends;
|
|
});
|
|
|
|
const localWorldFavGroupLength = computed(() => (group) => {
|
|
const favoriteGroup = localWorldFavorites[group];
|
|
if (!favoriteGroup) {
|
|
return 0;
|
|
}
|
|
return favoriteGroup.length;
|
|
});
|
|
|
|
const localAvatarFavGroupLength = computed(() => (group) => {
|
|
const favoriteGroup = localAvatarFavorites[group];
|
|
if (!favoriteGroup) {
|
|
return 0;
|
|
}
|
|
return favoriteGroup.length;
|
|
});
|
|
|
|
function syncFavoriteSelection(list, selectionRef) {
|
|
if (!Array.isArray(list)) {
|
|
selectionRef.value = [];
|
|
return;
|
|
}
|
|
const availableIds = new Set(list.map((item) => item.id));
|
|
const filtered = selectionRef.value.filter((id) =>
|
|
availableIds.has(id)
|
|
);
|
|
if (filtered.length !== selectionRef.value.length) {
|
|
selectionRef.value = filtered;
|
|
}
|
|
}
|
|
|
|
watch(
|
|
() => watchState.isLoggedIn,
|
|
(isLoggedIn) => {
|
|
friendStore.localFavoriteFriends.clear();
|
|
cachedFavorites.clear();
|
|
cachedFavoritesByObjectId.clear();
|
|
cachedFavoriteGroups.value = {};
|
|
favoriteFriendGroups.value = [];
|
|
favoriteWorldGroups.value = [];
|
|
favoriteAvatarGroups.value = [];
|
|
isFavoriteLoading.value = false;
|
|
isFavoriteGroupLoading.value = false;
|
|
state.favoriteObjects.clear();
|
|
state.favoriteFriends_ = [];
|
|
state.favoriteWorlds_ = [];
|
|
state.favoriteAvatars_ = [];
|
|
replaceReactiveObject(localWorldFavorites, {});
|
|
replaceReactiveObject(localAvatarFavorites, {});
|
|
selectedFavoriteFriends.value = [];
|
|
selectedFavoriteWorlds.value = [];
|
|
selectedFavoriteAvatars.value = [];
|
|
favoriteDialog.value.visible = false;
|
|
worldImportDialogVisible.value = false;
|
|
avatarImportDialogVisible.value = false;
|
|
friendImportDialogVisible.value = false;
|
|
if (isLoggedIn) {
|
|
initFavorites();
|
|
}
|
|
},
|
|
{ flush: 'sync' }
|
|
);
|
|
|
|
function getCachedFavoriteGroupsByTypeName() {
|
|
const group = {};
|
|
|
|
for (const k in favoriteFriendGroups.value) {
|
|
const element = favoriteFriendGroups.value[k];
|
|
group[element.key] = element;
|
|
}
|
|
for (const k in favoriteWorldGroups.value) {
|
|
const element = favoriteWorldGroups.value[k];
|
|
group[element.key] = element;
|
|
}
|
|
for (const k in favoriteAvatarGroups.value) {
|
|
const element = favoriteAvatarGroups.value[k];
|
|
group[element.key] = element;
|
|
}
|
|
|
|
return group;
|
|
}
|
|
|
|
function getCachedFavoritesByObjectId(objectId) {
|
|
return cachedFavoritesByObjectId.get(objectId);
|
|
}
|
|
|
|
function handleFavoriteAdd(args) {
|
|
handleFavorite({
|
|
json: args.json,
|
|
params: {
|
|
favoriteId: args.json.id
|
|
}
|
|
});
|
|
if (!favoritesSortOrder.value.includes(args.params.favoriteId)) {
|
|
favoritesSortOrder.value.unshift(args.params.favoriteId);
|
|
}
|
|
|
|
if (
|
|
args.params.type === 'avatar' &&
|
|
!avatarStore.cachedAvatars.has(args.params.favoriteId)
|
|
) {
|
|
refreshFavoriteAvatars(args.params.tags);
|
|
}
|
|
|
|
if (
|
|
args.params.type === 'friend' &&
|
|
generalSettingsStore.localFavoriteFriendsGroups.includes(
|
|
'friend:' + args.params.tags
|
|
)
|
|
) {
|
|
friendStore.updateLocalFavoriteFriends();
|
|
}
|
|
updateFavoriteDialog(args.params.objectId);
|
|
}
|
|
|
|
function handleFavorite(args) {
|
|
args.ref = applyFavoriteCached(args.json);
|
|
applyFavorite(args.ref.type, args.ref.favoriteId);
|
|
friendStore.updateFriend(args.ref.favoriteId);
|
|
const { ref } = args;
|
|
const userDialog = userStore.userDialog;
|
|
if (userDialog.visible && ref.favoriteId === userDialog.id) {
|
|
userDialog.isFavorite = true;
|
|
}
|
|
const worldDialog = worldStore.worldDialog;
|
|
if (worldDialog.visible && ref.favoriteId === worldDialog.id) {
|
|
worldDialog.isFavorite = true;
|
|
}
|
|
const avatarDialog = avatarStore.avatarDialog;
|
|
if (avatarDialog.visible && ref.favoriteId === avatarDialog.id) {
|
|
avatarDialog.isFavorite = true;
|
|
}
|
|
}
|
|
|
|
function handleFavoriteDelete(objectId) {
|
|
const ref = getCachedFavoritesByObjectId(objectId);
|
|
if (typeof ref === 'undefined') {
|
|
return;
|
|
}
|
|
handleFavoriteAtDelete(ref);
|
|
}
|
|
|
|
function handleFavoriteGroup(args) {
|
|
args.ref = applyFavoriteGroup(args.json);
|
|
}
|
|
|
|
function handleFavoriteGroupClear(args) {
|
|
const key = `${args.params.type}:${args.params.group}`;
|
|
for (const ref of cachedFavorites.values()) {
|
|
if (ref.$groupKey !== key) {
|
|
continue;
|
|
}
|
|
handleFavoriteAtDelete(ref);
|
|
}
|
|
}
|
|
|
|
function handleFavoriteWorldList(args) {
|
|
for (const json of args.json) {
|
|
if (json.id === '???') {
|
|
continue;
|
|
}
|
|
worldStore.applyWorld(json);
|
|
}
|
|
}
|
|
|
|
function handleFavoriteAvatarList(args) {
|
|
for (const json of args.json) {
|
|
if (json.releaseStatus === 'hidden') {
|
|
continue;
|
|
}
|
|
avatarStore.applyAvatar(json);
|
|
}
|
|
}
|
|
|
|
function handleFavoriteAtDelete(ref) {
|
|
const favorite = state.favoriteObjects.get(ref.favoriteId);
|
|
removeFromArray(state.favoriteFriends_, favorite);
|
|
removeFromArray(state.favoriteWorlds_, favorite);
|
|
removeFromArray(state.favoriteAvatars_, favorite);
|
|
cachedFavorites.delete(ref.id);
|
|
cachedFavoritesByObjectId.delete(ref.favoriteId);
|
|
state.favoriteObjects.delete(ref.favoriteId);
|
|
friendStore.localFavoriteFriends.delete(ref.favoriteId);
|
|
favoritesSortOrder.value = favoritesSortOrder.value.filter(
|
|
(id) => id !== ref.favoriteId
|
|
);
|
|
|
|
friendStore.updateFriend(ref.favoriteId);
|
|
friendStore.updateSidebarFavorites();
|
|
const userDialog = userStore.userDialog;
|
|
if (userDialog.visible && userDialog.id === ref.favoriteId) {
|
|
userDialog.isFavorite = false;
|
|
}
|
|
const worldDialog = worldStore.worldDialog;
|
|
if (worldDialog.visible && worldDialog.id === ref.favoriteId) {
|
|
worldDialog.isFavorite = localWorldFavoritesList.value.includes(
|
|
worldDialog.id
|
|
);
|
|
}
|
|
const avatarDialog = avatarStore.avatarDialog;
|
|
if (avatarDialog.visible && avatarDialog.id === ref.favoriteId) {
|
|
avatarDialog.isFavorite = false;
|
|
}
|
|
countFavoriteGroups();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {'friend' | 'world' | 'vrcPlusWorld' | 'avatar'} type
|
|
* @param {string} objectId
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function applyFavorite(type, objectId) {
|
|
let ref;
|
|
const favorite = getCachedFavoritesByObjectId(objectId);
|
|
let ctx = state.favoriteObjects.get(objectId);
|
|
if (ctx) {
|
|
ctx = shallowReactive(ctx);
|
|
}
|
|
if (typeof favorite !== 'undefined') {
|
|
let isTypeChanged = false;
|
|
if (typeof ctx === 'undefined') {
|
|
ctx = {
|
|
id: objectId,
|
|
type,
|
|
groupKey: favorite.$groupKey,
|
|
ref: null,
|
|
name: ''
|
|
};
|
|
if (type === 'friend') {
|
|
ref = userStore.cachedUsers.get(objectId);
|
|
if (typeof ref === 'undefined') {
|
|
ref = friendStore.friendLog.get(objectId);
|
|
if (typeof ref !== 'undefined' && ref.displayName) {
|
|
ctx.name = ref.displayName;
|
|
}
|
|
} else {
|
|
ctx.ref = ref;
|
|
ctx.name = ref.displayName;
|
|
}
|
|
} else if (type === 'world' || type === 'vrcPlusWorld') {
|
|
ref = worldStore.cachedWorlds.get(objectId);
|
|
if (typeof ref !== 'undefined') {
|
|
ctx.ref = ref;
|
|
ctx.name = ref.name;
|
|
}
|
|
} else if (type === 'avatar') {
|
|
ref = avatarStore.cachedAvatars.get(objectId);
|
|
if (typeof ref !== 'undefined') {
|
|
ctx.ref = ref;
|
|
ctx.name = ref.name;
|
|
}
|
|
}
|
|
state.favoriteObjects.set(objectId, ctx);
|
|
isTypeChanged = true;
|
|
} else {
|
|
if (ctx.type !== type) {
|
|
// WTF???
|
|
isTypeChanged = true;
|
|
if (type === 'friend') {
|
|
removeFromArray(state.favoriteFriends_, ctx);
|
|
} else if (type === 'world' || type === 'vrcPlusWorld') {
|
|
removeFromArray(state.favoriteWorlds_, ctx);
|
|
} else if (type === 'avatar') {
|
|
removeFromArray(state.favoriteAvatars_, ctx);
|
|
}
|
|
}
|
|
if (type === 'friend') {
|
|
ref = userStore.cachedUsers.get(objectId);
|
|
if (typeof ref !== 'undefined') {
|
|
if (ctx.ref !== ref) {
|
|
ctx.ref = ref;
|
|
}
|
|
if (ctx.name !== ref.displayName) {
|
|
ctx.name = ref.displayName;
|
|
}
|
|
}
|
|
// else too bad
|
|
} else if (type === 'world' || type === 'vrcPlusWorld') {
|
|
ref = worldStore.cachedWorlds.get(objectId);
|
|
if (typeof ref !== 'undefined') {
|
|
if (ctx.ref !== ref) {
|
|
ctx.ref = ref;
|
|
}
|
|
if (ctx.name !== ref.name) {
|
|
ctx.name = ref.name;
|
|
}
|
|
} else {
|
|
// try fetch from local world favorites
|
|
const world =
|
|
await database.getCachedWorldById(objectId);
|
|
if (world) {
|
|
ctx.ref = world;
|
|
ctx.name = world.name;
|
|
ctx.deleted = true;
|
|
}
|
|
if (!world) {
|
|
// try fetch from local world history
|
|
const worldName =
|
|
await database.getGameLogWorldNameByWorldId(
|
|
objectId
|
|
);
|
|
if (worldName) {
|
|
ctx.name = worldName;
|
|
ctx.deleted = true;
|
|
}
|
|
}
|
|
}
|
|
} else if (type === 'avatar') {
|
|
ref = avatarStore.cachedAvatars.get(objectId);
|
|
if (typeof ref !== 'undefined') {
|
|
if (ctx.ref !== ref) {
|
|
ctx.ref = ref;
|
|
}
|
|
if (ctx.name !== ref.name) {
|
|
ctx.name = ref.name;
|
|
}
|
|
} else {
|
|
// try fetch from local avatar history
|
|
const avatar =
|
|
await database.getCachedAvatarById(objectId);
|
|
if (avatar) {
|
|
ctx.ref = avatar;
|
|
ctx.name = avatar.name;
|
|
ctx.deleted = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (isTypeChanged) {
|
|
if (type === 'friend') {
|
|
state.favoriteFriends_.push(ctx);
|
|
} else if (type === 'world' || type === 'vrcPlusWorld') {
|
|
state.favoriteWorlds_.push(ctx);
|
|
} else if (type === 'avatar') {
|
|
state.favoriteAvatars_.push(ctx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function refreshFavoriteGroups() {
|
|
if (isFavoriteGroupLoading.value) {
|
|
return;
|
|
}
|
|
isFavoriteGroupLoading.value = true;
|
|
processBulk({
|
|
fn: favoriteRequest.getFavoriteGroups,
|
|
N: -1,
|
|
params: {
|
|
n: 50,
|
|
offset: 0
|
|
},
|
|
handle: (args) => {
|
|
for (const json of args.json) {
|
|
handleFavoriteGroup({
|
|
json,
|
|
params: {
|
|
favoriteGroupId: json.id
|
|
}
|
|
});
|
|
}
|
|
},
|
|
done(ok) {
|
|
if (ok) {
|
|
buildFavoriteGroups();
|
|
}
|
|
isFavoriteGroupLoading.value = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
function buildFavoriteGroups() {
|
|
let group;
|
|
let groups;
|
|
let i;
|
|
// 450 = ['group_0', 'group_1', 'group_2'] x 150
|
|
favoriteFriendGroups.value = [];
|
|
for (i = 0; i < favoriteLimits.value.maxFavoriteGroups.friend; ++i) {
|
|
favoriteFriendGroups.value.push({
|
|
assign: false,
|
|
key: `friend:group_${i}`,
|
|
type: 'friend',
|
|
name: `group_${i}`,
|
|
displayName: `Group ${i + 1}`,
|
|
capacity: favoriteLimits.value.maxFavoritesPerGroup.friend,
|
|
count: 0,
|
|
visibility: 'private'
|
|
});
|
|
}
|
|
// 400 = ['worlds1', 'worlds2', 'worlds3', 'worlds4'] x 100
|
|
favoriteWorldGroups.value = [];
|
|
for (i = 0; i < favoriteLimits.value.maxFavoriteGroups.world; ++i) {
|
|
favoriteWorldGroups.value.push({
|
|
assign: false,
|
|
key: `world:worlds${i + 1}`,
|
|
type: 'world',
|
|
name: `worlds${i + 1}`,
|
|
displayName: `Group ${i + 1}`,
|
|
capacity: favoriteLimits.value.maxFavoritesPerGroup.world,
|
|
count: 0,
|
|
visibility: 'private'
|
|
});
|
|
}
|
|
// 400 = ['vrcPlusWorlds1', 'vrcPlusWorlds2', 'vrcPlusWorlds3', 'vrcPlusWorlds4'] x 100
|
|
for (
|
|
i = 0;
|
|
i < favoriteLimits.value.maxFavoriteGroups.vrcPlusWorld;
|
|
++i
|
|
) {
|
|
favoriteWorldGroups.value.push({
|
|
assign: false,
|
|
key: `vrcPlusWorld:vrcPlusWorlds${i + 1}`,
|
|
type: 'vrcPlusWorld',
|
|
name: `vrcPlusWorlds${i + 1}`,
|
|
displayName: `VRC+ Group ${i + 1}`,
|
|
capacity:
|
|
favoriteLimits.value.maxFavoritesPerGroup.vrcPlusWorld,
|
|
count: 0,
|
|
visibility: 'private'
|
|
});
|
|
}
|
|
// 350 = ['avatars1', ...] x 50
|
|
// Favorite Avatars (0/50)
|
|
// VRC+ Group 1..5 (0/50)
|
|
favoriteAvatarGroups.value = [];
|
|
for (i = 0; i < favoriteLimits.value.maxFavoriteGroups.avatar; ++i) {
|
|
favoriteAvatarGroups.value.push({
|
|
assign: false,
|
|
key: `avatar:avatars${i + 1}`,
|
|
type: 'avatar',
|
|
name: `avatars${i + 1}`,
|
|
displayName: `Group ${i + 1}`,
|
|
capacity: favoriteLimits.value.maxFavoritesPerGroup.avatar,
|
|
count: 0,
|
|
visibility: 'private'
|
|
});
|
|
}
|
|
const types = {
|
|
friend: favoriteFriendGroups.value,
|
|
world: favoriteWorldGroups.value,
|
|
vrcPlusWorld: favoriteWorldGroups.value,
|
|
avatar: favoriteAvatarGroups.value
|
|
};
|
|
const assigns = new Set();
|
|
// assign the same name first
|
|
for (const key in cachedFavoriteGroups.value) {
|
|
const ref = cachedFavoriteGroups.value[key];
|
|
groups = types[ref.type];
|
|
if (typeof groups === 'undefined') {
|
|
continue;
|
|
}
|
|
for (group of groups) {
|
|
if (group.assign === false && group.name === ref.name) {
|
|
group.assign = true;
|
|
if (ref.displayName) {
|
|
group.displayName = ref.displayName;
|
|
}
|
|
group.visibility = ref.visibility;
|
|
assigns.add(ref.id);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const key in cachedFavoriteGroups.value) {
|
|
const ref = cachedFavoriteGroups.value[key];
|
|
if (assigns.has(ref.id)) {
|
|
continue;
|
|
}
|
|
groups = types[ref.type];
|
|
if (typeof groups === 'undefined') {
|
|
continue;
|
|
}
|
|
for (group of groups) {
|
|
if (group.assign === false) {
|
|
group.assign = true;
|
|
group.key = `${group.type}:${ref.name}`;
|
|
group.name = ref.name;
|
|
group.displayName = ref.displayName;
|
|
assigns.add(ref.id);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
countFavoriteGroups();
|
|
}
|
|
|
|
function countFavoriteGroups() {
|
|
const cachedFavoriteGroups = getCachedFavoriteGroupsByTypeName();
|
|
for (const key in cachedFavoriteGroups) {
|
|
cachedFavoriteGroups[key].count = 0;
|
|
}
|
|
for (let ref of cachedFavorites.values()) {
|
|
let group = cachedFavoriteGroups[ref.$groupKey];
|
|
if (typeof group === 'undefined') {
|
|
continue;
|
|
}
|
|
++group.count;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function refreshFavorites() {
|
|
if (isFavoriteLoading.value) {
|
|
return;
|
|
}
|
|
isFavoriteLoading.value = true;
|
|
try {
|
|
const args = await favoriteRequest.getFavoriteLimits();
|
|
favoriteLimits.value = {
|
|
...favoriteLimits.value,
|
|
...args.json
|
|
};
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
let newFavoriteSortOrder = [];
|
|
processBulk({
|
|
fn: favoriteRequest.getFavorites,
|
|
N: -1,
|
|
params: {
|
|
n: 300,
|
|
offset: 0
|
|
},
|
|
handle(args) {
|
|
for (const json of args.json) {
|
|
newFavoriteSortOrder.push(json.favoriteId);
|
|
handleFavorite({
|
|
json,
|
|
params: {
|
|
favoriteId: json.id
|
|
}
|
|
});
|
|
}
|
|
},
|
|
done(ok) {
|
|
if (ok) {
|
|
for (const id of favoritesSortOrder.value) {
|
|
if (!newFavoriteSortOrder.includes(id)) {
|
|
const fav = cachedFavorites.get(id);
|
|
if (fav) {
|
|
handleFavoriteAtDelete(fav);
|
|
}
|
|
}
|
|
}
|
|
favoritesSortOrder.value = newFavoriteSortOrder;
|
|
}
|
|
refreshFavoriteItems();
|
|
refreshFavoriteGroups();
|
|
friendStore.updateLocalFavoriteFriends();
|
|
isFavoriteLoading.value = false;
|
|
watchState.isFavoritesLoaded = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param json
|
|
* @returns {any}
|
|
*/
|
|
function applyFavoriteGroup(json) {
|
|
let ref = cachedFavoriteGroups.value[json.id];
|
|
if (typeof ref === 'undefined') {
|
|
ref = {
|
|
id: '',
|
|
ownerId: '',
|
|
ownerDisplayName: '',
|
|
name: '',
|
|
displayName: '',
|
|
type: '',
|
|
visibility: '',
|
|
tags: [],
|
|
...json
|
|
};
|
|
cachedFavoriteGroups.value[ref.id] = ref;
|
|
} else {
|
|
Object.assign(ref, json);
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param json
|
|
* @returns {any}
|
|
*/
|
|
function applyFavoriteCached(json) {
|
|
let ref = cachedFavorites.get(json.id);
|
|
if (typeof ref === 'undefined') {
|
|
ref = {
|
|
id: '',
|
|
type: '',
|
|
favoriteId: '',
|
|
tags: [],
|
|
// VRCX
|
|
$groupKey: '',
|
|
//
|
|
...json
|
|
};
|
|
cachedFavorites.set(ref.id, ref);
|
|
cachedFavoritesByObjectId.set(ref.favoriteId, ref);
|
|
if (
|
|
ref.type === 'friend' &&
|
|
(generalSettingsStore.localFavoriteFriendsGroups.length === 0 ||
|
|
generalSettingsStore.localFavoriteFriendsGroups.includes(
|
|
ref.groupKey
|
|
))
|
|
) {
|
|
friendStore.localFavoriteFriends.add(ref.favoriteId);
|
|
friendStore.updateSidebarFavorites();
|
|
}
|
|
ref.$groupKey = `${ref.type}:${String(ref.tags[0])}`;
|
|
const group = getCachedFavoriteGroupsByTypeName()[ref.$groupKey];
|
|
if (typeof group !== 'undefined') {
|
|
++group.count;
|
|
}
|
|
} else {
|
|
if (ref.favoriteId !== json.favoriteId) {
|
|
cachedFavoritesByObjectId.delete(ref.favoriteId);
|
|
}
|
|
Object.assign(ref, json);
|
|
cachedFavoritesByObjectId.set(ref.favoriteId, ref);
|
|
}
|
|
|
|
return ref;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param tag
|
|
*/
|
|
async function refreshFavoriteAvatars(tag) {
|
|
const params = {
|
|
n: 300,
|
|
offset: 0,
|
|
tag
|
|
};
|
|
const args = await favoriteRequest.getFavoriteAvatars(params);
|
|
handleFavoriteAvatarList(args);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function refreshFavoriteItems() {
|
|
const types = {
|
|
world: [0, favoriteRequest.getFavoriteWorlds],
|
|
avatar: [0, favoriteRequest.getFavoriteAvatars]
|
|
};
|
|
const tags = [];
|
|
for (const ref of cachedFavorites.values()) {
|
|
const type = types[ref.type];
|
|
if (typeof type === 'undefined') {
|
|
continue;
|
|
}
|
|
if (ref.type === 'avatar' && !tags.includes(ref.tags[0])) {
|
|
tags.push(ref.tags[0]);
|
|
}
|
|
++type[0];
|
|
}
|
|
for (const type in types) {
|
|
const [N, fn] = types[type];
|
|
if (N > 0) {
|
|
if (type === 'avatar') {
|
|
for (const tag of tags) {
|
|
processBulk({
|
|
fn,
|
|
N,
|
|
handle: (args) => handleFavoriteAvatarList(args),
|
|
params: {
|
|
n: 300,
|
|
offset: 0,
|
|
tag
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
processBulk({
|
|
fn,
|
|
N,
|
|
handle: (args) => handleFavoriteWorldList(args),
|
|
params: {
|
|
n: 300,
|
|
offset: 0
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function showWorldImportDialog() {
|
|
worldImportDialogVisible.value = true;
|
|
}
|
|
|
|
function showAvatarImportDialog() {
|
|
avatarImportDialogVisible.value = true;
|
|
}
|
|
|
|
function showFriendImportDialog() {
|
|
friendImportDialogVisible.value = true;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} worldId
|
|
* @param {string} group
|
|
*/
|
|
function addLocalWorldFavorite(worldId, group) {
|
|
if (hasLocalWorldFavorite(worldId, group)) {
|
|
return;
|
|
}
|
|
const ref = worldStore.cachedWorlds.get(worldId);
|
|
if (typeof ref === 'undefined') {
|
|
return;
|
|
}
|
|
if (!localWorldFavorites[group]) {
|
|
localWorldFavorites[group] = [];
|
|
}
|
|
|
|
localWorldFavorites[group].unshift(ref);
|
|
database.addWorldToCache(ref);
|
|
database.addWorldToFavorites(worldId, group);
|
|
if (
|
|
favoriteDialog.value.visible &&
|
|
favoriteDialog.value.objectId === worldId
|
|
) {
|
|
updateFavoriteDialog(worldId);
|
|
}
|
|
if (
|
|
worldStore.worldDialog.visible &&
|
|
worldStore.worldDialog.id === worldId
|
|
) {
|
|
worldStore.worldDialog.isFavorite = true;
|
|
}
|
|
|
|
// update UI
|
|
sortLocalWorldFavorites();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} worldId
|
|
* @param {string} group
|
|
* @returns {boolean}
|
|
*/
|
|
function hasLocalWorldFavorite(worldId, group) {
|
|
const favoriteGroup = localWorldFavorites[group];
|
|
if (!favoriteGroup) {
|
|
return false;
|
|
}
|
|
for (let i = 0; i < favoriteGroup.length; ++i) {
|
|
if (favoriteGroup[i].id === worldId) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} avatarId
|
|
* @param {string} group
|
|
*/
|
|
function addLocalAvatarFavorite(avatarId, group) {
|
|
if (hasLocalAvatarFavorite(avatarId, group)) {
|
|
return;
|
|
}
|
|
const ref = avatarStore.cachedAvatars.get(avatarId);
|
|
if (typeof ref === 'undefined') {
|
|
return;
|
|
}
|
|
if (!localAvatarFavorites[group]) {
|
|
localAvatarFavorites[group] = [];
|
|
}
|
|
localAvatarFavorites[group].unshift(ref);
|
|
database.addAvatarToCache(ref);
|
|
database.addAvatarToFavorites(avatarId, group);
|
|
if (
|
|
favoriteDialog.value.visible &&
|
|
favoriteDialog.value.objectId === avatarId
|
|
) {
|
|
updateFavoriteDialog(avatarId);
|
|
}
|
|
if (
|
|
avatarStore.avatarDialog.visible &&
|
|
avatarStore.avatarDialog.id === avatarId
|
|
) {
|
|
avatarStore.avatarDialog.isFavorite = true;
|
|
}
|
|
|
|
// update UI
|
|
sortLocalAvatarFavorites();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} avatarId
|
|
* @param {string} group
|
|
* @returns {boolean}
|
|
*/
|
|
function hasLocalAvatarFavorite(avatarId, group) {
|
|
const favoriteGroup = localAvatarFavorites[group];
|
|
if (!favoriteGroup) {
|
|
return false;
|
|
}
|
|
for (let i = 0; i < favoriteGroup.length; ++i) {
|
|
if (favoriteGroup[i].id === avatarId) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function updateFavoriteDialog(objectId) {
|
|
const D = favoriteDialog.value;
|
|
if (!D.visible || D.objectId !== objectId) {
|
|
return;
|
|
}
|
|
D.currentGroup = {};
|
|
const favorite = state.favoriteObjects.get(objectId);
|
|
if (favorite) {
|
|
let group;
|
|
for (group of favoriteWorldGroups.value) {
|
|
if (favorite.groupKey === group.key) {
|
|
D.currentGroup = group;
|
|
return;
|
|
}
|
|
}
|
|
for (group of favoriteAvatarGroups.value) {
|
|
if (favorite.groupKey === group.key) {
|
|
D.currentGroup = group;
|
|
return;
|
|
}
|
|
}
|
|
for (group of favoriteFriendGroups.value) {
|
|
if (favorite.groupKey === group.key) {
|
|
D.currentGroup = group;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} group
|
|
*/
|
|
function deleteLocalAvatarFavoriteGroup(group) {
|
|
let i;
|
|
// remove from cache if no longer in favorites
|
|
const avatarIdRemoveList = new Set();
|
|
const favoriteGroup = localAvatarFavorites[group];
|
|
for (i = 0; i < favoriteGroup.length; ++i) {
|
|
avatarIdRemoveList.add(favoriteGroup[i].id);
|
|
}
|
|
|
|
delete localAvatarFavorites[group];
|
|
database.deleteAvatarFavoriteGroup(group);
|
|
|
|
for (i = 0; i < localAvatarFavoriteGroups.value.length; ++i) {
|
|
const groupName = localAvatarFavoriteGroups.value[i];
|
|
if (!localAvatarFavorites[groupName]) {
|
|
continue;
|
|
}
|
|
for (let j = 0; j < localAvatarFavorites[groupName].length; ++j) {
|
|
const avatarId = localAvatarFavorites[groupName][j].id;
|
|
if (avatarIdRemoveList.has(avatarId)) {
|
|
avatarIdRemoveList.delete(avatarId);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
avatarIdRemoveList.forEach((id) => {
|
|
// remove from cache if no longer in favorites
|
|
let avatarInFavorites = false;
|
|
loop: for (
|
|
let i = 0;
|
|
i < localAvatarFavoriteGroups.value.length;
|
|
++i
|
|
) {
|
|
const groupName = localAvatarFavoriteGroups.value[i];
|
|
if (!localAvatarFavorites[groupName] || group === groupName) {
|
|
continue loop;
|
|
}
|
|
for (
|
|
let j = 0;
|
|
j < localAvatarFavorites[groupName].length;
|
|
++j
|
|
) {
|
|
const avatarId = localAvatarFavorites[groupName][j].id;
|
|
if (id === avatarId) {
|
|
avatarInFavorites = true;
|
|
break loop;
|
|
}
|
|
}
|
|
}
|
|
if (!avatarInFavorites) {
|
|
if (!avatarStore.avatarHistory.includes(id)) {
|
|
database.removeAvatarFromCache(id);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function sortLocalAvatarFavorites() {
|
|
if (!appearanceSettingsStore.sortFavorites) {
|
|
for (let i = 0; i < localAvatarFavoriteGroups.value.length; ++i) {
|
|
const group = localAvatarFavoriteGroups.value[i];
|
|
if (localAvatarFavorites[group]) {
|
|
localAvatarFavorites[group].sort(compareByName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} newName
|
|
* @param {string} group
|
|
*/
|
|
function renameLocalAvatarFavoriteGroup(newName, group) {
|
|
if (localAvatarFavoriteGroups.value.includes(newName)) {
|
|
toast.error(
|
|
t('prompt.local_favorite_group_rename.message.error', {
|
|
name: newName
|
|
})
|
|
);
|
|
return;
|
|
}
|
|
localAvatarFavorites[newName] = localAvatarFavorites[group];
|
|
|
|
delete localAvatarFavorites[group];
|
|
database.renameAvatarFavoriteGroup(newName, group);
|
|
sortLocalAvatarFavorites();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} group
|
|
*/
|
|
function newLocalAvatarFavoriteGroup(group) {
|
|
if (localAvatarFavoriteGroups.value.includes(group)) {
|
|
toast.error(
|
|
t('prompt.new_local_favorite_group.message.error', {
|
|
name: group
|
|
})
|
|
);
|
|
return;
|
|
}
|
|
if (!localAvatarFavorites[group]) {
|
|
localAvatarFavorites[group] = [];
|
|
}
|
|
sortLocalAvatarFavorites();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function getLocalAvatarFavorites() {
|
|
const localGroups = new Set();
|
|
const localListSet = new Set();
|
|
const localFavorites = Object.create(null);
|
|
|
|
const avatarCache = await database.getAvatarCache();
|
|
for (let i = 0; i < avatarCache.length; ++i) {
|
|
const ref = avatarCache[i];
|
|
if (!avatarStore.cachedAvatars.has(ref.id)) {
|
|
avatarStore.applyAvatar(ref);
|
|
}
|
|
}
|
|
|
|
const favorites = await database.getAvatarFavorites();
|
|
for (let i = 0; i < favorites.length; ++i) {
|
|
const favorite = favorites[i];
|
|
|
|
localListSet.add(favorite.avatarId);
|
|
|
|
if (!localFavorites[favorite.groupName]) {
|
|
localFavorites[favorite.groupName] = [];
|
|
}
|
|
localGroups.add(favorite.groupName);
|
|
|
|
let ref = avatarStore.cachedAvatars.get(favorite.avatarId);
|
|
if (typeof ref === 'undefined') {
|
|
ref = { id: favorite.avatarId };
|
|
}
|
|
localFavorites[favorite.groupName].unshift(ref);
|
|
}
|
|
|
|
let groupsArr = Array.from(localGroups);
|
|
if (groupsArr.length === 0) {
|
|
// default group
|
|
localFavorites.Favorites = [];
|
|
groupsArr = ['Favorites'];
|
|
}
|
|
|
|
replaceReactiveObject(localAvatarFavorites, localFavorites);
|
|
|
|
sortLocalAvatarFavorites();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} avatarId
|
|
* @param {string} group
|
|
*/
|
|
function removeLocalAvatarFavorite(avatarId, group) {
|
|
let i;
|
|
const favoriteGroup = localAvatarFavorites[group];
|
|
for (i = 0; i < favoriteGroup.length; ++i) {
|
|
if (favoriteGroup[i].id === avatarId) {
|
|
favoriteGroup.splice(i, 1);
|
|
}
|
|
}
|
|
|
|
// remove from cache if no longer in favorites
|
|
let avatarInFavorites = false;
|
|
for (i = 0; i < localAvatarFavoriteGroups.value.length; ++i) {
|
|
const groupName = localAvatarFavoriteGroups.value[i];
|
|
if (!localAvatarFavorites[groupName] || group === groupName) {
|
|
continue;
|
|
}
|
|
for (let j = 0; j < localAvatarFavorites[groupName].length; ++j) {
|
|
const id = localAvatarFavorites[groupName][j].id;
|
|
if (id === avatarId) {
|
|
avatarInFavorites = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!avatarInFavorites) {
|
|
if (!avatarStore.avatarHistory.includes(avatarId)) {
|
|
database.removeAvatarFromCache(avatarId);
|
|
}
|
|
}
|
|
database.removeAvatarFromFavorites(avatarId, group);
|
|
if (
|
|
favoriteDialog.value.visible &&
|
|
favoriteDialog.value.objectId === avatarId
|
|
) {
|
|
updateFavoriteDialog(avatarId);
|
|
}
|
|
if (
|
|
avatarStore.avatarDialog.visible &&
|
|
avatarStore.avatarDialog.id === avatarId
|
|
) {
|
|
avatarStore.avatarDialog.isFavorite =
|
|
getCachedFavoritesByObjectId(avatarId);
|
|
}
|
|
|
|
// update UI
|
|
sortLocalAvatarFavorites();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} group
|
|
*/
|
|
function deleteLocalWorldFavoriteGroup(group) {
|
|
let i;
|
|
// remove from cache if no longer in favorites
|
|
const worldIdRemoveList = new Set();
|
|
const favoriteGroup = localWorldFavorites[group];
|
|
for (i = 0; i < favoriteGroup.length; ++i) {
|
|
worldIdRemoveList.add(favoriteGroup[i].id);
|
|
}
|
|
|
|
delete localWorldFavorites[group];
|
|
database.deleteWorldFavoriteGroup(group);
|
|
|
|
for (i = 0; i < localWorldFavoriteGroups.value.length; ++i) {
|
|
const groupName = localWorldFavoriteGroups.value[i];
|
|
if (!localWorldFavorites[groupName]) {
|
|
continue;
|
|
}
|
|
for (let j = 0; j < localWorldFavorites[groupName].length; ++j) {
|
|
const worldId = localWorldFavorites[groupName][j].id;
|
|
if (worldIdRemoveList.has(worldId)) {
|
|
worldIdRemoveList.delete(worldId);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
worldIdRemoveList.forEach((id) => {
|
|
database.removeWorldFromCache(id);
|
|
});
|
|
}
|
|
|
|
function sortLocalWorldFavorites() {
|
|
if (!appearanceSettingsStore.sortFavorites) {
|
|
for (let i = 0; i < localWorldFavoriteGroups.value.length; ++i) {
|
|
const group = localWorldFavoriteGroups.value[i];
|
|
if (localWorldFavorites[group]) {
|
|
localWorldFavorites[group].sort(compareByName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check invalid local avatar favorites
|
|
* @param {string | null} targetGroup - Target group to check, null for all groups
|
|
* @param {Function | null} onProgress - Progress callback function, receives (current, total) parameters
|
|
* @returns {Promise<{total: number, invalid: number, invalidIds: string[]}>}
|
|
*/
|
|
async function checkInvalidLocalAvatars(
|
|
targetGroup = null,
|
|
onProgress = null
|
|
) {
|
|
const result = {
|
|
total: 0,
|
|
invalid: 0,
|
|
invalidIds: []
|
|
};
|
|
|
|
const groupsToCheck = targetGroup
|
|
? [targetGroup]
|
|
: localAvatarFavoriteGroups.value;
|
|
|
|
for (const group of groupsToCheck) {
|
|
const favoriteGroup = localAvatarFavorites[group];
|
|
if (favoriteGroup && favoriteGroup.length > 0) {
|
|
result.total += favoriteGroup.length;
|
|
}
|
|
}
|
|
|
|
let currentIndex = 0;
|
|
|
|
for (const group of groupsToCheck) {
|
|
const favoriteGroup = localAvatarFavorites[group];
|
|
if (!favoriteGroup || favoriteGroup.length === 0) {
|
|
continue;
|
|
}
|
|
|
|
for (const favorite of favoriteGroup) {
|
|
currentIndex++;
|
|
|
|
if (typeof onProgress === 'function') {
|
|
onProgress(currentIndex, result.total);
|
|
}
|
|
|
|
try {
|
|
await avatarRequest.getAvatar({
|
|
avatarId: favorite.id
|
|
});
|
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
} catch (err) {
|
|
result.invalid++;
|
|
result.invalidIds.push(favorite.id);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Remove invalid avatars from local favorites
|
|
* @param {string[]} avatarIds - Array of avatar IDs to remove
|
|
* @param {string | null} targetGroup - Target group, null for all groups
|
|
* @returns {Promise<{removed: number, removedIds: string[]}>}
|
|
*/
|
|
async function removeInvalidLocalAvatars(avatarIds, targetGroup = null) {
|
|
const result = {
|
|
removed: 0,
|
|
removedIds: []
|
|
};
|
|
|
|
const groupsToCheck = targetGroup
|
|
? [targetGroup]
|
|
: localAvatarFavoriteGroups.value;
|
|
|
|
for (const group of groupsToCheck) {
|
|
const favoriteGroup = localAvatarFavorites[group];
|
|
if (!favoriteGroup) {
|
|
continue;
|
|
}
|
|
|
|
for (const avatarId of avatarIds) {
|
|
const index = favoriteGroup.findIndex(
|
|
(fav) => fav.id === avatarId
|
|
);
|
|
if (index !== -1) {
|
|
removeLocalAvatarFavorite(avatarId, group);
|
|
result.removed++;
|
|
if (!result.removedIds.includes(avatarId)) {
|
|
result.removedIds.push(avatarId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} newName
|
|
* @param {string} group
|
|
*/
|
|
function renameLocalWorldFavoriteGroup(newName, group) {
|
|
if (localWorldFavoriteGroups.value.includes(newName)) {
|
|
toast.error(
|
|
t('prompt.local_favorite_group_rename.message.error', {
|
|
name: newName
|
|
})
|
|
);
|
|
return;
|
|
}
|
|
localWorldFavorites[newName] = localWorldFavorites[group];
|
|
|
|
delete localWorldFavorites[group];
|
|
database.renameWorldFavoriteGroup(newName, group);
|
|
sortLocalWorldFavorites();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} worldId
|
|
* @param {string} group
|
|
*/
|
|
function removeLocalWorldFavorite(worldId, group) {
|
|
let i;
|
|
const favoriteGroup = localWorldFavorites[group];
|
|
for (i = 0; i < favoriteGroup.length; ++i) {
|
|
if (favoriteGroup[i].id === worldId) {
|
|
favoriteGroup.splice(i, 1);
|
|
}
|
|
}
|
|
|
|
// remove from cache if no longer in favorites
|
|
let worldInFavorites = false;
|
|
for (i = 0; i < localWorldFavoriteGroups.value.length; ++i) {
|
|
const groupName = localWorldFavoriteGroups.value[i];
|
|
if (!localWorldFavorites[groupName] || group === groupName) {
|
|
continue;
|
|
}
|
|
for (let j = 0; j < localWorldFavorites[groupName].length; ++j) {
|
|
const id = localWorldFavorites[groupName][j].id;
|
|
if (id === worldId) {
|
|
worldInFavorites = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!worldInFavorites) {
|
|
database.removeWorldFromCache(worldId);
|
|
}
|
|
database.removeWorldFromFavorites(worldId, group);
|
|
if (
|
|
favoriteDialog.value.visible &&
|
|
favoriteDialog.value.objectId === worldId
|
|
) {
|
|
updateFavoriteDialog(worldId);
|
|
}
|
|
if (
|
|
worldStore.worldDialog.visible &&
|
|
worldStore.worldDialog.id === worldId
|
|
) {
|
|
worldStore.worldDialog.isFavorite =
|
|
getCachedFavoritesByObjectId(worldId);
|
|
}
|
|
|
|
// update UI
|
|
sortLocalWorldFavorites();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function getLocalWorldFavorites() {
|
|
const localGroups = new Set();
|
|
const localListSet = new Set();
|
|
const localFavorites = Object.create(null);
|
|
|
|
const worldCache = await database.getWorldCache();
|
|
for (let i = 0; i < worldCache.length; ++i) {
|
|
const ref = worldCache[i];
|
|
if (!worldStore.cachedWorlds.has(ref.id)) {
|
|
worldStore.applyWorld(ref);
|
|
}
|
|
}
|
|
|
|
const favorites = await database.getWorldFavorites();
|
|
for (let i = 0; i < favorites.length; ++i) {
|
|
const favorite = favorites[i];
|
|
|
|
localListSet.add(favorite.worldId);
|
|
|
|
if (!localFavorites[favorite.groupName]) {
|
|
localFavorites[favorite.groupName] = [];
|
|
}
|
|
localGroups.add(favorite.groupName);
|
|
|
|
let ref = worldStore.cachedWorlds.get(favorite.worldId);
|
|
if (typeof ref === 'undefined') {
|
|
ref = { id: favorite.worldId };
|
|
}
|
|
localFavorites[favorite.groupName].unshift(ref);
|
|
}
|
|
|
|
let groupsArr = Array.from(localGroups);
|
|
if (groupsArr.length === 0) {
|
|
localFavorites.Favorites = [];
|
|
// default group
|
|
groupsArr = ['Favorites'];
|
|
}
|
|
|
|
replaceReactiveObject(localWorldFavorites, localFavorites);
|
|
|
|
sortLocalWorldFavorites();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} group
|
|
*/
|
|
function newLocalWorldFavoriteGroup(group) {
|
|
if (localWorldFavoriteGroups.value.includes(group)) {
|
|
toast.error(
|
|
t('prompt.new_local_favorite_group.message.error', {
|
|
name: group
|
|
})
|
|
);
|
|
return;
|
|
}
|
|
if (!localWorldFavorites[group]) {
|
|
localWorldFavorites[group] = [];
|
|
}
|
|
sortLocalWorldFavorites();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} objectId
|
|
*/
|
|
function deleteFavoriteNoConfirm(objectId) {
|
|
if (!objectId) {
|
|
return;
|
|
}
|
|
favoriteDialog.value.visible = true;
|
|
favoriteRequest
|
|
.deleteFavorite({
|
|
objectId
|
|
})
|
|
.then(() => {
|
|
favoriteDialog.value.visible = false;
|
|
})
|
|
.finally(() => {
|
|
favoriteDialog.value.loading = false;
|
|
});
|
|
}
|
|
|
|
function showFavoriteDialog(type, objectId) {
|
|
const D = favoriteDialog.value;
|
|
D.type = type;
|
|
D.objectId = objectId;
|
|
D.visible = true;
|
|
updateFavoriteDialog(objectId);
|
|
}
|
|
|
|
async function saveSortFavoritesOption() {
|
|
getLocalWorldFavorites();
|
|
appearanceSettingsStore.setSortFavorites();
|
|
}
|
|
|
|
async function initFavorites() {
|
|
refreshFavorites();
|
|
getLocalWorldFavorites();
|
|
getLocalAvatarFavorites();
|
|
}
|
|
|
|
function compareByFavoriteSortOrder(a, b) {
|
|
const indexA = favoritesSortOrder.value.indexOf(a.id);
|
|
const indexB = favoritesSortOrder.value.indexOf(b.id);
|
|
return indexA - indexB;
|
|
}
|
|
|
|
return {
|
|
state,
|
|
|
|
favoriteFriends,
|
|
favoriteWorlds,
|
|
favoriteAvatars,
|
|
isFavoriteGroupLoading,
|
|
favoriteFriendGroups,
|
|
cachedFavoriteGroups,
|
|
favoriteLimits,
|
|
cachedFavorites,
|
|
favoriteWorldGroups,
|
|
favoriteAvatarGroups,
|
|
isFavoriteLoading,
|
|
friendImportDialogInput,
|
|
worldImportDialogInput,
|
|
avatarImportDialogInput,
|
|
worldImportDialogVisible,
|
|
avatarImportDialogVisible,
|
|
friendImportDialogVisible,
|
|
localWorldFavorites,
|
|
localAvatarFavorites,
|
|
localAvatarFavoritesList,
|
|
localAvatarFavoriteGroups,
|
|
favoriteDialog,
|
|
localWorldFavoritesList,
|
|
|
|
localWorldFavoriteGroups,
|
|
groupedByGroupKeyFavoriteFriends,
|
|
selectedFavoriteFriends,
|
|
selectedFavoriteWorlds,
|
|
selectedFavoriteAvatars,
|
|
localWorldFavGroupLength,
|
|
localAvatarFavGroupLength,
|
|
favoritesSortOrder,
|
|
|
|
initFavorites,
|
|
applyFavorite,
|
|
refreshFavoriteGroups,
|
|
refreshFavorites,
|
|
applyFavoriteGroup,
|
|
refreshFavoriteAvatars,
|
|
showWorldImportDialog,
|
|
showAvatarImportDialog,
|
|
showFriendImportDialog,
|
|
addLocalWorldFavorite,
|
|
hasLocalWorldFavorite,
|
|
hasLocalAvatarFavorite,
|
|
addLocalAvatarFavorite,
|
|
updateFavoriteDialog,
|
|
deleteLocalAvatarFavoriteGroup,
|
|
renameLocalAvatarFavoriteGroup,
|
|
newLocalAvatarFavoriteGroup,
|
|
getLocalAvatarFavorites,
|
|
removeLocalAvatarFavorite,
|
|
deleteLocalWorldFavoriteGroup,
|
|
sortLocalWorldFavorites,
|
|
renameLocalWorldFavoriteGroup,
|
|
removeLocalWorldFavorite,
|
|
getLocalWorldFavorites,
|
|
newLocalWorldFavoriteGroup,
|
|
deleteFavoriteNoConfirm,
|
|
showFavoriteDialog,
|
|
saveSortFavoritesOption,
|
|
handleFavoriteWorldList,
|
|
handleFavoriteGroupClear,
|
|
handleFavoriteGroup,
|
|
handleFavoriteDelete,
|
|
handleFavoriteAdd,
|
|
getCachedFavoritesByObjectId,
|
|
checkInvalidLocalAvatars,
|
|
removeInvalidLocalAvatars
|
|
};
|
|
});
|