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

Commit 3b7f832

Browse files
authored
Merge pull request #266 from zallo-labs/Z-296-upgrade-docs
Z 296 upgrade docs
2 parents bc314bd + ff72557 commit 3b7f832

File tree

10 files changed

+264
-68
lines changed

10 files changed

+264
-68
lines changed

app/src/api/environment.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { getAuthManager } from './auth-manager';
1313
import { mapExchange } from './network/map';
1414
import { missingFieldHandlers } from './field-handlers';
1515
import { persistedQueryExchange } from './network/persistedQuery';
16-
import { retryExchange } from './network/retry';
16+
import { exponentialBackoffDelayWithJitter, retryExchange } from './network/retry';
1717
import { PersitedRecordSource } from './PersistedRecordSource';
1818

1919
const environmentAtom = atom(async (get) => {
@@ -49,6 +49,7 @@ export async function getEnvironment({ key, approver, persist }: EnvironmentConf
4949
},
5050
});
5151

52+
const retries = 8;
5253
const network = createNetworkLayer({
5354
store,
5455
exchanges: [
@@ -58,15 +59,19 @@ export async function getEnvironment({ key, approver, persist }: EnvironmentConf
5859
onGraphQLError: (result) => console.error('[GraphQL Error]', result),
5960
onNetworkError: (result) => console.error('[Network Error]', result),
6061
}),
61-
retryExchange(),
62+
retryExchange({ maxAttempts: retries }),
6263
authExchange(authManager),
6364
persistedQueryExchange(),
6465
fetchExchange({ url: `${CONFIG.apiUrl}/graphql` }),
6566
subscriptionExchange(
6667
createClient({
6768
url: CONFIG.apiGqlWs,
6869
lazy: true,
69-
retryAttempts: 15,
70+
retryAttempts: retries,
71+
retryWait: (retries: number) =>
72+
new Promise((resolve) =>
73+
setTimeout(resolve, exponentialBackoffDelayWithJitter(retries)),
74+
),
7075
connectionParams: () => authManager.getAuthHeaders(undefined, undefined),
7176
}),
7277
),

app/src/api/network/retry.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,24 @@ import { Exchange } from './layer';
33

44
export interface RetryExchangeOptions {
55
maxAttempts?: number;
6-
backoff?: (attempt: number) => number;
6+
delay?: (attempt: number) => number;
77
}
88

99
export function retryExchange({
10-
maxAttempts = 1,
11-
backoff = (n) => 200 * 2 ** n,
10+
maxAttempts = 3,
11+
delay = exponentialBackoffDelayWithJitter,
1212
}: RetryExchangeOptions = {}): Exchange {
1313
return ({ forward }) =>
1414
(operations$) =>
1515
operations$.pipe(
1616
forward,
1717
retry({
18-
delay: (_err, attempt) => timer(backoff(attempt)),
18+
delay: (_err, retries) => timer(delay(retries)),
1919
count: maxAttempts,
2020
}),
2121
);
2222
}
23+
24+
export function exponentialBackoffDelayWithJitter(retries: number) {
25+
return 200 * 2 ** retries + Math.random() * 1000;
26+
}

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

+10-10
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Text } from 'react-native-paper';
44
import { useLocalParams } from '~/hooks/useLocalParams';
55
import { AccountParams } from '~/app/(nav)/[account]/_layout';
66
import { createStyles, useStyles } from '@theme/styles';
7-
import { AddIcon, EditOutlineIcon, InfoIcon, NavigateNextIcon, UpdateIcon } from '@theme/icons';
7+
import { AddIcon, EditOutlineIcon, NavigateNextIcon } from '@theme/icons';
88
import { ScrollView, View } from 'react-native';
99
import { AccountApproverItem } from '#/account/AccountApproverItem';
1010
import { ListItem } from '#/list/ListItem';
@@ -23,6 +23,7 @@ import { MenuOrSearchIcon } from '#/Appbar/MenuOrSearchIcon';
2323
import { graphql } from 'relay-runtime';
2424
import { useLazyQuery } from '~/api';
2525
import { settings_AccountSettingsQuery } from '~/api/__generated__/settings_AccountSettingsQuery.graphql';
26+
import { UpgradePolicyItem } from '#/account/UpgradePolicyItem';
2627

2728
const Query = graphql`
2829
query settings_AccountSettingsQuery($account: UAddress!) {
@@ -75,6 +76,7 @@ function AccountSettingsPane_() {
7576
const policies = a.policies
7677
.filter((p) => p.key !== PolicyPresetKey.upgrade)
7778
.sort((a, b) => a.key - b.key);
79+
const upgradePolicy = a.policies.find((p) => p.key === PolicyPresetKey.upgrade);
7880

7981
return (
8082
<FirstPane fixed>
@@ -156,15 +158,13 @@ function AccountSettingsPane_() {
156158
</Link>
157159
))}
158160

159-
{/* TODO: link to upgrade policy docs */}
160-
<ListItem
161-
leading={UpdateIcon}
162-
headline="Upgrade"
163-
supporting="Learn more - coming soon"
164-
trailing={InfoIcon}
165-
containerStyle={styles.item}
166-
disabled
167-
/>
161+
{upgradePolicy && (
162+
<UpgradePolicyItem
163+
account={account}
164+
policyKey={upgradePolicy.key}
165+
containerStyle={styles.item}
166+
/>
167+
)}
168168

169169
<Link
170170
href={{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { DialogActions } from '#/Dialog/DialogActions';
2+
import { DialogModal } from '#/Dialog/DialogModal';
3+
import { ListItem, ListItemProps } from '#/list/ListItem';
4+
import { Portal } from '@gorhom/portal';
5+
import { ExternalLinkIcon, UpdateIcon } from '@theme/icons';
6+
import { createStyles, useStyles } from '@theme/styles';
7+
import { Link } from 'expo-router';
8+
import { PolicyKey, UAddress } from 'lib';
9+
import { useState } from 'react';
10+
import { View } from 'react-native';
11+
import { Button, Checkbox, Dialog, Text } from 'react-native-paper';
12+
import { useRemovePolicy } from '~/hooks/mutations/useRemovePolicy';
13+
import { CONFIG } from '~/util/config';
14+
15+
const UPGRADE_POLICY_HREF = CONFIG.docsUrl + '/upgrades';
16+
17+
export interface UpgradePolicyItemProps extends Partial<ListItemProps> {
18+
account: UAddress;
19+
policyKey: PolicyKey;
20+
}
21+
22+
export function UpgradePolicyItem({ account, policyKey, ...props }: UpgradePolicyItemProps) {
23+
const { styles } = useStyles(stylesheet);
24+
const remove = useRemovePolicy();
25+
26+
const [showDialog, setShowDialog] = useState(false);
27+
const [risksAccepted, setRisksAccepted] = useState(false);
28+
29+
return (
30+
<>
31+
<Link asChild href={UPGRADE_POLICY_HREF}>
32+
<ListItem
33+
leading={UpdateIcon}
34+
headline="Automatic upgrades"
35+
supporting="Enabled"
36+
trailing={ExternalLinkIcon}
37+
onLongPress={async () => {
38+
setShowDialog(true);
39+
}}
40+
{...props}
41+
/>
42+
</Link>
43+
44+
{showDialog && (
45+
<Portal>
46+
<DialogModal>
47+
<Dialog.Title>Automatic upgrades</Dialog.Title>
48+
49+
<Dialog.Content>
50+
<Text variant="bodyMedium">
51+
Opting-out of the upgrade policy is{' '}
52+
<Text variant="titleSmall">not recommended</Text> and may lead to compatibility
53+
issues with the Zallo app
54+
</Text>
55+
56+
<View style={styles.checkboxContainer}>
57+
<Checkbox
58+
status={risksAccepted ? 'checked' : 'unchecked'}
59+
onPress={() => setRisksAccepted((c) => !c)}
60+
/>
61+
62+
<Text variant="titleSmall">
63+
I have read and understand the implications of opting out of the{' '}
64+
<Link href={UPGRADE_POLICY_HREF} style={styles.link}>
65+
Upgrade Policy
66+
</Link>
67+
</Text>
68+
</View>
69+
</Dialog.Content>
70+
71+
<DialogActions>
72+
<Button textColor={styles.cancel.color} onPress={() => setShowDialog(false)}>
73+
Cancel
74+
</Button>
75+
76+
<Button
77+
textColor={styles.destructive.color}
78+
disabled={!risksAccepted}
79+
onPress={async () => {
80+
setShowDialog(false);
81+
await remove(account, policyKey);
82+
}}
83+
>
84+
Opt-out of automatic upgrades
85+
</Button>
86+
</DialogActions>
87+
</DialogModal>
88+
</Portal>
89+
)}
90+
</>
91+
);
92+
}
93+
94+
const stylesheet = createStyles(({ colors }) => ({
95+
checkboxContainer: {
96+
flexDirection: 'row',
97+
alignItems: 'center',
98+
gap: 16,
99+
// marginVertical: 8,
100+
marginTop: 16,
101+
},
102+
link: {
103+
color: colors.tertiary,
104+
},
105+
cancel: {
106+
color: colors.onSurfaceVariant,
107+
},
108+
destructive: {
109+
color: colors.error,
110+
},
111+
}));

app/src/components/policy/PolicySideSheet.tsx

+3-28
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { useRouter } from 'expo-router';
21
import { useForm } from 'react-hook-form';
32
import { FormTextField } from '#/fields/FormTextField';
43
import { Actions } from '#/layout/Actions';
@@ -16,7 +15,7 @@ import { PolicySideSheet_account$key } from '~/api/__generated__/PolicySideSheet
1615
import { PolicySideSheet_policy$key } from '~/api/__generated__/PolicySideSheet_policy.graphql';
1716
import { useMutation } from '~/api';
1817
import { PolicySideSheet_renameMutation } from '~/api/__generated__/PolicySideSheet_renameMutation.graphql';
19-
import { PolicySideSheet_removeMutation } from '~/api/__generated__/PolicySideSheet_removeMutation.graphql';
18+
import { useRemovePolicy } from '~/hooks/mutations/useRemovePolicy';
2019

2120
const trimmed = (v: string) => v.trim();
2221

@@ -53,20 +52,6 @@ const Rename = graphql`
5352
}
5453
`;
5554

56-
const Remove = graphql`
57-
mutation PolicySideSheet_removeMutation($account: UAddress!, $key: PolicyKey!) {
58-
removePolicy(input: { account: $account, key: $key }) {
59-
id
60-
draft {
61-
id
62-
proposal {
63-
id
64-
}
65-
}
66-
}
67-
}
68-
`;
69-
7055
interface Inputs {
7156
name: string;
7257
}
@@ -80,9 +65,8 @@ export function PolicySideSheet(props: PolicySideSheetProps) {
8065
const { styles } = useStyles(stylesheet);
8166
const account = useFragment(Account, props.account);
8267
const policy = useFragment(Policy, props.policy);
83-
const router = useRouter();
8468
const rename = useMutation<PolicySideSheet_renameMutation>(Rename);
85-
const remove = useMutation<PolicySideSheet_removeMutation>(Remove);
69+
const remove = useRemovePolicy();
8670
const confirmRemove = useConfirmRemoval({
8771
title: 'Remove policy',
8872
message: 'Are you sure you want to remove this policy?',
@@ -118,16 +102,7 @@ export function PolicySideSheet(props: PolicySideSheetProps) {
118102
style={styles.removeButton}
119103
labelStyle={styles.removeLabel}
120104
onPress={async () => {
121-
if (await confirmRemove()) {
122-
const proposal = (await remove({ account: account.address, key: policy.key }))
123-
.removePolicy.draft?.proposal;
124-
125-
if (proposal)
126-
router.push({
127-
pathname: `/(nav)/transaction/[id]`,
128-
params: { id: proposal.id },
129-
});
130-
}
105+
if (await confirmRemove()) await remove(account.address, policy.key);
131106
}}
132107
>
133108
Remove policy
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { showError } from '#/provider/SnackbarProvider';
2+
import { useRouter } from 'expo-router';
3+
import { PolicyKey, UAddress } from 'lib';
4+
import { graphql } from 'relay-runtime';
5+
import { useMutation } from '~/api';
6+
import { useRemovePolicyMutation } from '~/api/__generated__/useRemovePolicyMutation.graphql';
7+
8+
export function useRemovePolicy() {
9+
const router = useRouter();
10+
11+
const commit = useMutation<useRemovePolicyMutation>(graphql`
12+
mutation useRemovePolicyMutation($account: UAddress!, $key: PolicyKey!) {
13+
removePolicy(input: { account: $account, key: $key }) {
14+
id
15+
draft {
16+
id
17+
proposal {
18+
id
19+
}
20+
}
21+
}
22+
}
23+
`);
24+
25+
return async (account: UAddress, key: PolicyKey) => {
26+
const r = await commit({ account, key });
27+
const proposal = r.removePolicy.draft?.proposal;
28+
29+
if (proposal) {
30+
router.push({
31+
pathname: `/(nav)/transaction/[id]`,
32+
params: { id: proposal.id },
33+
});
34+
} else {
35+
showError('Something went wrong removing policy', { event: { response: r } });
36+
}
37+
};
38+
}
File renamed without changes.

docs/docs/02-terms-of-service.md docs/docs/02-Legal/01-terms-of-service.md

+9-10
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
---
2+
slug: /terms-of-service
23
categories:
34
- Legal
45
---
56

67
# Terms of Service
78

8-
**Last Updated: July 16, 2024**
9-
10-
## 1. Introduction
9+
**Last Updated: July 19, 2024**
1110

1211
These Terms of Service ("Terms") govern your access to and use of the Zallo Labs Pty Ltd ("us", "we", or "our") Services, including but not limited to our website (https://zallo.io), its subdomains, and our app.
1312

14-
By using our Services, you agree to be bound by the Terms and our [Privacy Policy](./03-privacy.md).
13+
By using our Services, you agree to be bound by the Terms, [Privacy Policy](/privacy) and [Upgrade Policy](/upgrades).
1514

16-
## 2. User responsibility and key management
15+
## 1. User responsibility and key management
1716

1817
Accounts are self-custodial, meaning that we do not collect or store your private keys, recovery phrases, and passwords (collectively, "Secret Keys").
1918

@@ -24,24 +23,24 @@ You are solely responsible for:
2423
- All activities that occur under your account, including any loss of funds
2524
- Properly securing your account by means of security policies and procedures
2625

27-
## 3. Liability
26+
## 2. Liability
2827

29-
To the fullest extent permitted by law, we are not liable for any losses, damages, or liabilities of any kind arising from or in connecting with:
28+
To the fullest extent permitted by law, we are not liable for any losses, damages, or liabilities of any kind arising from or in connection with:
3029
- Your use of our Services
3130
- Your violation of the Terms
3231
- Use of third-party services or products accessed through our Services
3332
- Any other circumstances beyond our reasonable control
3433

35-
## 4. Changes to the Terms
34+
## 3. Changes to the Terms
3635

3736
You will be notified of any changes to the Terms on our website or app.
3837
By continuing to use our Services after changes to the Terms, you accept the revised Terms.
3938

40-
## 5. Prohibited Uses
39+
## 4. Prohibited Uses
4140

4241
You may not use our Services for any purpose that is fraudulent, or otherwise violates any applicable law or regulation.
4342

44-
## 6. Suspension and discountinuation of Services
43+
## 5. Suspension and discontinuation of Services
4544

4645
We retain sole discretion to temporarily suspend or discontinue your access to our Services, without prior notice, if we have reasonable suspicion that you engage in Prohibited Uses.
4746

0 commit comments

Comments
 (0)