fix: fetch latest contact list

This commit is contained in:
Shusui MOYATANI
2023-07-09 02:34:07 +09:00
parent 27095c0e46
commit dca108084a
4 changed files with 97 additions and 40 deletions

View File

@@ -1,4 +1,13 @@
import { Component, createSignal, createMemo, Show, Switch, Match, createEffect } from 'solid-js';
import {
Component,
createSignal,
createMemo,
Show,
Switch,
Match,
createEffect,
onMount,
} from 'solid-js';
import { createMutation } from '@tanstack/solid-query';
import ArrowPath from 'heroicons/24/outline/arrow-path.svg';
@@ -18,7 +27,7 @@ import useModalState from '@/hooks/useModalState';
import { useTranslation } from '@/i18n/useTranslation';
import useCommands from '@/nostr/useCommands';
import useFollowers from '@/nostr/useFollowers';
import useFollowings from '@/nostr/useFollowings';
import useFollowings, { fetchLatestFollowings } from '@/nostr/useFollowings';
import useProfile from '@/nostr/useProfile';
import usePubkey from '@/nostr/usePubkey';
import useSubscription from '@/nostr/useSubscription';
@@ -27,6 +36,7 @@ import ensureNonNull from '@/utils/ensureNonNull';
import epoch from '@/utils/epoch';
import npubEncodeFallback from '@/utils/npubEncodeFallback';
import sleep from '@/utils/sleep';
import stripMargin from '@/utils/stripMargin';
import timeout from '@/utils/timeout';
export type ProfileDisplayProps = {
@@ -130,18 +140,36 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
if (p == null) return;
setUpdatingContacts(true);
await refetchMyFollowing();
await sleep(3000);
const current = myFollowingPubkeys();
console.debug('current pubkeys', current);
const latest = await fetchLatestFollowings({ pubkey: p });
const msg = stripMargin`
フォローリストが空のようです。初めてのフォローであれば問題ありません。
そうでなければ、リレーとの接続がうまくいっていない可能性があります。ページを再読み込みしてリレーと再接続してください。
また、他のクライアントと同じリレーを設定できているどうかご確認ください。
続行しますか?
`;
if ((latest.data() == null || latest.followingPubkeys().length === 0) && !window.confirm(msg))
return;
if ((latest?.data()?.created_at ?? 0) < (myFollowingQuery.data?.created_at ?? 0)) {
window.alert(
'最新のフォローリストを取得できませんでした。リレーの接続状況が悪い可能性があります。',
);
return;
}
await updateContactsMutation.mutateAsync({
relayUrls: config().relayUrls,
pubkey: p,
content: myFollowingQuery.data?.content ?? '',
followingPubkeys: uniq(update(current)),
content: latest.data()?.content ?? '',
followingPubkeys: uniq(update(latest.followingPubkeys())),
});
} catch (err) {
console.error('failed to update contact list', err);
window.alert('フォローリストの更新に失敗しました。');
} finally {
setUpdatingContacts(false);
}

View File

@@ -24,13 +24,43 @@ export type UseFollowings = {
query: CreateQueryResult<NostrEvent | null>;
};
export const fetchLatestFollowings = (
const buildMethods = (dataProvider: () => NostrEvent | undefined | null) => {
const followings = () => {
const data = dataProvider();
if (data == null) return [];
const result: Following[] = [];
// TODO zodにする
const event = genericEvent(data);
event.pTags().forEach((tag) => {
const [, followingPubkey, mainRelayUrl, petname] = tag;
const following: Following = { pubkey: followingPubkey, petname };
if (mainRelayUrl != null && mainRelayUrl.length > 0) {
following.mainRelayUrl = mainRelayUrl;
}
result.push(following);
});
return result;
};
const followingPubkeys = (): string[] => followings().map((follow) => follow.pubkey);
return { followings, followingPubkeys, data: dataProvider };
};
export const fetchLatestFollowings = async (
{ pubkey }: UseFollowingsProps,
signal?: AbortSignal,
): Promise<NostrEvent> => {
) => {
const task = new BatchedEventsTask({ type: 'Followings', pubkey });
registerTask({ task, signal });
return task.latestEventPromise();
const latestFollowings = await task.latestEventPromise();
return buildMethods(() => latestFollowings);
};
const useFollowings = (propsProvider: () => UseFollowingsProps | null): UseFollowings => {
@@ -51,39 +81,16 @@ const useFollowings = (propsProvider: () => UseFollowingsProps | null): UseFollo
{
staleTime: 5 * 60 * 1000, // 5 min
cacheTime: 24 * 60 * 60 * 1000, // 24 hour
refetchOnMount: false,
refetchOnMount: true,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchInterval: 0,
},
);
const followings = () => {
if (query.data == null) return [];
const result: Following[] = [];
// TODO zodにする
const event = genericEvent(query.data);
event.pTags().forEach((tag) => {
const [, followingPubkey, mainRelayUrl, petname] = tag;
const following: Following = { pubkey: followingPubkey, petname };
if (mainRelayUrl != null && mainRelayUrl.length > 0) {
following.mainRelayUrl = mainRelayUrl;
}
result.push(following);
});
return result;
};
const followingPubkeys = (): string[] => followings().map((follow) => follow.pubkey);
const invalidateFollowings = (): Promise<void> => queryClient.invalidateQueries(genQueryKey());
return { followings, followingPubkeys, invalidateFollowings, query };
return { ...buildMethods(() => query.data), invalidateFollowings, query };
};
export default useFollowings;

View File

@@ -2,10 +2,8 @@ import { createSignal } from 'solid-js';
import { SimplePool } from 'nostr-tools';
const [pool] = createSignal<SimplePool>(new SimplePool());
const [pool] = createSignal<SimplePool>(new SimplePool({ eoseSubTimeout: 7500 }));
const usePool = () => {
return pool;
};
const usePool = () => pool;
export default usePool;

24
src/utils/stripMargin.ts Normal file
View File

@@ -0,0 +1,24 @@
const stripMargin = (strings: TemplateStringsArray, ...values: any[]) => {
const s = String.raw(strings, values);
const result = [];
const lines = s.split('\n');
for (let i = 0; i < lines.length; i += 1) {
const line = lines[i];
const marginRemoved = line.trimStart();
if (result.length > 0 || marginRemoved.length > 0) {
result.push(marginRemoved);
}
}
while (result.length > 0) {
const lastLine = result[result.length - 1];
if (lastLine.length > 0) break;
result.pop();
}
return result.join('\n');
};
export default stripMargin;