Make posts appear in feed

This commit is contained in:
Alex Erdei 2025-05-02 13:58:30 +01:00
parent 187bc6fde2
commit e35464dcfa
4 changed files with 157 additions and 19 deletions

View File

@ -8,7 +8,7 @@ import outgoingMessagesReducer from "../features/outgoingMessages/outgoingMessag
import linkReducer from "../features/link/linkSlice";
import accountPageReducer from "../features/accountPage/accountPageSlice";
export default configureStore({
const store = configureStore({
reducer: {
user: userReducer,
currentUser: currentUserReducer,
@ -20,3 +20,17 @@ export default configureStore({
accountPage: accountPageReducer,
},
});
if (import.meta.env.DEV) {
store.subscribe(() => {
const s = store.getState();
console.log("[DEBUG] users:", s.users);
console.log("[DEBUG] currentUser:", s.currentUser);
console.log("[DEBUG] posts:", s.posts);
});
}
export default store;

View File

@ -22,7 +22,7 @@ import { currentUserUpdated } from "../features/currentUser/currentUserSlice";
import { usersUpdated } from "../features/users/usersSlice";
import { postsUpdated } from "../features/posts/postsSlice";
import { postsLoaded, postsUpdated } from "../features/posts/postsSlice";
import { incomingMessagesUpdated } from "../features/incomingMessages/incomingMessagesSlice";
@ -40,6 +40,8 @@ const LOGIN_URL = `${API_BASE}/login`;
const USERS_URL = `${API_BASE}/users`;
const POSTS_URL = `${API_BASE}/posts`;
const LS_TOKEN = "fakebook.jwt";
const LS_USER_ID = "fakebook.user_id";
@ -149,6 +151,16 @@ export function subscribeAuth() {
isEmailVerified: !!u.isEmailVerified,
})
);
// ------------------------------------------------------------------
// cold-start: get the full feed once
// ------------------------------------------------------------------
subscribePosts(); // <—— fetches /posts and dispatches postsLoaded
currentUserOnline(); // mark myself online immediately
} catch (err) {
console.warn("[Auth] subscribeAuth failed:", err.message);
@ -229,6 +241,8 @@ export async function signInUser(user) {
);
store.dispatch(errorOccured(""));
currentUserOnline(); // mark myself online immediately
} catch (err) {
store.dispatch(errorOccured(err.message));
@ -314,6 +328,14 @@ function openSocket() {
hub.on("fakebook.users.put", (raw) => {
store.dispatch(usersUpdated([parseMsg(raw)]));
});
hub.on("fakebook.posts.post", (raw) =>
store.dispatch(postsUpdated([JSON.parse(raw)]))
);
hub.on("fakebook.posts.put", (raw) =>
store.dispatch(postsUpdated([JSON.parse(raw)]))
);
})
.catch((err) => {
@ -510,21 +532,36 @@ export function subscribeUsers() {
};
}
/* ------------------------- posts ------------------------------------- */
/* ------------------------------------------------------------------ */
/* posts subscription: replaces old in-memory mock version */
/* ------------------------------------------------------------------ */
export function subscribePosts() {
setTimeout(() => {
store.dispatch(
postsUpdated(
DB.posts.map((p) => ({
...p,
timestamp: new Date(p.timestamp).toLocaleString(),
}))
)
);
}, 0);
let cancelled = false;
return () => {};
(async () => {
try {
const token = localStorage.getItem(LS_TOKEN);
const arr = await $fetch(`${POSTS_URL}?limit=-1`, {
headers: token ? { Authorization: `Bearer ${token}` } : {},
});
if (!cancelled) {
store.dispatch(postsLoaded(arr)); // full list once
}
} catch (err) {
console.warn("[subscribePosts] failed:", err.message);
}
})();
/* return unsubscribe fn (keeps contract identical) */
return () => {
cancelled = true;
};
}
export async function upload(post) {

View File

@ -1,17 +1,65 @@
import { createSlice } from "@reduxjs/toolkit";
import mapRestPost from "../../utils/mapRestPost";
export const postsSlice = createSlice({
name: "posts",
initialState: [],
initialState: [], // still an array
reducers: {
/* full reload (initial screen) --------------------------------- */
postsLoaded: (_, action) => action.payload.map(mapRestPost),
/* incremental updates: insert new or patch existing ------------ */
postsUpdated: (state, action) => {
const updatedState = [];
action.payload.forEach((post) => updatedState.push(post));
return updatedState;
const incoming = action.payload; // array of raw rows
incoming.forEach((raw) => {
const postID = raw.post_id ?? raw.postID;
const idx = state.findIndex((p) => p.postID === postID);
if (idx === -1) {
/* NEW */
state.push(mapRestPost(raw));
return;
}
/* MERGE PARTIAL UPDATE */
const cur = state[idx];
const next = { ...cur };
if (raw.text !== undefined) next.text = raw.text;
if (raw.photoURL !== undefined) next.photoURL = raw.photoURL;
if (raw.youtubeURL !== undefined) next.youtubeURL = raw.youtubeURL;
if (raw.isPhoto !== undefined) next.isPhoto = !!raw.isPhoto;
if (raw.isYoutube !== undefined) next.isYoutube = !!raw.isYoutube;
if (raw.comments !== undefined)
next.comments = JSON.parse(raw.comments);
if (raw.likes !== undefined) next.likes = JSON.parse(raw.likes);
if (raw.timestamp !== undefined)
next.timestamp = new Date(raw.timestamp);
state[idx] = next;
});
},
},
});
export const { postsUpdated } = postsSlice.actions;
export const { postsLoaded, postsUpdated } = postsSlice.actions;
export default postsSlice.reducer;

39
src/utils/mapRestPost.js Normal file
View File

@ -0,0 +1,39 @@
/* Converts the “Magic / Firebase row into the shape the UI expects.
comments / likes arrive as JSON-encoded strings
timestamp comes as 2025-03-18 14:18:43 (UTC); we store it as Date
isPhoto / isYoutube arrive as 0|1 (or false|true) boolean
*/
export default function mapRestPost(row) {
/* helper coerce all falsy / 0 / "0" / false values to boolean */
const bool = (v) => v === true || v === 1 || v === "1";
return {
postID: row.post_id,
userID: row.user_id,
text: row.text,
photoURL: row.photoURL,
youtubeURL: row.youtubeURL,
isPhoto: bool(row.isPhoto),
isYoutube: bool(row.isYoutube),
comments: JSON.parse(row.comments || "[]"),
likes: JSON.parse(row.likes || "[]"),
/* keep the original ISO string too if you need it elsewhere */
timestamp: new Date(row.timestamp).toLocaleString(),
};
}