bring back smug satisfaction

hermes domain formatting fixes

confirm to add contact

bump to rc4

send me sats modal

hide button if no hermes env
This commit is contained in:
Paul Miller
2024-04-04 16:19:33 -05:00
parent c313debd34
commit 817f2263d3
9 changed files with 176 additions and 47 deletions

View File

@@ -54,7 +54,7 @@
"@capacitor/toast": "^5.0.6",
"@kobalte/core": "^0.12.6",
"@kobalte/tailwindcss": "^0.9.0",
"@mutinywallet/mutiny-wasm": "0.6.2-rc3",
"@mutinywallet/mutiny-wasm": "0.6.2-rc4",
"@modular-forms/solid": "^0.20.0",
"@solid-primitives/upload": "^0.0.117",
"@solidjs/meta": "^0.29.3",

8
pnpm-lock.yaml generated
View File

@@ -51,8 +51,8 @@ dependencies:
specifier: ^0.20.0
version: 0.20.0(solid-js@1.8.16)
'@mutinywallet/mutiny-wasm':
specifier: 0.6.2-rc3
version: 0.6.2-rc3
specifier: 0.6.2-rc4
version: 0.6.2-rc4
'@solid-primitives/upload':
specifier: ^0.0.117
version: 0.0.117(solid-js@1.8.16)
@@ -2085,8 +2085,8 @@ packages:
solid-js: 1.8.16
dev: false
/@mutinywallet/mutiny-wasm@0.6.2-rc3:
resolution: {integrity: sha512-/FxSLHuMxIuXYs2C6KbMNzBuGeSHri+BkIrtuJFI1JWG3+XcSiJF4HU2mqAwaZQH9jg25x+LvY71aZ7pM3tM/w==}
/@mutinywallet/mutiny-wasm@0.6.2-rc4:
resolution: {integrity: sha512-u9xzX6V6fAhOd1liOf6hsSzklTP/bkGBsqgN0DXfEu1p3alho3/5R0fqF64WbOQgwNBRS2b9WX0HDIZiXoz4Ow==}
dev: false
/@nodelib/fs.scandir@2.1.5:

View File

