olo / data.js
EricaLuvGemma's picture
Upload 12 files
2906f02 verified
Raw History Blame Contribute Delete
6.56 kB
/* ============================================================
Shared state + mock data layer.
Everything here reads/writes localStorage so the four screens
can be clicked through without a backend. Swap the functions
in this file for fetch() calls once the API exists — the
shape of the returned objects is meant to match what the
FastAPI endpoints will eventually return.
============================================================ */
const TIERS = {
high_school: { label: "High school", desc: "Popular kid in a small pond" },
college: { label: "College", desc: "Building a name on campus" },
postgrad: { label: "Post-grad", desc: "Working the world at large" },
};
const ADJ = ["quiet", "bright", "lucky", "vivid", "loose", "sharp", "plain", "sunny", "faded", "wild"];
const NOUN = ["fern", "harbor", "kestrel", "static", "current", "atlas", "orbit", "linen", "ember", "gravel"];
function generateUsername(name){
const base = (name || "user").trim().split(/\s+/)[0].toLowerCase().replace(/[^a-z0-9]/g, "");
const word = NOUN[Math.floor(Math.random() * NOUN.length)];
const num = Math.floor(Math.random() * 89) + 10;
return `${base || "user"}.${word}${num}`;
}
function getCharacter(){
const raw = localStorage.getItem("sf_character");
return raw ? JSON.parse(raw) : null;
}
function saveCharacter(c){
localStorage.setItem("sf_character", JSON.stringify(c));
}
function requireOnboarding(){
if (!getCharacter()){
window.location.href = "onboarding.html";
}
}
function initials(name){
return (name || "?").trim().split(/\s+/).map(w => w[0]).slice(0,2).join("").toUpperCase();
}
function timeAgo(ts){
const diff = Math.max(0, Date.now() - ts);
const m = Math.floor(diff / 60000);
if (m < 1) return "now";
if (m < 60) return `${m}m`;
const h = Math.floor(m / 60);
if (h < 24) return `${h}h`;
return `${Math.floor(h / 24)}d`;
}
/* ---- NPC roster (shared across explore / dms / feed reactions) ---- */
const NPCS = [
{ handle: "rae.holloway41", name: "Rae Holloway", role: "Class of 2027, front row energy", tier: "high_school" },
{ handle: "denny.arc09", name: "Denny Okafor", role: "Runs the campus radio slot", tier: "college" },
{ handle: "marlo.fenn77", name: "Marlo Fenn", role: "Small business owner, ships fast", tier: "postgrad" },
{ handle: "pia.lund22", name: "Pia Lund", role: "Yearbook committee, sees everything", tier: "high_school" },
{ handle: "theo.gravel14", name: "Theo Gravel", role: "Local news, campus desk", tier: "college" },
{ handle: "wren.static88", name: "Wren Ashby", role: "Talent scout, always in your mentions", tier: "postgrad" },
{ handle: "cole.ember03", name: "Cole Bishara", role: "Used to post a lot. Doesn't anymore.", tier: "high_school", retired: true },
];
const NEWS_CHANNELS = [
{ handle: "the.dispatch", name: "The Dispatch", role: "Local coverage, mostly rumor" },
{ handle: "campus.wire", name: "Campus Wire", role: "Who's up, who's out" },
];
function seedPosts(tier){
const now = Date.now();
return [
{
id: "p1",
author: NPCS[0],
body: "spent four hours on a project due tomorrow and used none of it wisely",
ts: now - 1000 * 60 * 42,
seen: 118, followed: 34, likes: 22,
comments: [
{ author: NPCS[3], body: "this is a weekly occurrence at this point" },
{ author: NPCS[1], body: "same energy every single time" },
],
},
{
id: "p2",
author: NPCS[2],
body: "small batch of the candles restocked. shipping starts friday if anyone wants in",
ts: now - 1000 * 60 * 60 * 3,
seen: 203, followed: 61, likes: 47,
comments: [
{ author: NPCS[4], body: "these sold out last time in about a day" },
],
},
{
id: "p3",
author: NPCS[6],
body: "i remember when i thought i was the main character. couldn't make it as a social media giant. be careful what you believe online",
ts: now - 1000 * 60 * 60 * 20,
seen: 76, followed: 12, likes: 9,
comments: [],
retired: true,
},
];
}
function seedDMs(){
const now = Date.now();
return [
{
handle: NPCS[2].handle,
unread: true,
messages: [
{ from: "them", body: "hey, saw your posts. if you send a few of my products around and it lands, I'll cover you for it.", ts: now - 1000 * 60 * 50 },
],
},
{
handle: NPCS[0].handle,
unread: false,
messages: [
{ from: "them", body: "you coming to the thing friday", ts: now - 1000 * 60 * 60 * 5 },
{ from: "me", body: "thinking about it", ts: now - 1000 * 60 * 60 * 4 },
],
},
{
handle: NPCS[5].handle,
unread: true,
messages: [
{ from: "them", body: "your numbers this week are worth a real conversation. free later?", ts: now - 1000 * 60 * 60 * 9 },
],
},
];
}
function getPosts(){
let raw = localStorage.getItem("sf_posts");
if (!raw){
const seeded = seedPosts();
localStorage.setItem("sf_posts", JSON.stringify(seeded));
return seeded;
}
return JSON.parse(raw);
}
function savePosts(posts){
localStorage.setItem("sf_posts", JSON.stringify(posts));
}
function getDMs(){
let raw = localStorage.getItem("sf_dms");
if (!raw){
const seeded = seedDMs();
localStorage.setItem("sf_dms", JSON.stringify(seeded));
return seeded;
}
return JSON.parse(raw);
}
function saveDMs(dms){
localStorage.setItem("sf_dms", JSON.stringify(dms));
}
function npcByHandle(handle){
return NPCS.find(n => n.handle === handle) || NEWS_CHANNELS.find(n => n.handle === handle);
}
/* ---- likes ---- */
function getLikedIds(){
const raw = localStorage.getItem("sf_likes");
return raw ? JSON.parse(raw) : [];
}
function saveLikedIds(ids){
localStorage.setItem("sf_likes", JSON.stringify(ids));
}
/* Toggles the like on a post for the current user, persists both the
post's like count and the user's liked-id set, and returns the
updated { liked, likes } so the caller can update the DOM in place. */
function toggleLike(postId){
const posts = getPosts();
const post = posts.find(p => p.id === postId);
if (!post) return null;
let liked = getLikedIds();
const isLiked = liked.includes(postId);
if (isLiked){
liked = liked.filter(id => id !== postId);
post.likes = Math.max(0, (post.likes || 0) - 1);
} else {
liked.push(postId);
post.likes = (post.likes || 0) + 1;
}
saveLikedIds(liked);
savePosts(posts);
return { liked: !isLiked, likes: post.likes };
}