refactor(life): remove life categories and ratings

Drop life_tags and life_post_ratings tables and related schema
Remove category selection and rating UI from Life posts
Simplify Life feed filters and API endpoints
This commit is contained in:
2026-05-07 15:38:32 +08:00
parent e9d356a656
commit a781bc559b
11 changed files with 89 additions and 696 deletions

View File

@@ -4,7 +4,6 @@ import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import ConfirmDialog from '../components/ConfirmDialog.vue';
import LifeRatingControl from '../components/LifeRatingControl.vue';
import LifeReactionUsersModal from '../components/LifeReactionUsersModal.vue';
import LoadMoreSentinel from '../components/LoadMoreSentinel.vue';
import PageHeader from '../components/PageHeader.vue';
@@ -64,8 +63,6 @@ const commentErrors = ref<Record<string, string>>({});
const reactionPickerPostId = ref<number | null>(null);
const reactionBusyPostId = ref<number | null>(null);
const reactionErrors = ref<Record<number, string>>({});
const ratingBusyPostId = ref<number | null>(null);
const ratingErrors = ref<Record<number, string>>({});
const moderationBusyPostId = ref<number | null>(null);
const moderationErrors = ref<Record<number, string>>({});
const reactionUsersModal = ref<{ postId: number; reactionType: LifeReactionType | null } | null>(null);
@@ -89,7 +86,6 @@ function can(permissionKey: string) {
const canComment = computed(() => can('life.comments.create'));
const canLikeComments = computed(() => can('life.comments.like'));
const canReact = computed(() => can('life.reactions.set'));
const canRate = computed(() => can('life.ratings.set'));
const canCommentOnPost = computed(() => canComment.value && post.value?.moderationStatus === 'approved');
const commentSortOptions = computed<Array<{ value: CommentSort; label: string }>>(() => [
{ value: 'oldest', label: t('pages.life.sortOldest') },
@@ -279,10 +275,6 @@ function isReactionBusy(postId: number) {
return reactionBusyPostId.value === postId;
}
function isRatingBusy(postId: number) {
return ratingBusyPostId.value === postId;
}
function canManage(currentPost: LifePost) {
return (currentUser.value?.id === currentPost.author?.id && can('life.posts.update')) || can('life.posts.update-any');
}
@@ -316,10 +308,6 @@ function canUseReactions() {
return canReact.value && reactionBusyPostId.value === null;
}
function canUseRatings(currentPost: LifePost) {
return canRate.value && ratingBusyPostId.value === null && currentPost.moderationStatus === 'approved' && currentPost.category?.isRateable === true;
}
function reactionTotal(currentPost: LifePost) {
return reactionOptions.reduce((count, option) => count + (currentPost.reactionCounts[option.type] ?? 0), 0);
}
@@ -546,25 +534,6 @@ async function toggleReaction(currentPost: LifePost, reactionType: LifeReactionT
}
}
async function toggleRating(currentPost: LifePost, rating: number) {
if (!canUseRatings(currentPost)) {
return;
}
ratingBusyPostId.value = currentPost.id;
clearRatingError(currentPost.id);
try {
const updatedPost =
currentPost.myRating === rating ? await api.deleteLifeRating(currentPost.id) : await api.setLifeRating(currentPost.id, rating);
replacePost(updatedPost);
} catch (error) {
setRatingError(currentPost.id, error instanceof Error && error.message ? error.message : t('pages.life.ratingFailed'));
} finally {
ratingBusyPostId.value = null;
}
}
function setReactionError(postId: number, message: string) {
reactionErrors.value = { ...reactionErrors.value, [postId]: message };
}
@@ -575,16 +544,6 @@ function clearReactionError(postId: number) {
reactionErrors.value = nextErrors;
}
function setRatingError(postId: number, message: string) {
ratingErrors.value = { ...ratingErrors.value, [postId]: message };
}
function clearRatingError(postId: number) {
const nextErrors = { ...ratingErrors.value };
delete nextErrors[postId];
ratingErrors.value = nextErrors;
}
function setCommentError(key: string, message: string) {
commentErrors.value = { ...commentErrors.value, [key]: message };
}
@@ -929,8 +888,7 @@ onUnmounted(() => {
<p class="life-post__body">{{ post.body }}</p>
<div v-if="post.category || post.gameVersion" class="life-post__tags" :aria-label="t('pages.life.postMeta')">
<span v-if="post.category" class="life-post__tag">{{ post.category.name }}</span>
<div v-if="post.gameVersion" class="life-post__tags" :aria-label="t('pages.life.postMeta')">
<span v-if="post.gameVersion" class="life-post__tag life-post__tag--version">
<Icon :icon="iconVersion" class="ui-icon" aria-hidden="true" />
{{ post.gameVersion.name }}
@@ -944,16 +902,6 @@ onUnmounted(() => {
<div class="life-post__engagement">
<div class="life-post__engagement-actions">
<LifeRatingControl
v-if="post.category?.isRateable"
:rating-average="post.ratingAverage"
:rating-count="post.ratingCount"
:my-rating="post.myRating"
:disabled="!canUseRatings(post)"
:busy="isRatingBusy(post.id)"
@rate="toggleRating(post, $event)"
/>
<div class="life-reactions">
<div class="life-reaction-control">
<button
@@ -1068,7 +1016,6 @@ onUnmounted(() => {
<span>{{ post.moderationReason }}</span>
</p>
<p v-if="ratingErrors[post.id]" class="life-form__error" role="alert">{{ ratingErrors[post.id] }}</p>
<p v-if="moderationErrors[post.id]" class="life-form__error" role="alert">{{ moderationErrors[post.id] }}</p>
<p v-if="reactionErrors[post.id]" class="life-form__error" role="alert">{{ reactionErrors[post.id] }}</p>