276 lines
8.7 KiB
JavaScript
276 lines
8.7 KiB
JavaScript
import { authService, state } from '../state.js';
|
|
import { loadProfileSnapshot } from './user-profile-params.js';
|
|
import { buildAvatarInitials as buildAvatarInitialsFromComponent } from '../components/avatar-image.js';
|
|
|
|
function normalizeLogin(value) {
|
|
return String(value || '').trim();
|
|
}
|
|
|
|
function normKey(value) {
|
|
return normalizeLogin(value).toLowerCase();
|
|
}
|
|
|
|
function uniqueLogins(list) {
|
|
const out = [];
|
|
const seen = new Set();
|
|
(Array.isArray(list) ? list : []).forEach((item) => {
|
|
const login = normalizeLogin(item);
|
|
if (!login) return;
|
|
const key = normKey(login);
|
|
if (seen.has(key)) return;
|
|
seen.add(key);
|
|
out.push(login);
|
|
});
|
|
return out;
|
|
}
|
|
|
|
function listContainsLogin(list, login) {
|
|
const targetKey = normKey(login);
|
|
if (!targetKey) return false;
|
|
return uniqueLogins(list).some((value) => normKey(value) === targetKey);
|
|
}
|
|
|
|
function toFieldMap(snapshot) {
|
|
const map = {};
|
|
(snapshot?.fields || []).forEach((field) => {
|
|
map[field.key] = String(field.value || '').trim();
|
|
});
|
|
return map;
|
|
}
|
|
|
|
function toToggleMap(snapshot) {
|
|
const map = {};
|
|
(snapshot?.toggles || []).forEach((toggle) => {
|
|
map[toggle.key] = Boolean(toggle.enabled);
|
|
});
|
|
return map;
|
|
}
|
|
|
|
function readArray(payload, key) {
|
|
const aliases = {
|
|
outKnownPersons: ['outKnownPersons', 'outKnownPerson', 'out_known_persons'],
|
|
inKnownPersons: ['inKnownPersons', 'inKnownPerson', 'in_known_persons'],
|
|
outShineConfirmed: ['outShineConfirmed', 'outShineConfident', 'out_shine_confirmed'],
|
|
inShineConfirmed: ['inShineConfirmed', 'inShineConfident', 'in_shine_confirmed'],
|
|
outShineSeen: ['outShineSeen', 'out_shine_seen'],
|
|
inShineSeen: ['inShineSeen', 'in_shine_seen'],
|
|
};
|
|
const keys = aliases[key] || [key];
|
|
let value = null;
|
|
for (const oneKey of keys) {
|
|
const candidate = payload?.[oneKey];
|
|
if (Array.isArray(candidate)) {
|
|
value = candidate;
|
|
break;
|
|
}
|
|
}
|
|
return Array.isArray(value) ? uniqueLogins(value) : null;
|
|
}
|
|
|
|
function feedOwnerLogins(feedPayload) {
|
|
const rows = Array.isArray(feedPayload?.followedUsersChannels) ? feedPayload.followedUsersChannels : [];
|
|
const owners = rows
|
|
.map((row) => normalizeLogin(row?.channel?.ownerLogin))
|
|
.filter(Boolean);
|
|
return uniqueLogins(owners);
|
|
}
|
|
|
|
async function buildRelationsModel(login) {
|
|
const cleanLogin = normalizeLogin(login);
|
|
if (!cleanLogin) {
|
|
return {
|
|
outFriends: [],
|
|
inFriends: [],
|
|
outContacts: [],
|
|
inContacts: [],
|
|
outFollows: [],
|
|
inFollows: [],
|
|
outParents: [],
|
|
inParents: [],
|
|
outChildren: [],
|
|
inChildren: [],
|
|
outSiblings: [],
|
|
inSiblings: [],
|
|
outKnownPersons: [],
|
|
inKnownPersons: [],
|
|
outShineConfirmed: [],
|
|
inShineConfirmed: [],
|
|
outShineSeen: [],
|
|
inShineSeen: [],
|
|
};
|
|
}
|
|
|
|
const graph = await authService.getUserConnectionsGraph(cleanLogin);
|
|
|
|
let outContacts = readArray(graph, 'outContacts');
|
|
let outFollows = readArray(graph, 'outFollows');
|
|
|
|
const isCurrentSessionLogin = normKey(cleanLogin) === normKey(state.session.login);
|
|
|
|
if (outContacts === null && isCurrentSessionLogin) {
|
|
try {
|
|
const contacts = await authService.listContacts();
|
|
outContacts = uniqueLogins(contacts?.contacts || []);
|
|
} catch {
|
|
outContacts = [];
|
|
}
|
|
}
|
|
if (outContacts === null) outContacts = [];
|
|
|
|
if (outFollows === null) {
|
|
try {
|
|
const feed = await authService.listSubscriptionsFeed(cleanLogin, 200);
|
|
outFollows = feedOwnerLogins(feed);
|
|
} catch {
|
|
outFollows = [];
|
|
}
|
|
}
|
|
|
|
return {
|
|
outFriends: readArray(graph, 'outFriends') || [],
|
|
inFriends: readArray(graph, 'inFriends') || [],
|
|
outContacts,
|
|
inContacts: readArray(graph, 'inContacts') || [],
|
|
outFollows,
|
|
inFollows: readArray(graph, 'inFollows') || [],
|
|
outParents: readArray(graph, 'outParents') || [],
|
|
inParents: readArray(graph, 'inParents') || [],
|
|
outChildren: readArray(graph, 'outChildren') || [],
|
|
inChildren: readArray(graph, 'inChildren') || [],
|
|
outSiblings: readArray(graph, 'outSiblings') || [],
|
|
inSiblings: readArray(graph, 'inSiblings') || [],
|
|
outKnownPersons: readArray(graph, 'outKnownPersons') || [],
|
|
inKnownPersons: readArray(graph, 'inKnownPersons') || [],
|
|
outShineConfirmed: readArray(graph, 'outShineConfirmed') || [],
|
|
inShineConfirmed: readArray(graph, 'inShineConfirmed') || [],
|
|
outShineSeen: readArray(graph, 'outShineSeen') || [],
|
|
inShineSeen: readArray(graph, 'inShineSeen') || [],
|
|
};
|
|
}
|
|
|
|
export function buildIdentityLines({ login, firstName, lastName }) {
|
|
const lines = [];
|
|
const first = String(firstName || '').trim();
|
|
const last = String(lastName || '').trim();
|
|
const cleanLogin = normalizeLogin(login);
|
|
|
|
if (first) lines.push(first);
|
|
if (last) lines.push(last);
|
|
lines.push(cleanLogin || 'unknown');
|
|
|
|
return lines;
|
|
}
|
|
|
|
export const buildAvatarInitials = buildAvatarInitialsFromComponent;
|
|
|
|
export async function loadCurrentRelations() {
|
|
const login = normalizeLogin(state.session.login);
|
|
if (!login) {
|
|
return {
|
|
outFriends: [],
|
|
inFriends: [],
|
|
outContacts: [],
|
|
inContacts: [],
|
|
outFollows: [],
|
|
inFollows: [],
|
|
outParents: [],
|
|
inParents: [],
|
|
outChildren: [],
|
|
inChildren: [],
|
|
outSiblings: [],
|
|
inSiblings: [],
|
|
outKnownPersons: [],
|
|
inKnownPersons: [],
|
|
outShineConfirmed: [],
|
|
inShineConfirmed: [],
|
|
outShineSeen: [],
|
|
inShineSeen: [],
|
|
};
|
|
}
|
|
return buildRelationsModel(login);
|
|
}
|
|
|
|
export function relationFlagsForTarget(relations, targetLogin) {
|
|
return {
|
|
outFriend: listContainsLogin(relations?.outFriends, targetLogin),
|
|
inFriend: listContainsLogin(relations?.inFriends, targetLogin),
|
|
outContact: listContainsLogin(relations?.outContacts, targetLogin),
|
|
inContact: listContainsLogin(relations?.inContacts, targetLogin),
|
|
outFollow: listContainsLogin(relations?.outFollows, targetLogin),
|
|
inFollow: listContainsLogin(relations?.inFollows, targetLogin),
|
|
outParent: listContainsLogin(relations?.outParents, targetLogin),
|
|
inParent: listContainsLogin(relations?.inParents, targetLogin),
|
|
outChild: listContainsLogin(relations?.outChildren, targetLogin),
|
|
inChild: listContainsLogin(relations?.inChildren, targetLogin),
|
|
outSibling: listContainsLogin(relations?.outSiblings, targetLogin),
|
|
inSibling: listContainsLogin(relations?.inSiblings, targetLogin),
|
|
outKnownPerson: listContainsLogin(relations?.outKnownPersons, targetLogin),
|
|
inKnownPerson: listContainsLogin(relations?.inKnownPersons, targetLogin),
|
|
outShineConfirmed: listContainsLogin(relations?.outShineConfirmed, targetLogin),
|
|
inShineConfirmed: listContainsLogin(relations?.inShineConfirmed, targetLogin),
|
|
outShineSeen: listContainsLogin(relations?.outShineSeen, targetLogin),
|
|
inShineSeen: listContainsLogin(relations?.inShineSeen, targetLogin),
|
|
};
|
|
}
|
|
|
|
export async function loadUserProfileCard(login) {
|
|
const cleanLogin = normalizeLogin(login);
|
|
if (!cleanLogin) throw new Error('Пустой login');
|
|
|
|
const [user, snapshot] = await Promise.all([
|
|
authService.getUser(cleanLogin),
|
|
loadProfileSnapshot(cleanLogin),
|
|
]);
|
|
|
|
if (!user?.exists) throw new Error('Пользователь не найден');
|
|
|
|
const canonicalLogin = normalizeLogin(user.login || cleanLogin);
|
|
const fields = toFieldMap(snapshot);
|
|
const toggles = toToggleMap(snapshot);
|
|
|
|
return {
|
|
login: canonicalLogin,
|
|
blockchainName: normalizeLogin(user.blockchainName),
|
|
firstName: fields.first_name || '',
|
|
lastName: fields.last_name || '',
|
|
address: fields.address || '',
|
|
web: fields.web || '',
|
|
phone: fields.phone || '',
|
|
gender: String(snapshot?.gender || 'unknown').trim().toLowerCase() || 'unknown',
|
|
official: Boolean(toggles.official),
|
|
shine: Boolean(toggles.shine),
|
|
avatar: snapshot?.avatar?.txId
|
|
? {
|
|
ar: String(snapshot.avatar.txId).trim(),
|
|
sha256Hex: String(snapshot?.avatar?.sha256Hex || '').trim().toLowerCase(),
|
|
}
|
|
: null,
|
|
};
|
|
}
|
|
|
|
export async function loadRelationsForPair({ currentLogin, targetLogin }) {
|
|
const cleanCurrent = normalizeLogin(currentLogin);
|
|
const cleanTarget = normalizeLogin(targetLogin);
|
|
const currentRelations = await buildRelationsModel(cleanCurrent);
|
|
let flags = relationFlagsForTarget(currentRelations, cleanTarget);
|
|
|
|
if (!flags.inContact || !flags.inFollow) {
|
|
try {
|
|
const targetRelations = await buildRelationsModel(cleanTarget);
|
|
const backFlags = relationFlagsForTarget(targetRelations, cleanCurrent);
|
|
flags = {
|
|
...flags,
|
|
inContact: flags.inContact || backFlags.outContact,
|
|
inFollow: flags.inFollow || backFlags.outFollow,
|
|
};
|
|
} catch {
|
|
// ignore fallback failures for incoming direction
|
|
}
|
|
}
|
|
|
|
return {
|
|
...flags,
|
|
source: currentRelations,
|
|
};
|
|
}
|