diff --git a/src/app/components/RelaySettingsActionItems.svelte b/src/app/components/RelaySettingsActionItems.svelte
deleted file mode 100644
index cc2f7f00..00000000
--- a/src/app/components/RelaySettingsActionItems.svelte
+++ /dev/null
@@ -1,140 +0,0 @@
-
-
-
-
-
-
- Health Check
-
-
-
- {$actionItems.length} Issue{$actionItems.length === 1 ? "" : "s"} Detected
-
-
-
- {PLATFORM_NAME} actively checks your connection to the network in the background to discover relays
- that are offline, that you don't have access to, or are otherwise causing trouble.
-
- {#each $actionItems as actionItem}
-
- {/each}
- {#if $actionItems.length > 0}
-
-
- Apply All Recommendations
-
- {/if}
-
diff --git a/src/app/components/RelaySettingsActionItem.svelte b/src/app/components/RelaySettingsHealthCheck.svelte
similarity index 52%
rename from src/app/components/RelaySettingsActionItem.svelte
rename to src/app/components/RelaySettingsHealthCheck.svelte
index 260ab870..f562b226 100644
--- a/src/app/components/RelaySettingsActionItem.svelte
+++ b/src/app/components/RelaySettingsHealthCheck.svelte
@@ -1,27 +1,26 @@
-
-
-
{title}
-
{subtitle}
+
{healthCheck.title}
+
{healthCheck.description}
- {action}
+ {healthCheck.action}
diff --git a/src/app/components/RelaySettingsHealthChecks.svelte b/src/app/components/RelaySettingsHealthChecks.svelte
new file mode 100644
index 00000000..aca78b0e
--- /dev/null
+++ b/src/app/components/RelaySettingsHealthChecks.svelte
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+ Health Check
+
+
+
+ {$pendingHealthChecks.length} Issue{$pendingHealthChecks.length === 1 ? "" : "s"} Detected
+
+
+
+ {PLATFORM_NAME} actively checks your connection to the network in the background to discover relays
+ that are offline, that you don't have access to, or are otherwise causing trouble.
+
+ {#each $pendingHealthChecks as healthCheck}
+
+ {/each}
+ {#if $pendingHealthChecks.length > 0}
+
+
+ Apply All Recommendations
+
+ {/if}
+
diff --git a/src/app/util/health.ts b/src/app/util/health.ts
new file mode 100644
index 00000000..f9f6f61a
--- /dev/null
+++ b/src/app/util/health.ts
@@ -0,0 +1,107 @@
+import {derived, get} from "svelte/store"
+import {not, ifLet, sample} from "@welshman/lib"
+import {getRelaysFromList, RelayMode} from "@welshman/util"
+import {
+ getRelay,
+ setWriteRelays,
+ setReadRelays,
+ setSearchRelays,
+ setMessagingRelays,
+ userRelayList,
+ userSearchRelayList,
+ userMessagingRelayList,
+} from "@welshman/app"
+import {hasNip50, DEFAULT_RELAYS, DEFAULT_MESSAGING_RELAYS} from "@app/core/state"
+
+export type HealthCheckContext = {
+ readRelays: string[]
+ writeRelays: string[]
+ messagingRelays: string[]
+ searchRelays: string[]
+}
+
+export type HealthCheck = {
+ title: string
+ description: string
+ action: string
+ isPending: (context: HealthCheckContext) => boolean
+ apply: (context: HealthCheckContext) => unknown
+}
+
+export const healthCheckContext = derived(
+ [userRelayList, userSearchRelayList, userMessagingRelayList],
+ ([$userRelayList, $userSearchRelayList, $userMessagingRelayList]) => {
+ return {
+ readRelays: getRelaysFromList($userRelayList, RelayMode.Read),
+ writeRelays: getRelaysFromList($userRelayList, RelayMode.Write),
+ searchRelays: getRelaysFromList($userSearchRelayList),
+ messagingRelays: getRelaysFromList($userMessagingRelayList),
+ }
+ },
+)
+
+const healthChecks: HealthCheck[] = [
+ {
+ title: "Missing Inbox Relays",
+ description: "Other people aren't currently able to reliably tag you in public notes.",
+ action: "Update",
+ isPending: context => context.readRelays.length <= 1,
+ apply: () => setReadRelays(DEFAULT_RELAYS),
+ },
+ {
+ title: "Missing Outbox Relays",
+ description: "Other people aren't currently able to reliably find your public notes.",
+ action: "Update",
+ isPending: context => context.writeRelays.length <= 1,
+ apply: () => setWriteRelays(DEFAULT_RELAYS),
+ },
+ {
+ title: "Missing DM Relays",
+ description: "You aren't currently able to reliably send or receive direct messages.",
+ action: "Update",
+ isPending: context => context.messagingRelays.length <= 1,
+ apply: () => setMessagingRelays(DEFAULT_MESSAGING_RELAYS),
+ },
+ {
+ title: "Too Many Inbox Relays",
+ description:
+ "You have more inbox relays than is really necessary, which can affect resource usage.",
+ action: "Prune Selections",
+ isPending: context => context.readRelays.length > 8,
+ apply: context => setReadRelays(sample(5, context.readRelays)),
+ },
+ {
+ title: "Too Many Outbox Relays",
+ description:
+ "You have more outbox relays than is really necessary, which can affect resource usage.",
+ action: "Prune Selections",
+ isPending: context => context.writeRelays.length > 8,
+ apply: context => setWriteRelays(sample(5, context.writeRelays)),
+ },
+ {
+ title: "Too Many DM Relays",
+ description:
+ "You have more DM relays than is really necessary, which can affect resource usage.",
+ action: "Prune Selections",
+ isPending: context => context.messagingRelays.length > 8,
+ apply: context => setMessagingRelays(sample(5, context.messagingRelays)),
+ },
+ {
+ title: "Invalid Search Relays",
+ description: "Some of your search relays don't support search.",
+ action: "Remove Invalid",
+ isPending: context => context.searchRelays.some(url => not(ifLet(getRelay(url), hasNip50))),
+ apply: context =>
+ setSearchRelays(context.searchRelays.filter(url => ifLet(getRelay(url), hasNip50))),
+ },
+]
+
+export const isHealthCheckPending = (healthCheck: HealthCheck) =>
+ healthCheck.isPending(get(healthCheckContext))
+
+export const applyHealthCheck = (healthCheck: HealthCheck) =>
+ healthCheck.apply(get(healthCheckContext))
+
+export const pendingHealthChecks = derived(healthCheckContext, ctx =>
+ healthChecks.filter(hc => hc.isPending(ctx)),
+)
diff --git a/src/routes/settings/relays/+page.svelte b/src/routes/settings/relays/+page.svelte
index a9299931..210aaaab 100644
--- a/src/routes/settings/relays/+page.svelte
+++ b/src/routes/settings/relays/+page.svelte
@@ -23,7 +23,7 @@
import ForbiddenCircle from "@assets/icons/forbidden-circle.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
import RelaySettingsItem from "@app/components/RelaySettingsItem.svelte"
- import RelaySettingsActionItems from "@app/components/RelaySettingsActionItems.svelte"
+ import RelaySettingsHealthChecks from "@app/components/RelaySettingsHealthChecks.svelte"
import {hasNip50} from "@app/core/state"
import {discoverRelays} from "@app/core/requests"
@@ -44,7 +44,7 @@