| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- const { div, h2, p, section, button, form, img, a, textarea, input, br, span, strong } = require("../server/node_modules/hyperaxe");
- const { template, i18n, userLink} = require('./main_views');
- const { renderUrl } = require('../backend/renderUrl');
- const { getConfig } = require('../configs/config-manager');
- const DEFAULT_HASH_ENC = "%260000000000000000000000000000000000000000000%3D.sha256";
- const DEFAULT_HASH_PATH_RE = /\/image\/\d+\/%260000000000000000000000000000000000000000000%3D\.sha256$/;
- function isDefaultImageId(v){
- if (!v) return true;
- if (typeof v === 'string') {
- if (v === DEFAULT_HASH_ENC) return true;
- if (DEFAULT_HASH_PATH_RE.test(v)) return true;
- }
- return false;
- }
- function toImageUrl(imgId, size=256){
- if (!imgId || isDefaultImageId(imgId)) return '/assets/images/default-avatar.png';
- if (typeof imgId === 'string' && imgId.startsWith('/image/')) {
- return imgId.replace('/image/256/','/image/'+size+'/').replace('/image/512/','/image/'+size+'/');
- }
- return `/image/${size}/${encodeURIComponent(imgId)}`;
- }
- function extractAboutImageId(about){
- if (!about || typeof about !== 'object') return null;
- const aimg = about.image;
- if (!aimg) return null;
- if (typeof aimg === 'string') return aimg;
- return aimg.link || aimg.url || null;
- }
- function resolvePhoto(photoField, size = 256) {
- if (!photoField) return '/assets/images/default-avatar.png';
- if (typeof photoField === 'string') {
- if (photoField.startsWith('/assets/')) return photoField;
- if (photoField.startsWith('/blob/')) return photoField;
- if (photoField.startsWith('/image/')) {
- if (isDefaultImageId(photoField)) return '/assets/images/default-avatar.png';
- return photoField.replace('/image/256/','/image/'+size+'/').replace('/image/512/','/image/'+size+'/');
- }
- }
- return toImageUrl(photoField, size);
- }
- const generateFilterButtons = (filters, currentFilter) =>
- filters.map(mode =>
- form({ method: 'GET', action: '/inhabitants' },
- input({ type: 'hidden', name: 'filter', value: mode }),
- button({
- type: 'submit',
- class: currentFilter === mode ? 'filter-btn active' : 'filter-btn'
- }, i18n[mode + 'Button'] || i18n[mode + 'SectionTitle'] || mode)
- )
- );
- function lastActivityBadge(user, isMe) {
- const bucket = user && user.lastActivityBucket;
- const dotClass =
- bucket === 'green' ? 'green' : bucket === 'orange' ? 'orange' : bucket === 'red' ? 'red' : null;
- if (!dotClass) return [];
- const items = [
- span({ class: 'inhabitant-last-activity' },
- `${i18n.inhabitantActivityLevel}: `,
- span({ class: `activity-dot ${dotClass}` }, '●'))
- ];
- const currentTheme = getConfig().themes.current;
- const src = isMe ? (currentTheme === 'OasisKIT' ? 'KIT' : (currentTheme === 'OasisMobile' || process.env.OASIS_MOBILE === '1') ? 'MOBILE' : 'DESKTOP') : (user && user.deviceSource) || null;
- if (src) {
- const upper = String(src).toUpperCase();
- const deviceClass = upper === 'KIT' ? 'device-kit' : upper === 'MOBILE' ? 'device-mobile' : 'device-desktop';
- items.push(span({ class: 'inhabitant-last-activity' },
- `${i18n.deviceLabel || 'Device'}: `,
- span({ class: deviceClass }, src)));
- }
- return [div({ class: 'inhabitant-activity-group' }, ...items)];
- }
- const lightboxId = (id) => 'inhabitant_' + String(id || 'unknown').replace(/[^a-zA-Z0-9_-]/g, '_');
- const renderInhabitantCard = (user, filter, currentUserId) => {
- const isMe = user.id === currentUserId;
- return div({ class: 'inhabitant-card' },
- div({ class: 'inhabitant-left' },
- a(
- { href: `/author/${encodeURIComponent(user.id)}` },
- img({ class: 'inhabitant-photo-details', src: resolvePhoto(user.photo, 256), alt: user.name || 'Anonymous' })
- ),
- br(),
- ...lastActivityBadge(user, isMe),
- div({ class: 'inhabitant-karma-ubi' },
- span({ class: 'karma-line' }, `${i18n.bankingUserEngagementScore}: `, strong(String(typeof user.karmaScore === 'number' ? user.karmaScore : 0)))
- )
- ),
- div({ class: 'inhabitant-details' },
- h2(user.name || 'Anonymous'),
- user.description ? p(...renderUrl(user.description)) : null,
- filter === 'MATCHSKILLS' && user.commonSkills?.length
- ? div({ class: 'matchskills' },
- p(`${i18n.commonSkills}: ${user.commonSkills.join(', ')}`),
- p(`${i18n.matchScore}: ${Math.round(user.matchScore * 100)}%`)
- )
- : null,
- filter === 'SUGGESTED' && user.mutualCount
- ? p(`${i18n.mutualFollowers}: ${user.mutualCount}`) : null,
- filter === 'blocked' && user.isBlocked
- ? p(i18n.blockedLabel) : null,
- p(userLink(user.id)),
- user.ecoAddress
- ? div({ class: "eco-wallet" },
- p(`${i18n.bankWalletConnected}: `, strong(user.ecoAddress))
- )
- : div({ class: "eco-wallet" },
- p(i18n.ecoWalletNotConfigured || "ECOin Wallet not configured")
- ),
- div(
- { class: 'cv-actions' },
- !isMe
- ? form(
- { method: 'GET', action: `/inhabitant/${encodeURIComponent(user.id)}` },
- button({ type: 'submit', class: 'btn' }, i18n.inhabitantviewDetails)
- )
- : p(i18n.relationshipYou),
- !isMe
- ? form(
- { method: 'GET', action: '/pm' },
- input({ type: 'hidden', name: 'recipients', value: user.id }),
- button({ type: 'submit', class: 'btn' }, i18n.pmCreateButton)
- )
- : null
- )
- )
- );
- };
- const renderGalleryInhabitants = inhabitants =>
- div(
- { class: "gallery" },
- inhabitants.length
- ? inhabitants.map(u =>
- a({ href: `#${lightboxId(u.id)}`, class: "gallery-item" },
- img({ src: resolvePhoto(u.photo, 256), alt: u.name || "Anonymous", class: "gallery-image" })
- )
- )
- : p(i18n.noInhabitantsFound)
- );
- const renderLightbox = inhabitants =>
- inhabitants.map(u =>
- div(
- { id: lightboxId(u.id), class: "lightbox" },
- a({ href: "#", class: "lightbox-close" }, "×"),
- img({ src: resolvePhoto(u.photo, 256), class: "lightbox-image", alt: u.name || "Anonymous" })
- )
- );
- function stripAndCollectImgs(text) {
- if (!text || typeof text !== 'string') return { clean: '', imgs: [] };
- const imgs = [];
- let clean = text;
- const rawImgRe = /<img[^>]*src="([^"]+)"[^>]*>/gi;
- clean = clean.replace(rawImgRe, (_, src) => { imgs.push(src); return ''; });
- const encImgRe = /<img[^&]*src="([^&]*)"[^&]*>/gi;
- clean = clean.replace(encImgRe, (_, src) => { imgs.push(src.replace(/&/g, '&')); return ''; });
- return { clean, imgs };
- }
- function msgIdOf(m) {
- return m && (m.key || m.value?.key || m.value?.content?.root || m.value?.content?.branch || null);
- }
- exports.inhabitantsView = (inhabitants, filter, query, currentUserId) => {
- const title = filter === 'contacts' ? i18n.yourContacts
- : filter === 'CVs' ? i18n.allCVs
- : filter === 'MATCHSKILLS' ? i18n.matchSkills
- : filter === 'SUGGESTED' ? i18n.suggestedSectionTitle
- : filter === 'blocked' ? i18n.blockedSectionTitle
- : filter === 'GALLERY' ? i18n.gallerySectionTitle
- : filter === 'TOP KARMA' ? i18n.topkarmaSectionTitle
- : filter === 'TOP ACTIVITY' ? i18n.topactivitySectionTitle
- : i18n.allInhabitants;
- const showCVFilters = filter === 'CVs' || filter === 'MATCHSKILLS';
- const filters = ['all', 'TOP ACTIVITY', 'TOP KARMA', 'contacts', 'SUGGESTED', 'blocked', 'CVs', 'MATCHSKILLS', 'GALLERY'];
- return template(
- title,
- section(
- div({ class: 'tags-header' },
- h2(title),
- p(i18n.discoverPeople)
- ),
- div({ class: 'filters' },
- form({ method: 'GET', action: '/inhabitants' },
- input({ type: 'hidden', name: 'filter', value: filter }),
- input({
- type: 'text',
- name: 'search',
- placeholder: i18n.searchInhabitantsPlaceholder,
- value: (query && query.search) || ''
- }),
- showCVFilters
- ? [
- input({ type: 'text', name: 'location', placeholder: i18n.filterLocation, value: (query && query.location) || '' }),
- input({ type: 'text', name: 'language', placeholder: i18n.filterLanguage, value: (query && query.language) || '' }),
- input({ type: 'text', name: 'skills', placeholder: i18n.filterSkills, value: (query && query.skills) || '' })
- ]
- : null,
- br(),
- button({ type: 'submit' }, i18n.applyFilters)
- )
- ),
- div({ class: 'inhabitant-action' },
- ...generateFilterButtons(filters, filter)
- ),
- filter === 'GALLERY'
- ? renderGalleryInhabitants(inhabitants)
- : div({ class: 'inhabitants-list' },
- inhabitants.length
- ? inhabitants.map(user => renderInhabitantCard(user, filter, currentUserId))
- : p({ class: 'no-results' }, i18n.noInhabitantsFound)
- ),
- ...renderLightbox(inhabitants)
- )
- );
- };
- exports.inhabitantsProfileView = (payload, currentUserId) => {
- const safe = payload && typeof payload === 'object' ? payload : {};
- const about = (safe.about && typeof safe.about === 'object') ? safe.about : {};
- const cv = (safe.cv && typeof safe.cv === 'object') ? safe.cv : {};
- const feed = Array.isArray(safe.feed) ? safe.feed : [];
- const viewedId = typeof safe.viewedId === 'string' ? safe.viewedId : '';
- const id = (cv && cv.author) || (about && about.about) || viewedId || '';
- const baseName = ((cv && cv.name) || (about && about.name) || '').trim();
- const name = baseName || (i18n.unnamed || 'Anonymous');
- const description = (cv && cv.description) || (about && about.description) || '';
- const listPhoto = (typeof safe.photo === 'string' && safe.photo.trim()) ? safe.photo : null;
- const rawCandidate = listPhoto || extractAboutImageId(about) || (cv && cv.photo) || null;
- const image = (
- typeof rawCandidate === 'string' &&
- rawCandidate.startsWith('/image/') &&
- !DEFAULT_HASH_PATH_RE.test(rawCandidate) &&
- rawCandidate.indexOf(DEFAULT_HASH_ENC) === -1
- )
- ? rawCandidate.replace('/image/512/','/image/256/').replace('/image/1024/','/image/256/')
- : resolvePhoto(rawCandidate, 256);
- const location = (cv && cv.location) || '';
- const languages = typeof (cv && cv.languages) === 'string'
- ? (cv.languages || '').split(',').map(x => x.trim()).filter(Boolean)
- : Array.isArray(cv && cv.languages) ? cv.languages : [];
- const skills = [
- ...((cv && cv.personalSkills) || []),
- ...((cv && cv.oasisSkills) || []),
- ...((cv && cv.educationalSkills) || []),
- ...((cv && cv.professionalSkills) || [])
- ];
- const status = (cv && cv.status) || '';
- const preferences = (cv && cv.preferences) || '';
- const createdAt = (cv && cv.createdAt) ? new Date(cv.createdAt).toLocaleString() : '';
- const isMe = id && id === currentUserId;
- const title = i18n.inhabitantProfileTitle || i18n.inhabitantviewDetails;
- const karmaScore = typeof safe.karmaScore === 'number' ? safe.karmaScore : 0;
- const providedBucket = typeof safe.lastActivityBucket === 'string' ? safe.lastActivityBucket : null;
- const dotClass = providedBucket === 'green' ? 'green' : providedBucket === 'orange' ? 'orange' : 'red';
- const detailNodes = [
- description ? p(...renderUrl(description)) : null,
- location ? p(`${i18n.locationLabel}: ${location}`) : null,
- languages.length ? p(`${i18n.languagesLabel}: ${languages.join(', ').toUpperCase()}`) : null,
- skills.length ? p(`${i18n.skillsLabel}: ${skills.join(', ')}`) : null,
- status ? p(`${i18n.statusLabel || 'Status'}: ${status}`) : null,
- preferences ? p(`${i18n.preferencesLabel || 'Preferences'}: ${preferences}`) : null,
- createdAt ? p(`${i18n.createdAtLabel || 'Created at'}: ${createdAt}`) : null
- ].filter(Boolean);
- return template(
- name,
- section(
- div({ class: 'tags-header' },
- h2(title),
- p(i18n.discoverPeople)
- ),
- div({ class: 'mode-buttons' },
- ...generateFilterButtons(['all', 'TOP ACTIVITY', 'TOP KARMA', 'contacts', 'SUGGESTED', 'blocked', 'CVs', 'MATCHSKILLS', 'GALLERY'], 'all')
- ),
- div({ class: 'inhabitant-card' },
- div({ class: 'inhabitant-left' },
- img({ class: 'inhabitant-photo-details', src: image, alt: name || 'Anonymous' }),
- h2(name || 'Anonymous'),
- ...lastActivityBadge({ lastActivityBucket: dotClass, deviceSource: safe.deviceSource }, isMe),
- div({ class: 'inhabitant-karma-ubi' },
- span({ class: 'karma-line' }, `${i18n.bankingUserEngagementScore}: `, strong(String(karmaScore)))
- ),
- (!isMe && (id || viewedId))
- ? form(
- { method: 'GET', action: '/pm' },
- input({ type: 'hidden', name: 'recipients', value: id || viewedId }),
- button({ type: 'submit', class: 'btn' }, i18n.pmCreateButton)
- )
- : null
- ),
- detailNodes.length ? div({ class: 'inhabitant-details' }, ...detailNodes) : null
- ),
- feed.length
- ? section({ class: 'profile-feed' },
- h2(i18n.latestInteractions),
- ...feed.map(m => {
- const raw = (m.value?.content?.text || '').replace(/<br\s*\/?>/g, '');
- const parts = stripAndCollectImgs(raw);
- const tid = msgIdOf(m);
- const visitBtn = tid
- ? form({ method: 'GET', action: `/thread/${encodeURIComponent(tid)}#${encodeURIComponent(tid)}` },
- button({ type:'submit', class:'filter-btn' }, i18n.visitContent)
- )
- : null;
- return div({ class: 'post' },
- visitBtn,
- parts.clean && parts.clean.trim() ? p(...renderUrl(parts.clean)) : null,
- ...(parts.imgs || []).map(src => img({ src, class: 'post-image', alt: 'image' }))
- );
- })
- )
- : null
- )
- );
- };
- exports.lastActivityBadge = lastActivityBadge;
|