Files
SHiNE-server/shine-UI/js/services/user-profile-params.js

223 lines
8.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { authService, state } from '../state.js';
import {
buildArweaveAvatarValue,
parseArweaveAvatarValue,
validateArweaveTxId,
validateSha256Hex,
} from './arweave-file-service.js';
export const profileFieldDefs = [
{ key: 'first_name', readKeys: ['first_name'], label: 'Имя', placeholder: 'Введите имя' },
{ key: 'last_name', readKeys: ['last_name'], label: 'Фамилия', placeholder: 'Введите фамилию' },
{ key: 'address', readKeys: ['address'], label: 'Адрес', placeholder: 'Город, улица, дом' },
{ key: 'web', readKeys: ['web'], label: 'Links', placeholder: 'Сайт, профиль или другая ссылка' },
{ key: 'phone', readKeys: ['phone'], label: 'Телефон', placeholder: '+7 ...' },
{ key: 'about', readKeys: ['about'], label: 'О себе', placeholder: 'Коротко расскажите о себе', maxLength: 160, multiline: true },
{ key: 'spiritual_path', readKeys: ['spiritual_path'], label: 'Духовный путь', placeholder: 'Расскажите о своём духовном пути, опыте, практиках и взглядах', maxLength: 5000, multiline: true },
];
export const profileToggleDefs = []; // legacy boolean toggles are no longer used by the new UI.
export const PROFILE_ACCOUNT_ROLE_PRIMARY = 'primary';
export const PROFILE_ACCOUNT_ROLE_NON_VOTING = 'non_voting';
export const PROFILE_SHINE_SHINING = 'shining';
export const PROFILE_SHINE_UNKNOWN = 'unknown';
export const PROFILE_SHINE_NOT_INTERESTED = 'not_interested';
export const PROFILE_GENDER_MALE = 'male';
export const PROFILE_GENDER_FEMALE = 'female';
export const PROFILE_GENDER_UNKNOWN = 'unknown';
export const PROFILE_GENDER_VALUES = Object.freeze([
PROFILE_GENDER_MALE,
PROFILE_GENDER_FEMALE,
PROFILE_GENDER_UNKNOWN,
]);
function normalizeItem(param, payload) {
if (!param) return null;
if (payload && typeof payload === 'object') {
const value = String(payload?.value || payload?.param_value || '');
const timeMs = Number(payload?.time_ms || payload?.timeMs || 0);
if (!value && !timeMs) return null;
return { param, value, timeMs };
}
return null;
}
function parseToggleValue(value) {
const normalized = String(value || '').trim().toLowerCase();
return normalized === 'true' || normalized === 'yes' || normalized === '1';
}
function normalizeGenderValue(value) {
const normalized = String(value || '').trim().toLowerCase();
if (normalized === PROFILE_GENDER_MALE) return PROFILE_GENDER_MALE;
if (normalized === PROFILE_GENDER_FEMALE) return PROFILE_GENDER_FEMALE;
return PROFILE_GENDER_UNKNOWN;
}
async function getStoragePwd() {
const storagePwd = state.session.storagePwdInMemory;
if (!storagePwd) {
throw new Error('Нет storagePwd в памяти сессии. Выполните вход заново.');
}
return storagePwd;
}
function normalizeListItems(payload) {
const rows = Array.isArray(payload?.params) ? payload.params : [];
const normalized = [];
for (let i = 0; i < rows.length; i += 1) {
const row = rows[i];
if (!row || typeof row !== 'object') continue;
const param = String(row.param || '').trim();
if (!param) continue;
const item = normalizeItem(param, row);
if (item) normalized.push(item);
}
return normalized;
}
function loadLatestByAliasesFromItems(items, aliases) {
if (!Array.isArray(items) || !items.length || !Array.isArray(aliases) || !aliases.length) return null;
const aliasSet = new Set(aliases.map((alias) => String(alias || '').trim().toLowerCase()).filter(Boolean));
if (!aliasSet.size) return null;
let latest = null;
for (let i = 0; i < items.length; i += 1) {
const item = items[i];
const itemParam = String(item?.param || '').trim().toLowerCase();
if (!itemParam || !aliasSet.has(itemParam)) continue;
if (!latest || Number(item.timeMs || 0) > Number(latest.timeMs || 0)) {
latest = item;
}
}
return latest;
}
export async function loadProfileSnapshot(login) {
const payload = await authService.listUserParams(login);
const items = normalizeListItems(payload);
const fields = [];
for (let i = 0; i < profileFieldDefs.length; i += 1) {
const field = profileFieldDefs[i];
const latest = loadLatestByAliasesFromItems(items, field.readKeys);
fields.push({
key: field.key,
label: field.label,
placeholder: field.placeholder,
maxLength: Number(field.maxLength || 300),
multiline: Boolean(field.multiline),
value: latest?.value || '',
timeMs: latest?.timeMs || 0,
});
}
const latestAccountRole = loadLatestByAliasesFromItems(items, ['account_role']);
const rawAccountRole = String(latestAccountRole?.value || '').trim().toLowerCase();
const accountRole = rawAccountRole === PROFILE_ACCOUNT_ROLE_PRIMARY || rawAccountRole === PROFILE_ACCOUNT_ROLE_NON_VOTING
? rawAccountRole
: '';
const latestShine = loadLatestByAliasesFromItems(items, ['shine']);
const rawShine = String(latestShine?.value || '').trim().toLowerCase();
let shineStatus = '';
if (rawShine === PROFILE_SHINE_SHINING || rawShine === 'yes') shineStatus = PROFILE_SHINE_SHINING;
else if (rawShine === PROFILE_SHINE_UNKNOWN) shineStatus = PROFILE_SHINE_UNKNOWN;
else if (rawShine === PROFILE_SHINE_NOT_INTERESTED) shineStatus = PROFILE_SHINE_NOT_INTERESTED;
// Legacy shine=no is intentionally ignored. Legacy official=yes/no is not read at all.
const toggles = [
{ key: 'account_role', label: 'Роль аккаунта', enabled: accountRole === PROFILE_ACCOUNT_ROLE_PRIMARY, rawValue: accountRole, timeMs: latestAccountRole?.timeMs || 0 },
{ key: 'shine', label: 'Сияние', enabled: shineStatus === PROFILE_SHINE_SHINING, rawValue: shineStatus, timeMs: latestShine?.timeMs || 0 },
];
const latestGender = loadLatestByAliasesFromItems(items, ['gender']);
const gender = normalizeGenderValue(latestGender?.value || PROFILE_GENDER_UNKNOWN);
const latestAvatar = loadLatestByAliasesFromItems(items, ['ava']);
const parsedAvatar = parseArweaveAvatarValue(latestAvatar?.value || '');
const avatar = parsedAvatar.ok
? {
value: buildArweaveAvatarValue(parsedAvatar.txId, parsedAvatar.sha256Hex),
source: 'arweave',
txId: parsedAvatar.txId,
sha256Hex: parsedAvatar.sha256Hex || '',
timeMs: latestAvatar?.timeMs || 0,
}
: {
value: '',
source: '',
txId: '',
sha256Hex: '',
timeMs: latestAvatar?.timeMs || 0,
};
return {
fields,
toggles,
accountRole,
accountRoleTimeMs: latestAccountRole?.timeMs || 0,
shineStatus,
shineTimeMs: latestShine?.timeMs || 0,
gender,
genderTimeMs: latestGender?.timeMs || 0,
avatar,
};
}
export async function saveProfileParamBlock(login, key, value) {
const storagePwd = await getStoragePwd();
await authService.addBlockUserParam({
login,
param: key,
value: String(value ?? '').trim(),
storagePwd,
});
}
export async function saveProfileStatus(login, key, value) {
const cleanKey = String(key || '').trim();
const cleanValue = String(value || '').trim().toLowerCase();
if (cleanKey === 'account_role' && ![PROFILE_ACCOUNT_ROLE_PRIMARY, PROFILE_ACCOUNT_ROLE_NON_VOTING].includes(cleanValue)) {
throw new Error('Некорректное значение account_role');
}
if (cleanKey === 'shine' && ![PROFILE_SHINE_SHINING, PROFILE_SHINE_UNKNOWN, PROFILE_SHINE_NOT_INTERESTED].includes(cleanValue)) {
throw new Error('Некорректное значение shine');
}
const storagePwd = await getStoragePwd();
await authService.addBlockUserParam({ login, param: cleanKey, value: cleanValue, storagePwd });
}
// Kept only for old callers outside the current UI. New code must use saveProfileStatus().
export async function saveProfileToggle(login, key, enabled) {
if (key === 'shine') return saveProfileStatus(login, 'shine', enabled ? PROFILE_SHINE_SHINING : PROFILE_SHINE_UNKNOWN);
if (key === 'official') throw new Error('Параметр official устарел. Используйте account_role.');
throw new Error(`Неизвестный legacy toggle: ${key}`);
}
export async function saveProfileGender(login, gender) {
const normalized = normalizeGenderValue(gender);
const storagePwd = await getStoragePwd();
await authService.addBlockUserParam({
login,
param: 'gender',
value: normalized,
storagePwd,
});
}
export async function saveProfileAvatarArweave(login, txId, sha256Hex) {
const cleanTxId = String(txId || '').trim();
const cleanSha = String(sha256Hex || '').trim().toLowerCase();
if (!validateArweaveTxId(cleanTxId)) {
throw new Error('Некорректный Transaction ID Arweave');
}
if (!validateSha256Hex(cleanSha)) {
throw new Error('Некорректный SHA256 хэш аватара');
}
await saveProfileParamBlock(login, 'ava', buildArweaveAvatarValue(cleanTxId, cleanSha));
}