323 lines
14 KiB
JavaScript
323 lines
14 KiB
JavaScript
/**
|
|
* Frontend-only template specs.
|
|
* Variants:
|
|
* - news
|
|
* - breaking (news urgent)
|
|
* - finance (news finance)
|
|
* - market
|
|
* - friends
|
|
* - events
|
|
*/
|
|
export const TEMPLATE_SPECS = {
|
|
cluster: {
|
|
mini_spec: {
|
|
size: { w: 270, h: 108 },
|
|
background: { type: "gradient", value: "newsBlue" },
|
|
radius: 20,
|
|
layers: [
|
|
{ id: "icon", type: "icon", x: 12, y: 12, w: 72, h: 72, text: "\uf1ea", style: "text.icon", bind: "data.categoryIcon" },
|
|
{ id: "imageOverlay", type: "image", x: 12, y: 12, w: 72, h: 72, bind: "data.image", radius: 12 },
|
|
{ id: "badge", type: "chip", x: 96, y: 10, text: "MAIN NEWS", style: "chip.news" },
|
|
{ id: "title", type: "text", x: 96, y: 34, w: 156, h: 46, bind: "data.headline", style: "text.title", maxLines: 2 },
|
|
{ id: "meta", type: "text", x: 96, y: 82, w: 156, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 },
|
|
],
|
|
},
|
|
full_spec: {
|
|
size: { w: 380, h: 280 },
|
|
background: { type: "solid", value: "surface" },
|
|
radius: 24,
|
|
layers: [
|
|
{ id: "badge", type: "chip", x: 16, y: 12, text: "MAIN NEWS", style: "chip.news" },
|
|
{ id: "title", type: "text", x: 16, y: 52, w: 348, h: 76, bind: "data.headline", style: "text.h1", maxLines: 3 },
|
|
{ id: "summary", type: "text", x: 16, y: 136, w: 348, h: 90, bind: "data.summary", style: "text.body", maxLines: 4 },
|
|
],
|
|
},
|
|
},
|
|
news: {
|
|
mini_spec: {
|
|
size: { w: 240, h: 96 },
|
|
background: { type: "gradient", value: "newsBlue" },
|
|
radius: 18,
|
|
layers: [
|
|
{ id: "icon", type: "icon", x: 12, y: 12, w: 72, h: 72, text: "\uf1ea", style: "text.icon", bind: "data.categoryIcon" },
|
|
{ id: "imageOverlay", type: "image", x: 12, y: 12, w: 72, h: 72, bind: "data.image", radius: 12 },
|
|
{ id: "badge", type: "chip", x: 94, y: 10, text: "NEWS", style: "chip.news" },
|
|
{ id: "title", type: "text", x: 94, y: 34, w: 134, h: 42, bind: "data.headline", style: "text.title", maxLines: 2 },
|
|
{ id: "meta", type: "text", x: 94, y: 76, w: 134, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 },
|
|
],
|
|
},
|
|
full_spec: {
|
|
size: { w: 360, h: 260 },
|
|
background: { type: "solid", value: "surface" },
|
|
radius: 22,
|
|
layers: [
|
|
{ id: "badge", type: "chip", x: 16, y: 12, text: "NEWS", style: "chip.news" },
|
|
{ id: "title", type: "text", x: 16, y: 52, w: 328, h: 72, bind: "data.headline", style: "text.h1", maxLines: 3 },
|
|
{ id: "summary", type: "text", x: 16, y: 132, w: 328, h: 78, bind: "data.summary", style: "text.body", maxLines: 4 },
|
|
],
|
|
},
|
|
},
|
|
|
|
breaking: {
|
|
mini_spec: {
|
|
size: { w: 240, h: 96 },
|
|
background: { type: "gradient", value: "breakingRed" },
|
|
radius: 18,
|
|
layers: [
|
|
{ id: "icon", type: "icon", x: 12, y: 12, w: 72, h: 72, text: "\uf0e7", style: "text.icon", bind: "data.categoryIcon" },
|
|
{ id: "imageOverlay", type: "image", x: 12, y: 12, w: 72, h: 72, bind: "data.image", radius: 12 },
|
|
{ id: "badge", type: "chip", x: 94, y: 10, text: "BREAKING", style: "chip.danger" },
|
|
{ id: "title", type: "text", x: 94, y: 34, w: 134, h: 42, bind: "data.headline", style: "text.title", maxLines: 2 },
|
|
{ id: "meta", type: "text", x: 94, y: 76, w: 134, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 },
|
|
],
|
|
},
|
|
full_spec: {
|
|
size: { w: 360, h: 260 },
|
|
background: { type: "gradient", value: "breakingRed" },
|
|
radius: 22,
|
|
layers: [
|
|
{ id: "badge", type: "chip", x: 16, y: 12, text: "BREAKING", style: "chip.danger" },
|
|
{ id: "title", type: "text", x: 16, y: 52, w: 328, h: 72, bind: "data.headline", style: "text.h1", maxLines: 3 },
|
|
{ id: "summary", type: "text", x: 16, y: 132, w: 328, h: 78, bind: "data.summary", style: "text.body", maxLines: 4 },
|
|
],
|
|
},
|
|
},
|
|
|
|
finance: {
|
|
mini_spec: {
|
|
size: { w: 240, h: 96 },
|
|
background: { type: "gradient", value: "financeTeal" },
|
|
radius: 18,
|
|
layers: [
|
|
{ id: "icon", type: "icon", x: 12, y: 12, w: 72, h: 72, text: "\uf201", style: "text.icon", bind: "data.categoryIcon" },
|
|
{ id: "imageOverlay", type: "image", x: 12, y: 12, w: 72, h: 72, bind: "data.image", radius: 12 },
|
|
{ id: "badge", type: "chip", x: 94, y: 10, text: "FINANCE", style: "chip.news" },
|
|
{ id: "title", type: "text", x: 94, y: 34, w: 134, h: 42, bind: "data.headline", style: "text.title", maxLines: 2 },
|
|
{ id: "meta", type: "text", x: 94, y: 76, w: 134, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 },
|
|
],
|
|
},
|
|
full_spec: {
|
|
size: { w: 360, h: 260 },
|
|
background: { type: "gradient", value: "financeTeal" },
|
|
radius: 22,
|
|
layers: [
|
|
{ id: "badge", type: "chip", x: 16, y: 12, text: "FINANCE", style: "chip.news" },
|
|
{ id: "title", type: "text", x: 16, y: 52, w: 328, h: 72, bind: "data.headline", style: "text.h1", maxLines: 3 },
|
|
{ id: "summary", type: "text", x: 16, y: 132, w: 328, h: 78, bind: "data.summary", style: "text.body", maxLines: 4 },
|
|
],
|
|
},
|
|
},
|
|
|
|
market: {
|
|
mini_spec: {
|
|
size: { w: 240, h: 96 },
|
|
background: { type: "gradient", value: "marketGold" },
|
|
radius: 18,
|
|
layers: [
|
|
{ id: "icon", type: "icon", x: 12, y: 12, w: 72, h: 72, text: "\uf3d1", style: "text.icon", bind: "data.categoryIcon" },
|
|
{ id: "imageOverlay", type: "image", x: 12, y: 12, w: 72, h: 72, bind: "data.image", radius: 12 },
|
|
{ id: "badge", type: "chip", x: 94, y: 10, text: "MARKET", style: "chip.news" },
|
|
{ id: "title", type: "text", x: 94, y: 34, w: 134, h: 42, bind: "data.headline", style: "text.title", maxLines: 2 },
|
|
{ id: "meta", type: "text", x: 94, y: 76, w: 134, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 },
|
|
],
|
|
},
|
|
full_spec: {
|
|
size: { w: 360, h: 260 },
|
|
background: { type: "gradient", value: "marketGold" },
|
|
radius: 22,
|
|
layers: [
|
|
{ id: "badge", type: "chip", x: 16, y: 12, text: "MARKET", style: "chip.news" },
|
|
{ id: "title", type: "text", x: 16, y: 52, w: 328, h: 72, bind: "data.headline", style: "text.h1", maxLines: 3 },
|
|
{ id: "summary", type: "text", x: 16, y: 132, w: 328, h: 78, bind: "data.summary", style: "text.body", maxLines: 4 },
|
|
],
|
|
},
|
|
},
|
|
|
|
friends: {
|
|
mini_spec: {
|
|
size: { w: 240, h: 96 },
|
|
background: { type: "gradient", value: "friendsGreen" },
|
|
radius: 18,
|
|
layers: [
|
|
{ id: "icon", type: "icon", x: 12, y: 12, w: 72, h: 72, text: "\uf500", style: "text.icon", bind: "data.categoryIcon" },
|
|
{ id: "imageOverlay", type: "image", x: 12, y: 12, w: 72, h: 72, bind: "data.image", radius: 12 },
|
|
{ id: "badge", type: "chip", x: 94, y: 10, text: "FRIENDS", style: "chip.news" },
|
|
{ id: "title", type: "text", x: 94, y: 34, w: 134, h: 42, bind: "data.headline", style: "text.title", maxLines: 2 },
|
|
{ id: "meta", type: "text", x: 94, y: 76, w: 134, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 },
|
|
],
|
|
},
|
|
full_spec: {
|
|
size: { w: 360, h: 260 },
|
|
background: { type: "gradient", value: "friendsGreen" },
|
|
radius: 22,
|
|
layers: [
|
|
{ id: "badge", type: "chip", x: 16, y: 12, text: "FRIENDS", style: "chip.news" },
|
|
{ id: "title", type: "text", x: 16, y: 52, w: 328, h: 72, bind: "data.headline", style: "text.h1", maxLines: 3 },
|
|
{ id: "summary", type: "text", x: 16, y: 132, w: 328, h: 78, bind: "data.summary", style: "text.body", maxLines: 4 },
|
|
],
|
|
},
|
|
},
|
|
|
|
events: {
|
|
mini_spec: {
|
|
size: { w: 240, h: 96 },
|
|
background: { type: "gradient", value: "eventsPurple" },
|
|
radius: 18,
|
|
layers: [
|
|
{ id: "icon", type: "icon", x: 12, y: 12, w: 72, h: 72, text: "\uf073", style: "text.icon", bind: "data.categoryIcon" },
|
|
{ id: "imageOverlay", type: "image", x: 12, y: 12, w: 72, h: 72, bind: "data.image", radius: 12 },
|
|
{ id: "badge", type: "chip", x: 94, y: 10, text: "EVENTS", style: "chip.news" },
|
|
{ id: "title", type: "text", x: 94, y: 34, w: 134, h: 42, bind: "data.headline", style: "text.title", maxLines: 2 },
|
|
{ id: "meta", type: "text", x: 94, y: 76, w: 134, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 },
|
|
],
|
|
},
|
|
full_spec: {
|
|
size: { w: 360, h: 260 },
|
|
background: { type: "gradient", value: "eventsPurple" },
|
|
radius: 22,
|
|
layers: [
|
|
{ id: "badge", type: "chip", x: 16, y: 12, text: "EVENTS", style: "chip.news" },
|
|
{ id: "title", type: "text", x: 16, y: 52, w: 328, h: 72, bind: "data.headline", style: "text.h1", maxLines: 3 },
|
|
{ id: "summary", type: "text", x: 16, y: 132, w: 328, h: 78, bind: "data.summary", style: "text.body", maxLines: 4 },
|
|
],
|
|
},
|
|
},
|
|
};
|
|
|
|
function parseDateAny(raw) {
|
|
if (!raw) return null;
|
|
if (raw instanceof Date) return raw;
|
|
const s = String(raw).trim();
|
|
if (!s) return null;
|
|
// handle "YYYY-MM-DD HH:mm:ss"
|
|
const fixed = s.includes(" ") && !s.includes("T") ? s.replace(" ", "T") : s;
|
|
const d = new Date(fixed);
|
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
}
|
|
|
|
function isBreaking(post) {
|
|
// Only show as BREAKING if explicitly tagged
|
|
if (post?.breaking === true || post?.is_breaking === true) return true;
|
|
|
|
// Or if title contains breaking/urgent/alerte keywords
|
|
const t = (post?.title || "").toString().toLowerCase();
|
|
if (t.includes("breaking") || t.includes("urgent") || t.includes("alerte")) return true;
|
|
|
|
// Removed: automatic breaking news for recent posts
|
|
// Now only explicit tags or keywords trigger breaking news style
|
|
return false;
|
|
}
|
|
|
|
function normCat(post) {
|
|
if (isClusterPost(post)) return "cluster";
|
|
const c = (post?.category || post?.Category || "").toString().toUpperCase();
|
|
const sub = (post?.sub_category || post?.subCategory || "").toString().toLowerCase();
|
|
|
|
if (c === "MARKET") return "market";
|
|
if (c === "FRIENDS") return "friends";
|
|
if (c === "EVENT" || c === "EVENTS") return "events";
|
|
|
|
if (c === "NEWS" && (sub.includes("finan") || sub.includes("finance"))) return "finance";
|
|
if (c === "NEWS" && isBreaking(post)) return "breaking";
|
|
|
|
return "news";
|
|
}
|
|
|
|
export function getTemplateKeyForPost(post) {
|
|
return normCat(post);
|
|
}
|
|
|
|
export function getTemplateSpecForPost(post, variant /* "mini"|"full" */) {
|
|
const key = getTemplateKeyForPost(post);
|
|
const t = TEMPLATE_SPECS[key] || TEMPLATE_SPECS.news;
|
|
return variant === "full" ? t.full_spec : t.mini_spec;
|
|
}
|
|
|
|
function getCategoryIcon(post) {
|
|
const category = (post?.category || "").toUpperCase();
|
|
const subCategory = (post?.sub_category || "").toLowerCase();
|
|
|
|
// Mapping des icônes par catégorie et sous-catégorie
|
|
const iconMap = {
|
|
NEWS: {
|
|
default: "\uf1ea", // fa-newspaper
|
|
world: "\uf57d", // fa-globe-americas
|
|
politics: "\uf0a3", // fa-landmark-flag
|
|
tech: "\uf2db", // fa-microchip
|
|
finance: "\uf201", // fa-chart-line
|
|
local: "\uf3c5", // fa-map-marker-alt
|
|
},
|
|
FRIENDS: {
|
|
default: "\uf500", // fa-user-group
|
|
nearby: "\uf3c5", // fa-location-crosshairs
|
|
chats: "\uf086", // fa-comments
|
|
groups: "\uf0c0", // fa-users
|
|
stories: "\uf02d", // fa-book-open
|
|
favs: "\uf004", // fa-heart
|
|
},
|
|
EVENT: {
|
|
default: "\uf073", // fa-calendar
|
|
today: "\uf783", // fa-calendar-day
|
|
weekend: "\uf784", // fa-calendar-week
|
|
music: "\uf001", // fa-music
|
|
sports: "\uf1e3", // fa-football
|
|
local: "\uf3c5", // fa-map-marker-alt
|
|
},
|
|
EVENTS: {
|
|
default: "\uf073", // fa-calendar
|
|
today: "\uf783", // fa-calendar-day
|
|
weekend: "\uf784", // fa-calendar-week
|
|
music: "\uf001", // fa-music
|
|
sports: "\uf1e3", // fa-football
|
|
local: "\uf3c5", // fa-map-marker-alt
|
|
},
|
|
MARKET: {
|
|
default: "\uf3d1", // fa-store
|
|
actu: "\uf1ea", // fa-newspaper
|
|
tech: "\uf2db", // fa-microchip
|
|
finan: "\uf555", // fa-money-bill-trend-up
|
|
sport: "\uf434", // fa-basketball
|
|
deals: "\uf02b", // fa-tags
|
|
},
|
|
};
|
|
|
|
const categoryIcons = iconMap[category] || iconMap.NEWS;
|
|
return categoryIcons[subCategory] || categoryIcons.default;
|
|
}
|
|
|
|
export function adaptPostToTemplateData(post, opts = {}) {
|
|
const headline = post?.cluster_title || post?.title || "Untitled";
|
|
const author = post?.author ? `by ${post.author}` : "";
|
|
const sub = post?.sub_category ? String(post.sub_category) : "";
|
|
const clusterCount = Array.isArray(post?.cluster_members) ? post.cluster_members.length : 0;
|
|
const source = clusterCount > 1 ? `${clusterCount} related reports` : (author || sub || "");
|
|
const summary = post?.snippet || post?.body || "";
|
|
const url = post?.url || "";
|
|
const size = (opts.size || "full").toString();
|
|
const rawImage =
|
|
size === "mini"
|
|
? (post?.image_small || post?.image_medium || post?.image || post?.media_url || "")
|
|
: (post?.image_medium || post?.image_large || post?.image_small || post?.image || post?.media_url || "");
|
|
const image = normalizeImageUrl(rawImage) || `/og/category?cat=${encodeURIComponent((post?.category || "NEWS").toString().toUpperCase())}&variant=mini`;
|
|
const categoryIcon = getCategoryIcon(post);
|
|
return { headline, source, summary, url, image, categoryIcon };
|
|
}
|
|
|
|
function normalizeImageUrl(raw) {
|
|
const value = (raw || "").toString().trim();
|
|
if (!value) return "";
|
|
if (value.startsWith("//")) return `https:${value}`;
|
|
if (value.startsWith("http://")) return `https://${value.slice(7)}`;
|
|
return value;
|
|
}
|
|
|
|
function isClusterPost(post) {
|
|
const clusterPostId = Number(post?.cluster_post_id);
|
|
const postId = Number(post?.post_id);
|
|
if (Number.isFinite(clusterPostId) && Number.isFinite(postId) && clusterPostId > 0 && postId > 0) {
|
|
return clusterPostId === postId;
|
|
}
|
|
const url = (post?.url || "").toString();
|
|
return url.includes("/cluster/");
|
|
}
|