From 08a65bf88d7f226ebe87f63429bea617c1567100 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:07:46 -0800 Subject: [PATCH 1/4] [ECO-2205] Refactor indexer scaling, update README (#407) --- src/cloud-formation/README.md | 6 ++++++ .../deploy-indexer-production.yaml | 3 +++ src/cloud-formation/indexer.cfn.yaml | 20 ++++++++++++++----- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/cloud-formation/README.md b/src/cloud-formation/README.md index 3471c27da..aea6e7d49 100644 --- a/src/cloud-formation/README.md +++ b/src/cloud-formation/README.md @@ -41,6 +41,10 @@ indexer deployments. ## Setup +1. Choose an [AWS region][aws regions] like `us-east-2` (Ohio) if you are going + to get indexer data from the [Aptos Labs gRPC endpoint], which is located in + [GCP region][gcp regions] `us-central1` (Iowa). + 1. [Make Route 53 the DNS service for a domain you own], which will automatically be configured with a subdomain for each deployment environment. @@ -494,6 +498,7 @@ REST and WebSocket endpoints. [auto-selection of aurora az]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbinstance.html#cfn-rds-dbinstance-availabilityzone [availability zone]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-availability-zones [aws cloudformation]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html +[aws regions]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions [aws routes docs]: https://docs.aws.amazon.com/vpc/latest/userguide/subnet-route-tables.html#route-table-routes [aws-recommended vpc cidr block]: https://docs.aws.amazon.com/vpc/latest/userguide/vpc-cidr-blocks.html [az-specific nat gateways]: https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-basics.html @@ -513,6 +518,7 @@ REST and WebSocket endpoints. [ecr pull through cache rule creation docs]: https://docs.aws.amazon.com/AmazonECR/latest/userguide/pull-through-cache-creating-rule.html [ecs task execution iam role]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html [fault tolerant replica promotion]: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Concepts.AuroraHighAvailability.html#Aurora.Managing.FaultTolerance +[gcp regions]: https://cloud.google.com/compute/docs/regions-zones#available [gitsync]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/git-sync.html [gitsync event]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/git-sync-status.html#git-sync-status-sync-events [gitsync iam role]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/git-sync-prereq.html#git-sync-prereq-iam diff --git a/src/cloud-formation/deploy-indexer-production.yaml b/src/cloud-formation/deploy-indexer-production.yaml index 4c1b2c338..c3295933a 100644 --- a/src/cloud-formation/deploy-indexer-production.yaml +++ b/src/cloud-formation/deploy-indexer-production.yaml @@ -1,6 +1,9 @@ --- parameters: + BrokerCpu: 1024 BrokerImageVersion: '1.0.1' + BrokerMemory: 2048 + DbMaxCapacity: 8 DeployAlb: 'true' DeployAlbDnsRecord: 'true' DeployBastionHost: 'true' diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index d4adc4c0b..181d7fe4e 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -107,10 +107,16 @@ Parameters: BastionHostAmiId: Default: 'ami-030cb86c12b18e236' Type: 'AWS::EC2::Image::Id' + BrokerCpu: + Default: 256 + Type: 'Number' BrokerImageVersion: Type: 'String' + BrokerMemory: + Default: 512 + Type: 'Number' DbMaxCapacity: - Default: 16 + Default: 1 Type: 'Number' DbMinCapacity: Default: 0.5 @@ -654,10 +660,10 @@ Resources: - 'Constants' - 'Networking' - 'BrokerPort' - Cpu: '1024' + Cpu: !Ref 'BrokerCpu' ExecutionRoleArn: !GetAtt 'ContainerRole.Arn' Family: !Ref 'AWS::StackName' - Memory: '2048' + Memory: !Ref 'BrokerMemory' NetworkMode: 'awsvpc' RequiresCompatibilities: - 'FARGATE' @@ -676,6 +682,10 @@ Resources: # Cluster for running ECS containers. ContainerCluster: Condition: 'DeployContainers' + Properties: + ClusterSettings: + - Name: 'containerInsights' + Value: 'enabled' Type: 'AWS::ECS::Cluster' # Log group for ECS task logging. ContainerLogGroup: @@ -1305,10 +1315,10 @@ Resources: - 'Constants' - 'Networking' - 'PostgrestHealthCheckPort' - Cpu: '1024' + Cpu: '256' ExecutionRoleArn: !GetAtt 'ContainerRole.Arn' Family: !Ref 'AWS::StackName' - Memory: '2048' + Memory: '512' NetworkMode: 'awsvpc' RequiresCompatibilities: - 'FARGATE' From 134f5e99cb69f4a0ecbd88b2c584173ab368576a Mon Sep 17 00:00:00 2001 From: Bogdan Crisan Date: Fri, 22 Nov 2024 20:47:59 +0100 Subject: [PATCH 2/4] [ECO-2483] Cache VPN queries (#402) Co-authored-by: Matt <90358481+xbtmatt@users.noreply.github.com> --- src/typescript/frontend/src/configs/index.ts | 1 - .../src/configs/local-storage-keys.ts | 42 ++++++++++++++++++- .../src/context/language-context/helpers.ts | 5 ++- .../src/context/theme-context/index.tsx | 8 ++-- .../src/hooks/use-is-user-geoblocked.ts | 12 +++++- src/typescript/frontend/src/utils/index.ts | 5 ++- 6 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/typescript/frontend/src/configs/index.ts b/src/typescript/frontend/src/configs/index.ts index c9e178638..6f2955f5c 100644 --- a/src/typescript/frontend/src/configs/index.ts +++ b/src/typescript/frontend/src/configs/index.ts @@ -1,3 +1,2 @@ -export { default as LOCAL_STORAGE_KEYS } from "./local-storage-keys"; export { EN, languages, languageList } from "./languages"; export { default as REGEX } from "./regex"; diff --git a/src/typescript/frontend/src/configs/local-storage-keys.ts b/src/typescript/frontend/src/configs/local-storage-keys.ts index fc728ef83..e36b0c3ff 100644 --- a/src/typescript/frontend/src/configs/local-storage-keys.ts +++ b/src/typescript/frontend/src/configs/local-storage-keys.ts @@ -1,8 +1,48 @@ +import { parseJSON, stringifyJSON } from "utils"; import packages from "../../package.json"; const LOCAL_STORAGE_KEYS = { theme: `${packages.name}_theme`, language: `${packages.name}_language`, + geoblocking: `${packages.name}_geoblocking`, }; -export default LOCAL_STORAGE_KEYS; +const LOCAL_STORAGE_CACHE_TIME = { + theme: Infinity, + language: Infinity, + geoblocking: 7 * 24 * 60 * 60 * 1000, // 7 days. +}; + +export type LocalStorageCache = { + expiry: number; + data: T | null; +}; + +/** + * Note that this data is not validated and any change in data type returned from this function + * should be validated to ensure that persisted cache data between multiple builds can cause errors + * with unexpected data types. + */ +export function readLocalStorageCache(key: keyof typeof LOCAL_STORAGE_KEYS): T | null { + const str = localStorage.getItem(key); + if (str === null) { + return null; + } + const cache = parseJSON>(str); + try { + if (new Date(cache.expiry) > new Date()) { + return cache.data; + } + } catch (e) { + return null; + } + return null; +} + +export function writeLocalStorageCache(key: keyof typeof LOCAL_STORAGE_KEYS, data: T) { + const cache: LocalStorageCache = { + expiry: new Date().getTime() + LOCAL_STORAGE_CACHE_TIME[key], + data, + }; + localStorage.setItem(key, stringifyJSON>(cache)); +} diff --git a/src/typescript/frontend/src/context/language-context/helpers.ts b/src/typescript/frontend/src/context/language-context/helpers.ts index 3c32798ba..a2f03c293 100644 --- a/src/typescript/frontend/src/context/language-context/helpers.ts +++ b/src/typescript/frontend/src/context/language-context/helpers.ts @@ -1,10 +1,11 @@ -import { EN, LOCAL_STORAGE_KEYS, REGEX } from "configs"; +import { EN, REGEX } from "configs"; +import { readLocalStorageCache } from "configs/local-storage-keys"; export const getLanguageCodeFromLocalStorage = () => { if (typeof window === "undefined") { return EN.locale; } - return localStorage.getItem(LOCAL_STORAGE_KEYS.language) ?? EN.locale; + return readLocalStorageCache("language") ?? EN.locale; }; export const translatedTextIncludesVariable = (translatedText: string) => { diff --git a/src/typescript/frontend/src/context/theme-context/index.tsx b/src/typescript/frontend/src/context/theme-context/index.tsx index 0f049a704..a083b2c42 100644 --- a/src/typescript/frontend/src/context/theme-context/index.tsx +++ b/src/typescript/frontend/src/context/theme-context/index.tsx @@ -6,7 +6,7 @@ import { type DefaultTheme } from "styled-components"; import dark from "theme/dark"; import light from "theme/light"; -import { LOCAL_STORAGE_KEYS } from "configs"; +import { readLocalStorageCache, writeLocalStorageCache } from "configs/local-storage-keys"; type ContextType = { theme: DefaultTheme; @@ -28,7 +28,7 @@ const ThemeContextProvider: React.FC> = ({ children }) => const [theme, setTheme] = useState(() => { const themeFromStorage = getThemeValueFromLS(); - localStorage.setItem(LOCAL_STORAGE_KEYS.theme, themeFromStorage); + writeLocalStorageCache("theme", themeFromStorage); return { theme: themeValues[themeFromStorage], key: themeFromStorage }; }); @@ -42,12 +42,12 @@ const ThemeContextProvider: React.FC> = ({ children }) => const themeFromStorage = getThemeValueFromLS(); const newValue = themeFromStorage === LIGHT ? DARK : LIGHT; - localStorage.setItem(LOCAL_STORAGE_KEYS.theme, newValue); + writeLocalStorageCache("theme", newValue); setTheme({ theme: themeValues[newValue], key: newValue }); } function getThemeValueFromLS() { - let themeFromStorage = localStorage.getItem(LOCAL_STORAGE_KEYS.theme) ?? LIGHT; + let themeFromStorage = readLocalStorageCache("theme") ?? LIGHT; if (!(themeFromStorage in themeValues)) { themeFromStorage = LIGHT; diff --git a/src/typescript/frontend/src/hooks/use-is-user-geoblocked.ts b/src/typescript/frontend/src/hooks/use-is-user-geoblocked.ts index c8882d86c..b887d1146 100644 --- a/src/typescript/frontend/src/hooks/use-is-user-geoblocked.ts +++ b/src/typescript/frontend/src/hooks/use-is-user-geoblocked.ts @@ -1,5 +1,6 @@ import { useQuery } from "@tanstack/react-query"; import { MS_IN_ONE_DAY } from "components/charts/const"; +import { readLocalStorageCache, writeLocalStorageCache } from "configs/local-storage-keys"; import { isUserGeoblocked } from "utils/geolocation"; const SEVEN_DAYS_MS = 7 * MS_IN_ONE_DAY; @@ -9,7 +10,16 @@ const useIsUserGeoblocked = (args?: { explicitlyGeoblocked: boolean }) => { const { explicitlyGeoblocked = false } = args ?? {}; const { data } = useQuery({ queryKey: ["geoblocked"], - queryFn: () => isUserGeoblocked(), + queryFn: async () => { + let geoblocked = readLocalStorageCache("geoblocking"); + + if (geoblocked === null) { + geoblocked = await isUserGeoblocked(); + writeLocalStorageCache("geoblocking", geoblocked); + } + + return geoblocked; + }, staleTime: SEVEN_DAYS_MS, placeholderData: (prev) => prev, }); diff --git a/src/typescript/frontend/src/utils/index.ts b/src/typescript/frontend/src/utils/index.ts index 4eb2eb686..869d3c68c 100644 --- a/src/typescript/frontend/src/utils/index.ts +++ b/src/typescript/frontend/src/utils/index.ts @@ -12,11 +12,12 @@ export { getStylesFromResponsiveValue } from "./styled-components-helpers"; export { isDisallowedEventKey } from "./check-is-disallowed-event-key"; export { getEmptyListTr } from "./get-empty-list-tr"; -export const stringifyJSON = (data: object) => - JSON.stringify(data, (_, value) => { +export function stringifyJSON(data: T) { + return JSON.stringify(data, (_, value) => { if (typeof value === "bigint") return value.toString() + "n"; return value; }); +} export const parseJSON = (json: string): T => JSON.parse(json, (_, value) => { From 1b74b72c240ea348c4b3fcd9866b8069634ff8f7 Mon Sep 17 00:00:00 2001 From: Bogdan Crisan Date: Tue, 26 Nov 2024 05:42:34 +0100 Subject: [PATCH 3/4] [ECO-2275] Focus emoji picker on input click (#351) --- .../src/components/emoji-picker/EmojiPickerWithInput.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/typescript/frontend/src/components/emoji-picker/EmojiPickerWithInput.tsx b/src/typescript/frontend/src/components/emoji-picker/EmojiPickerWithInput.tsx index 498e1803a..026c9a352 100644 --- a/src/typescript/frontend/src/components/emoji-picker/EmojiPickerWithInput.tsx +++ b/src/typescript/frontend/src/components/emoji-picker/EmojiPickerWithInput.tsx @@ -239,7 +239,15 @@ export const EmojiPickerWithInput = ({ e.stopPropagation(); }} onClick={() => { + const shadowRoot = document.querySelector("em-emoji-picker") + ?.shadowRoot as ShadowRoot; + const pickerInputElement = shadowRoot.querySelector( + "div.search input" + ) as HTMLInputElement; setPickerInvisible(false); + if (pickerInvisible) { + pickerInputElement.focus(); + } }} data-testid="emoji-input" style={{ fontFamily: EMOJI_FONT_FAMILY }} From 1c75fc9e6ab4f77e8e95c8259c078beb95756fd1 Mon Sep 17 00:00:00 2001 From: Bogdan Crisan Date: Tue, 26 Nov 2024 18:29:10 +0100 Subject: [PATCH 4/4] [ECO-2502] Use swap as default tab on mobile (#413) --- .../components/pages/emojicoin/components/mobile-grid/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typescript/frontend/src/components/pages/emojicoin/components/mobile-grid/index.tsx b/src/typescript/frontend/src/components/pages/emojicoin/components/mobile-grid/index.tsx index 372ad3513..7d2cf7c56 100644 --- a/src/typescript/frontend/src/components/pages/emojicoin/components/mobile-grid/index.tsx +++ b/src/typescript/frontend/src/components/pages/emojicoin/components/mobile-grid/index.tsx @@ -22,7 +22,7 @@ const DISPLAY_HEADER_ABOVE_CHART = false; const HEIGHT = DISPLAY_HEADER_ABOVE_CHART ? "min-h-[320px]" : "min-h-[365px]"; const MobileGrid = (props: GridProps) => { - const [tab, setTab] = useState(1); + const [tab, setTab] = useState(2); const { t } = translationFunction(); return (