Skip to content
This repository was archived by the owner on Feb 8, 2025. It is now read-only.

Commit de4d281

Browse files
authored
Merge pull request #262 from zallo-labs/Z-303-fix-rm-transaction
Z 303 fix rm transaction
2 parents 7b7991b + 04372d4 commit de4d281

File tree

15 files changed

+242
-105
lines changed

15 files changed

+242
-105
lines changed

app/src/api/ApiProvider.tsx

+2-26
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,12 @@
1-
import { ReactNode, useEffect, useState } from 'react';
1+
import { ReactNode } from 'react';
22
import { RelayEnvironmentProvider } from 'react-relay';
3-
import { getEnvironment, setEnvironment, useApiEnvironment } from './environment';
4-
import { withSuspense } from '#/skeleton/withSuspense';
5-
import { Splash } from '#/Splash';
6-
import { useApproverWallet } from '@network/useApprover';
7-
import { Environment } from 'relay-runtime';
8-
9-
let promisedEnvironment: Promise<Environment> | undefined;
3+
import { useApiEnvironment } from './environment';
104

115
export interface ApiProviderProps {
126
children: ReactNode;
137
}
148

159
export function ApiProvider({ children }: ApiProviderProps) {
16-
// const approver = useApproverWallet();
17-
// console.log(approver);
18-
19-
// const [env, setEnv] = useState<Environment | null>(null);
20-
// useEffect(() => {
21-
// getEnvironment({
22-
// key: 'main',
23-
// approver: new Promise<typeof approver>((resolve) => resolve(approver)),
24-
// persist: true,
25-
// }).then((env) => {
26-
// setEnv(env);
27-
// setEnvironment(env);
28-
// });
29-
// }, [approver]);
30-
31-
// if (!env) return <Splash />;
32-
3310
return <RelayEnvironmentProvider environment={useApiEnvironment()}>{children}</RelayEnvironmentProvider>;
3411
}
3512

36-
// export const ApiProvider = withSuspense(ApiProvider_, <Splash />);

app/src/api/PersistedRecordSource.ts

+34-14
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,52 @@ export class PersitedRecordSource extends RecordSource {
2323
}
2424

2525
set(dataID: DataID, record: Record): void {
26-
this.save();
26+
this.saveAfterDelay();
2727
return super.set(dataID, record);
2828
}
2929

30-
private save() {
31-
// Clear pending save
32-
if (this.pending) {
33-
clearTimeout(this.pending.timer);
34-
this.pending.listener.remove();
35-
this.pending = undefined;
36-
}
37-
38-
const save = () => {
39-
AsyncStorage.setItem(this.key, JSON.stringify(this.toJSON()));
40-
};
30+
private saveAfterDelay() {
31+
this.clearPending();
4132

4233
// Save after delay (debounce)
4334
const timer = setTimeout(() => {
44-
InteractionManager.runAfterInteractions(save);
35+
InteractionManager.runAfterInteractions(() => this.save());
4536
}, DELAY);
4637

4738
// Save pending immediately on background
4839
const listener = AppState.addEventListener('change', (state) => {
49-
if (state === 'background') save();
40+
if (state === 'background') this.save();
5041
});
5142

5243
this.pending = { timer, listener };
5344
}
45+
46+
private clearPending() {
47+
if (this.pending) {
48+
clearTimeout(this.pending.timer);
49+
this.pending.listener.remove();
50+
this.pending = undefined;
51+
}
52+
}
53+
54+
private save() {
55+
this.clearPending();
56+
57+
const allRecords = this.toJSON();
58+
59+
const filteredRecords = Object.fromEntries(
60+
Object.entries(allRecords)
61+
.filter(([key]) => !key.startsWith('client:local'))
62+
.map(([key, value]) => {
63+
if (value && '__invalidated_at' in value) {
64+
const { __invalidated_at, ...rest } = value;
65+
return [key, rest];
66+
}
67+
68+
return [key, value];
69+
}),
70+
);
71+
72+
AsyncStorage.setItem(this.key, JSON.stringify(filteredRecords));
73+
}
5474
}

app/src/api/environment.ts

+2-20
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { PrivateKeyAccount } from 'viem';
33
import { atom, useAtomValue } from 'jotai';
44
import { DANGEROUS_approverAtom } from '@network/useApprover';
55
import { InteractionManager } from 'react-native';
6-
import { atomFamily } from 'jotai/utils';
76
import { createNetworkLayer } from './network/layer';
87
import { fetchExchange } from './network/fetch';
98
import { CONFIG } from '~/util/config';
@@ -26,30 +25,13 @@ export function useApiEnvironment() {
2625
return useAtomValue(environmentAtom);
2726
}
2827

