@@ -56,6 +64,7 @@ export default function MailboxSetting() {
{t('write relays description')}
{t('read & write relays notice')}
+
{relays.map((relay) => (
diff --git a/src/i18n/en.ts b/src/i18n/en.ts
index 3480c63e..68ea7968 100644
--- a/src/i18n/en.ts
+++ b/src/i18n/en.ts
@@ -12,6 +12,7 @@ export default {
Profile: 'Profile',
Logout: 'Logout',
Following: 'Following',
+ followings: 'followings',
reposted: 'reposted',
'just now': 'just now',
'n minutes ago': '{{n}} minutes ago',
@@ -153,6 +154,11 @@ export default {
Unmute: 'Unmute',
'mute author': 'mute author',
'mute user': 'mute user',
- 'unmute user': 'unmute user'
+ 'unmute user': 'unmute user',
+ 'Append n relays': 'Append {{n}} relays',
+ Append: 'Append',
+ 'Select relays to append': 'Select relays to append',
+ 'calculating...': 'calculating...',
+ 'Calculate optimal read relays': 'Calculate optimal read relays'
}
}
diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts
index 387f51bb..bb732c51 100644
--- a/src/i18n/zh.ts
+++ b/src/i18n/zh.ts
@@ -12,6 +12,7 @@ export default {
Profile: '个人资料',
Logout: '退出登录',
Following: '关注',
+ followings: '关注',
reposted: '转发',
'just now': '刚刚',
'n minutes ago': '{{n}} 分钟前',
@@ -154,6 +155,11 @@ export default {
Unmute: '取消屏蔽',
'mute author': '屏蔽作者',
'mute user': '屏蔽用户',
- 'unmute user': '取消屏蔽用户'
+ 'unmute user': '取消屏蔽用户',
+ 'Append n relays': '追加 {{n}} 个服务器',
+ Append: '追加',
+ 'Select relays to append': '选择要追加的服务器',
+ 'calculating...': '计算中...',
+ 'Calculate optimal read relays': '计算最佳读服务器'
}
}
diff --git a/src/services/client.service.ts b/src/services/client.service.ts
index 9d4befdb..dbc36a88 100644
--- a/src/services/client.service.ts
+++ b/src/services/client.service.ts
@@ -54,7 +54,8 @@ class ClientService extends EventTarget {
private relayListEventDataLoader = new DataLoader(
this.relayListEventBatchLoadFn.bind(this),
{
- cacheMap: new LRUCache>({ max: 10000 })
+ cacheMap: new LRUCache>({ max: 10000 }),
+ maxBatchSize: 10
}
)
private relayInfoDataLoader = new DataLoader(async (urls) => {
@@ -446,6 +447,58 @@ class ClientService extends EventTarget {
return infos.map((info) => (info ? (info instanceof Error ? undefined : info) : undefined))
}
+ async calculateOptimalReadRelays(pubkey: string) {
+ const followings = await this.fetchFollowings(pubkey)
+ const [selfRelayListEvent, ...relayListEvents] = await this.relayListEventDataLoader.loadMany([
+ pubkey,
+ ...followings
+ ])
+ const selfReadRelays =
+ selfRelayListEvent && !(selfRelayListEvent instanceof Error)
+ ? getRelayListFromRelayListEvent(selfRelayListEvent).read
+ : []
+ const pubkeyRelayListMap = new Map()
+ relayListEvents.forEach((evt) => {
+ if (evt && !(evt instanceof Error)) {
+ pubkeyRelayListMap.set(evt.pubkey, getRelayListFromRelayListEvent(evt).write)
+ }
+ })
+
+ let uncoveredPubkeys = [...followings]
+ const readRelays: { url: string; pubkeys: string[] }[] = []
+ while (uncoveredPubkeys.length) {
+ const relayMap = new Map()
+ uncoveredPubkeys.forEach((pubkey) => {
+ const relays = pubkeyRelayListMap.get(pubkey)
+ if (relays) {
+ relays.forEach((url) => {
+ relayMap.set(url, (relayMap.get(url) || []).concat(pubkey))
+ })
+ }
+ })
+ let maxCoveredRelay: { url: string; pubkeys: string[] } | undefined
+ for (const [url, pubkeys] of relayMap.entries()) {
+ if (!maxCoveredRelay) {
+ maxCoveredRelay = { url, pubkeys }
+ } else if (pubkeys.length > maxCoveredRelay.pubkeys.length) {
+ maxCoveredRelay = { url, pubkeys }
+ } else if (
+ pubkeys.length === maxCoveredRelay.pubkeys.length &&
+ selfReadRelays.includes(url)
+ ) {
+ maxCoveredRelay = { url, pubkeys }
+ }
+ }
+ if (!maxCoveredRelay) break
+
+ readRelays.push(maxCoveredRelay)
+ uncoveredPubkeys = uncoveredPubkeys.filter(
+ (pubkey) => !maxCoveredRelay!.pubkeys.includes(pubkey)
+ )
+ }
+ return readRelays
+ }
+
private async fetchEventById(relayUrls: string[], id: string): Promise {
const event = await this.fetchEventFromDefaultRelaysDataloader.load(id)
if (event) {