mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2025-12-21 16:24:22 +01:00
sync activity separately from regular sync
This commit is contained in:
committed by
Tony Giorgio
parent
9186da7fc6
commit
2d99da5245
@@ -1,5 +1,5 @@
|
|||||||
import { LoadingSpinner, NiceP } from "./layout"
|
import { NiceP } from "./layout";
|
||||||
import { For, Match, Show, Switch, createSignal } from "solid-js";
|
import { For, Match, Show, Switch, createEffect, createSignal } from "solid-js";
|
||||||
import { useMegaStore } from "~/state/megaStore";
|
import { useMegaStore } from "~/state/megaStore";
|
||||||
import { ActivityItem as MutinyActivity } from "@mutinywallet/mutiny-wasm";
|
import { ActivityItem as MutinyActivity } from "@mutinywallet/mutiny-wasm";
|
||||||
import { ActivityItem, HackActivityType } from "./ActivityItem";
|
import { ActivityItem, HackActivityType } from "./ActivityItem";
|
||||||
@@ -61,7 +61,7 @@ function UnifiedActivityItem(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function CombinedActivity(props: { limit?: number }) {
|
export function CombinedActivity(props: { limit?: number }) {
|
||||||
const [state, _actions] = useMegaStore();
|
const [state, actions] = useMegaStore();
|
||||||
|
|
||||||
const [detailsOpen, setDetailsOpen] = createSignal(false);
|
const [detailsOpen, setDetailsOpen] = createSignal(false);
|
||||||
const [detailsKind, setDetailsKind] = createSignal<HackActivityType>();
|
const [detailsKind, setDetailsKind] = createSignal<HackActivityType>();
|
||||||
@@ -75,6 +75,12 @@ export function CombinedActivity(props: { limit?: number }) {
|
|||||||
setDetailsOpen(true);
|
setDetailsOpen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (!state.wallet_loading && !state.is_syncing) {
|
||||||
|
actions.syncActivity();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Show when={detailsId() && detailsKind()}>
|
<Show when={detailsId() && detailsKind()}>
|
||||||
@@ -86,13 +92,8 @@ export function CombinedActivity(props: { limit?: number }) {
|
|||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={state.wallet_loading || !state.activity_has_loaded}>
|
|
||||||
<div class="p-4">
|
|
||||||
<LoadingSpinner wide />
|
|
||||||
</div>
|
|
||||||
</Match>
|
|
||||||
<Match when={state.activity.length === 0}>
|
<Match when={state.activity.length === 0}>
|
||||||
<div class="w-full text-center">
|
<div class="w-full text-center pb-4">
|
||||||
<NiceP>Receive some sats to get started</NiceP>
|
<NiceP>Receive some sats to get started</NiceP>
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
|
|||||||
@@ -36,12 +36,14 @@ export default function App() {
|
|||||||
</Show>
|
</Show>
|
||||||
{/* <ButtonLink href="/activity">View All</ButtonLink> */}
|
{/* <ButtonLink href="/activity">View All</ButtonLink> */}
|
||||||
</VStack>
|
</VStack>
|
||||||
|
<Show when={state.activity && state.activity.length > 0}>
|
||||||
<A
|
<A
|
||||||
href="/activity"
|
href="/activity"
|
||||||
class="text-m-red active:text-m-red/80 text-xl font-semibold no-underline self-center"
|
class="text-m-red active:text-m-red/80 text-xl font-semibold no-underline self-center"
|
||||||
>
|
>
|
||||||
View All
|
View All
|
||||||
</A>
|
</A>
|
||||||
|
</Show>
|
||||||
</Card>
|
</Card>
|
||||||
<p class="self-center text-neutral-500 mt-4">
|
<p class="self-center text-neutral-500 mt-4">
|
||||||
Bugs? Feedback?{" "}
|
Bugs? Feedback?{" "}
|
||||||
|
|||||||
@@ -51,17 +51,17 @@ export const Button: ParentComponent<ButtonProps> = props => {
|
|||||||
class={button({
|
class={button({
|
||||||
class: local.class || "",
|
class: local.class || "",
|
||||||
intent: local.intent,
|
intent: local.intent,
|
||||||
layout: local.layout,
|
layout: local.layout
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Show when={props.loading} fallback={slot()} >
|
<Show when={props.loading} fallback={slot()}>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
{/* TODO: constrain this to the exact height of the button */}
|
{/* TODO: constrain this to the exact height of the button */}
|
||||||
<LoadingSpinner />
|
<LoadingSpinner wide />
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
</button >
|
</button>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ButtonLinkProps extends JSX.ButtonHTMLAttributes<HTMLAnchorElement>, StyleProps {
|
interface ButtonLinkProps extends JSX.ButtonHTMLAttributes<HTMLAnchorElement>, StyleProps {
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export const DefaultMain: ParentComponent = (props) => {
|
|||||||
export const FullscreenLoader = () => {
|
export const FullscreenLoader = () => {
|
||||||
return (
|
return (
|
||||||
<div class="w-full h-[100dvh] flex justify-center items-center">
|
<div class="w-full h-[100dvh] flex justify-center items-center">
|
||||||
<LoadingSpinner />
|
<LoadingSpinner wide />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -88,11 +88,9 @@ export const MutinyWalletGuard: ParentComponent = (props) => {
|
|||||||
const [state, _] = useMegaStore();
|
const [state, _] = useMegaStore();
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<FullscreenLoader />}>
|
<Suspense fallback={<FullscreenLoader />}>
|
||||||
<Show when={state.mutiny_wallet}>
|
<Show when={state.mutiny_wallet && !state.wallet_loading}>{props.children}</Show>
|
||||||
{props.children}
|
|
||||||
</Show>
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LoadingSpinner = (props: { big?: boolean, wide?: boolean }) => {
|
export const LoadingSpinner = (props: { big?: boolean, wide?: boolean }) => {
|
||||||
|
|||||||
@@ -35,18 +35,23 @@ export function WaitlistAlreadyIn() {
|
|||||||
const [posts] = createResource("", postsFetcher);
|
const [posts] = createResource("", postsFetcher);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main class='flex flex-col gap-4 sm:gap-4 py-8 px-4 max-w-xl mx-auto items-start drop-shadow-blue-glow'>
|
<main class="flex flex-col gap-4 sm:gap-4 py-8 px-4 max-w-xl mx-auto items-start drop-shadow-blue-glow">
|
||||||
<a href="https://mutinywallet.com">
|
<a href="https://mutinywallet.com">
|
||||||
<img src={logo} class="h-10" alt="logo" />
|
<img src={logo} class="h-10" alt="logo" />
|
||||||
</a>
|
</a>
|
||||||
<h1 class="text-4xl font-bold">You're on a list!</h1>
|
<h1 class="text-4xl font-bold">You're on a list!</h1>
|
||||||
<h2 class="text-xl pr-4">
|
<h2 class="text-xl pr-4">We'll message you when Mutiny Wallet is ready.</h2>
|
||||||
We'll message you when Mutiny Wallet is ready.
|
|
||||||
</h2>
|
|
||||||
<div class="px-4 sm:px-8 py-8 rounded-xl bg-half-black w-full">
|
<div class="px-4 sm:px-8 py-8 rounded-xl bg-half-black w-full">
|
||||||
<h2 class="text-sm font-semibold uppercase">Recent Updates</h2>
|
<h2 class="text-sm font-semibold uppercase">Recent Updates</h2>
|
||||||
<Show when={!posts.loading} fallback={<div class="h-[10rem]"><LoadingSpinner big /></div>}>
|
<Show
|
||||||
<Notes notes={posts() && posts() || []} />
|
when={!posts.loading}
|
||||||
|
fallback={
|
||||||
|
<div class="h-[10rem]">
|
||||||
|
<LoadingSpinner big wide />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Notes notes={(posts() && posts()) || []} />
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
import { For, Show, createResource } from "solid-js";
|
import { For, Show, createResource } from "solid-js";
|
||||||
import NavBar from "~/components/NavBar";
|
import NavBar from "~/components/NavBar";
|
||||||
import { Button, Card, DefaultMain, LargeHeader, NiceP, MutinyWalletGuard, SafeArea, VStack } from "~/components/layout";
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
DefaultMain,
|
||||||
|
LargeHeader,
|
||||||
|
NiceP,
|
||||||
|
MutinyWalletGuard,
|
||||||
|
SafeArea,
|
||||||
|
VStack
|
||||||
|
} from "~/components/layout";
|
||||||
import { BackLink } from "~/components/layout/BackLink";
|
import { BackLink } from "~/components/layout/BackLink";
|
||||||
import { CombinedActivity } from "~/components/Activity";
|
import { CombinedActivity } from "~/components/Activity";
|
||||||
import { A } from "solid-start";
|
import { A } from "solid-start";
|
||||||
import settings from '~/assets/icons/settings.svg';
|
import settings from "~/assets/icons/settings.svg";
|
||||||
import { Tabs } from "@kobalte/core";
|
import { Tabs } from "@kobalte/core";
|
||||||
import { gradientsPerContact } from "~/utils/gradientHash";
|
import { gradientsPerContact } from "~/utils/gradientHash";
|
||||||
import { ContactEditor } from "~/components/ContactEditor";
|
import { ContactEditor } from "~/components/ContactEditor";
|
||||||
@@ -12,34 +21,35 @@ import { ContactFormValues, ContactViewer } from "~/components/ContactViewer";
|
|||||||
import { useMegaStore } from "~/state/megaStore";
|
import { useMegaStore } from "~/state/megaStore";
|
||||||
import { Contact } from "@mutinywallet/mutiny-wasm";
|
import { Contact } from "@mutinywallet/mutiny-wasm";
|
||||||
import { showToast } from "~/components/Toaster";
|
import { showToast } from "~/components/Toaster";
|
||||||
|
import { LoadingShimmer } from "~/components/BalanceBox";
|
||||||
|
|
||||||
function ContactRow() {
|
function ContactRow() {
|
||||||
const [state, _actions] = useMegaStore();
|
const [state, _actions] = useMegaStore();
|
||||||
const [contacts, { refetch }] = createResource(async () => {
|
const [contacts, { refetch }] = createResource(async () => {
|
||||||
const contacts = state.mutiny_wallet?.get_contacts();
|
const contacts = state.mutiny_wallet?.get_contacts();
|
||||||
console.log(contacts)
|
console.log(contacts);
|
||||||
|
|
||||||
// FIXME: this is just types shenanigans I believe
|
// FIXME: this is just types shenanigans I believe
|
||||||
const c: Contact[] = []
|
const c: Contact[] = [];
|
||||||
if (contacts) {
|
if (contacts) {
|
||||||
for (const contact in contacts) {
|
for (const contact in contacts) {
|
||||||
c.push(contacts[contact])
|
c.push(contacts[contact]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c || []
|
return c || [];
|
||||||
})
|
});
|
||||||
const [gradients] = createResource(contacts, gradientsPerContact);
|
const [gradients] = createResource(contacts, gradientsPerContact);
|
||||||
|
|
||||||
async function createContact(contact: ContactFormValues) {
|
async function createContact(contact: ContactFormValues) {
|
||||||
// FIXME: npub not valid? other undefineds
|
// FIXME: npub not valid? other undefineds
|
||||||
const c = new Contact(contact.name, undefined, undefined, undefined);
|
const c = new Contact(contact.name, undefined, undefined, undefined);
|
||||||
await state.mutiny_wallet?.create_new_contact(c)
|
await state.mutiny_wallet?.create_new_contact(c);
|
||||||
refetch();
|
refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
async function saveContact(_contact: ContactFormValues) {
|
async function saveContact(_contact: ContactFormValues) {
|
||||||
showToast(new Error("Unimplemented"))
|
showToast(new Error("Unimplemented"));
|
||||||
// await editContact(contact)
|
// await editContact(contact)
|
||||||
refetch();
|
refetch();
|
||||||
}
|
}
|
||||||
@@ -51,29 +61,50 @@ function ContactRow() {
|
|||||||
<div class="flex gap-4 flex-1 overflow-x-scroll disable-scrollbars">
|
<div class="flex gap-4 flex-1 overflow-x-scroll disable-scrollbars">
|
||||||
<For each={contacts()}>
|
<For each={contacts()}>
|
||||||
{(contact) => (
|
{(contact) => (
|
||||||
<ContactViewer contact={contact} gradient={gradients()?.get(contact.name)} saveContact={saveContact} />
|
<ContactViewer
|
||||||
|
contact={contact}
|
||||||
|
gradient={gradients()?.get(contact.name)}
|
||||||
|
saveContact={saveContact}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const TAB = "flex-1 inline-block px-8 py-4 text-lg font-semibold rounded-lg ui-selected:bg-white/10 bg-neutral-950 hover:bg-white/10"
|
const TAB =
|
||||||
|
"flex-1 inline-block px-8 py-4 text-lg font-semibold rounded-lg ui-selected:bg-white/10 bg-neutral-950 hover:bg-white/10";
|
||||||
|
|
||||||
export default function Activity() {
|
export default function Activity() {
|
||||||
|
const [state, _actions] = useMegaStore();
|
||||||
return (
|
return (
|
||||||
<MutinyWalletGuard>
|
<MutinyWalletGuard>
|
||||||
<SafeArea>
|
<SafeArea>
|
||||||
<DefaultMain>
|
<DefaultMain>
|
||||||
<BackLink />
|
<BackLink />
|
||||||
<LargeHeader action={<A class="md:hidden p-2 hover:bg-white/5 rounded-lg active:bg-m-blue" href="/settings"><img src={settings} alt="Settings" /></A>}>Activity</LargeHeader>
|
<LargeHeader
|
||||||
|
action={
|
||||||
|
<A
|
||||||
|
class="md:hidden p-2 hover:bg-white/5 rounded-lg active:bg-m-blue"
|
||||||
|
href="/settings"
|
||||||
|
>
|
||||||
|
<img src={settings} alt="Settings" />
|
||||||
|
</A>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Activity
|
||||||
|
</LargeHeader>
|
||||||
<ContactRow />
|
<ContactRow />
|
||||||
<Tabs.Root defaultValue="mutiny">
|
<Tabs.Root defaultValue="mutiny">
|
||||||
<Tabs.List class="relative flex justify-around mt-4 mb-8 gap-1 bg-neutral-950 p-1 rounded-xl">
|
<Tabs.List class="relative flex justify-around mt-4 mb-8 gap-1 bg-neutral-950 p-1 rounded-xl">
|
||||||
<Tabs.Trigger value="mutiny" class={TAB}>Mutiny</Tabs.Trigger>
|
<Tabs.Trigger value="mutiny" class={TAB}>
|
||||||
<Tabs.Trigger value="nostr" class={TAB}>Nostr</Tabs.Trigger>
|
Mutiny
|
||||||
|
</Tabs.Trigger>
|
||||||
|
<Tabs.Trigger value="nostr" class={TAB}>
|
||||||
|
Nostr
|
||||||
|
</Tabs.Trigger>
|
||||||
{/* <Tabs.Indicator class="absolute bg-m-blue transition-all bottom-[-1px] h-[2px]" /> */}
|
{/* <Tabs.Indicator class="absolute bg-m-blue transition-all bottom-[-1px] h-[2px]" /> */}
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
<Tabs.Content value="mutiny">
|
<Tabs.Content value="mutiny">
|
||||||
@@ -81,7 +112,9 @@ export default function Activity() {
|
|||||||
<Card title="Activity">
|
<Card title="Activity">
|
||||||
<div class="p-1" />
|
<div class="p-1" />
|
||||||
<VStack>
|
<VStack>
|
||||||
|
<Show when={!state.wallet_loading} fallback={<LoadingShimmer />}>
|
||||||
<CombinedActivity />
|
<CombinedActivity />
|
||||||
|
</Show>
|
||||||
</VStack>
|
</VStack>
|
||||||
</Card>
|
</Card>
|
||||||
</Tabs.Content>
|
</Tabs.Content>
|
||||||
@@ -89,7 +122,9 @@ export default function Activity() {
|
|||||||
<VStack>
|
<VStack>
|
||||||
<div class="my-8 flex flex-col items-center gap-4 text-center max-w-[20rem] mx-auto">
|
<div class="my-8 flex flex-col items-center gap-4 text-center max-w-[20rem] mx-auto">
|
||||||
<NiceP>Import your contacts from nostr to see who they're zapping.</NiceP>
|
<NiceP>Import your contacts from nostr to see who they're zapping.</NiceP>
|
||||||
<Button disabled intent="blue">Coming soon</Button>
|
<Button disabled intent="blue">
|
||||||
|
Coming soon
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</VStack>
|
</VStack>
|
||||||
</Tabs.Content>
|
</Tabs.Content>
|
||||||
@@ -98,5 +133,5 @@ export default function Activity() {
|
|||||||
<NavBar activeTab="activity" />
|
<NavBar activeTab="activity" />
|
||||||
</SafeArea>
|
</SafeArea>
|
||||||
</MutinyWalletGuard>
|
</MutinyWalletGuard>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,6 @@ export type MegaStore = [
|
|||||||
wallet_loading: boolean;
|
wallet_loading: boolean;
|
||||||
nwc_enabled: boolean;
|
nwc_enabled: boolean;
|
||||||
activity: MutinyActivity[];
|
activity: MutinyActivity[];
|
||||||
activity_has_loaded: boolean;
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fetchUserStatus(): Promise<UserStatus>;
|
fetchUserStatus(): Promise<UserStatus>;
|
||||||
@@ -46,6 +45,7 @@ export type MegaStore = [
|
|||||||
setHasBackedUp(): void;
|
setHasBackedUp(): void;
|
||||||
listTags(): Promise<MutinyTagItem[]>;
|
listTags(): Promise<MutinyTagItem[]>;
|
||||||
setNwc(enabled: boolean): void;
|
setNwc(enabled: boolean): void;
|
||||||
|
syncActivity(): Promise<void>;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -67,8 +67,7 @@ export const Provider: ParentComponent = (props) => {
|
|||||||
dismissed_restore_prompt: localStorage.getItem("dismissed_restore_prompt") === "true",
|
dismissed_restore_prompt: localStorage.getItem("dismissed_restore_prompt") === "true",
|
||||||
wallet_loading: true,
|
wallet_loading: true,
|
||||||
nwc_enabled: localStorage.getItem("nwc_enabled") === "true",
|
nwc_enabled: localStorage.getItem("nwc_enabled") === "true",
|
||||||
activity: [] as MutinyActivity[],
|
activity: [] as MutinyActivity[]
|
||||||
activity_has_loaded: false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
@@ -135,14 +134,11 @@ export const Provider: ParentComponent = (props) => {
|
|||||||
setState({ is_syncing: true });
|
setState({ is_syncing: true });
|
||||||
const newBalance = await state.mutiny_wallet?.get_balance();
|
const newBalance = await state.mutiny_wallet?.get_balance();
|
||||||
const price = await state.mutiny_wallet?.get_bitcoin_price();
|
const price = await state.mutiny_wallet?.get_bitcoin_price();
|
||||||
const activity = await state.mutiny_wallet?.get_activity();
|
|
||||||
setState({
|
setState({
|
||||||
balance: newBalance,
|
balance: newBalance,
|
||||||
last_sync: Date.now(),
|
last_sync: Date.now(),
|
||||||
price: price || 0
|
price: price || 0
|
||||||
});
|
});
|
||||||
setState("activity", reconcile(activity, { merge: true }));
|
|
||||||
setState({ activity_has_loaded: true });
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
@@ -150,6 +146,14 @@ export const Provider: ParentComponent = (props) => {
|
|||||||
setState({ is_syncing: false });
|
setState({ is_syncing: false });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async syncActivity(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const activity = await state.mutiny_wallet?.get_activity();
|
||||||
|
setState("activity", reconcile(activity, { merge: true }));
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
setScanResult(scan_result: ParsedParams) {
|
setScanResult(scan_result: ParsedParams) {
|
||||||
setState({ scan_result });
|
setState({ scan_result });
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user