From d7a0e2a6b7a1d90c56d0e98e9c792adec0f02615 Mon Sep 17 00:00:00 2001 From: leonincs Date: Thu, 23 Apr 2026 19:00:02 +0800 Subject: [PATCH] fix feed empty list rendering --- backend/internal/feed/handler.go | 11 +++++ backend/internal/social/handler.go | 7 +++ backend/internal/video/comment_handler.go | 3 ++ backend/internal/video/like_handler.go | 3 ++ backend/internal/video/video_handler.go | 3 ++ frontend/src/api/comment.ts | 6 ++- frontend/src/api/feed.ts | 21 +++++---- frontend/src/api/normalize.ts | 57 +++++++++++++++++++++++ frontend/src/api/social.ts | 11 +++-- frontend/src/api/video.ts | 6 ++- 10 files changed, 112 insertions(+), 16 deletions(-) create mode 100644 frontend/src/api/normalize.ts diff --git a/backend/internal/feed/handler.go b/backend/internal/feed/handler.go index 8c4378c..48a427d 100644 --- a/backend/internal/feed/handler.go +++ b/backend/internal/feed/handler.go @@ -37,6 +37,7 @@ func (f *FeedHandler) ListLatest(c *gin.Context) { c.JSON(500, gin.H{"error": err.Error()}) return } + feedItems.VideoList = nonNilFeedVideoItems(feedItems.VideoList) c.JSON(200, feedItems) } @@ -85,6 +86,7 @@ func (f *FeedHandler) ListLikesCount(c *gin.Context) { c.JSON(500, gin.H{"error": err.Error()}) return } + feedItems.VideoList = nonNilFeedVideoItems(feedItems.VideoList) c.JSON(200, feedItems) } @@ -110,6 +112,7 @@ func (f *FeedHandler) ListByFollowing(c *gin.Context) { c.JSON(500, gin.H{"error": err.Error()}) return } + feedItems.VideoList = nonNilFeedVideoItems(feedItems.VideoList) c.JSON(200, feedItems) } @@ -160,5 +163,13 @@ func (f *FeedHandler) ListByPopularity(c *gin.Context) { c.JSON(500, gin.H{"error": err.Error()}) return } + resp.VideoList = nonNilFeedVideoItems(resp.VideoList) c.JSON(200, resp) } + +func nonNilFeedVideoItems(items []FeedVideoItem) []FeedVideoItem { + if items == nil { + return []FeedVideoItem{} + } + return items +} diff --git a/backend/internal/social/handler.go b/backend/internal/social/handler.go index 4882bed..22e20bf 100644 --- a/backend/internal/social/handler.go +++ b/backend/internal/social/handler.go @@ -1,6 +1,7 @@ package social import ( + "feedsystem_video_go/internal/account" "feedsystem_video_go/internal/middleware/jwt" "net/http" @@ -89,6 +90,9 @@ func (h *SocialHandler) GetAllFollowers(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } + if followers == nil { + followers = []*account.Account{} + } c.JSON(http.StatusOK, GetAllFollowersResponse{Followers: followers}) } @@ -114,5 +118,8 @@ func (h *SocialHandler) GetAllVloggers(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } + if vloggers == nil { + vloggers = []*account.Account{} + } c.JSON(http.StatusOK, GetAllVloggersResponse{Vloggers: vloggers}) } diff --git a/backend/internal/video/comment_handler.go b/backend/internal/video/comment_handler.go index 021ae9a..0ed9a7b 100644 --- a/backend/internal/video/comment_handler.go +++ b/backend/internal/video/comment_handler.go @@ -90,5 +90,8 @@ func (h *CommentHandler) GetAllComments(c *gin.Context) { c.JSON(400, gin.H{"error": err.Error()}) return } + if comments == nil { + comments = []Comment{} + } c.JSON(200, comments) } diff --git a/backend/internal/video/like_handler.go b/backend/internal/video/like_handler.go index c0c0b62..66d595f 100644 --- a/backend/internal/video/like_handler.go +++ b/backend/internal/video/like_handler.go @@ -106,5 +106,8 @@ func (lh *LikeHandler) ListMyLikedVideos(c *gin.Context) { c.JSON(500, gin.H{"error": err.Error()}) return } + if videos == nil { + videos = []Video{} + } c.JSON(200, videos) } diff --git a/backend/internal/video/video_handler.go b/backend/internal/video/video_handler.go index 486bb08..3fd7576 100644 --- a/backend/internal/video/video_handler.go +++ b/backend/internal/video/video_handler.go @@ -207,6 +207,9 @@ func (vh *VideoHandler) ListByAuthorID(c *gin.Context) { c.JSON(400, gin.H{"error": err.Error()}) return } + if videos == nil { + videos = []Video{} + } c.JSON(200, videos) } diff --git a/frontend/src/api/comment.ts b/frontend/src/api/comment.ts index 4f14a9b..798c3d5 100644 --- a/frontend/src/api/comment.ts +++ b/frontend/src/api/comment.ts @@ -1,8 +1,10 @@ import { postJson } from './client' +import { normalizeCommentList } from './normalize' import type { Comment, MessageResponse } from './types' -export function listAll(videoId: number) { - return postJson('/comment/listAll', { video_id: videoId }) +export async function listAll(videoId: number) { + const comments = await postJson('/comment/listAll', { video_id: videoId }) + return normalizeCommentList(comments) } export function publish(videoId: number, content: string) { diff --git a/frontend/src/api/feed.ts b/frontend/src/api/feed.ts index 4c1b4bc..a7c8ad4 100644 --- a/frontend/src/api/feed.ts +++ b/frontend/src/api/feed.ts @@ -1,23 +1,28 @@ import { postJson } from './client' +import { normalizeFeedVideoList } from './normalize' import type { ListByFollowingResponse, ListByPopularityResponse, ListLatestResponse, ListLikesCountResponse } from './types' -export function listLatest(input: { limit: number; latest_time: number }) { - return postJson('/feed/listLatest', input) +export async function listLatest(input: { limit: number; latest_time: number }) { + const res = await postJson('/feed/listLatest', input) + return { ...res, video_list: normalizeFeedVideoList(res.video_list) } } -export function listLikesCount(input: { limit: number; likes_count_before?: number; id_before?: number }) { +export async function listLikesCount(input: { limit: number; likes_count_before?: number; id_before?: number }) { const body: Record = { limit: input.limit } if (typeof input.likes_count_before === 'number' || typeof input.id_before === 'number') { body.likes_count_before = input.likes_count_before ?? 0 body.id_before = input.id_before ?? 0 } - return postJson('/feed/listLikesCount', body) + const res = await postJson('/feed/listLikesCount', body) + return { ...res, video_list: normalizeFeedVideoList(res.video_list) } } -export function listByPopularity(input: { limit: number; as_of: number; offset: number }) { - return postJson('/feed/listByPopularity', input) +export async function listByPopularity(input: { limit: number; as_of: number; offset: number }) { + const res = await postJson('/feed/listByPopularity', input) + return { ...res, video_list: normalizeFeedVideoList(res.video_list) } } -export function listByFollowing(input: { limit: number; latest_time: number }) { - return postJson('/feed/listByFollowing', input, { authRequired: true }) +export async function listByFollowing(input: { limit: number; latest_time: number }) { + const res = await postJson('/feed/listByFollowing', input, { authRequired: true }) + return { ...res, video_list: normalizeFeedVideoList(res.video_list) } } diff --git a/frontend/src/api/normalize.ts b/frontend/src/api/normalize.ts new file mode 100644 index 0000000..1b06a34 --- /dev/null +++ b/frontend/src/api/normalize.ts @@ -0,0 +1,57 @@ +import type { Account, Comment, FeedAuthor, FeedVideoItem, Video } from './types' + +export function listOrEmpty(value: T[] | null | undefined): T[] { + return Array.isArray(value) ? value : [] +} + +export function normalizeAccount(value: Account | null | undefined): Account { + return { + id: Number(value?.id ?? 0), + username: value?.username || '匿名用户', + } +} + +function normalizeAuthor(value: FeedAuthor | null | undefined): FeedAuthor { + return { + id: Number(value?.id ?? 0), + username: value?.username || '匿名用户', + } +} + +export function normalizeFeedVideoItem(value: FeedVideoItem): FeedVideoItem { + return { + ...value, + author: normalizeAuthor(value.author), + title: value.title || '未命名视频', + description: value.description || '', + play_url: value.play_url || '', + cover_url: value.cover_url || '', + create_time: Number(value.create_time ?? 0), + likes_count: Number(value.likes_count ?? 0), + is_liked: Boolean(value.is_liked), + } +} + +export function normalizeFeedVideoList(value: FeedVideoItem[] | null | undefined): FeedVideoItem[] { + return listOrEmpty(value).map(normalizeFeedVideoItem) +} + +export function normalizeVideoList(value: Video[] | null | undefined): Video[] { + return listOrEmpty(value).map((video) => ({ + ...video, + username: video.username || '匿名用户', + title: video.title || '未命名视频', + description: video.description || '', + play_url: video.play_url || '', + cover_url: video.cover_url || '', + likes_count: Number(video.likes_count ?? 0), + })) +} + +export function normalizeCommentList(value: Comment[] | null | undefined): Comment[] { + return listOrEmpty(value).map((comment) => ({ + ...comment, + username: comment.username || '匿名用户', + content: comment.content || '', + })) +} diff --git a/frontend/src/api/social.ts b/frontend/src/api/social.ts index e6d3b4c..6af442e 100644 --- a/frontend/src/api/social.ts +++ b/frontend/src/api/social.ts @@ -1,4 +1,5 @@ import { postJson } from './client' +import { listOrEmpty, normalizeAccount } from './normalize' import type { GetAllFollowersResponse, GetAllVloggersResponse, MessageResponse } from './types' export function follow(vloggerId: number) { @@ -9,18 +10,20 @@ export function unfollow(vloggerId: number) { return postJson('/social/unfollow', { vlogger_id: vloggerId }, { authRequired: true }) } -export function getAllFollowers(vloggerId?: number) { - return postJson( +export async function getAllFollowers(vloggerId?: number) { + const res = await postJson( '/social/getAllFollowers', vloggerId ? { vlogger_id: vloggerId } : {}, { authRequired: true }, ) + return { ...res, followers: listOrEmpty(res.followers).map(normalizeAccount) } } -export function getAllVloggers(followerId?: number) { - return postJson( +export async function getAllVloggers(followerId?: number) { + const res = await postJson( '/social/getAllVloggers', followerId ? { follower_id: followerId } : {}, { authRequired: true }, ) + return { ...res, vloggers: listOrEmpty(res.vloggers).map(normalizeAccount) } } diff --git a/frontend/src/api/video.ts b/frontend/src/api/video.ts index 85e8781..21b2727 100644 --- a/frontend/src/api/video.ts +++ b/frontend/src/api/video.ts @@ -1,4 +1,5 @@ import { postForm, postJson } from './client' +import { normalizeVideoList } from './normalize' import type { Video } from './types' export function publishVideo(input: { title: string; description: string; play_url: string; cover_url: string }) { @@ -19,8 +20,9 @@ export function uploadCover(file: File) { return postForm('/video/uploadCover', fd, { authRequired: true }) } -export function listByAuthorId(authorId: number) { - return postJson('/video/listByAuthorID', { author_id: authorId }) +export async function listByAuthorId(authorId: number) { + const videos = await postJson('/video/listByAuthorID', { author_id: authorId }) + return normalizeVideoList(videos) } export function getDetail(id: number) {