280 lines
8.2 KiB
JavaScript
280 lines
8.2 KiB
JavaScript
const { requireLogin } = require("../../utils/auth");
|
||
const { del, get, post } = require("../../utils/api");
|
||
const { getEnabledModules, getActiveClassId, showError } = require("../../utils/page-helpers");
|
||
const { isModuleEnabled } = require("../../utils/modules");
|
||
|
||
function initialOf(name) {
|
||
return String(name || "班").slice(0, 1);
|
||
}
|
||
|
||
function formatTime(value) {
|
||
return String(value || "").replace("T", " ").slice(0, 16);
|
||
}
|
||
|
||
function sameId(left, right) {
|
||
return Number(left) === Number(right);
|
||
}
|
||
|
||
function datasetBoolean(value) {
|
||
return value === true || value === "true" || value === 1 || value === "1";
|
||
}
|
||
|
||
function normalizeTimeline(item, comments = [], currentUserId = null) {
|
||
return {
|
||
...item,
|
||
author_initial: initialOf(item.author_name),
|
||
created_at_text: formatTime(item.created_at),
|
||
like_action_text: item.has_liked ? "已赞" : "赞",
|
||
like_action_class: item.has_liked ? "active" : "",
|
||
comments: comments.map((comment) => ({
|
||
...comment,
|
||
created_at_text: formatTime(comment.created_at),
|
||
can_delete: sameId(currentUserId, comment.author_id)
|
||
}))
|
||
};
|
||
}
|
||
|
||
Page({
|
||
data: {
|
||
timelines: [],
|
||
timelineEnabled: false,
|
||
commentPostId: null,
|
||
commentText: "",
|
||
replyToName: "",
|
||
commentPlaceholder: "写评论",
|
||
commentInputFocus: false,
|
||
keyboardOpen: false,
|
||
loading: false,
|
||
likingId: null,
|
||
commenting: false
|
||
},
|
||
|
||
onShow() {
|
||
if (!requireLogin()) return;
|
||
this.load();
|
||
},
|
||
|
||
async load() {
|
||
const enabledModules = getEnabledModules();
|
||
const classId = getActiveClassId();
|
||
const timelineEnabled = isModuleEnabled("timeline", enabledModules);
|
||
if (!timelineEnabled) {
|
||
this.setData({ timelines: [], timelineEnabled: false });
|
||
return;
|
||
}
|
||
this.setData({ loading: true });
|
||
try {
|
||
const currentUser = getApp().globalData.user || wx.getStorageSync("auth_user") || {};
|
||
const res = await get("/api/timeline/", { page_size: 20, class_id: classId });
|
||
const commentResults = await Promise.all(
|
||
(res.items || []).map((item) =>
|
||
get(`/api/timeline/${item.id}/comments`, { page_size: 6 }).catch(() => ({ items: [] }))
|
||
)
|
||
);
|
||
const timelines = (res.items || []).map((item, index) => (
|
||
normalizeTimeline(item, commentResults[index].items || [], currentUser.id)
|
||
));
|
||
this.setData({ timelines, timelineEnabled: true });
|
||
} catch (error) {
|
||
showError(error);
|
||
} finally {
|
||
this.setData({ loading: false });
|
||
}
|
||
},
|
||
|
||
openTimeline(event) {
|
||
wx.navigateTo({ url: `/pages/timeline-detail/index?id=${event.currentTarget.dataset.id}` });
|
||
},
|
||
|
||
openCompose() {
|
||
wx.navigateTo({ url: "/pages/timeline-create/index" });
|
||
},
|
||
|
||
previewImage(event) {
|
||
const current = event.currentTarget.dataset.src;
|
||
const postId = Number(event.currentTarget.dataset.postId);
|
||
const postItem = this.data.timelines.find((item) => item.id === postId);
|
||
const urls = postItem && postItem.image_urls ? postItem.image_urls : [];
|
||
if (!current || !urls.length) return;
|
||
wx.previewImage({ current, urls });
|
||
},
|
||
|
||
async toggleLike(event) {
|
||
const id = Number(event.currentTarget.dataset.id);
|
||
if (!id || this.data.likingId) return;
|
||
this.setData({ likingId: id });
|
||
try {
|
||
const result = await post(`/api/timeline/${id}/like`);
|
||
const timelines = this.data.timelines.map((item) => {
|
||
if (item.id !== id) return item;
|
||
return normalizeTimeline({
|
||
...item,
|
||
has_liked: Boolean(result.liked),
|
||
like_count: Number(result.like_count || 0)
|
||
}, item.comments || [], (getApp().globalData.user || wx.getStorageSync("auth_user") || {}).id);
|
||
});
|
||
this.setData({ timelines });
|
||
} catch (error) {
|
||
showError(error, "操作失败");
|
||
} finally {
|
||
this.setData({ likingId: null });
|
||
}
|
||
},
|
||
|
||
openComment(event) {
|
||
const id = Number(event.currentTarget.dataset.id);
|
||
const postItem = this.data.timelines.find((item) => item.id === id);
|
||
this.setData({
|
||
commentPostId: id,
|
||
commentText: "",
|
||
replyToName: "",
|
||
commentPlaceholder: postItem ? `评论 ${postItem.author_name}` : "写评论",
|
||
commentInputFocus: false
|
||
}, () => {
|
||
this.focusCommentInput();
|
||
});
|
||
},
|
||
|
||
replyComment(event) {
|
||
const postId = Number(event.currentTarget.dataset.postId);
|
||
const commentId = Number(event.currentTarget.dataset.commentId);
|
||
const name = event.currentTarget.dataset.name || "";
|
||
const canDelete = datasetBoolean(event.currentTarget.dataset.canDelete);
|
||
if (canDelete && commentId) {
|
||
this.openOwnCommentActions(postId, commentId, name);
|
||
return;
|
||
}
|
||
this.startReply(postId, name);
|
||
},
|
||
|
||
startReply(postId, name) {
|
||
this.setData({
|
||
commentPostId: postId,
|
||
commentText: "",
|
||
replyToName: name,
|
||
commentPlaceholder: name ? `回复 ${name}` : "写评论",
|
||
commentInputFocus: false
|
||
}, () => {
|
||
this.focusCommentInput();
|
||
});
|
||
},
|
||
|
||
closeComment() {
|
||
if (this.data.commenting) return;
|
||
this.setData({
|
||
commentPostId: null,
|
||
commentText: "",
|
||
replyToName: "",
|
||
commentInputFocus: false,
|
||
keyboardOpen: false
|
||
});
|
||
},
|
||
|
||
noop() {},
|
||
|
||
onCommentInput(event) {
|
||
this.setData({ commentText: event.detail.value });
|
||
},
|
||
|
||
onCommentFocus(event) {
|
||
const height = event.detail.height || 0;
|
||
this.setData({
|
||
commentInputFocus: true,
|
||
keyboardOpen: height > 0
|
||
});
|
||
},
|
||
|
||
onKeyboardHeightChange(event) {
|
||
const height = event.detail.height || 0;
|
||
this.setData({ keyboardOpen: height > 0 });
|
||
},
|
||
|
||
onCommentBlur() {
|
||
this.setData({ commentInputFocus: false, keyboardOpen: false });
|
||
},
|
||
|
||
focusCommentInput() {
|
||
const focus = () => {
|
||
if (this.data.commentPostId) {
|
||
this.setData({ commentInputFocus: true });
|
||
}
|
||
};
|
||
if (wx.nextTick) {
|
||
wx.nextTick(() => setTimeout(focus, 120));
|
||
return;
|
||
}
|
||
setTimeout(focus, 120);
|
||
},
|
||
|
||
async submitComment() {
|
||
const postId = this.data.commentPostId;
|
||
const text = this.data.commentText.trim();
|
||
if (!postId || !text) {
|
||
wx.showToast({ title: "请输入评论", icon: "none" });
|
||
return;
|
||
}
|
||
const content = this.data.replyToName ? `回复 @${this.data.replyToName}:${text}` : text;
|
||
this.setData({ commenting: true });
|
||
try {
|
||
await post(`/api/timeline/${postId}/comments`, { content });
|
||
const detail = await get(`/api/timeline/${postId}`);
|
||
const currentUser = getApp().globalData.user || wx.getStorageSync("auth_user") || {};
|
||
const timelines = this.data.timelines.map((item) => (
|
||
item.id === postId ? normalizeTimeline(detail, detail.comments || [], currentUser.id) : item
|
||
));
|
||
this.setData({
|
||
timelines,
|
||
commentPostId: null,
|
||
commentText: "",
|
||
replyToName: "",
|
||
commentInputFocus: false,
|
||
keyboardOpen: false
|
||
});
|
||
wx.showToast({ title: "已评论", icon: "success" });
|
||
} catch (error) {
|
||
showError(error, "评论失败");
|
||
} finally {
|
||
this.setData({ commenting: false });
|
||
}
|
||
},
|
||
|
||
deleteComment(postId, commentId) {
|
||
wx.showModal({
|
||
title: "删除评论",
|
||
content: "确认删除这条评论?",
|
||
confirmText: "删除",
|
||
confirmColor: "#b42318",
|
||
success: async (res) => {
|
||
if (!res.confirm) return;
|
||
try {
|
||
await del(`/api/timeline/comments/${commentId}`);
|
||
const detail = await get(`/api/timeline/${postId}`);
|
||
const currentUser = getApp().globalData.user || wx.getStorageSync("auth_user") || {};
|
||
const timelines = this.data.timelines.map((item) => (
|
||
item.id === postId ? normalizeTimeline(detail, detail.comments || [], currentUser.id) : item
|
||
));
|
||
this.setData({ timelines });
|
||
wx.showToast({ title: "已删除", icon: "success" });
|
||
} catch (error) {
|
||
showError(error, "删除失败");
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
openOwnCommentActions(postId, commentId, name) {
|
||
wx.showActionSheet({
|
||
itemList: ["回复", "删除评论"],
|
||
itemColor: "#6b1f2b",
|
||
success: (res) => {
|
||
if (res.tapIndex === 0) {
|
||
this.startReply(postId, name);
|
||
}
|
||
if (res.tapIndex === 1) {
|
||
this.deleteComment(postId, commentId);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
});
|