Fix channel/author subscription confirmation and follow trigger persistence

This commit is contained in:
DrygMira 2026-04-14 13:47:59 +03:00
parent 7bdb3118ae
commit e17a6765ec
2 changed files with 137 additions and 20 deletions

View File

@ -111,18 +111,23 @@ async function resolveChannelTargetFromInput(rawInput) {
}
const ownerFeed = await authService.listSubscriptionsFeed(ownerLogin, 500);
const ownChannels = (ownerFeed?.ownedChannels || []).filter((item) => (
String(item?.channel?.ownerBlockchainName || '') === String(user.blockchainName)
));
const match = ownChannels.find((item) => (
const ownChannels = Array.isArray(ownerFeed?.ownedChannels) ? ownerFeed.ownedChannels : [];
const matches = ownChannels.filter((item) => (
String(item?.channel?.channelName || '').trim().toLowerCase() === channelName
));
if (!match) {
if (!matches.length) {
throw new Error('Канал не найден у указанного автора.');
}
const primaryMatches = matches.filter((item) => (
String(item?.channel?.ownerBlockchainName || '') === String(user.blockchainName || '')
));
const pool = primaryMatches.length ? primaryMatches : matches;
const match = [...pool].sort((a, b) => (
Number(b?.channel?.channelRoot?.blockNumber || -1) - Number(a?.channel?.channelRoot?.blockNumber || -1)
))[0];
return {
ownerBlockchainName: String(match?.channel?.ownerBlockchainName || user.blockchainName),
rootBlockNumber: Number(match?.channel?.channelRoot?.blockNumber),
@ -195,6 +200,36 @@ function renderSuggestions(container, values, onPick) {
});
}
function normalizeComparableLogin(value) {
return normalizeLoginInput(value).toLowerCase();
}
function isFollowedUserVisible(targetLogin) {
const expected = normalizeComparableLogin(targetLogin);
if (!expected) return false;
const rows = Array.isArray(state.channelsFeed?.followedUsersChannels)
? state.channelsFeed.followedUsersChannels
: [];
return rows.some((row) => normalizeComparableLogin(row?.channel?.ownerLogin) === expected);
}
function isFollowedChannelVisible(target) {
const rows = Array.isArray(state.channelsFeed?.followedChannels)
? state.channelsFeed.followedChannels
: [];
const expectedBch = String(target?.ownerBlockchainName || '');
const expectedNo = Number(target?.rootBlockNumber);
const expectedHash = normalizeHash(target?.rootBlockHash);
if (!expectedBch || !Number.isFinite(expectedNo)) return false;
return rows.some((row) => {
const rowBch = String(row?.channel?.ownerBlockchainName || '');
const rowNo = Number(row?.channel?.channelRoot?.blockNumber);
const rowHash = normalizeHash(row?.channel?.channelRoot?.blockHash);
return rowBch === expectedBch && rowNo === expectedNo && rowHash === expectedHash;
});
}
function openSimpleSubscribeModal({ kind, kindLabel, submitLabel, unfollow = false, onSuccess }) {
const targetHint = kind === 'channel'
? '<p class="meta-muted">Канал: user/channel, имя канала или bch:number:hash.</p>'
@ -306,35 +341,62 @@ function openSimpleSubscribeModal({ kind, kindLabel, submitLabel, unfollow = fal
errorEl.textContent = '';
try {
let channelTarget = null;
let userTargetLogin = '';
if (kind === 'user') {
userTargetLogin = normalizeLoginInput(value);
await authService.addBlockFollowUser({
login,
targetLogin: normalizeLoginInput(value),
targetLogin: userTargetLogin,
storagePwd,
unfollow,
});
} else if (kind === 'channel') {
const target = await resolveChannelTargetFromInput(value);
if (!target?.ownerBlockchainName || !Number.isFinite(target.rootBlockNumber)) {
channelTarget = await resolveChannelTargetFromInput(value);
if (!channelTarget?.ownerBlockchainName || !Number.isFinite(channelTarget.rootBlockNumber)) {
throw new Error('Канал не найден.');
}
await authService.addBlockFollowChannel({
login,
storagePwd,
targetBlockchainName: target.ownerBlockchainName,
targetBlockNumber: target.rootBlockNumber,
targetBlockHashHex: target.rootBlockHash,
targetBlockchainName: channelTarget.ownerBlockchainName,
targetBlockNumber: channelTarget.rootBlockNumber,
targetBlockHashHex: channelTarget.rootBlockHash,
unfollow,
});
} else {
throw new Error('Неподдерживаемый тип подписки');
}
if (typeof onSuccess === 'function') {
await onSuccess();
}
if (kind === 'user') {
const visible = isFollowedUserVisible(userTargetLogin);
if (!unfollow && !visible) {
throw new Error('Подписка не подтвердилась после обновления списка.');
}
if (unfollow && visible) {
throw new Error('Отписка не подтвердилась после обновления списка.');
}
}
if (kind === 'channel') {
const visible = isFollowedChannelVisible(channelTarget);
if (!unfollow && !visible) {
throw new Error('Подписка на канал не подтвердилась после обновления списка.');
}
if (unfollow && visible) {
throw new Error('Отписка от канала не подтвердилась после обновления списка.');
}
}
softHaptic(15);
showToast(unfollow ? 'Отписка выполнена' : 'Подписка выполнена');
close();
if (typeof onSuccess === 'function') onSuccess();
} catch (error) {
errorEl.textContent = toUserMessage(error, `${submitText} не удалось.`);
setBusy(false);

View File

@ -212,12 +212,30 @@ public final class DatabaseTriggersInstaller {
SELECT
NEW.login,
NEW.msg_sub_type,
NEW.to_login,
COALESCE(
NEW.to_login,
CASE
WHEN NEW.to_bch_name IS NOT NULL
AND length(NEW.to_bch_name) > 4
AND substr(NEW.to_bch_name, length(NEW.to_bch_name) - 3, 1) = '-'
THEN substr(NEW.to_bch_name, 1, length(NEW.to_bch_name) - 4)
ELSE NULL
END
),
NEW.to_bch_name,
NEW.to_block_number,
NEW.to_block_hash
WHERE NEW.msg_sub_type IN (%d, %d, %d)
AND NEW.to_login IS NOT NULL
AND COALESCE(
NEW.to_login,
CASE
WHEN NEW.to_bch_name IS NOT NULL
AND length(NEW.to_bch_name) > 4
AND substr(NEW.to_bch_name, length(NEW.to_bch_name) - 3, 1) = '-'
THEN substr(NEW.to_bch_name, 1, length(NEW.to_bch_name) - 4)
ELSE NULL
END
) IS NOT NULL
AND NEW.to_bch_name IS NOT NULL;
-- 2) если запись есССЊ †обновляем акСуальнСРµ to_*
@ -228,27 +246,64 @@ public final class DatabaseTriggersInstaller {
to_block_hash = NEW.to_block_hash
WHERE login = NEW.login
AND rel_type = NEW.msg_sub_type
AND to_login = NEW.to_login
AND NEW.msg_sub_type IN (%d, %d, %d)
AND NEW.to_login IS NOT NULL
AND to_login = COALESCE(
NEW.to_login,
CASE
WHEN NEW.to_bch_name IS NOT NULL
AND length(NEW.to_bch_name) > 4
AND substr(NEW.to_bch_name, length(NEW.to_bch_name) - 3, 1) = '-'
THEN substr(NEW.to_bch_name, 1, length(NEW.to_bch_name) - 4)
ELSE NULL
END
)
AND NEW.msg_sub_type IN (%d, %d)
AND COALESCE(
NEW.to_login,
CASE
WHEN NEW.to_bch_name IS NOT NULL
AND length(NEW.to_bch_name) > 4
AND substr(NEW.to_bch_name, length(NEW.to_bch_name) - 3, 1) = '-'
THEN substr(NEW.to_bch_name, 1, length(NEW.to_bch_name) - 4)
ELSE NULL
END
) IS NOT NULL
AND NEW.to_bch_name IS NOT NULL;
-- UNFRIEND/UNCONTACT/UNFOLLOW:
-- удаляем СЃРѕРѕСРІРµССЃСРІСѓСЋСее "позитивное" СЃРѕСЃСРѕСЏРЅРёРµ
DELETE FROM connections_state
WHERE login = NEW.login
AND to_login = NEW.to_login
AND to_login = COALESCE(
NEW.to_login,
CASE
WHEN NEW.to_bch_name IS NOT NULL
AND length(NEW.to_bch_name) > 4
AND substr(NEW.to_bch_name, length(NEW.to_bch_name) - 3, 1) = '-'
THEN substr(NEW.to_bch_name, 1, length(NEW.to_bch_name) - 4)
ELSE NULL
END
)
AND rel_type = CASE NEW.msg_sub_type
WHEN %d THEN %d
WHEN %d THEN %d
WHEN %d THEN %d
ELSE rel_type
END
AND COALESCE(
NEW.to_login,
CASE
WHEN NEW.to_bch_name IS NOT NULL
AND length(NEW.to_bch_name) > 4
AND substr(NEW.to_bch_name, length(NEW.to_bch_name) - 3, 1) = '-'
THEN substr(NEW.to_bch_name, 1, length(NEW.to_bch_name) - 4)
ELSE NULL
END
) IS NOT NULL
AND NEW.msg_sub_type IN (%d, %d, %d);
END;
""".formatted(
FRIEND, CONTACT, FOLLOW,
FRIEND, CONTACT, FOLLOW,
FRIEND, CONTACT,
UNFRIEND, FRIEND,
UNCONTACT, CONTACT,