Skip to content

Commit

Permalink
payment memos (daimo-eth#905)
Browse files Browse the repository at this point in the history
* payment memos

* rc

* remove available learn more, make less busy

* pushNotifier: add memo test
  • Loading branch information
nalinbhardwaj authored Mar 29, 2024
1 parent b3b4336 commit 89380d1
Show file tree
Hide file tree
Showing 41 changed files with 733 additions and 254 deletions.
23 changes: 15 additions & 8 deletions apps/daimo-mobile/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,16 @@ function AppBody() {
const snapPoints = settings?.snapPoints || defaultSnapPoints;
const enableSwipeClose = settings?.enableSwipeClose || false;

const openBottomSheet = (sheet: GlobalBottomSheet) => {
Keyboard.dismiss();
setBottomSheet(sheet);
};

// Global shake gesture > open Send Debug Log sheet
useEffect(() => {
const subscription = RNShake.addListener(() => {
Keyboard.dismiss();
setBottomSheet({ action: "debug" });
});
const subscription = RNShake.addListener(() =>
openBottomSheet({ action: "debug" })
);
return () => subscription.remove();
}, []);

Expand Down Expand Up @@ -149,16 +153,19 @@ function AppBody() {
[]
);

// Handle dispatch > open bottom sheet
const openFC = () => setBottomSheet({ action: "connectFarcaster" });
const linkFC = () => setBottomSheet({ action: "linkFarcaster" });
const openChecklist = () => setBottomSheet({ action: "onboardingChecklist" });
// Farcaster: Handle dispatch > open bottom sheet
const openFC = () => openBottomSheet({ action: "connectFarcaster" });
const linkFC = () => openBottomSheet({ action: "linkFarcaster" });
const openChecklist = () =>
openBottomSheet({ action: "onboardingChecklist" });
useEffect(() => dispatcher.register("connectFarcaster", openFC), []);
useEffect(() => dispatcher.register("linkFarcaster", linkFC), []);
useEffect(
() => dispatcher.register("onboardingChecklist", openChecklist),
[]
);

// Common: Handle dispatch > hide bottom sheet
const hideSheet = () => setBottomSheet(null);
useEffect(() => dispatcher.register("hideBottomSheet", hideSheet), []);

Expand Down
4 changes: 2 additions & 2 deletions apps/daimo-mobile/src/action/useSendAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,9 @@ function loadOpSender({
? getWrappedPasskeySigner(daimoChain)
: getWrappedRawSigner(enclaveKeyName, keySlot!);

const sender: OpSenderCallback = async (op: UserOpHex) => {
const sender: OpSenderCallback = async (op: UserOpHex, memo?: string) => {
console.info(`[SEND] sending op ${JSON.stringify(op)}`);
return rpcFunc.sendUserOpV2.mutate({ op });
return rpcFunc.sendUserOpV2.mutate({ op, memo });
};

promise = (async () => {
Expand Down
13 changes: 7 additions & 6 deletions apps/daimo-mobile/src/common/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export type QRScreenOptions = "PAY ME" | "SCAN";
export type ParamListHome = {
Home: undefined;
QR: { option: QRScreenOptions | undefined };
Account:
Profile:
| { eAcc: EAccount; inviterEAcc: EAccount | undefined }
| { link: DaimoLinkAccount | DaimoLinkInviteCode };
HistoryOp: { op: DisplayOpEvent };
Expand Down Expand Up @@ -81,7 +81,7 @@ export type ParamListSend = {
SendTransfer: SendNavProp;
QR: { option: QRScreenOptions | undefined };
SendLink: { recipient?: MsgContact };
Account:
Profile:
| { eAcc: EAccount; inviterEAcc: EAccount | undefined }
| { link: DaimoLinkAccount };
HistoryOp: { op: DisplayOpEvent };
Expand All @@ -90,7 +90,7 @@ export type ParamListSend = {
export type ParamListInvite = {
Invite: undefined;
YourInvites: undefined;
Account:
Profile:
| { eAcc: EAccount; inviterEAcc: EAccount | undefined }
| { link: DaimoLinkAccount };
};
Expand All @@ -110,6 +110,7 @@ export interface SendNavProp {
| DaimoLinkTag;
recipient?: EAccountContact;
dollars?: `${number}`;
memo?: string;
requestId?: `${bigint}`;
autoFocus?: boolean;
}
Expand Down Expand Up @@ -225,7 +226,7 @@ async function goTo(nav: MainNav, link: DaimoLink) {
break;
}
case "account": {
nav.navigate("HomeTab", { screen: "Account", params: { link } });
nav.navigate("HomeTab", { screen: "Profile", params: { link } });
break;
}
case "request":
Expand All @@ -246,7 +247,7 @@ async function goTo(nav: MainNav, link: DaimoLink) {
break;
}
case "invite": {
nav.navigate("HomeTab", { screen: "Account", params: { link } });
nav.navigate("HomeTab", { screen: "Profile", params: { link } });
break;
}
default:
Expand Down Expand Up @@ -275,5 +276,5 @@ export function navToAccountPage(account: EAccount, nav: MainNav) {
account: getEAccountStr(account),
} as DaimoLinkAccount;

nav.push("Account", { link: accountLink });
nav.push("Profile", { link: accountLink });
}
8 changes: 4 additions & 4 deletions apps/daimo-mobile/src/view/TabNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import { useEffect } from "react";
import { Animated, Platform } from "react-native";
import { EdgeInsets, useSafeAreaInsets } from "react-native-safe-area-context";

import { AccountScreen } from "./screen/AccountScreen";
import HomeScreen from "./screen/HomeScreen";
import { InviteScreen } from "./screen/InviteScreen";
import { NotificationsScreen } from "./screen/NotificationsScreen";
import { ProfileScreen } from "./screen/ProfileScreen";
import { QRScreen } from "./screen/QRScreen";
import { SettingsScreen } from "./screen/SettingsScreen";
import { YourInvitesScreen } from "./screen/YourInvitesScreen";
Expand Down Expand Up @@ -301,7 +301,7 @@ function SendTab() {
<SendStack.Screen name="SendTransfer" component={SendTransferScreen} />
<SendStack.Screen name="QR" component={QRScreen} />
<SendStack.Screen name="SendLink" component={SendNoteScreen} />
<SendStack.Screen name="Account" component={AccountScreen} />
<SendStack.Screen name="Profile" component={ProfileScreen} />
</SendStack.Group>
</SendStack.Navigator>
);
Expand All @@ -325,7 +325,7 @@ function HomeTab() {
<HomeStack.Group>
<HomeStack.Screen name="Home" component={HomeScreen} />
<HomeStack.Screen name="QR" component={QRScreen} />
<HomeStack.Screen name="Account" component={AccountScreen} />
<HomeStack.Screen name="Profile" component={ProfileScreen} />
<HomeStack.Screen name="Note" component={NoteScreen} />
<HomeStack.Screen name="Receive" component={ReceiveScreenV2} />
<HomeStack.Screen name="ReceiveSearch" component={ReceiveNavScreen} />
Expand All @@ -346,7 +346,7 @@ function InviteTab() {
<InviteStack.Group>
<InviteStack.Screen name="Invite" component={InviteScreen} />
<InviteStack.Screen name="YourInvites" component={YourInvitesScreen} />
<HomeStack.Screen name="Account" component={AccountScreen} />
<InviteStack.Screen name="Profile" component={ProfileScreen} />
</InviteStack.Group>
</InviteStack.Navigator>
);
Expand Down
10 changes: 6 additions & 4 deletions apps/daimo-mobile/src/view/screen/HistoryOpScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ function TransferBody({
const coinName = chainConfig.tokenSymbol.toUpperCase();
const chainName = chainConfig.chainL2.name.toUpperCase();

const subtitleElements = [getFeeText(op.feeAmount), coinName, chainName];
if (op.type === "transfer" && op.memo) subtitleElements.unshift(op.memo);
const subtitle = subtitleElements.join(" • ");

return (
<View>
<TextCenter>
Expand All @@ -208,9 +212,7 @@ function TransferBody({
/>
<Spacer h={8} />
<TextCenter>
<TextBodyCaps color={color.grayMid}>
<FeeText amount={op.feeAmount} />{coinName}{chainName}
</TextBodyCaps>
<TextBodyCaps color={color.grayMid}>{subtitle}</TextBodyCaps>
</TextCenter>
<Spacer h={32} />
<OpRow op={op} otherAcc={other} />
Expand Down Expand Up @@ -257,7 +259,7 @@ function OpRow({ op, otherAcc }: { op: OpEvent; otherAcc: EAccount }) {
);
}

function FeeText({ amount }: { amount?: number }) {
function getFeeText(amount?: number) {
if (amount == null) {
return "PENDING";
}
Expand Down
4 changes: 1 addition & 3 deletions apps/daimo-mobile/src/view/screen/InviteScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -353,9 +353,7 @@ const styles = StyleSheet.create({
backgroundColor: color.white,
borderColor: color.grayLight,
borderWidth: 1,
shadowOffset: { height: 2, width: -1 },
elevation: 2,
shadowOpacity: 0.1,
...ss.container.shadow,
},
codeView: {
flexDirection: "row",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import {
assert,
canRequestFrom,
canSendTo,
formatDaimoLink,
getAccountName,
getAddressContraction,
getEAccountStr,
timeMonth,
} from "@daimo/common";
import { daimoChainFromId, teamDaimoFaucetAddr } from "@daimo/contract";
import { NativeStackScreenProps } from "@react-navigation/native-stack";
import { useCallback, useEffect, useRef } from "react";
import { ActivityIndicator, Linking, StyleSheet, View } from "react-native";
import { useCallback, useEffect, useMemo, useRef } from "react";
import { ActivityIndicator, StyleSheet, View } from "react-native";
import { useSharedValue } from "react-native-reanimated";

import {
Expand All @@ -24,47 +26,61 @@ import {
useNav,
} from "../../common/nav";
import { addLastTransferTimes } from "../../logic/daimoContacts";
import { env } from "../../logic/env";
import { useFetchLinkStatus } from "../../logic/linkStatus";
import { Account } from "../../model/account";
import { AccountCopyLinkButton } from "../shared/AccountCopyLinkButton";
import { ButtonBig } from "../shared/Button";
import { ContactBubble } from "../shared/ContactBubble";
import { ExplorerBadge } from "../shared/ExplorerBadge";
import { FarcasterButton } from "../shared/FarcasterBubble";
import { HistoryListSwipe } from "../shared/HistoryList";
import { ScreenHeader } from "../shared/ScreenHeader";
import Spacer from "../shared/Spacer";
import { SwipeUpDownRef } from "../shared/SwipeUpDown";
import { ErrorBanner } from "../shared/error";
import { shareURL } from "../shared/shareURL";
import { color, ss } from "../shared/style";
import { TextBody } from "../shared/text";
import { TextBody, TextH2 } from "../shared/text";
import { useSwipeUpDown } from "../shared/useSwipeUpDown";
import { useWithAccount } from "../shared/withAccount";

type Props = NativeStackScreenProps<ParamListHome, "Account">;
type Props = NativeStackScreenProps<ParamListHome, "Profile">;

export function AccountScreen(props: Props) {
const Inner = useWithAccount(AccountScreenInner);
export function ProfileScreen(props: Props) {
const Inner = useWithAccount(ProfileScreenInner);
return <Inner {...props} />;
}

function AccountScreenInner(props: Props & { account: Account }) {
function ProfileScreenInner(props: Props & { account: Account }) {
const goBack = useExitBack();
const goHome = useExitToHome();

const { params } = props.route;

const onShare = useMemo(() => {
if (!("eAcc" in params && params.eAcc)) return;
const accountName = getEAccountStr(params.eAcc);

return () => {
shareURL(formatDaimoLink({ type: "account", account: accountName }));
console.log(`[PROFILE] triggered share`);
};
}, [params]);

return (
<View style={[ss.container.screen, styles.noPadding]}>
<View style={styles.screenPadding}>
<ScreenHeader title="Account" onBack={goBack || goHome} />
<ScreenHeader
title="Profile"
onBack={goBack || goHome}
onShare={onShare}
/>
</View>
<Spacer h={32} />
{"link" in params && (
<AccountScreenLoader account={props.account} link={params.link} />
<ProfileScreenLoader account={props.account} link={params.link} />
)}
{"eAcc" in params && (
<AccountScreenBody
<ProfileScreenBody
account={props.account}
eAcc={params.eAcc}
inviterEAcc={params.inviterEAcc}
Expand All @@ -74,7 +90,7 @@ function AccountScreenInner(props: Props & { account: Account }) {
);
}

function AccountScreenLoader({
function ProfileScreenLoader({
account,
link,
}: {
Expand Down Expand Up @@ -106,7 +122,7 @@ function AccountScreenLoader({

// Show account
console.log(`[ACCOUNT] loaded account: ${JSON.stringify(status.data)}`);
nav.navigate("Account", { eAcc, inviterEAcc });
nav.navigate("Profile", { eAcc, inviterEAcc });
}, [status.data]);

assert(["account", "invite"].includes(link.type), "Bad link type");
Expand Down Expand Up @@ -138,7 +154,7 @@ function getTeamDaimoFaucetAcc(): EAccount {
return { addr: teamDaimoFaucetAddr, label: AddrLabel.Faucet };
}

function AccountScreenBody({
function ProfileScreenBody({
account,
eAcc,
inviterEAcc,
Expand All @@ -150,13 +166,6 @@ function AccountScreenBody({
const nav = useNav();
const bottomSheetRef = useRef<SwipeUpDownRef>(null);

const openExplorer = useCallback(() => {
const { chainConfig } = env(daimoChainFromId(account.homeChainId));
const explorer = chainConfig.chainL2.blockExplorers!.default;
const url = `${explorer.url}/address/${eAcc.addr}`;
Linking.openURL(url);
}, [account, eAcc]);

const recipient = addLastTransferTimes(account, eAcc);

const canSend = canSendTo(eAcc);
Expand Down Expand Up @@ -235,14 +244,18 @@ function AccountScreenBody({
<View style={styles.mainContent}>
<ContactBubble contact={{ type: "eAcc", ...eAcc }} size={64} />
<Spacer h={16} />
<AccountCopyLinkButton eAcc={eAcc} size="h2" center />
<TextH2>{getAccountName(eAcc)}</TextH2>
<Spacer h={4} />
{subtitle}
{fcAccount && (
<>
<FarcasterButton fcAccount={fcAccount} />
</>
)}
<Spacer h={4} />
<View style={{ flexDirection: "row", alignItems: "center" }}>
{fcAccount && <FarcasterButton fcAccount={fcAccount} />}
{fcAccount && <Spacer w={8} />}
<ExplorerBadge
daimoChain={daimoChainFromId(account.homeChainId)}
address={eAcc.addr}
/>
</View>
</View>
<Spacer h={24} />
<View style={[ss.container.padH8, styles.buttonGroup]}>
Expand All @@ -258,13 +271,6 @@ function AccountScreenBody({
)}
</View>
<Spacer h={16} />
<View style={ss.container.padH8}>
<ButtonBig
type="subtle"
title="VIEW ON BLOCK EXPLORER"
onPress={openExplorer}
/>
</View>
</View>
{bottomSheet}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ function RequestScreenInnerV2({
subtitle="Request USDC from someone using any messaging app"
/>
)}
<Spacer h={64} />
<Spacer h={24} />
{recipient && <RecipientDisplay recipient={recipient} />}
<Spacer h={32} />
<AmountChooser
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export function FulfillRequestButton({
case "idle":
if (sendDisabledReason != null)
return <TextError>{sendDisabledReason}</TextError>;
if (dollars === 0) return null;
if (dollars === 0) return "Payments are public";
return `Total incl. fees ${getAmountText({
dollars: cost.totalDollars,
})}`;
Expand Down
Loading

0 comments on commit 89380d1

Please sign in to comment.