source: Klonkt/src/services/PlatformIcons.js@ 746d98a

main
Last change on this file since 746d98a was 746d98a, checked in by Robin <roboburr@…>, 6 hours ago

Wardmodus: een besloten Klonkt met alleen het profiel openbaar

Robin, 30-9: "Ward Profile mode gaat actief wanneer ward gekoppeld is, dus
wanneer de persoon guardian(s) heeft. Een besloten Klonkt met enkel het profiel
van de ward publiek. Wij denken dat dit het beste valt bij emancipation om de
functie weer naar normaal Klonkt te brengen."

AFGELEID, NIET OPGESLAGEN. Heeft de site geaccepteerde guardians, dan staat de
modus aan; is de set leeg, dan uit. listGuardians telt alleen geaccepteerde
guardianships, dus een voorstel zet niets dicht, en de set leegmaken is
emancipatie (FEP-633c 3.4) -- precies waar het ontwerp de weg terug legt. Een
schakelaar zou een tweede waarheid naast de guardianship zijn.

STANDAARD DICHT (middleware/ward-profile.js, in server.js direct na de
siteresolver en de schrijfpoort). Een bezoeker ziet op / het profiel en verder
niets: een pagina gaat terug naar het profiel, al het andere is 404, een POST
bereikt zijn handler niet, en een htmx-navigatie krijgt HX-Redirect in plaats
van een brokstuk. Wat niet op de lijst staat komt er niet langs, zodat een
nieuwe route niet vanzelf een kind openzet.

Open blijft: inloggen, account, beheer, het guardianpaneel, /lang, de bestanden
van de pagina zelf, en /audio/stream -- die bewaakt elk bestand zelf, en een
track die de ward op de fediverse opende moet daar speelbaar blijven. De
federatie (ActivityPub, WebFinger, OAuth voor Shaer, /media, /assets, /og) hangt
al VOOR de poort; de paden staan toch op de lijst, voor als de volgorde ooit
omgaat, want dan zou een ward stil van de fediverse verdwijnen.

Wie alles ziet: de ward zelf, en god, kijker en sitebeheer. Een andere
ingelogde gebruiker is gewoon een bezoeker.

DE PROFIELPAGINA (pages/ward-profile.ejs) mag verzorgd, want dit is alles wat
een bezoeker ziet: foto met een ring in de accentkleur, de naam groot, de
handle op een pil, de bio, de links als brede knoppen in hun merkkleur, een
volgknop die de bestaande fediverse-dialoog opent (een volgverzoek aan een ward
gaat eerst langs de guardians), en een neutrale regel dat berichten alleen
voor volgers zijn. Nergens "kind" of "ward": dat de account guardians heeft
staat al openbaar op de fediverse. Dezelfde bronnen als de profielkop, zodat
het profiel op geen twee plekken iets anders kan zeggen. De chrome biedt een
bezoeker niets aan wat dicht is: geen zoekknop, geen archief, geen
Lezen/Grid-schakelaar.