29-
const approverEnvironment = atomFamily(
30-
(approver: PrivateKeyAccount) =>
31-
atom(async () => {
32-
const promisedApprover = new Promise<PrivateKeyAccount>((resolve) => resolve(approver));
33-
return getEnvironment({ key: approver.address, approver: promisedApprover, persist: false });
34-
}),
35-
(a, b) => a.address === b.address,
36-
);
37-
38-
export function useApproverApiEnvironment(approver: PrivateKeyAccount) {
39-
return useAtomValue(approverEnvironment(approver));
40-
}
41-
4228
export interface EnvironmentConfig {
4329
key: string;
4430
approver: Promise<PrivateKeyAccount>;
4531
persist?: boolean;
4632
}
4733

48-
export let environment: Environment | undefined;
49-
export function setEnvironment(env: Environment) {
50-
environment = env;
51-
}
52-
34+
let environment: Environment | undefined;
5335
export async function getEnvironment({ key, approver, persist }: EnvironmentConfig) {
5436
if (environment) return environment;
5537

@@ -67,7 +49,7 @@ export async function getEnvironment({ key, approver, persist }: EnvironmentConf
6749
InteractionManager.runAfterInteractions(run);
6850
},
6951
});
70-
persist && store.notify(undefined, true); // Invalidate persisted data
52+
// persist && store.notify(undefined, true); // Invalidate persisted data
7153

