feat: Add app_id parameter to engagement functions for proper routing
All engagement functions (like, truth vote, share, view, comment) now accept app_id in addition to guid. This enables the backend to route requests to the correct system based on app_id. - news-global → routes to news-app (ES-based) - no app_id → routes to native post system (PostgreSQL/YCQL) Future apps can be routed based on their app_id. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
5d5fae63c5
commit
cd9ab30ab7
|
|
@ -415,10 +415,11 @@ export async function fetchPostTruth(postId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function votePostTruth(postId, vote, guid = null) {
|
export async function votePostTruth(postId, vote, guid = null, appId = null) {
|
||||||
if (!postId && !guid) throw new Error("post_id or guid required");
|
if (!postId && !guid) throw new Error("post_id or guid required");
|
||||||
const payload = { post_id: postId, vote };
|
const payload = { post_id: postId, vote };
|
||||||
if (guid) payload.guid = guid;
|
if (guid) payload.guid = guid;
|
||||||
|
if (appId) payload.app_id = appId;
|
||||||
const body = JSON.stringify(payload);
|
const body = JSON.stringify(payload);
|
||||||
const res = await fetch(`${apiBase()}/post/truth-vote`, {
|
const res = await fetch(`${apiBase()}/post/truth-vote`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|
@ -778,11 +779,12 @@ export async function fetchProfile(username = "") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function recordPostView(postId, guid = null) {
|
export async function recordPostView(postId, guid = null, appId = null) {
|
||||||
if (!postId && !guid) return { ok: false };
|
if (!postId && !guid) return { ok: false };
|
||||||
try {
|
try {
|
||||||
const body = { post_id: postId };
|
const body = { post_id: postId };
|
||||||
if (guid) body.guid = guid;
|
if (guid) body.guid = guid;
|
||||||
|
if (appId) body.app_id = appId;
|
||||||
const res = await fetch(`${apiBase()}/post/view`, {
|
const res = await fetch(`${apiBase()}/post/view`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
|
|
@ -796,11 +798,12 @@ export async function recordPostView(postId, guid = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function togglePostLike(postId, like = true, guid = null) {
|
export async function togglePostLike(postId, like = true, guid = null, appId = null) {
|
||||||
if (!postId && !guid) return { ok: false };
|
if (!postId && !guid) return { ok: false };
|
||||||
try {
|
try {
|
||||||
const body = { post_id: postId, like };
|
const body = { post_id: postId, like };
|
||||||
if (guid) body.guid = guid;
|
if (guid) body.guid = guid;
|
||||||
|
if (appId) body.app_id = appId;
|
||||||
const res = await fetch(`${apiBase()}/post/like`, {
|
const res = await fetch(`${apiBase()}/post/like`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json", ...authHeaders() },
|
headers: { "Content-Type": "application/json", ...authHeaders() },
|
||||||
|
|
@ -814,11 +817,12 @@ export async function togglePostLike(postId, like = true, guid = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function recordPostShare(postId, guid = null) {
|
export async function recordPostShare(postId, guid = null, appId = null) {
|
||||||
if (!postId && !guid) return { ok: false };
|
if (!postId && !guid) return { ok: false };
|
||||||
try {
|
try {
|
||||||
const body = { post_id: postId };
|
const body = { post_id: postId };
|
||||||
if (guid) body.guid = guid;
|
if (guid) body.guid = guid;
|
||||||
|
if (appId) body.app_id = appId;
|
||||||
const res = await fetch(`${apiBase()}/post/share`, {
|
const res = await fetch(`${apiBase()}/post/share`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
|
|
@ -832,11 +836,12 @@ export async function recordPostShare(postId, guid = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchPostComments(postId, limit = 20, guid = null) {
|
export async function fetchPostComments(postId, limit = 20, guid = null, appId = null) {
|
||||||
if (!postId && !guid) return [];
|
if (!postId && !guid) return [];
|
||||||
const qs = new URLSearchParams();
|
const qs = new URLSearchParams();
|
||||||
qs.set("post_id", String(postId));
|
qs.set("post_id", String(postId));
|
||||||
if (guid) qs.set("guid", guid);
|
if (guid) qs.set("guid", guid);
|
||||||
|
if (appId) qs.set("app_id", appId);
|
||||||
if (limit) qs.set("limit", String(limit));
|
if (limit) qs.set("limit", String(limit));
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${apiBase()}/post/comments?${qs.toString()}`, {
|
const res = await fetch(`${apiBase()}/post/comments?${qs.toString()}`, {
|
||||||
|
|
@ -850,12 +855,13 @@ export async function fetchPostComments(postId, limit = 20, guid = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createPostComment(postId, body, guid = null) {
|
export async function createPostComment(postId, body, guid = null, appId = null) {
|
||||||
if (!postId && !guid) return { ok: false };
|
if (!postId && !guid) return { ok: false };
|
||||||
if (!body) return { ok: false };
|
if (!body) return { ok: false };
|
||||||
try {
|
try {
|
||||||
const reqBody = { post_id: postId, body };
|
const reqBody = { post_id: postId, body };
|
||||||
if (guid) reqBody.guid = guid;
|
if (guid) reqBody.guid = guid;
|
||||||
|
if (appId) reqBody.app_id = appId;
|
||||||
const res = await fetch(`${apiBase()}/post/comment`, {
|
const res = await fetch(`${apiBase()}/post/comment`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json", ...authHeaders() },
|
headers: { "Content-Type": "application/json", ...authHeaders() },
|
||||||
|
|
|
||||||
|
|
@ -1812,7 +1812,7 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
||||||
|
|
||||||
if ((postID > 0 || postData?.guid) && !modalWrap.__swViewed) {
|
if ((postID > 0 || postData?.guid) && !modalWrap.__swViewed) {
|
||||||
modalWrap.__swViewed = true;
|
modalWrap.__swViewed = true;
|
||||||
recordPostView(postID, postData?.guid).then((res) => {
|
recordPostView(postID, postData?.guid, postData?.app_id).then((res) => {
|
||||||
const counts = res?.counts;
|
const counts = res?.counts;
|
||||||
if (counts) {
|
if (counts) {
|
||||||
updateStat(modalWrap, "views", counts.view_count);
|
updateStat(modalWrap, "views", counts.view_count);
|
||||||
|
|
@ -1858,7 +1858,7 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
||||||
}
|
}
|
||||||
trackEvent("post_like_click", { post_id: postID, context: "map" });
|
trackEvent("post_like_click", { post_id: postID, context: "map" });
|
||||||
likeBtn.disabled = true;
|
likeBtn.disabled = true;
|
||||||
const res = await togglePostLike(postID, !liked);
|
const res = await togglePostLike(postID, !liked, postData?.guid, postData?.app_id);
|
||||||
if (res?.ok) {
|
if (res?.ok) {
|
||||||
liked = !!res.liked;
|
liked = !!res.liked;
|
||||||
likeBtn.textContent = liked ? "Liked" : "Like";
|
likeBtn.textContent = liked ? "Liked" : "Like";
|
||||||
|
|
@ -1889,7 +1889,7 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
||||||
await navigator.clipboard.writeText(shareUrl);
|
await navigator.clipboard.writeText(shareUrl);
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
const res = await recordPostShare(postID, postData?.guid);
|
const res = await recordPostShare(postID, postData?.guid, postData?.app_id);
|
||||||
const counts = res?.counts;
|
const counts = res?.counts;
|
||||||
if (counts) {
|
if (counts) {
|
||||||
updateStat(modalWrap, "views", counts.view_count);
|
updateStat(modalWrap, "views", counts.view_count);
|
||||||
|
|
@ -1945,7 +1945,7 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
trackEvent("truth_vote_click", { post_id: postID || postData?.guid, vote, context: "map" });
|
trackEvent("truth_vote_click", { post_id: postID || postData?.guid, vote, context: "map" });
|
||||||
const stats = await votePostTruth(postID, vote, postData?.guid);
|
const stats = await votePostTruth(postID, vote, postData?.guid, postData?.app_id);
|
||||||
if (stats) {
|
if (stats) {
|
||||||
currentVote = vote;
|
currentVote = vote;
|
||||||
updateButtonStyles();
|
updateButtonStyles();
|
||||||
|
|
@ -2188,7 +2188,7 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
||||||
|
|
||||||
const commentsBody = modalWrap.querySelector(".sw-livechat-body");
|
const commentsBody = modalWrap.querySelector(".sw-livechat-body");
|
||||||
if (commentsBody && postID > 0) {
|
if (commentsBody && postID > 0) {
|
||||||
fetchPostComments(postID, 20).then((items) => renderComments(commentsBody, items));
|
fetchPostComments(postID, 20, postData?.guid, postData?.app_id).then((items) => renderComments(commentsBody, items));
|
||||||
}
|
}
|
||||||
if (commentBtn && commentInput && postID > 0) {
|
if (commentBtn && commentInput && postID > 0) {
|
||||||
commentBtn.addEventListener("click", async () => {
|
commentBtn.addEventListener("click", async () => {
|
||||||
|
|
@ -2200,10 +2200,10 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
||||||
if (!body) return;
|
if (!body) return;
|
||||||
trackEvent("post_comment_submit", { post_id: postID, context: "map" });
|
trackEvent("post_comment_submit", { post_id: postID, context: "map" });
|
||||||
commentBtn.disabled = true;
|
commentBtn.disabled = true;
|
||||||
const res = await createPostComment(postID, body, postData?.guid);
|
const res = await createPostComment(postID, body, postData?.guid, postData?.app_id);
|
||||||
if (res?.ok) {
|
if (res?.ok) {
|
||||||
commentInput.value = "";
|
commentInput.value = "";
|
||||||
const existing = await fetchPostComments(postID, 20, postData?.guid);
|
const existing = await fetchPostComments(postID, 20, postData?.guid, postData?.app_id);
|
||||||
renderComments(commentsBody, existing);
|
renderComments(commentsBody, existing);
|
||||||
const counts = res?.counts;
|
const counts = res?.counts;
|
||||||
if (counts) {
|
if (counts) {
|
||||||
|
|
|
||||||
|
|
@ -624,7 +624,7 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
|
||||||
// Initial data fetch
|
// Initial data fetch
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!postId) return;
|
if (!postId) return;
|
||||||
recordPostView(postId, post?.guid).then(res => {
|
recordPostView(postId, post?.guid, post?.app_id).then(res => {
|
||||||
if (res?.counts) setCounts(c => ({ ...c, views: res.counts.view_count || c.views, likes: res.counts.like_count || c.likes }));
|
if (res?.counts) setCounts(c => ({ ...c, views: res.counts.view_count || c.views, likes: res.counts.like_count || c.likes }));
|
||||||
});
|
});
|
||||||
fetchPostLikeState(postId).then(res => { if (res?.liked !== undefined) setLiked(res.liked); });
|
fetchPostLikeState(postId).then(res => { if (res?.liked !== undefined) setLiked(res.liked); });
|
||||||
|
|
@ -690,18 +690,18 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
|
||||||
const newLiked = !liked;
|
const newLiked = !liked;
|
||||||
setLiked(newLiked);
|
setLiked(newLiked);
|
||||||
setCounts(c => ({ ...c, likes: c.likes + (newLiked ? 1 : -1) }));
|
setCounts(c => ({ ...c, likes: c.likes + (newLiked ? 1 : -1) }));
|
||||||
const res = await togglePostLike(postId, newLiked, post?.guid);
|
const res = await togglePostLike(postId, newLiked, post?.guid, post?.app_id);
|
||||||
if (res?.counts) setCounts(c => ({ ...c, likes: res.counts.like_count ?? c.likes }));
|
if (res?.counts) setCounts(c => ({ ...c, likes: res.counts.like_count ?? c.likes }));
|
||||||
}, [postId, liked, post?.guid]);
|
}, [postId, liked, post?.guid, post?.app_id]);
|
||||||
|
|
||||||
const handleShare = useCallback(async () => {
|
const handleShare = useCallback(async () => {
|
||||||
if (!postId) return;
|
if (!postId) return;
|
||||||
const shareUrl = `${window.location.origin}/p/${postId}`;
|
const shareUrl = `${window.location.origin}/p/${postId}`;
|
||||||
if (navigator.share) { try { await navigator.share({ title: post?.title, url: shareUrl }); } catch {} }
|
if (navigator.share) { try { await navigator.share({ title: post?.title, url: shareUrl }); } catch {} }
|
||||||
else { navigator.clipboard?.writeText(shareUrl); }
|
else { navigator.clipboard?.writeText(shareUrl); }
|
||||||
const res = await recordPostShare(postId, post?.guid);
|
const res = await recordPostShare(postId, post?.guid, post?.app_id);
|
||||||
if (res?.counts) setCounts(c => ({ ...c, shares: res.counts.share_count ?? c.shares }));
|
if (res?.counts) setCounts(c => ({ ...c, shares: res.counts.share_count ?? c.shares }));
|
||||||
}, [postId, post?.title, post?.guid]);
|
}, [postId, post?.title, post?.guid, post?.app_id]);
|
||||||
|
|
||||||
const handleTruthVote = useCallback(async (vote) => {
|
const handleTruthVote = useCallback(async (vote) => {
|
||||||
if (!postId) return;
|
if (!postId) return;
|
||||||
|
|
@ -709,9 +709,9 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
|
||||||
window.dispatchEvent(new CustomEvent("sociowire:auth-required"));
|
window.dispatchEvent(new CustomEvent("sociowire:auth-required"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const res = await votePostTruth(postId, vote, post?.guid);
|
const res = await votePostTruth(postId, vote, post?.guid, post?.app_id);
|
||||||
if (res) setTruth({ score: res.truth_score ?? res.user_score ?? truth.score, count: res.vote_count ?? truth.count });
|
if (res) setTruth({ score: res.truth_score ?? res.user_score ?? truth.score, count: res.vote_count ?? truth.count });
|
||||||
}, [postId, truth, token, post?.guid]);
|
}, [postId, truth, token, post?.guid, post?.app_id]);
|
||||||
|
|
||||||
const handleComment = useCallback(async () => {
|
const handleComment = useCallback(async () => {
|
||||||
const body = commentInput.trim();
|
const body = commentInput.trim();
|
||||||
|
|
@ -727,7 +727,7 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
|
||||||
// Retry logic - try up to 3 times with delay
|
// Retry logic - try up to 3 times with delay
|
||||||
let res = null;
|
let res = null;
|
||||||
for (let attempt = 0; attempt < 3; attempt++) {
|
for (let attempt = 0; attempt < 3; attempt++) {
|
||||||
res = await createPostComment(postId, body, post?.guid);
|
res = await createPostComment(postId, body, post?.guid, post?.app_id);
|
||||||
if (res?.ok) break;
|
if (res?.ok) break;
|
||||||
if (attempt < 2) await new Promise(r => setTimeout(r, 500)); // wait before retry
|
if (attempt < 2) await new Promise(r => setTimeout(r, 500)); // wait before retry
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue