Fix channel/author subscription confirmation and follow trigger persistence
This commit is contained in:
parent
7bdb3118ae
commit
e17a6765ec
@ -111,18 +111,23 @@ async function resolveChannelTargetFromInput(rawInput) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ownerFeed = await authService.listSubscriptionsFeed(ownerLogin, 500);
|
const ownerFeed = await authService.listSubscriptionsFeed(ownerLogin, 500);
|
||||||
const ownChannels = (ownerFeed?.ownedChannels || []).filter((item) => (
|
const ownChannels = Array.isArray(ownerFeed?.ownedChannels) ? ownerFeed.ownedChannels : [];
|
||||||
String(item?.channel?.ownerBlockchainName || '') === String(user.blockchainName)
|
const matches = ownChannels.filter((item) => (
|
||||||
));
|
|
||||||
|
|
||||||
const match = ownChannels.find((item) => (
|
|
||||||
String(item?.channel?.channelName || '').trim().toLowerCase() === channelName
|
String(item?.channel?.channelName || '').trim().toLowerCase() === channelName
|
||||||
));
|
));
|
||||||
|
|
||||||
if (!match) {
|
if (!matches.length) {
|
||||||
throw new Error('Канал не найден у указанного автора.');
|
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 {
|
return {
|
||||||
ownerBlockchainName: String(match?.channel?.ownerBlockchainName || user.blockchainName),
|
ownerBlockchainName: String(match?.channel?.ownerBlockchainName || user.blockchainName),
|
||||||
rootBlockNumber: Number(match?.channel?.channelRoot?.blockNumber),
|
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 }) {
|
function openSimpleSubscribeModal({ kind, kindLabel, submitLabel, unfollow = false, onSuccess }) {
|
||||||
const targetHint = kind === 'channel'
|
const targetHint = kind === 'channel'
|
||||||
? '<p class="meta-muted">Канал: user/channel, имя канала или bch:number:hash.</p>'
|
? '<p class="meta-muted">Канал: user/channel, имя канала или bch:number:hash.</p>'
|
||||||
@ -306,35 +341,62 @@ function openSimpleSubscribeModal({ kind, kindLabel, submitLabel, unfollow = fal
|
|||||||
errorEl.textContent = '';
|
errorEl.textContent = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
let channelTarget = null;
|
||||||
|
let userTargetLogin = '';
|
||||||
|
|
||||||
if (kind === 'user') {
|
if (kind === 'user') {
|
||||||
|
userTargetLogin = normalizeLoginInput(value);
|
||||||
await authService.addBlockFollowUser({
|
await authService.addBlockFollowUser({
|
||||||
login,
|
login,
|
||||||
targetLogin: normalizeLoginInput(value),
|
targetLogin: userTargetLogin,
|
||||||
storagePwd,
|
storagePwd,
|
||||||
unfollow,
|
unfollow,
|
||||||
});
|
});
|
||||||
} else if (kind === 'channel') {
|
} else if (kind === 'channel') {
|
||||||
const target = await resolveChannelTargetFromInput(value);
|
channelTarget = await resolveChannelTargetFromInput(value);
|
||||||
if (!target?.ownerBlockchainName || !Number.isFinite(target.rootBlockNumber)) {
|
if (!channelTarget?.ownerBlockchainName || !Number.isFinite(channelTarget.rootBlockNumber)) {
|
||||||
throw new Error('Канал не найден.');
|
throw new Error('Канал не найден.');
|
||||||
}
|
}
|
||||||
|
|
||||||
await authService.addBlockFollowChannel({
|
await authService.addBlockFollowChannel({
|
||||||
login,
|
login,
|
||||||
storagePwd,
|
storagePwd,
|
||||||
targetBlockchainName: target.ownerBlockchainName,
|
targetBlockchainName: channelTarget.ownerBlockchainName,
|
||||||
targetBlockNumber: target.rootBlockNumber,
|
targetBlockNumber: channelTarget.rootBlockNumber,
|
||||||
targetBlockHashHex: target.rootBlockHash,
|
targetBlockHashHex: channelTarget.rootBlockHash,
|
||||||
unfollow,
|
unfollow,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Неподдерживаемый тип подписки');
|
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);
|
softHaptic(15);
|
||||||
showToast(unfollow ? 'Отписка выполнена' : 'Подписка выполнена');
|
showToast(unfollow ? 'Отписка выполнена' : 'Подписка выполнена');
|
||||||
close();
|
close();
|
||||||
if (typeof onSuccess === 'function') onSuccess();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorEl.textContent = toUserMessage(error, `${submitText} не удалось.`);
|
errorEl.textContent = toUserMessage(error, `${submitText} не удалось.`);
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
|
|||||||
@ -212,12 +212,30 @@ public final class DatabaseTriggersInstaller {
|
|||||||
SELECT
|
SELECT
|
||||||
NEW.login,
|
NEW.login,
|
||||||
NEW.msg_sub_type,
|
NEW.msg_sub_type,
|
||||||
|
COALESCE(
|
||||||
NEW.to_login,
|
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_bch_name,
|
||||||
NEW.to_block_number,
|
NEW.to_block_number,
|
||||||
NEW.to_block_hash
|
NEW.to_block_hash
|
||||||
WHERE NEW.msg_sub_type IN (%d, %d, %d)
|
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;
|
AND NEW.to_bch_name IS NOT NULL;
|
||||||
|
|
||||||
-- 2) если запись есть — обновляем актуальные to_*
|
-- 2) если запись есть — обновляем актуальные to_*
|
||||||
@ -228,27 +246,64 @@ public final class DatabaseTriggersInstaller {
|
|||||||
to_block_hash = NEW.to_block_hash
|
to_block_hash = NEW.to_block_hash
|
||||||
WHERE login = NEW.login
|
WHERE login = NEW.login
|
||||||
AND rel_type = NEW.msg_sub_type
|
AND rel_type = NEW.msg_sub_type
|
||||||
AND to_login = NEW.to_login
|
AND to_login = COALESCE(
|
||||||
AND NEW.msg_sub_type IN (%d, %d, %d)
|
NEW.to_login,
|
||||||
AND NEW.to_login IS NOT NULL
|
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;
|
AND NEW.to_bch_name IS NOT NULL;
|
||||||
|
|
||||||
-- UNFRIEND/UNCONTACT/UNFOLLOW:
|
-- UNFRIEND/UNCONTACT/UNFOLLOW:
|
||||||
-- удаляем соответствующее "позитивное" состояние
|
-- удаляем соответствующее "позитивное" состояние
|
||||||
DELETE FROM connections_state
|
DELETE FROM connections_state
|
||||||
WHERE login = NEW.login
|
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
|
AND rel_type = CASE NEW.msg_sub_type
|
||||||
WHEN %d THEN %d
|
WHEN %d THEN %d
|
||||||
WHEN %d THEN %d
|
WHEN %d THEN %d
|
||||||
WHEN %d THEN %d
|
WHEN %d THEN %d
|
||||||
ELSE rel_type
|
ELSE rel_type
|
||||||
END
|
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);
|
AND NEW.msg_sub_type IN (%d, %d, %d);
|
||||||
END;
|
END;
|
||||||
""".formatted(
|
""".formatted(
|
||||||
FRIEND, CONTACT, FOLLOW,
|
FRIEND, CONTACT, FOLLOW,
|
||||||
FRIEND, CONTACT, FOLLOW,
|
FRIEND, CONTACT,
|
||||||
|
|
||||||
UNFRIEND, FRIEND,
|
UNFRIEND, FRIEND,
|
||||||
UNCONTACT, CONTACT,
|
UNCONTACT, CONTACT,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user