DE PROFIELLINKS verhuizen van Appearance naar het account (/account#links),
bij naam, bio en foto. Twee vallen onderweg, allebei getoetst:

  • Appearance schreef profile_links altijd, uit de formuliervelden. Met de velden weg zou elke keer opslaan daar de links wissen. Die pagina schrijft ze niet meer.
  • ownedSite haalde profile_links niet op, dus de accountpagina toonde nul links en opslaan wiste ze. Gevonden bij het bekijken, niet door een toets; nu wel.

buildProfileLinks staat in PlatformIcons.js, en de rijen-knoppen in een eigen
module (mod/profile-links.js).

Getoetst: gewone Klonkt zonder guardians, een voorstel zet niets dicht,
gekoppeld alleen het profiel, pagina's terug, 404 voor wat geen pagina is, geen
handler voor een POST, HX-Redirect, wat open moet blijft open, ward en beheer
zien alles, een andere ingelogde is een bezoeker, emancipatie maakt het weer
gewoon, de links bij het account en Appearance wist ze niet. Tegenproef per
zaak. Volledige suite 1325 groen.

Nagekeken in de browser op 375 en 1280 pixels: het profiel past op een
telefoonscherm, knoppen van 55 pixels, geen overloop, de volgknop opent de
dialoog; de accountpagina toont de bestaande links en voegt er een toe.

Co-Authored-By: Claude Opus 5.5 <noreply@…>

  • Property mode set to 100644
File size: 14.4 KB
Line 
1/**
2 * PlatformIcons — curated list of social/streaming platforms for the
3 * profile-header social links. Mirrors v9's pcms_profile_platforms() but
4 * with inline SVG so we don't need an icons.svg sprite.
5 *
6 * Each entry: { key, label, brand, host, svg } where:
7 * - key : stable id used in DB (sites.profile_links[].platform)
8 * - label : human readable name
9 * - brand : hex color for hover/icon background
10 * - host : domain hint (used to autodetect from URL if user pastes one)
11 * - svg : inline path data (single <svg> ready)
12 */
13
14export const PLATFORMS = {
15 spotify: {
16 label: 'Spotify',
17 brand: '#1DB954',
18 host: 'spotify.com',
19 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.66 0 12 0zm5.521 17.34c-.24.359-.66.48-1.021.24-2.82-1.74-6.36-2.101-10.561-1.141-.418.122-.779-.179-.899-.539-.12-.421.18-.78.54-.9 4.56-1.021 8.52-.6 11.64 1.32.42.18.479.659.301 1.02zm1.44-3.3c-.301.42-.841.6-1.262.3-3.239-1.98-8.159-2.58-11.939-1.38-.479.12-1.02-.12-1.14-.6-.12-.48.12-1.021.6-1.141C9.6 9.9 15 10.561 18.72 12.84c.361.181.54.78.241 1.2zm.12-3.36C15.24 8.4 8.82 8.16 5.16 9.301c-.6.179-1.2-.181-1.38-.721-.18-.601.18-1.2.72-1.381 4.26-1.26 11.28-1.02 15.721 1.621.539.3.719 1.02.42 1.56-.299.421-1.02.599-1.559.3z"/></svg>',
20 },
21 bandcamp: {
22 label: 'Bandcamp',
23 brand: '#629AA9',
24 host: 'bandcamp.com',
25 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M0 18.75l7.437-13.5H24l-7.438 13.5H0z"/></svg>',
26 },
27 soundcloud: {
28 label: 'SoundCloud',
29 brand: '#FF5500',
30 host: 'soundcloud.com',
31 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M1.175 12.225c-.051 0-.094.046-.101.1l-.233 2.154.233 2.105c.007.058.05.098.101.098.05 0 .09-.04.099-.098l.255-2.105-.27-2.154c0-.057-.045-.1-.09-.1m-.899.828c-.06 0-.091.037-.104.094L0 14.479l.165 1.308c0 .055.045.094.09.094.06 0 .091-.039.104-.094l.21-1.308-.21-1.342c-.013-.057-.044-.094-.09-.094m1.83-1.229c-.061 0-.12.045-.12.104l-.21 2.586.21 2.49c0 .06.06.12.12.12.06 0 .12-.06.12-.12l.255-2.49-.27-2.586c0-.06-.045-.104-.105-.104m.945-.089c-.075 0-.135.06-.15.135l-.193 2.679.21 2.475c.016.06.075.12.135.12.075 0 .135-.06.15-.135l.21-2.475-.225-2.679c0-.06-.06-.12-.135-.12m1.155.36c-.005-.09-.075-.149-.159-.149-.09 0-.158.06-.164.149l-.217 2.404.2 2.55c0 .09.075.149.159.149.074 0 .149-.06.149-.149l.255-2.55-.225-2.404m.832-1.738c-.082-.045-.166-.067-.262-.067-.075 0-.15.015-.21.045-.06.045-.105.119-.105.179l-.195 4.014.21 2.4c0 .09.075.165.165.165.105 0 .179-.075.21-.165l.225-2.4-.225-4.041c0-.075-.06-.135-.135-.135m.945-.149c-.075 0-.149.045-.165.105L5.7 14.524l.195 2.34c0 .105.09.181.18.181.105 0 .195-.075.21-.181l.21-2.34-.225-4.184c0-.06-.06-.105-.165-.105m1.064.149c-.075 0-.179.06-.195.179l-.135 4.034.15 2.318c0 .105.09.195.21.195.12 0 .195-.09.21-.195l.18-2.318-.21-4.034c0-.119-.075-.179-.21-.179m1.038-.494c-.045 0-.105.015-.149.045-.045.045-.075.105-.075.165l-.135 4.493.135 2.31c.014.135.119.225.224.225.135 0 .24-.09.255-.225l.165-2.31-.165-4.493c-.014-.135-.12-.21-.255-.21M22.275 13.7c-.42 0-.825.075-1.215.225-.27-2.91-2.7-5.175-5.685-5.175-.735 0-1.439.135-2.085.36-.255.09-.314.179-.314.359v9.524c.014.196.165.346.359.36h8.94c2.13 0 3.825-1.695 3.825-3.81 0-2.116-1.695-3.843-3.825-3.843"/></svg>',
32 },
33 applemusic: {
34 label: 'Apple Music',
35 brand: '#FC3C44',
36 host: 'music.apple.com',
37 // The classic Apple mark (bitten apple + leaf). The previous path was a garbled
38 // app-tile outline that didn't read as the Apple logo at icon size.
39 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.03 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.56-1.702"/></svg>',
40 },
41 youtube: {
42 label: 'YouTube',
43 brand: '#FF0000',
44 host: 'youtube.com',
45 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>',
46 },
47 vimeo: {
48 label: 'Vimeo',
49 brand: '#1AB7EA',
50 host: 'vimeo.com',
51 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M23.977 6.416c-.105 2.338-1.739 5.543-4.894 9.609-3.268 4.247-6.026 6.37-8.29 6.37-1.409 0-2.578-1.294-3.553-3.881L5.322 11.4C4.603 8.816 3.834 7.522 3.01 7.522c-.179 0-.806.378-1.881 1.132L0 7.197c1.185-1.044 2.351-2.084 3.501-3.128C5.08 2.701 6.266 1.984 7.055 1.91c1.867-.18 3.016 1.1 3.447 3.838.465 2.953.789 4.789.971 5.507.539 2.45 1.131 3.674 1.776 3.674.502 0 1.256-.796 2.265-2.385 1.004-1.589 1.54-2.797 1.612-3.628.144-1.371-.395-2.061-1.614-2.061-.574 0-1.167.121-1.777.391 1.186-3.868 3.434-5.757 6.762-5.637 2.473.06 3.628 1.664 3.493 4.797l-.013.01z"/></svg>',
52 },
53 instagram: {
54 label: 'Instagram',
55 brand: '#E1306C',
56 host: 'instagram.com',
57 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 1 0 0 12.324 6.162 6.162 0 0 0 0-12.324zM12 16a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm6.406-11.845a1.44 1.44 0 1 0 0 2.881 1.44 1.44 0 0 0 0-2.881z"/></svg>',
58 },
59 twitter: {
60 label: 'X (Twitter)',
61 brand: '#000000',
62 host: 'twitter.com',
63 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>',
64 },
65 mastodon: {
66 label: 'Mastodon',
67 brand: '#6364FF',
68 host: 'mastodon.social',
69 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>',
70 },
71 github: {
72 label: 'GitHub',
73 brand: '#181717',
74 host: 'github.com',
75 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>',
76 },
77 website: {
78 label: 'Website',
79 brand: '#999999',
80 host: '',
81 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm-1 17.93C7.05 17.44 4 14.07 4 10c0-.62.08-1.21.21-1.79L9 13v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V5h2c1.1 0 2-.9 2-2v-.41C17.93 3.93 20 6.72 20 10c0 2.04-.77 3.9-2.1 5.39z"/></svg>',
82 },
83 email: {
84 label: 'Email',
85 brand: '#666666',
86 host: '',
87 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6zm-2 0l-8 5-8-5h16zm0 12H4V8l8 5 8-5v10z"/></svg>',
88 },
89 telegram: {
90 label: 'Telegram',
91 brand: '#229ED9',
92 host: 't.me',
93 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg>',
94 },
95 whatsapp: {
96 label: 'WhatsApp',
97 brand: '#25D366',
98 host: 'wa.me',
99 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 0 1-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 0 1-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 0 1 2.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0 0 12.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 0 0 5.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 0 0-3.48-8.413z"/></svg>',
100 },
101 phone: {
102 label: 'Telefoon',
103 brand: '#34A853',
104 host: '',
105 svg: '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/></svg>',
106 },
107};
108
109export const PLATFORM_ORDER = [
110 'spotify','bandcamp','soundcloud','applemusic','youtube','vimeo',
111 'instagram','twitter','mastodon','github','website','email','telegram','whatsapp','phone',
112];
113
114// Contact-type platforms (shown grouped in the profile summary).
115export const CONTACT_PLATFORMS = ['email','phone','telegram','whatsapp'];
116
117// Build the real href for a profile link (tel:/mailto: prefixes where needed).
118export function linkHref(platform, url) {
119 const u = String(url || '').trim();
120 if (platform === 'email' && u && u.indexOf('mailto:') !== 0) return 'mailto:' + u;
121 if (platform === 'phone' && u && !/^tel:/i.test(u)) return 'tel:' + u.replace(/[^\d+]/g, '');
122 return u;
123}
124
125export function listPlatforms() {
126 return PLATFORM_ORDER.map(k => ({ key: k, ...PLATFORMS[k] }));
127}
128
129/**
130 * De profiellinks uit een formulier (profile_link_platform[] + profile_link_url[])
131 * als de JSON die in sites.profile_links staat, of null als er niets over is.
132 *
133 * Stond in routes/admin-sites.js, toen de links op de Appearance-pagina stonden.
134 * Sinds 30-9 bewerk je ze bij je profiel (/account), en een tweede kopie van
135 * deze keuring loopt gegarandeerd uit de pas -- dus hij staat hier.
136 */
137export function buildProfileLinks(body) {
138 const platforms = (body && body.profile_link_platform) || [];
139 const urls = (body && body.profile_link_url) || [];
140 const arr = [];
141 const platformsArr = Array.isArray(platforms) ? platforms : [platforms];
142 const urlsArr = Array.isArray(urls) ? urls : [urls];
143 for (let i = 0; i < platformsArr.length; i++) {
144 const p = (platformsArr[i] || '').toString().trim();
145 const u = (urlsArr[i] || '').toString().trim();
146 if (!p || !u) continue;
147 if (!PLATFORMS[p]) continue;
148 if (!/^https?:\/\//i.test(u) && p !== 'email') continue;
149 if (p === 'email' && !/^mailto:|^[^\s@]+@[^\s@]+$/i.test(u)) continue;
150 arr.push({ platform: p, url: u });
151 }
152 return arr.length ? JSON.stringify(arr) : null;
153}
154
155/** sites.profile_links terug als lijst; een kapotte waarde is een lege lijst. */
156export function parseProfileLinks(json) {
157 if (!json) return [];
158 try { const a = JSON.parse(json); return Array.isArray(a) ? a : []; } catch { return []; }
159}
160
161export default { PLATFORMS, PLATFORM_ORDER, listPlatforms, CONTACT_PLATFORMS, linkHref, buildProfileLinks, parseProfileLinks };
Note: See TracBrowser for help on using the repository browser.