7254
const network = createNetworkLayer({
7355
store,

app/src/app/(nav)/[account]/settings/approver/[address].tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import { graphql } from 'relay-runtime';
2121
import { useLazyLoadQuery } from 'react-relay';
2222
import { Address_ApproverSettingsQuery } from '~/api/__generated__/Address_ApproverSettingsQuery.graphql';
2323
import { asChain } from 'lib';
24+
import { ScreenSkeleton } from '#/skeleton/ScreenSkeleton';
25+
import { withSuspense } from '#/skeleton/withSuspense';
2426

2527
const Query = graphql`
2628
query Address_ApproverSettingsQuery($account: UAddress!, $approver: Address!) {
@@ -48,7 +50,7 @@ const ApproverSettingsParams = AccountParams.extend({
4850
address: zAddress(),
4951
});
5052

51-
export default function ApproverSettingsScreen() {
53+
function ApproverSettingsScreen() {
5254
const params = useLocalParams(ApproverSettingsParams);
5355
const { address } = params;
5456
const showSheet = useSetAtom(SIDE_SHEET);
@@ -121,3 +123,7 @@ const styles = createStyles({
121123
gap: 8,
122124
},
123125
});
126+
127+
export default withSuspense(ApproverSettingsScreen, <ScreenSkeleton />);
128+
129+
export { ErrorBoundary } from '#/ErrorBoundary';

app/src/app/(nav)/contacts/[address].tsx

-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ function ContactScreen_(props: ContactScreenProps) {
8686
address: asUAddress(address, chain),
8787
previousAddress: current?.address,
8888
});
89-
router.back();
9089
reset(input);
9190
});
9291

app/src/app/(nav)/message/[id].tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const Query = graphql`
3131
id
3232
chain
3333
...AccountSection_account
34+
...useRemoveMessage_account
3435
}
3536
dapp {
3637
...DappHeader_dappMetadata
@@ -53,7 +54,7 @@ export default function MessageScreen() {
5354
const { id } = useLocalParams(MessageScreenParams);
5455

5556
const { message: m, user } = useLazyLoadQuery<Id_MessageScreenQuery>(Query, { id });
56-
const remove = useRemoveMessage(m);
57+
const remove = useRemoveMessage({ account: m.account, message: m });
5758

5859
return (
5960
<SideSheetLayout defaultVisible>

app/src/app/(nav)/transaction/[id].tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ const Transaction = graphql`
6060
id
6161
address
6262
...AccountSection_account
63+
...useRemoveTransaction_account
6364
}
6465
dapp {
6566
...DappHeader_dappMetadata
@@ -82,7 +83,7 @@ function TransactionScreen() {
8283

8384
const query = useLazyLoadQuery<Id_TransactionScreenQuery>(Query, { id });
8485
const t = useFragment<Id_TransactionScreen_transaction$key>(Transaction, query.transaction);
85-
const remove = useRemoveTransaction({ transaction: t });
86+
const remove = useRemoveTransaction({ account: t.account, transaction: t });
8687

8788
useSubscription(
8889
useMemo(() => ({ subscription: Subscription, variables: { transaction: id } }), [id]),

app/src/app/(sheet)/select/address.tsx

-2
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,6 @@ function SelectAddressSheet() {
8585
contacts: include.includes('contacts'),
8686
});
8787

88-
console.log(query);
89-
9088
const accounts =
9189
query.accounts?.filter(
9290
(a) => !disabled?.has(a.address) && (!chain || asChain(a.address) === chain),

app/src/components/GlobalSubscriptions/useTransfersSubscription.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ export function useTransfersSubscription(params: UseTransfersSubscriptionParams)
3939
const t = data?.transfer;
4040
if (!t) return;
4141

42-
// Invalidate Token.balance
43-
if (t.token) store.get(t.token.id)?.invalidateRecord();
42+
// TODO: Invalidate Token.balance; disabled to prevent home screen from suspending whilst re-fetching
43+
// if (t.token) store.get(t.token.id)?.invalidateRecord();
4444

4545
// Prepend to Account.transfers
4646
if (t.incoming && !t.internal) {

app/src/components/message/useRemoveMessage.ts

+78-9
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,53 @@
11
import { useRouter } from 'expo-router';
22
import { useFragment } from 'react-relay';
3-
import { graphql } from 'relay-runtime';
3+
import { graphql, SelectorStoreUpdater } from 'relay-runtime';
44
import { useMutation } from '~/api';
5+
import { useRemoveMessage_account$key } from '~/api/__generated__/useRemoveMessage_account.graphql';
56
import { useRemoveMessage_message$key } from '~/api/__generated__/useRemoveMessage_message.graphql';
6-
import { useRemoveMessageMutation } from '~/api/__generated__/useRemoveMessageMutation.graphql';
7+
import {
8+
useRemoveMessageMutation,
9+
useRemoveMessageMutation$data,
10+
} from '~/api/__generated__/useRemoveMessageMutation.graphql';
11+
import { useRemoveMessageUpdatableQuery } from '~/api/__generated__/useRemoveMessageUpdatableQuery.graphql';
712
import { useConfirmRemoval } from '~/hooks/useConfirm';
813
import { useSelectedAccount } from '~/hooks/useSelectedAccount';
914

15+
graphql`
16+
fragment useRemoveMessage_assignable_message on Message @assignable {
17+
__typename
18+
}
19+
`;
20+
21+
const Account = graphql`
22+
fragment useRemoveMessage_account on Account {
23+
address
24+
proposals {
25+
id
26+
...useRemoveMessage_assignable_message
27+
}
28+
pendingProposals: proposals(input: { pending: true }) {
29+
id
30+
...useRemoveMessage_assignable_message
31+
}
32+
}
33+
`;
34+
1035
const Message = graphql`
1136
fragment useRemoveMessage_message on Message {
1237
id
1338
signature
1439
}
1540
`;
1641

17-
export function useRemoveMessage(messageFrag: useRemoveMessage_message$key) {
18-
const m = useFragment(Message, messageFrag);
42+
export interface RemoveMessageParams {
43+
account: useRemoveMessage_account$key;
44+
message: useRemoveMessage_message$key;
45+
}
46+
47+
export function useRemoveMessage(params: RemoveMessageParams) {
48+
const account = useFragment(Account, params.account);
49+
const m = useFragment(Message, params.message);
1950
const router = useRouter();
20-
const account = useSelectedAccount();
2151
const confirmRemoval = useConfirmRemoval({
2252
message: 'Are you sure you want to remove this message proposal?',
2353
});
@@ -35,11 +65,50 @@ export function useRemoveMessage(messageFrag: useRemoveMessage_message$key) {
3565

3666
return async () => {
3767
if (await confirmRemoval()) {
38-
await commit({ proposal: m.id });
68+
router.replace({
69+
pathname: '/(nav)/[account]/(home)/activity',
70+
params: { account: account.address },
71+
});
72+
73+
const updater: SelectorStoreUpdater<useRemoveMessageMutation$data> = (store, data) => {
74+
const id = data?.removeMessage;
75+
if (!id) return;
76+
77+
// Remove from proposals
78+
const { updatableData } = store.readUpdatableQuery<useRemoveMessageUpdatableQuery>(
79+
graphql`
80+
query useRemoveMessageUpdatableQuery($address: UAddress!) @updatable {
81+
account(address: $address) {
82+
proposals {
83+
...useProposeMessage_assignable_proposal
84+
}
85+
pendingProposals: proposals(input: { pending: true }) {
86+
...useProposeMessage_assignable_proposal
87+
}
88+
}
89+
}
90+
`,
91+
{ address: account.address },
92+
);
93+
94+
if (updatableData.account) {
95+
// @ts-expect-error one __typename is 'string' the other is 'Transaction'
96+
updatableData.account.proposals = account.proposals.filter((p) => p.id !== id);
97+
// @ts-expect-error one __typename is 'string' the other is 'Transaction'
98+
updatableData.account.pendingProposals = account.pendingProposals.filter(
99+
(p) => p.id !== id,
100+
);
101+
}
102+
};
39103

40-
account
41-
? router.push({ pathname: '/(nav)/[account]/(home)/activity', params: { account } })
42-
: router.back();
104+
await commit(
105+
{ proposal: m.id },
106+
{
107+
optimisticResponse: { removeMessage: m.id },
108+
optimisticUpdater: updater,
109+
updater,
110+
},
111+
);
43112
}
44113
};
45114
}

0 commit comments

Comments
 (0)