@@ -56,7 +56,8 @@
"nym": "Nym"
},
"deleted": "Your nostr profile has been deleted.",
"no_lightning_address": "No lightning address set"
"no_lightning_address": "No lightning address set",
"pay_me": "Send me sats"
},
"chat": {
"prompt": "This is a new conversation. Try asking for money!",
@@ -262,7 +263,9 @@
"sweep_delay": "Funds may take a few days to be swept back into the wallet",
"no_details": "No channel details found, which means this channel has likely been closed.",
"back_home": "back home"
}
},
"start_a_chat": "Start a chat?",
"start_a_chat_are_you_sure": "This user isn't in your contact list."
},
"scanner": {
"paste": "Paste Something",

View File

@@ -3,7 +3,14 @@ import { cache, createAsync, revalidate, useNavigate } from "@solidjs/router";
import { Plus, Save, Search, Shuffle, Users } from "lucide-solid";
import { createEffect, createSignal, For, Match, Show, Switch } from "solid-js";
import { ActivityDetailsModal, ButtonCard, NiceP } from "~/components";
import {
ActivityDetailsModal,
Button,
ButtonCard,
ContactButton,
NiceP,
SimpleDialog
} from "~/components";
import { useI18n } from "~/i18n/context";
import { PrivacyLevel } from "~/routes";
import { useMegaStore } from "~/state/megaStore";
@@ -11,6 +18,7 @@ import {
actuallyFetchNostrProfile,
hexpubFromNpub,
profileToPseudoContact,
PseudoContact,
timeAgo
} from "~/utils";
@@ -36,6 +44,7 @@ export interface IActivityItem {
export function UnifiedActivityItem(props: {
item: IActivityItem;
onClick: (id: string, kind: HackActivityType) => void;
onNewContactClick: (profile: PseudoContact) => void;
}) {
const navigate = useNavigate();
@@ -165,7 +174,9 @@ export function UnifiedActivityItem(props: {
primaryOnClick={() =>
primaryContact()?.id
? navigate(`/chat/${primaryContact()?.id}`)
: undefined
: profileFromNostr()
? props.onNewContactClick(profileFromNostr()!)
: undefined
}
amountOnClick={click}
primaryName={
@@ -194,6 +205,74 @@ export function UnifiedActivityItem(props: {
);
}
function NewContactModal(props: { profile: PseudoContact; close: () => void }) {
const i18n = useI18n();
const navigate = useNavigate();
const [state, _actions] = useMegaStore();
async function createContact() {
try {
const existingContact =
await state.mutiny_wallet?.get_contact_for_npub(
props.profile.hexpub
);
if (existingContact) {
navigate(`/chat/${existingContact.id}`);
return;
}
const contactId = await state.mutiny_wallet?.create_new_contact(
props.profile.name,
props.profile.hexpub,
props.profile.ln_address,
props.profile.lnurl,
props.profile.image_url
);
if (!contactId) {
throw new Error("no contact id returned");
}
const tagItem = await state.mutiny_wallet?.get_tag_item(contactId);
if (!tagItem) {
throw new Error("no contact returned");
}
navigate(`/chat/${contactId}`);
} catch (e) {
console.error(e);
}
}
return (
<SimpleDialog
title={i18n.t("activity.start_a_chat")}
open
setOpen={() => {
props.close();
}}
>
<NiceP>{i18n.t("activity.start_a_chat_are_you_sure")}</NiceP>
<ContactButton contact={props.profile} onClick={() => {}} />
<div class="flex-end flex w-full justify-end gap-2">
<Button
layout="small"
intent="red"
onClick={() => props.close()}
>
{i18n.t("modals.confirm_dialog.cancel")}
</Button>
<Button layout="small" intent="blue" onClick={createContact}>
{i18n.t("common.continue")}
</Button>
</div>
</SimpleDialog>
);
}
export function CombinedActivity() {
const [state, _actions] = useMegaStore();
const i18n = useI18n();
@@ -239,6 +318,8 @@ export function CombinedActivity() {
}
});
const [newContact, setNewContact] = createSignal<PseudoContact>();
return (
<>
<Show when={detailsId() && detailsKind()}>
@@ -249,6 +330,12 @@ export function CombinedActivity() {
setOpen={setDetailsOpen}
/>
</Show>
<Show when={newContact()}>
<NewContactModal
profile={newContact()!}
close={() => setNewContact(undefined)}
/>
</Show>
<Switch>
<Match when={activity().length === 0}>
<Show when={state.federations?.length === 0}>
@@ -301,6 +388,7 @@ export function CombinedActivity() {
<UnifiedActivityItem
item={activityItem}
onClick={openDetailsModal}
onNewContactClick={setNewContact}
/>
)}
</For>

View File

@@ -1,23 +1,31 @@
import { Copy, QrCode } from "lucide-solid";
import { createSignal, Match, Switch } from "solid-js";
import { createMemo, createSignal, Match, Show, Switch } from "solid-js";
import { QRCodeSVG } from "solid-qr-code";
import { FancyCard, SimpleDialog } from "~/components";
import { FancyCard, LabelCircle, SimpleDialog } from "~/components";
import { useI18n } from "~/i18n/context";
import { UserProfile } from "~/routes";
import { useCopy } from "~/utils";
export function LightningAddressShower(props: { lud16: string }) {
export function LightningAddressShower(props: {
lud16?: string;
profile?: UserProfile;
}) {
const i18n = useI18n();
const [showQr, setShowQr] = createSignal(false);
const [copy, copied] = useCopy({ copiedTimeout: 1000 });
const lud16 = createMemo(() => {
return props.lud16 || props.profile?.lud16 || "";
});
return (
<FancyCard>
<Switch>
<Match when={props.lud16}>
<Match when={lud16()}>
<p class="break-all text-center font-system-mono text-base ">
{props.lud16}
{lud16()}
</p>
<div class="flex w-full justify-center gap-8">
<button onClick={() => setShowQr(true)}>
@@ -28,7 +36,7 @@ export function LightningAddressShower(props: { lud16: string }) {
classList={{
"bg-m-red rounded": copied()
}}
onClick={() => copy(props.lud16)}
onClick={() => copy(lud16())}
>
<Copy class="inline-block" />
</button>
@@ -38,11 +46,30 @@ export function LightningAddressShower(props: { lud16: string }) {
setOpen={(open) => {
setShowQr(open);
}}
title={"Lightning Address"}
title={i18n.t("profile.pay_me")}
>
<Show when={props.profile}>
<div class="flex flex-col gap-2">
<div class="flex w-full justify-center">
<LabelCircle
name={props.profile?.name}
image_url={props.profile?.picture}
contact
label={false}
size="large"
/>
</div>
<h2 class="text-center text-lg font-semibold">
{props.profile?.name}
</h2>
</div>
<p class="break-all text-center font-system-mono text-base ">
{lud16()}
</p>
</Show>
<div class="w-[10rem] self-center rounded bg-white p-[1rem]">
<QRCodeSVG
value={"lightning:" + props.lud16 || ""}
value={"lightning:" + lud16() || ""}
class="h-full max-h-[256px] w-full"
/>
</div>

View File

@@ -243,6 +243,8 @@ function MessageList(props: {
<UnifiedActivityItem
item={combined.content as IActivityItem}
onClick={openDetailsModal}
// This isn't applicable here
onNewContactClick={() => {}}
/>
</div>
</Show>

View File

@@ -17,6 +17,13 @@ import { useI18n } from "~/i18n/context";
import { useMegaStore } from "~/state/megaStore";
import { DEFAULT_NOSTR_NAME } from "~/utils";
export type UserProfile = {
name: string;
picture?: string;
lud16?: string;
deleted: boolean | string;
};
export function Profile() {
const [state, _actions] = useMegaStore();
const i18n = useI18n();
@@ -26,12 +33,13 @@ export function Profile() {
const profile = createMemo(() => {
const profile = state.mutiny_wallet?.get_nostr_profile();
return {
const userProfile: UserProfile = {
name: profile?.display_name || profile?.name || DEFAULT_NOSTR_NAME,
picture: profile?.picture || undefined,
lud16: profile?.lud16 || undefined,
deleted: profile?.deleted || false
};
return userProfile;
});
const profileDeleted = createMemo(() => {
@@ -44,9 +52,9 @@ export function Profile() {
if (!hermes) {
return false;
}
const hermesDoman = new URL(hermes).hostname;
const afterAt = profile().lud16.split("@")[1];
if (afterAt && afterAt.includes(hermesDoman)) {
const hermesDomain = new URL(hermes).hostname;
const afterAt = profile().lud16!.split("@")[1];
if (afterAt && afterAt.includes(hermesDomain)) {
return true;
}
}
@@ -69,7 +77,7 @@ export function Profile() {
<Show when={profile().name}>{profile().name}</Show>
</h1>
<LightningAddressShower lud16={profile().lud16} />
<LightningAddressShower profile={profile()} />
</div>
<ButtonCard onClick={() => navigate("/editprofile")}>
<div class="flex items-center gap-2">
@@ -79,7 +87,11 @@ export function Profile() {
</div>
</ButtonCard>
<Show
when={state.federations?.length && !hasMutinyAddress()}
when={
state.federations?.length &&
!hasMutinyAddress() &&
import.meta.env.VITE_HERMES
}
>
<ButtonCard
onClick={() =>

View File

@@ -52,7 +52,11 @@ function HermesForm(props: { onSubmit: (name: string) => void }) {
}
});
const network = state.mutiny_wallet?.get_network() || "signet";
const hermes = import.meta.env.VITE_HERMES;
if (!hermes) {
throw new Error("Hermes not configured");
}
const hermesDomain = new URL(hermes).hostname;
const handleSubmit: SubmitHandler<HermesForm> = async (f: HermesForm) => {
setSuccess("");
@@ -67,13 +71,7 @@ function HermesForm(props: { onSubmit: (name: string) => void }) {
await state.mutiny_wallet?.reserve_lnurl_name(name);
console.log("lnurl name reserved:", name);
const hermes = import.meta.env.VITE_HERMES;
if (!hermes) {
throw new Error("Hermes not configured");
}
const hermesDoman = new URL(hermes).hostname;
const formattedName = `${name}${hermesDoman}`;
const formattedName = `${name}@${hermesDomain}`;
const existingProfile = state.mutiny_wallet?.get_nostr_profile();
@@ -109,12 +107,7 @@ function HermesForm(props: { onSubmit: (name: string) => void }) {
/>
</div>
<div class="flex-0 self-end pb-2 text-2xl text-m-grey-350">
<Show
when={network === "signet"}
fallback={"@mutiny.plus"}
>
@signet.mutiny.plus
</Show>
@{hermesDomain}
</div>
</div>
)}
@@ -145,7 +138,6 @@ export function LightningAddress() {
const [error, setError] = createSignal<Error>();
const [settingLnAddress, setSettingLnAddress] = createSignal(false);
// TODO: should be able to ask mutiny-node for this
const [lnurlName, { refetch }] = createResource(async () => {
try {
const name = await state.mutiny_wallet?.check_lnurl_name();
@@ -155,16 +147,17 @@ export function LightningAddress() {
}
});
const ios = Capacitor.getPlatform() === "ios";
// const ios = true;
const network = state.mutiny_wallet?.get_network() || "signet";
const hermes = import.meta.env.VITE_HERMES;
if (!hermes) {
throw new Error("Hermes not configured");
}
const hermesDomain = new URL(hermes).hostname;
const formattedLnAddress = createMemo(() => {
const name = lnurlName();
if (name) {
const suffix =
network === "signet" ? "@signet.mutiny.plus" : "@mutiny.plus";
return `${lnurlName()}${suffix}`;
return `${lnurlName()}@${hermesDomain}`;
}
});

View File

@@ -185,10 +185,9 @@ export function Plus() {
<Switch>
<Match when={state.mutiny_plus}>
<img src={party} class="mx-auto w-1/2" />
{/* <NiceP>{i18n.t("settings.plus.thanks")}</NiceP> */}
<NiceP>You're part of the Mutiny!</NiceP>
<NiceP>{i18n.t("settings.plus.thanks")}</NiceP>
{/* <Perks alreadySubbed /> */}
<Perks alreadySubbed />
<NiceP>
{i18n.t("settings.plus.renewal_time")}{" "}
<strong class="text-white">
@@ -204,7 +203,12 @@ export function Plus() {
{i18n.t("settings.plus.wallet_connection")}
</A>
</NiceP>
<Show when={state.federations?.length}>
<Show
when={
state.federations?.length &&
import.meta.env.VITE_HERMES
}
>
<ButtonCard
onClick={() =>
navigate("/settings/lightningaddress")