Skip to content

Commit

Permalink
Fix flickering when opening profile (#777)
Browse files Browse the repository at this point in the history
  • Loading branch information
JunichiSugiura authored Sep 25, 2024
1 parent 3446f22 commit 340a71d
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 105 deletions.
13 changes: 3 additions & 10 deletions packages/controller/src/iframe/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ export class IFrame<CallSender extends {}> implements Modal {
colorMode,
onClose,
onConnect,
methods = {},
}: Pick<ControllerOptions, "theme" | "config" | "colorMode"> & {
id: string;
url: URL;
onClose?: () => void;
onConnect: (child: AsyncMethodReturns<CallSender>) => void;
methods?: { [key: string]: (...args: any[]) => void };
}) {
if (typeof document === "undefined") {
return;
Expand Down Expand Up @@ -81,7 +83,7 @@ export class IFrame<CallSender extends {}> implements Modal {

connectToChild<CallSender>({
iframe: this.iframe,
methods: { close: () => this.close() },
methods: { close: () => this.close(), ...methods },
}).promise.then(onConnect);

this.resize();
Expand All @@ -99,15 +101,6 @@ export class IFrame<CallSender extends {}> implements Modal {
this.onClose = onClose;
}

currentUrl() {
return this.iframe ? new URL(this.iframe.src) : undefined;
}

updateUrl(url: URL) {
if (!this.iframe) return;
this.iframe.src = url.toString();
}

open() {
if (!this.container) return;
document.body.style.overflow = "hidden";
Expand Down
12 changes: 1 addition & 11 deletions packages/controller/src/iframe/profile.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PROFILE_URL } from "../constants";
import { Profile, ProfileOptions, ProfileContextTypeVariant } from "../types";
import { Profile, ProfileOptions } from "../types";
import { IFrame, IFrameOptions } from "./base";

export type ProfileIFrameOptions = IFrameOptions<Profile> &
Expand Down Expand Up @@ -28,14 +28,4 @@ export class ProfileIFrame extends IFrame<Profile> {
url: _url,
});
}

openTab(tab: ProfileContextTypeVariant) {
const url = super.currentUrl();
if (!url) return;

url.searchParams.set("tab", tab);

super.updateUrl(url);
super.open();
}
}
3 changes: 2 additions & 1 deletion packages/controller/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ export default class Controller {
return;
}

this.iframes.profile.openTab(tab);
this.profile.goTo(tab);
this.iframes.profile.open();
}

