Skip to content

Commit

Permalink
mobile: currency switcher (#1060)
Browse files Browse the repository at this point in the history
* api: getExchangeRates

* mobile: currency switcher

* mobile: send or request in non-USD
  • Loading branch information
dcposch authored May 22, 2024
1 parent 1185b6c commit f76231e
Show file tree
Hide file tree
Showing 22 changed files with 515 additions and 145 deletions.
1 change: 1 addition & 0 deletions apps/daimo-mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"react-native-reanimated": "~3.6.2",
"react-native-safe-area-context": "4.8.2",
"react-native-screens": "~3.29.0",
"react-native-select-dropdown": "^4.0.1",
"react-native-shake": "^5.6.0",
"react-native-svg": "14.1.0",
"stream-browserify": "^3.0.0",
Expand Down
3 changes: 2 additions & 1 deletion apps/daimo-mobile/src/common/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
} from "../logic/deeplink";
import { fetchInviteLinkStatus } from "../logic/linkStatus";
import { Account } from "../model/account";
import { MoneyEntry } from "../model/moneyEntry";

export type ParamListOnboarding = {
Intro: undefined;
Expand Down Expand Up @@ -118,7 +119,7 @@ export interface SendNavProp {
| DaimoLinkRequestV2
| DaimoLinkTag;
recipient?: EAccountContact;
dollars?: `${number}`;
money?: MoneyEntry;
memo?: string;
requestId?: `${bigint}`;
autoFocus?: boolean;
Expand Down
14 changes: 14 additions & 0 deletions apps/daimo-mobile/src/model/account.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SuggestedAction } from "@daimo/api";
import {
ChainGasConstants,
CurrencyExchangeRate,
DaimoInviteCodeStatus,
DaimoLinkNote,
DaimoRequestV2Status,
Expand Down Expand Up @@ -105,6 +106,9 @@ export type Account = {

/** Proposed swaps from non-home coin balances -> home coin balance */
proposedSwaps: ProposedSwap[];

/** Exchange rates for non-USD currencies, just for amount entry */
exchangeRates: CurrencyExchangeRate[];
};

export function toEAccount(account: Account): EAccount {
Expand Down Expand Up @@ -334,6 +338,7 @@ interface AccountV14 extends StoredModel {
notificationRequestStatuses: DaimoRequestV2Status[];
lastReadNotifTimestamp: number;
proposedSwaps: ProposedSwap[];
exchangeRates?: CurrencyExchangeRate[];
}

export function parseAccount(accountJSON?: string): Account | null {
Expand Down Expand Up @@ -385,6 +390,7 @@ export function parseAccount(accountJSON?: string): Account | null {
notificationRequestStatuses: [],
lastReadNotifTimestamp: 0,
proposedSwaps: [],
exchangeRates: [],
};
} else if (model.storageVersion === 9) {
console.log(`[ACCOUNT] MIGRATING v${model.storageVersion} account`);
Expand Down Expand Up @@ -424,6 +430,7 @@ export function parseAccount(accountJSON?: string): Account | null {
notificationRequestStatuses: [],
lastReadNotifTimestamp: 0,
proposedSwaps: [],
exchangeRates: [],
};
} else if (model.storageVersion === 10) {
console.log(`[ACCOUNT] MIGRATING v${model.storageVersion} account`);
Expand Down Expand Up @@ -463,6 +470,7 @@ export function parseAccount(accountJSON?: string): Account | null {
notificationRequestStatuses: [],
lastReadNotifTimestamp: 0,
proposedSwaps: [],
exchangeRates: [],
};
} else if (model.storageVersion === 11) {
console.log(`[ACCOUNT] MIGRATING v${model.storageVersion} account`);
Expand Down Expand Up @@ -503,6 +511,7 @@ export function parseAccount(accountJSON?: string): Account | null {
notificationRequestStatuses: [],
lastReadNotifTimestamp: 0,
proposedSwaps: [],
exchangeRates: [],
};
} else if (model.storageVersion === 12) {
console.log(`[ACCOUNT] MIGRATING v${model.storageVersion} account`);
Expand Down Expand Up @@ -542,6 +551,7 @@ export function parseAccount(accountJSON?: string): Account | null {
notificationRequestStatuses: [],
lastReadNotifTimestamp: 0,
proposedSwaps: [],
exchangeRates: [],
};
} else if (model.storageVersion === 13) {
console.log(`[ACCOUNT] MIGRATING v${model.storageVersion} account`);
Expand Down Expand Up @@ -582,6 +592,7 @@ export function parseAccount(accountJSON?: string): Account | null {
notificationRequestStatuses: [],
lastReadNotifTimestamp: 0,
proposedSwaps: [],
exchangeRates: [],
};
}

Expand Down Expand Up @@ -623,6 +634,7 @@ export function parseAccount(accountJSON?: string): Account | null {
notificationRequestStatuses: a.notificationRequestStatuses || [],
lastReadNotifTimestamp: a.lastReadNotifTimestamp || 0,
proposedSwaps: a.proposedSwaps || [],
exchangeRates: a.exchangeRates || [],
};
}

Expand Down Expand Up @@ -666,6 +678,7 @@ export function serializeAccount(account: Account | null): string {
notificationRequestStatuses: account.notificationRequestStatuses,
lastReadNotifTimestamp: account.lastReadNotifTimestamp,
proposedSwaps: account.proposedSwaps,
exchangeRates: account.exchangeRates,
};

return JSON.stringify(model);
Expand Down Expand Up @@ -724,5 +737,6 @@ export function createEmptyAccount(
notificationRequestStatuses: [],
lastReadNotifTimestamp: now(),
proposedSwaps: [],
exchangeRates: [],
};
}
25 changes: 25 additions & 0 deletions apps/daimo-mobile/src/model/moneyEntry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { CurrencyExchangeRate, currencyRateUSD } from "@daimo/common";

export interface LocalMoneyEntry {
currency: CurrencyExchangeRate;
localUnits: number;
}

export interface MoneyEntry extends LocalMoneyEntry {
dollars: number;
}

export const zeroUSDEntry = {
currency: currencyRateUSD,
localUnits: 0,
dollars: 0,
};

export function usdEntry(dollars: number | `${number}`): MoneyEntry {
const n = typeof dollars === "number" ? dollars : parseFloat(dollars);
return {
currency: currencyRateUSD,
localUnits: n,
dollars: n,
};
}
2 changes: 2 additions & 0 deletions apps/daimo-mobile/src/sync/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ async function fetchSync(
inviteLinkStatus: result.inviteLinkStatus,
invitees: result.invitees,
notificationRequestStatuses: result.notificationRequestStatuses,
numExchangeRates: (result.exchangeRates || []).length,
};
console.log(`[SYNC] got history ${JSON.stringify(syncSummary)}`);

Expand Down Expand Up @@ -281,6 +282,7 @@ function applySync(
invitees: result.invitees || [],
notificationRequestStatuses: result.notificationRequestStatuses || [],
proposedSwaps: result.proposedSwaps || [],
exchangeRates: result.exchangeRates || [],
};

console.log(
Expand Down
13 changes: 7 additions & 6 deletions apps/daimo-mobile/src/view/screen/receive/ReceiveScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
shareURL,
} from "../../../logic/externalAction";
import { Account } from "../../../model/account";
import { zeroUSDEntry } from "../../../model/moneyEntry";
import { AmountChooser } from "../../shared/AmountInput";
import { ButtonBig } from "../../shared/Button";
import { ContactDisplay } from "../../shared/ContactDisplay";
Expand All @@ -55,7 +56,7 @@ function RequestScreenInner({
account: Account;
fulfiller?: DaimoContact;
}) {
const [dollars, setDollars] = useState(0);
const [money, setMoney] = useState(zeroUSDEntry);

// On successful send, go home
const [as, setAS] = useActStatus("request");
Expand Down Expand Up @@ -92,15 +93,15 @@ function RequestScreenInner({
const txHash = await rpcFunc.createRequestSponsored.mutate({
recipient: account.address,
idString,
amount: `${dollarsToAmount(dollars)}`,
amount: `${dollarsToAmount(money.dollars)}`,
fulfiller: fulfiller?.type === "eAcc" ? fulfiller.addr : undefined,
});

const link: DaimoLinkRequestV2 = {
type: "requestv2",
id: idString,
recipient: account.name,
dollars: `${dollars}`,
dollars: `${money.dollars}`,
};

console.log(`[REQUEST] txHash ${txHash}`);
Expand Down Expand Up @@ -144,8 +145,8 @@ function RequestScreenInner({
{fulfiller && <ContactDisplay contact={fulfiller} />}
<Spacer h={32} />
<AmountChooser
dollars={dollars}
onSetDollars={setDollars}
moneyEntry={money}
onSetEntry={setMoney}
showAmountAvailable={false}
innerRef={textInputRef}
disabled={as.status !== "idle"}
Expand Down Expand Up @@ -173,7 +174,7 @@ function RequestScreenInner({
<View style={styles.buttonGrow}>
<ButtonBig
type={as.status === "success" ? "success" : "primary"}
disabled={dollars <= 0 || as.status !== "idle"}
disabled={money.dollars <= 0 || as.status !== "idle"}
title={as.status === "success" ? "Sent" : "Request"}
onPress={sendRequest}
/>
Expand Down
1 change: 0 additions & 1 deletion apps/daimo-mobile/src/view/screen/send/MemoDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ const styles = StyleSheet.create({
sheetContainer: {
// add horizontal space
marginHorizontal: 24,
// ...ss.container.debug,
...ss.container.shadow,
},
contentContainer: {
Expand Down
17 changes: 9 additions & 8 deletions apps/daimo-mobile/src/view/screen/send/SendNoteScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
getComposeExternalAction,
shareURL,
} from "../../../logic/externalAction";
import { zeroUSDEntry } from "../../../model/moneyEntry";
import { AmountChooser } from "../../shared/AmountInput";
import { ButtonBig, HelpButton } from "../../shared/Button";
import { ContactDisplay } from "../../shared/ContactDisplay";
Expand All @@ -39,7 +40,7 @@ export function SendNoteScreen({ route }: Props) {
const { recipient } = route.params || {};

// Send Payment Link shows available secure messaging apps
const [noteDollars, setNoteDollars] = useState(0);
const [noteMoney, setNoteMoney] = useState(zeroUSDEntry);

const textInputRef = useRef<TextInput>(null);
const [amountChosen, setAmountChosen] = useState(false);
Expand All @@ -52,7 +53,7 @@ export function SendNoteScreen({ route }: Props) {
const goHome = useExitToHome();
const resetAmount = useCallback(() => {
setAmountChosen(false);
setNoteDollars(0);
setNoteMoney(zeroUSDEntry);
textInputRef.current?.focus();
}, []);
const goBack = useCallback(() => {
Expand Down Expand Up @@ -110,8 +111,8 @@ export function SendNoteScreen({ route }: Props) {
<Spacer h={24} />
{!amountChosen && (
<AmountChooser
dollars={noteDollars}
onSetDollars={setNoteDollars}
moneyEntry={noteMoney}
onSetEntry={setNoteMoney}
showAmountAvailable
autoFocus
disabled={amountChosen}
Expand All @@ -120,8 +121,8 @@ export function SendNoteScreen({ route }: Props) {
)}
{amountChosen && (
<AmountChooser
dollars={noteDollars}
onSetDollars={setNoteDollars}
moneyEntry={noteMoney}
onSetEntry={setNoteMoney}
disabled
showAmountAvailable={false}
autoFocus={false}
Expand All @@ -134,13 +135,13 @@ export function SendNoteScreen({ route }: Props) {
<ButtonBig
type="primary"
title="Create Payment Link"
disabled={!(noteDollars > 0)}
disabled={!(noteMoney.dollars > 0)}
onPress={onChooseAmount}
/>
)}
{amountChosen && (
<NoteActionButton
dollars={noteDollars}
dollars={noteMoney.dollars}
externalAction={externalAction}
/>
)}
Expand Down
Loading

0 comments on commit f76231e

Please sign in to comment.