refactor: favorites tab

This commit is contained in:
pa
2025-11-09 21:59:24 +09:00
committed by Natsumi
parent 9069d8cefe
commit bbd5fa2b21
17 changed files with 4987 additions and 1841 deletions
@@ -1,132 +1,126 @@
<template>
<div @click="$emit('click')">
<div class="x-friend-item">
<template v-if="isLocalFavorite ? favorite.name : favorite.ref">
<div class="avatar">
<img :src="smallThumbnail" loading="lazy" />
<div :class="cardClasses" @click="$emit('click')">
<template v-if="localFavFakeRef">
<div class="favorites-search-card__content">
<div class="favorites-search-card__avatar" :class="{ 'is-empty': !localFavFakeRef.thumbnailImageUrl }">
<img v-if="localFavFakeRef.thumbnailImageUrl" :src="smallThumbnail" loading="lazy" />
</div>
<div class="detail">
<span class="name" v-text="localFavFakeRef.name"></span>
<span class="extra" v-text="localFavFakeRef.authorName"></span>
<div class="favorites-search-card__detail">
<div class="favorites-search-card__title">
<span class="name">{{ localFavFakeRef.name }}</span>
<span class="favorites-search-card__badges">
<el-tooltip
v-if="favorite.deleted"
placement="top"
:content="t('view.favorite.unavailable_tooltip')">
<i class="ri-error-warning-line"></i>
</el-tooltip>
<el-tooltip
v-if="!isLocalFavorite && favorite.ref?.releaseStatus === 'private'"
placement="top"
:content="t('view.favorite.private')">
<i class="ri-lock-line"></i>
</el-tooltip>
</span>
</div>
<span class="extra">{{ localFavFakeRef.authorName }}</span>
</div>
<div v-if="editFavoritesMode">
<FavoritesMoveDropdown
:favoriteGroup="favoriteAvatarGroups"
:currentFavorite="props.favorite"
:currentGroup="group"
type="avatar" />
<el-button v-if="!isLocalFavorite" type="text" size="small" style="margin-left: 5px" @click.stop>
</div>
<div class="favorites-search-card__actions">
<template v-if="editMode">
<div
v-if="!isLocalFavorite"
class="favorites-search-card__action favorites-search-card__action--checkbox"
@click.stop>
<el-checkbox v-model="isSelected"></el-checkbox>
</el-button>
</div>
<template v-else-if="!isLocalFavorite">
<el-tooltip
v-if="favorite.deleted"
placement="left"
:content="t('view.favorite.unavailable_tooltip')"
:teleported="false">
<el-icon><Warning /></el-icon>
</el-tooltip>
<el-tooltip
v-if="favorite.ref.releaseStatus === 'private'"
placement="left"
:content="t('view.favorite.private')"
:teleported="false">
<el-icon><Warning /></el-icon>
</el-tooltip>
<el-tooltip
v-if="favorite.ref.releaseStatus !== 'private' && !favorite.deleted"
placement="left"
:content="t('view.favorite.select_avatar_tooltip')"
:teleported="false">
<el-button
:disabled="currentUser.currentAvatar === favorite.id"
size="small"
:icon="Check"
circle
style="margin-left: 5px"
@click.stop="selectAvatarWithConfirmation(favorite.id)"></el-button>
</el-tooltip>
<el-tooltip placement="right" :content="t('view.favorite.unfavorite_tooltip')" :teleported="false">
<el-button
v-if="shiftHeld"
size="small"
:icon="Close"
circle
style="color: #f56c6c; margin-left: 5px"
@click.stop="deleteFavorite(favorite.id)"></el-button>
<el-button
v-else
type="default"
:icon="Star"
size="small"
circle
style="margin-left: 5px"
@click.stop="showFavoriteDialog('avatar', favorite.id)"></el-button>
</el-tooltip>
</div>
<div class="favorites-search-card__action-group">
<div class="favorites-search-card__action favorites-search-card__action--full" @click.stop>
<FavoritesMoveDropdown
:favoriteGroup="favoriteAvatarGroups"
:currentFavorite="props.favorite"
:currentGroup="group"
class="favorites-search-card__dropdown"
:is-local-favorite="isLocalFavorite"
type="avatar" />
</div>
<div class="favorites-search-card__action">
<el-tooltip
placement="left"
:content="
isLocalFavorite
? t('view.favorite.delete_tooltip')
: t('view.favorite.unfavorite_tooltip')
">
<el-button
size="small"
circle
class="favorites-search-card__action-btn"
:type="isLocalFavorite ? 'default' : 'default'"
@click.stop="handlePrimaryDeleteAction">
<i class="ri-delete-bin-line"></i>
</el-button>
</el-tooltip>
</div>
</div>
</template>
<template v-else>
<el-tooltip
placement="left"
:content="t('view.favorite.select_avatar_tooltip')"
:teleported="false">
<el-button
:disabled="currentUser.currentAvatar === favorite.id"
size="small"
circle
style="margin-left: 5px"
:icon="Check"
@click.stop="selectAvatarWithConfirmation(favorite.id)" />
</el-tooltip>
<div class="favorites-search-card__action-group">
<div class="favorites-search-card__action" v-if="canSelectAvatar">
<el-tooltip placement="top" :content="t('view.favorite.select_avatar_tooltip')">
<el-button
:disabled="currentUser.currentAvatar === favorite.id"
size="small"
:icon="Check"
circle
class="favorites-search-card__action-btn"
@click.stop="selectAvatarWithConfirmation(favorite.id)" />
</el-tooltip>
</div>
<div class="favorites-search-card__action">
<el-tooltip placement="bottom" :content="t('view.favorite.unfavorite_tooltip')">
<el-button
v-if="showDangerUnfavorite"
size="small"
:icon="Close"
circle
class="favorites-search-card__action-btn"
type="danger"
@click.stop="handlePrimaryDeleteAction" />
<el-button
v-else
type="default"
:icon="Star"
size="small"
circle
class="favorites-search-card__action-btn"
@click.stop="showFavoriteDialog('avatar', favorite.id)" />
</el-tooltip>
</div>
</div>
</template>
<el-tooltip
v-if="isLocalFavorite"
placement="right"
:content="t('view.favorite.unfavorite_tooltip')"
:teleported="false">
<el-button
v-if="shiftHeld"
size="small"
:icon="Close"
circle
style="color: #f56c6c; margin-left: 5px"
@click.stop="removeLocalAvatarFavorite(favorite.id, favoriteGroupName)" />
<el-button
v-else
type="default"
:icon="Star"
size="small"
circle
style="margin-left: 5px"
@click.stop="showFavoriteDialog('avatar', favorite.id)" />
</el-tooltip>
</template>
<template v-else>
<div class="avatar"></div>
<div class="detail">
<span class="name" v-text="favorite.name || favorite.id"></span>
</div>
</template>
<template v-else>
<div class="favorites-search-card__content">
<div class="favorites-search-card__avatar is-empty"></div>
<div class="favorites-search-card__detail">
<span class="name">{{ favorite.name || favorite.id }}</span>
</div>
<el-button
v-if="isLocalFavorite"
type="text"
:icon="Close"
size="small"
style="margin-left: 5px"
@click.stop="removeLocalAvatarFavorite(favorite.id, favoriteGroupName)"></el-button>
<el-button
v-else
type="text"
:icon="Close"
size="small"
style="margin-left: 5px"
@click.stop="deleteFavorite(favorite.id)"></el-button>
</template>
</div>
</div>
<div class="favorites-search-card__actions">
<div class="favorites-search-card__action">
<el-button circle type="default" size="small" @click.stop="handlePrimaryDeleteAction">
<i class="ri-delete-bin-line"></i>
</el-button>
</div>
</div>
</template>
</div>
</template>
<script setup>
import { Check, Close, Star, Warning } from '@element-plus/icons-vue';
import { Check, Close, Star } from '@element-plus/icons-vue';
import { computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
@@ -139,35 +133,74 @@
const props = defineProps({
favorite: Object,
group: [Object, String],
isLocalFavorite: Boolean
isLocalFavorite: Boolean,
editMode: { type: Boolean, default: false },
selected: { type: Boolean, default: false }
});
const emit = defineEmits(['click', 'handle-select']);
const emit = defineEmits(['click', 'toggle-select']);
const { t } = useI18n();
const { favoriteAvatarGroups, editFavoritesMode } = storeToRefs(useFavoriteStore());
const { favoriteAvatarGroups } = storeToRefs(useFavoriteStore());
const { removeLocalAvatarFavorite, showFavoriteDialog } = useFavoriteStore();
const { selectAvatarWithConfirmation } = useAvatarStore();
const { shiftHeld } = storeToRefs(useUiStore());
const { currentUser } = storeToRefs(useUserStore());
const isSelected = computed({
get: () => props.favorite.$selected,
set: (value) => emit('handle-select', value)
get: () => props.selected,
set: (value) => emit('toggle-select', value)
});
const localFavFakeRef = computed(() => (props.isLocalFavorite ? props.favorite : props.favorite?.ref));
const cardClasses = computed(() => [
'favorites-search-card',
'favorites-search-card--avatar',
{
'is-selected': props.selected,
'is-edit-mode': props.editMode
}
]);
const smallThumbnail = computed(() => {
if (!localFavFakeRef.value?.thumbnailImageUrl) {
return '';
}
return localFavFakeRef.value.thumbnailImageUrl.replace('256', '128');
});
const localFavFakeRef = computed(() => (props.isLocalFavorite ? props.favorite : props.favorite.ref));
const smallThumbnail = computed(
() => localFavFakeRef.value.thumbnailImageUrl?.replace('256', '128') || localFavFakeRef.value.thumbnailImageUrl
);
const favoriteGroupName = computed(() => {
if (typeof props.group === 'string') {
return props.group;
} else {
return props.group?.name;
}
return props.group?.name;
});
const canSelectAvatar = computed(() => {
if (props.isLocalFavorite) {
return true;
}
if (props.favorite?.deleted) {
return false;
}
return props.favorite?.ref?.releaseStatus !== 'private';
});
const showDangerUnfavorite = computed(() => {
if (props.isLocalFavorite) {
return shiftHeld.value;
}
return shiftHeld.value;
});
function handlePrimaryDeleteAction() {
if (props.isLocalFavorite) {
removeLocalAvatarFavorite(props.favorite.id, favoriteGroupName.value);
return;
}
deleteFavorite(props.favorite.id);
}
function deleteFavorite(objectId) {
favoriteRequest.deleteFavorite({ objectId });
}