Make user authentication workflow work
This commit is contained in:
parent
dd8f88d749
commit
03ecf07c6a
|
@ -1,20 +1,8 @@
|
|||
/* backend.js – FIRST MIGRATION STEP
|
||||
/* =====================================================================
|
||||
|
||||
------------------------------------
|
||||
Fakebook — back-end file (AUTH + mock social features)
|
||||
|
||||
0. NO Firebase imports at all.
|
||||
|
||||
1. Export *exactly* the same function names the UI already uses.
|
||||
|
||||
2. Provide no-op or mock implementations that fulfil those contracts.
|
||||
|
||||
3. Tiny in-memory cache (+ localStorage) so screens have something
|
||||
|
||||
to display and don’t blow up on undefined values.
|
||||
|
||||
*/
|
||||
|
||||
/* Redux store + slices stay exactly as they were */
|
||||
===================================================================== */
|
||||
|
||||
import store from "../app/store";
|
||||
|
||||
|
@ -22,8 +10,8 @@ import {
|
|||
signIn,
|
||||
signOut,
|
||||
errorOccured,
|
||||
loadingFinished,
|
||||
loadingStarted,
|
||||
loadingFinished,
|
||||
} from "../features/user/userSlice";
|
||||
|
||||
import { currentUserUpdated } from "../features/currentUser/currentUserSlice";
|
||||
|
@ -36,134 +24,490 @@ import { incomingMessagesUpdated } from "../features/incomingMessages/incomingMe
|
|||
|
||||
import { outgoingMessagesUpdated } from "../features/outgoingMessages/outgoingMessagesSlice";
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* --------------------------- CONSTANTS -------------------------------- */
|
||||
|
||||
/* Small helper utilities */
|
||||
const API_BASE = "https://alexerdei-team.us.ainiro.io/magic/modules/fakebook";
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
const REGISTER_URL = `${API_BASE}/register`;
|
||||
|
||||
const LOGIN_URL = `${API_BASE}/login`;
|
||||
|
||||
const USERS_URL = `${API_BASE}/users`;
|
||||
|
||||
const LS_TOKEN = "fakebook.jwt";
|
||||
|
||||
const LS_USER_ID = "fakebook.user_id";
|
||||
|
||||
/* --------------------------- Utilities -------------------------------- */
|
||||
|
||||
const genId = () => Math.random().toString(36).slice(2, 11);
|
||||
|
||||
const delay = (ms = 250) => new Promise((r) => setTimeout(r, ms));
|
||||
const delay = (ms = 200) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
/* Persist minimal mock DB so a reload keeps state (optional) */
|
||||
async function $fetch(url, opts = {}) {
|
||||
const res = await fetch(url, {
|
||||
headers: { "Content-Type": "application/json", ...(opts.headers || {}) },
|
||||
...opts,
|
||||
});
|
||||
|
||||
const LS_KEY = "__fakebook_mock_db__";
|
||||
const data = await res.json();
|
||||
|
||||
const loadDB = () => JSON.parse(localStorage.getItem(LS_KEY) || "{}");
|
||||
if (!res.ok) throw new Error(data.message || res.statusText);
|
||||
|
||||
const saveDB = (db) => localStorage.setItem(LS_KEY, JSON.stringify(db));
|
||||
return data;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* ---------------------- REST → UI mapper (moved up) ------------------ */
|
||||
|
||||
/* “Database” – lives in memory, persisted to localStorage */
|
||||
function mapRestUser(u) {
|
||||
return {
|
||||
userID: u.user_id,
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
firstname: u.firstname,
|
||||
|
||||
let DB = {
|
||||
currentUser: null, // { id, displayName, isEmailVerified }
|
||||
lastname: u.lastname,
|
||||
|
||||
users: [], // array of “profile” objects
|
||||
profilePictureURL: u.profilePictureURL,
|
||||
|
||||
posts: [], // array of posts
|
||||
backgroundPictureURL: u.backgroundPictureURL,
|
||||
|
||||
messages: [], // array of { id, sender, recipient, text, isRead, ... }
|
||||
photos: JSON.parse(u.photos || "[]"),
|
||||
|
||||
...loadDB(),
|
||||
posts: JSON.parse(u.posts || "[]"),
|
||||
|
||||
isOnline: !!u.isOnline,
|
||||
|
||||
isEmailVerified: !!u.isEmailVerified,
|
||||
|
||||
index: u.index ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
/* Convenience finders */
|
||||
/* ===================================================================== *
|
||||
|
||||
SECTION A — REAL AUTH WORKFLOW (FIXED) *
|
||||
|
||||
===================================================================== */
|
||||
|
||||
/* ---------------------- subscribeAuth -------------------------------- */
|
||||
|
||||
export function subscribeAuth() {
|
||||
store.dispatch(loadingStarted());
|
||||
|
||||
(async () => {
|
||||
const token = localStorage.getItem(LS_TOKEN);
|
||||
|
||||
const user_id = localStorage.getItem(LS_USER_ID);
|
||||
|
||||
if (!token || !user_id) {
|
||||
store.dispatch(signOut());
|
||||
|
||||
store.dispatch(loadingFinished());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
/* The users endpoint does NOT understand ?user_id=.
|
||||
|
||||
Workaround: fetch all users (limit=-1) then find ours. */
|
||||
|
||||
const users = await $fetch(`${USERS_URL}?limit=-1`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
const u = users.find((u) => u.user_id === user_id);
|
||||
|
||||
if (!u) throw new Error("User not found");
|
||||
|
||||
store.dispatch(
|
||||
signIn({
|
||||
id: user_id,
|
||||
|
||||
displayName: `${u.firstname} ${u.lastname}`,
|
||||
|
||||
isEmailVerified: !!u.isEmailVerified,
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
/* Token invalid / expired / user not found */
|
||||
|
||||
console.warn("[Auth] subscribeAuth failed:", err.message);
|
||||
|
||||
localStorage.removeItem(LS_TOKEN);
|
||||
|
||||
localStorage.removeItem(LS_USER_ID);
|
||||
|
||||
store.dispatch(signOut());
|
||||
} finally {
|
||||
store.dispatch(loadingFinished());
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {};
|
||||
}
|
||||
|
||||
/* ---------------------- createUserAccount ---------------------------- */
|
||||
|
||||
export async function createUserAccount(user) {
|
||||
store.dispatch(loadingStarted());
|
||||
|
||||
try {
|
||||
await $fetch(REGISTER_URL, {
|
||||
method: "POST",
|
||||
|
||||
body: JSON.stringify({
|
||||
firstname: user.firstname,
|
||||
|
||||
lastname: user.lastname,
|
||||
|
||||
email: user.email,
|
||||
|
||||
password: user.password,
|
||||
}),
|
||||
});
|
||||
|
||||
store.dispatch(errorOccured("")); // clear previous error
|
||||
} catch (err) {
|
||||
store.dispatch(errorOccured(err.message));
|
||||
} finally {
|
||||
store.dispatch(loadingFinished());
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------- signInUser (FIXED) -------------------------- */
|
||||
|
||||
export async function signInUser(user) {
|
||||
store.dispatch(loadingStarted());
|
||||
|
||||
try {
|
||||
/* 1. Login with email + password (as API expects) */
|
||||
|
||||
const url = `${LOGIN_URL}?email=${encodeURIComponent(
|
||||
user.email
|
||||
)}&password=${encodeURIComponent(user.password)}`;
|
||||
|
||||
const { token, user_id } = await $fetch(url);
|
||||
|
||||
/* 2. Persist session */
|
||||
|
||||
localStorage.setItem(LS_TOKEN, token);
|
||||
|
||||
localStorage.setItem(LS_USER_ID, user_id);
|
||||
|
||||
/* 3. Fetch FULL user list, find our profile */
|
||||
|
||||
const users = await $fetch(`${USERS_URL}?limit=-1`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
const profile = users.find((u) => u.user_id === user_id);
|
||||
|
||||
if (!profile) throw new Error("User not found");
|
||||
|
||||
if (!profile.isEmailVerified) {
|
||||
throw new Error("Please verify your email before to continue");
|
||||
}
|
||||
|
||||
/* 4. Dispatch sign-in */
|
||||
|
||||
store.dispatch(
|
||||
signIn({
|
||||
id: user_id,
|
||||
|
||||
displayName: `${profile.firstname} ${profile.lastname}`,
|
||||
|
||||
isEmailVerified: true,
|
||||
})
|
||||
);
|
||||
|
||||
store.dispatch(errorOccured(""));
|
||||
} catch (err) {
|
||||
store.dispatch(errorOccured(err.message));
|
||||
|
||||
localStorage.removeItem(LS_TOKEN);
|
||||
|
||||
localStorage.removeItem(LS_USER_ID);
|
||||
} finally {
|
||||
store.dispatch(loadingFinished());
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------- signUserOut ---------------------------------- */
|
||||
|
||||
export async function signUserOut() {
|
||||
/* mark offline in DB */
|
||||
await patchOnline(false);
|
||||
|
||||
/* clear session */
|
||||
localStorage.removeItem(LS_TOKEN);
|
||||
|
||||
localStorage.removeItem(LS_USER_ID);
|
||||
|
||||
store.dispatch(signOut());
|
||||
}
|
||||
|
||||
/* -------------------- sendPasswordReminder --------------------------- */
|
||||
|
||||
export function sendPasswordReminder(email) {
|
||||
console.info("[TODO] Implement password reminder for", email);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/* ===================================================================== *
|
||||
|
||||
SECTION B — IN-MEMORY MOCK (UNCHANGED) *
|
||||
|
||||
===================================================================== */
|
||||
|
||||
/* ------------- tiny DB (persists to localStorage for demo) ----------- */
|
||||
|
||||
const LS_DB = "__fakebook_mock_db__";
|
||||
|
||||
const DB = Object.assign(
|
||||
{
|
||||
currentUser: null,
|
||||
|
||||
users: [],
|
||||
|
||||
posts: [],
|
||||
|
||||
messages: [],
|
||||
},
|
||||
|
||||
JSON.parse(localStorage.getItem(LS_DB) || "{}")
|
||||
);
|
||||
|
||||
const persist = () => localStorage.setItem(LS_DB, JSON.stringify(DB));
|
||||
|
||||
const me = () => DB.users.find((u) => u.userID === DB.currentUser?.id);
|
||||
|
||||
const nowISO = () => new Date().toISOString();
|
||||
|
||||
/* Anytime we mutate DB we persist */
|
||||
|
||||
const commit = () => saveDB(DB);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/* PUBLIC API (ALL the names the UI already imports) */
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/* ---------- Generic helpers ---------- */
|
||||
/* --------------------- generic helpers ------------------------------- */
|
||||
|
||||
export async function getImageURL(path) {
|
||||
/* Pretend we have a CDN */
|
||||
|
||||
return `/assets/${path}`;
|
||||
}
|
||||
|
||||
/* ---------- Auth ---------- */
|
||||
/* ------------------- current user subscriptions ---------------------- */
|
||||
|
||||
export function subscribeAuth() {
|
||||
/* Mimic Firebase: emit immediately, return an unsubscribe fn */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
setTimeout(() => {
|
||||
if (DB.currentUser) {
|
||||
const { id, displayName, isEmailVerified } = DB.currentUser;
|
||||
/* Current user document */
|
||||
|
||||
store.dispatch(signIn({ id, displayName, isEmailVerified }));
|
||||
} else {
|
||||
store.dispatch(signOut());
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export function subscribeCurrentUser() {
|
||||
let cancelled = false;
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const token = localStorage.getItem(LS_TOKEN);
|
||||
|
||||
const user_id = localStorage.getItem(LS_USER_ID);
|
||||
|
||||
if (!token || !user_id) return;
|
||||
|
||||
const users = await $fetch(`${USERS_URL}?limit=-1`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
const meRow = users.find((u) => u.user_id === user_id);
|
||||
|
||||
if (meRow && !cancelled) {
|
||||
store.dispatch(currentUserUpdated(mapRestUser(meRow)));
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("[subscribeCurrentUser] failed:", err.message);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/* Online / offline flag */
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
async function patchOnline(isOnline) {
|
||||
const token = localStorage.getItem(LS_TOKEN);
|
||||
|
||||
const user_id = localStorage.getItem(LS_USER_ID);
|
||||
|
||||
if (!token || !user_id) return;
|
||||
|
||||
try {
|
||||
await $fetch(USERS_URL, {
|
||||
method: "PUT",
|
||||
|
||||
mode: "cors",
|
||||
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
|
||||
body: JSON.stringify({
|
||||
user_id,
|
||||
|
||||
isOnline: isOnline ? 1 : 0, // DB needs 1/0
|
||||
}),
|
||||
});
|
||||
|
||||
/* -------- optimistic Redux update (merge, not overwrite) -------- */
|
||||
|
||||
const cur = store.getState().currentUser;
|
||||
|
||||
store.dispatch(currentUserUpdated({ ...cur, isOnline })); // boolean
|
||||
|
||||
const usersNew = store
|
||||
.getState()
|
||||
.users.map((u) => (u.userID === user_id ? { ...u, isOnline } : u));
|
||||
|
||||
store.dispatch(usersUpdated(usersNew));
|
||||
} catch (err) {
|
||||
console.warn("[online/offline] PUT failed:", err.message);
|
||||
}
|
||||
}
|
||||
|
||||
store.dispatch(loadingFinished());
|
||||
export const currentUserOnline = () => patchOnline(true);
|
||||
|
||||
export const currentUserOffline = () => patchOnline(false);
|
||||
|
||||
/* ------------------------- users list -------------------------------- */
|
||||
|
||||
export function subscribeUsers() {
|
||||
let cancelled = false;
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const token = localStorage.getItem(LS_TOKEN);
|
||||
|
||||
const users = await $fetch(`${USERS_URL}?limit=-1`, {
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||
});
|
||||
|
||||
if (!cancelled) {
|
||||
store.dispatch(usersUpdated(users.map(mapRestUser)));
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("[subscribeUsers] failed:", err.message);
|
||||
}
|
||||
})();
|
||||
|
||||
/* return unsubscribe fn to keep same contract */
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}
|
||||
|
||||
/* ------------------------- posts ------------------------------------- */
|
||||
|
||||
export function subscribePosts() {
|
||||
setTimeout(() => {
|
||||
store.dispatch(
|
||||
postsUpdated(
|
||||
DB.posts.map((p) => ({
|
||||
...p,
|
||||
timestamp: new Date(p.timestamp).toLocaleString(),
|
||||
}))
|
||||
)
|
||||
);
|
||||
}, 0);
|
||||
|
||||
return () => {}; // no-op unsubscribe
|
||||
return () => {};
|
||||
}
|
||||
|
||||
export async function signUserOut() {
|
||||
store.dispatch(loadingStarted());
|
||||
|
||||
DB.currentUser = null;
|
||||
|
||||
commit();
|
||||
|
||||
store.dispatch(signOut());
|
||||
|
||||
store.dispatch(loadingFinished());
|
||||
}
|
||||
|
||||
/* createUserAccount mimics registration + e-mail verification mail */
|
||||
|
||||
export async function createUserAccount(user) {
|
||||
try {
|
||||
export async function upload(post) {
|
||||
await delay();
|
||||
|
||||
const uid = genId();
|
||||
const doc = { ...post, postID: genId(), timestamp: nowISO() };
|
||||
|
||||
DB.currentUser = {
|
||||
id: uid,
|
||||
DB.posts.unshift(doc);
|
||||
|
||||
displayName: `${user.firstname} ${user.lastname}`,
|
||||
if (me()) me().posts.unshift(doc.postID);
|
||||
|
||||
isEmailVerified: true,
|
||||
};
|
||||
persist();
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
return { id: doc.postID };
|
||||
}
|
||||
|
||||
/* Work out the index: */
|
||||
export function updatePost(post, postID) {
|
||||
const idx = DB.posts.findIndex((p) => p.postID === postID);
|
||||
|
||||
/* – 0 for the first user with this first+last name */
|
||||
if (idx !== -1) {
|
||||
DB.posts[idx] = { ...DB.posts[idx], ...post };
|
||||
persist();
|
||||
}
|
||||
}
|
||||
|
||||
/* – N for the N-th user who shares that name */
|
||||
/* ------------------------- storage ----------------------------------- */
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
export function addFileToStorage(file) {
|
||||
console.info("[Mock] saved file", file.name);
|
||||
|
||||
const duplicates = DB.users.filter(
|
||||
(u) => u.firstname === user.firstname && u.lastname === user.lastname
|
||||
).length;
|
||||
return Promise.resolve({
|
||||
ref: { fullPath: `${DB.currentUser?.id}/${file.name}` },
|
||||
});
|
||||
}
|
||||
|
||||
/* ------------------------- profile ----------------------------------- */
|
||||
|
||||
export function updateProfile(profile) {
|
||||
if (me()) Object.assign(me(), profile);
|
||||
persist();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/* ------------------------- messages ---------------------------------- */
|
||||
|
||||
export function subscribeMessages(kind) {
|
||||
const uid = DB.currentUser?.id;
|
||||
|
||||
const inc = kind === "incoming";
|
||||
|
||||
setTimeout(() => {
|
||||
const msgs = DB.messages.filter((m) =>
|
||||
inc ? m.recipient === uid : m.sender === uid
|
||||
);
|
||||
|
||||
store.dispatch(
|
||||
(inc ? incomingMessagesUpdated : outgoingMessagesUpdated)([...msgs])
|
||||
);
|
||||
}, 0);
|
||||
|
||||
return () => {};
|
||||
}
|
||||
|
||||
export function uploadMessage(msg) {
|
||||
DB.messages.push({ ...msg, id: genId(), timestamp: nowISO(), isRead: false });
|
||||
persist();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function updateToBeRead(id) {
|
||||
const m = DB.messages.find((m) => m.id === id);
|
||||
if (m) {
|
||||
m.isRead = true;
|
||||
persist();
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/* Ensure demo DB has at least one user */
|
||||
|
||||
if (!DB.users.length) {
|
||||
DB.users.push({
|
||||
userID: uid,
|
||||
userID: genId(),
|
||||
|
||||
firstname: user.firstname,
|
||||
firstname: "Demo",
|
||||
|
||||
lastname: user.lastname,
|
||||
lastname: "User",
|
||||
|
||||
profilePictureURL: "fakebook-avatar.jpeg",
|
||||
|
||||
|
@ -173,242 +517,12 @@ export async function createUserAccount(user) {
|
|||
|
||||
posts: [],
|
||||
|
||||
isOnline: false,
|
||||
isOnline: 0,
|
||||
|
||||
isEmailVerified: true,
|
||||
|
||||
index: duplicates, // 0 for first, 1 for second, …
|
||||
index: 0,
|
||||
});
|
||||
|
||||
commit();
|
||||
|
||||
console.info("[Mock] Registration OK — verification email “sent”.");
|
||||
} catch (err) {
|
||||
store.dispatch(errorOccured(err.message));
|
||||
persist();
|
||||
}
|
||||
}
|
||||
|
||||
export async function signInUser(user) {
|
||||
const NO_ERROR = "";
|
||||
|
||||
const EMAIL_VERIF_ERROR = "Please verify your email before to continue";
|
||||
|
||||
await delay();
|
||||
|
||||
/* Any credentials work in the mock */
|
||||
|
||||
const existing =
|
||||
DB.users.find((u) => u.firstname === user.email.split("@")[0]) ||
|
||||
DB.users[0];
|
||||
|
||||
if (!existing) {
|
||||
store.dispatch(errorOccured("No account found (mock)"));
|
||||
} else if (existing && !existing.isEmailVerified) {
|
||||
DB.currentUser = null;
|
||||
|
||||
store.dispatch(errorOccured(EMAIL_VERIF_ERROR));
|
||||
} else {
|
||||
DB.currentUser = {
|
||||
id: existing.userID,
|
||||
|
||||
displayName: `${existing.firstname} ${existing.lastname}`,
|
||||
|
||||
isEmailVerified: true,
|
||||
};
|
||||
|
||||
/* 👇 NEW: tell Redux that we’re signed in */
|
||||
|
||||
store.dispatch(
|
||||
signIn({
|
||||
id: DB.currentUser.id,
|
||||
|
||||
displayName: DB.currentUser.displayName,
|
||||
|
||||
isEmailVerified: true,
|
||||
})
|
||||
);
|
||||
|
||||
store.dispatch(errorOccured(NO_ERROR));
|
||||
|
||||
commit();
|
||||
}
|
||||
|
||||
store.dispatch(loadingFinished());
|
||||
}
|
||||
|
||||
export function sendPasswordReminder(email) {
|
||||
console.info(`[Mock] password reset email sent to ${email}`);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/* ---------- Current user doc ---------- */
|
||||
|
||||
export function subscribeCurrentUser() {
|
||||
const uid = store.getState().user.id;
|
||||
|
||||
/* Emit once */
|
||||
|
||||
setTimeout(() => {
|
||||
const doc = DB.users.find((u) => u.userID === uid);
|
||||
|
||||
store.dispatch(currentUserUpdated(doc || {}));
|
||||
}, 0);
|
||||
|
||||
/* Return unsubscribe noop */
|
||||
|
||||
return () => {};
|
||||
}
|
||||
|
||||
export async function currentUserOnline() {
|
||||
if (me()) {
|
||||
me().isOnline = true;
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
export async function currentUserOffline() {
|
||||
if (me()) {
|
||||
me().isOnline = false;
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Users list ---------- */
|
||||
|
||||
export function subscribeUsers() {
|
||||
/* Emit immediately */
|
||||
|
||||
setTimeout(() => {
|
||||
store.dispatch(usersUpdated(DB.users.slice()));
|
||||
}, 0);
|
||||
|
||||
return () => {};
|
||||
}
|
||||
|
||||
/* ---------- Posts ---------- */
|
||||
|
||||
export function subscribePosts() {
|
||||
setTimeout(() => {
|
||||
const postsWithStrings = DB.posts.map((p) => ({
|
||||
...p,
|
||||
|
||||
timestamp: new Date(p.timestamp).toLocaleString(),
|
||||
}));
|
||||
|
||||
store.dispatch(postsUpdated(postsWithStrings));
|
||||
}, 0);
|
||||
|
||||
return () => {};
|
||||
}
|
||||
|
||||
export async function upload(post) {
|
||||
await delay();
|
||||
|
||||
const doc = {
|
||||
...post,
|
||||
|
||||
postID: genId(),
|
||||
|
||||
timestamp: nowISO(),
|
||||
};
|
||||
|
||||
DB.posts.unshift(doc);
|
||||
|
||||
if (me()) {
|
||||
me().posts.unshift(doc.postID);
|
||||
}
|
||||
|
||||
commit();
|
||||
|
||||
return { id: doc.postID }; // mimic Firebase’s docRef id
|
||||
}
|
||||
|
||||
export function updatePost(post, postID) {
|
||||
const idx = DB.posts.findIndex((p) => p.postID === postID);
|
||||
|
||||
if (idx !== -1) {
|
||||
DB.posts[idx] = { ...DB.posts[idx], ...post };
|
||||
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Storage uploads ---------- */
|
||||
|
||||
export function addFileToStorage(file) {
|
||||
console.info("[Mock] file saved:", file.name);
|
||||
|
||||
return Promise.resolve({
|
||||
ref: { fullPath: `${DB.currentUser?.id}/${file.name}` },
|
||||
});
|
||||
}
|
||||
|
||||
/* ---------- Profile ---------- */
|
||||
|
||||
export function updateProfile(profile) {
|
||||
if (me()) {
|
||||
Object.assign(me(), profile);
|
||||
|
||||
commit();
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/* ---------- Messages ---------- */
|
||||
|
||||
export function subscribeMessages(kind) {
|
||||
const uid = DB.currentUser?.id;
|
||||
|
||||
const isIncoming = kind === "incoming";
|
||||
|
||||
setTimeout(() => {
|
||||
const msgs = DB.messages
|
||||
|
||||
.filter((m) => (isIncoming ? m.recipient === uid : m.sender === uid))
|
||||
|
||||
.map((m) => ({ ...m, timestamp: m.timestamp }));
|
||||
|
||||
store.dispatch(
|
||||
(isIncoming ? incomingMessagesUpdated : outgoingMessagesUpdated)(msgs)
|
||||
);
|
||||
}, 0);
|
||||
|
||||
return () => {};
|
||||
}
|
||||
|
||||
export function uploadMessage(msg) {
|
||||
DB.messages.push({
|
||||
...msg,
|
||||
|
||||
id: genId(),
|
||||
|
||||
timestamp: nowISO(),
|
||||
|
||||
isRead: false,
|
||||
});
|
||||
|
||||
commit();
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function updateToBeRead(messageID) {
|
||||
const m = DB.messages.find((m) => m.id === messageID);
|
||||
|
||||
if (m) {
|
||||
m.isRead = true;
|
||||
commit();
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/* ------ internal helper still referenced by UI code ---------------- */
|
||||
|
||||
function updateUserPosts() {
|
||||
/* kept only to satisfy imports; real logic in upload() above */
|
||||
}
|
||||
|
||||
export { updateUserPosts }; // keep named export so imports don’t break
|
||||
|
|
Loading…
Reference in New Issue