async disconnect() {
Expand Down
4 changes: 3 additions & 1 deletion packages/controller/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ export interface Keychain {
delegateAccount(): string;
}

export interface Profile {}
export interface Profile {
goTo(tab: ProfileContextTypeVariant): void;
}

export interface Modal {
open: () => void;
Expand Down
2 changes: 1 addition & 1 deletion packages/keychain/src/hooks/policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { constants } from "starknet";
import { Policy } from "@cartridge/controller";

import { diff } from "utils/controller";
import { normalize as normalizeOrigin } from "utils/url";
import { normalizeOrigin } from "@cartridge/utils";

export function useUrlPolicys(): {
chainId?: constants.StarknetChainId;
Expand Down
8 changes: 1 addition & 7 deletions packages/keychain/src/utils/connection/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ export * from "./types";

import { ConnectError, Policy, ResponseCodes } from "@cartridge/controller";
import { connectToParent } from "@cartridge/penpal";
import { normalize as normalizeOrigin } from "utils/url";
import Controller from "utils/controller";
import { connectFactory, disconnectFactory } from "./connect";
import { execute } from "./execute";
Expand All @@ -15,6 +14,7 @@ import { ConnectionCtx } from "./types";
import { deployFactory } from "./deploy";
import { openSettingsFactory } from "./settings";
import { delegateAccount } from "./delegate";
import { normalize } from "@cartridge/utils";

export function connectToController<ParentMethods extends {}>({
setOrigin,
Expand Down Expand Up @@ -56,12 +56,6 @@ export function connectToController<ParentMethods extends {}>({
});
}

function normalize<Promise>(
fn: (origin: string) => Promise,
): (origin: string) => Promise {
return (origin: string) => fn(normalizeOrigin(origin));
}

function validate<T>(
fn: (controller: Controller, origin: string) => T,
): (origin: string) => T | (() => Promise<ConnectError>) {
Expand Down
52 changes: 0 additions & 52 deletions packages/keychain/src/utils/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,58 +20,6 @@ export const StarkscanUrl = (chainId: constants.StarknetChainId) => ({
`${BASE_URL[chainId]}/class/${address}${fragment ? `#${fragment}` : ""}`,
});

const DEFAULT_PORT_BY_PROTOCOL: { [index: string]: string } = {
"http:": "80",
"https:": "443",
};

const URL_REGEX = /^(https?:)?\/\/([^/:]+)?(:(\d+))?/;

const opaqueOriginSchemes = ["file:", "data:"];

/**
* Converts a src value into an origin.
*/
export const normalize = (src: string): string => {
if (src && opaqueOriginSchemes.find((scheme) => src.startsWith(scheme))) {
// The origin of the child document is an opaque origin and its
// serialization is "null"
// https://html.spec.whatwg.org/multipage/origin.html#origin
return "null";
}

// Note that if src is undefined, then srcdoc is being used instead of src
// and we can follow this same logic below to get the origin of the parent,
// which is the origin that we will need to use.

const location = document.location;

const regexResult = URL_REGEX.exec(src);
let protocol: string;
let hostname: string;
let port: string;

if (regexResult) {
// It's an absolute URL. Use the parsed info.
// regexResult[1] will be undefined if the URL starts with //
protocol = regexResult[1] ? regexResult[1] : location.protocol;
hostname = regexResult[2];
port = regexResult[4];
} else {
// It's a relative path. Use the current location's info.
protocol = location.protocol;
hostname = location.hostname;
port = location.port;
}

// If the port is the default for the protocol, we don't want to add it to the origin string
// or it won't match the message's event.origin.

const portSuffix =
port && port !== DEFAULT_PORT_BY_PROTOCOL[protocol] ? `:${port}` : "";
return `${protocol}//${hostname}${portSuffix}`;
};

export const PopupCenter = (
url: string,
title: string,
Expand Down
7 changes: 3 additions & 4 deletions packages/profile/src/components/inventory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export function Inventory() {
<CardHeader>
<CardTitle>Token</CardTitle>
</CardHeader>
{ERC_20_TOKENS.map((t) => (
{ERC_20_TOKENS.map((t, i) => (
<CardContent
key={t.address}
key={t.address + i}
className="flex gap-x-1.5 items-center"
>
<img src={t.logoUrl} className="w-5 h-5" />
Expand All @@ -49,9 +49,8 @@ export function Inventory() {

<CardContent className="grid grid-cols-3 gap-2 place-items-center">
{Array.from({ length: 28 }).map((_, i) => (
<div className="w-32 h-32">
<div className="w-32 h-32" key={i}>
<img
key={i}
src={
"https://github.com/BibliothecaDAO/loot-survivor/blob/main/ui/public/golden-token.png?raw=true"
}
Expand Down
36 changes: 18 additions & 18 deletions packages/profile/src/components/provider/connection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "react";
import { useQueryParams } from "./hooks";
import { ProfileContextTypeVariant } from "@cartridge/controller";
import { normalize } from "@cartridge/utils";

type ConnectionContextType = {
parent: ParentMethods;
Expand Down Expand Up @@ -42,30 +43,12 @@ export const ConnectionContext =
export function ConnectionProvider({ children }: { children: ReactNode }) {
const [state, setState] = useState<ConnectionContextType>(initialState);

useEffect(() => {
const connection = connectToParent<ParentMethods>({
// methods: {}
});
connection.promise.then((parent) => {
setState((state) => ({ ...state, parent }));
});

return () => {
connection.destroy();
};
}, []);

const searchParams = useQueryParams();
useEffect(() => {
setState((state) => ({
...state,
address: decodeURIComponent(searchParams.get("address")!),
username: decodeURIComponent(searchParams.get("username")!),
context: {
type:
(searchParams.get("tab") as ProfileContextTypeVariant) ??
state.context,
},
}));
}, [searchParams]);

Expand All @@ -76,6 +59,23 @@ export function ConnectionProvider({ children }: { children: ReactNode }) {
}));
}, []);

useEffect(() => {
const connection = connectToParent<ParentMethods>({
methods: {
goTo: normalize(() => (tab: ProfileContextTypeVariant) => {
setContext({ type: tab });
}),
},
});
connection.promise.then((parent) => {
setState((state) => ({ ...state, parent }));
});

return () => {
connection.destroy();
};
}, [setContext]);

return (
<ConnectionContext.Provider value={{ ...state, setContext }}>
{children}
Expand Down
58 changes: 58 additions & 0 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,61 @@ function sizeLen(size: FormatAddressSize) {
return 20;
}
}

export function normalize<Promise>(
fn: (origin: string) => Promise,
): (origin: string) => Promise {
return (origin: string) => fn(normalizeOrigin(origin));
}

const DEFAULT_PORT_BY_PROTOCOL: { [index: string]: string } = {
"http:": "80",
"https:": "443",
};

const URL_REGEX = /^(https?:)?\/\/([^/:]+)?(:(\d+))?/;

const opaqueOriginSchemes = ["file:", "data:"];

/**
* Converts a src value into an origin.
*/
export function normalizeOrigin(src: string): string {
if (src && opaqueOriginSchemes.find((scheme) => src.startsWith(scheme))) {
// The origin of the child document is an opaque origin and its
// serialization is "null"
// https://html.spec.whatwg.org/multipage/origin.html#origin
return "null";
}

// Note that if src is undefined, then srcdoc is being used instead of src
// and we can follow this same logic below to get the origin of the parent,
// which is the origin that we will need to use.

const location = document.location;

const regexResult = URL_REGEX.exec(src);
let protocol: string;
let hostname: string;
let port: string;

if (regexResult) {
// It's an absolute URL. Use the parsed info.
// regexResult[1] will be undefined if the URL starts with //
protocol = regexResult[1] ? regexResult[1] : location.protocol;
hostname = regexResult[2];
port = regexResult[4];
} else {
// It's a relative path. Use the current location's info.
protocol = location.protocol;
hostname = location.hostname;
port = location.port;
}

// If the port is the default for the protocol, we don't want to add it to the origin string
// or it won't match the message's event.origin.

const portSuffix =
port && port !== DEFAULT_PORT_BY_PROTOCOL[protocol] ? `:${port}` : "";
return `${protocol}//${hostname}${portSuffix}`;
}

0 comments on commit 340a71d

Please sign in to comment.