Skip to content

Commit 30d13d6

Browse files
authored
Merge pull request #217 from Automattic/instanceId
Correctly remember last url when multiple blocks are on the same page
2 parents a511cc0 + d417228 commit 30d13d6

File tree

14 files changed

+118
-44
lines changed

14 files changed

+118
-44
lines changed

frontend/block/block.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
"icon": "format-chat",
99
"description": "Matrix client",
1010
"attributes": {
11+
"instanceId": {
12+
"type": "string",
13+
"default": ""
14+
},
1115
"defaultHomeserver": {
1216
"type": "string",
1317
"default": ""

frontend/block/edit.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useBlockProps } from "@wordpress/block-editor";
22
import { ResizableBox } from "@wordpress/components";
3-
import { WPElement } from '@wordpress/element';
3+
import { useEffect, WPElement } from '@wordpress/element';
44
import { getIframeUrl } from "../app";
55
import { Block, BlockProps, parseAttributes } from "../components/block";
66
import './editor.scss';
@@ -11,13 +11,30 @@ interface Props {
1111
setAttributes: Function,
1212
}
1313

14+
let instanceIds: string[] = [];
15+
1416
export default function Edit(props: Props): WPElement {
1517
const { attributes, setAttributes } = props;
16-
const parsedAttributes = parseAttributes(attributes);
1718

19+
// Set instanceId if it's not set, or if it already exists on the page (which happens when the block is duplicated).
20+
useEffect(() => {
21+
// @ts-ignore
22+
let { instanceId } = attributes;
23+
const hasInstanceId = instanceId && instanceId !== "";
24+
const isInstanceIdDuplicated = hasInstanceId && instanceIds.includes(instanceId);
25+
26+
if (!hasInstanceId || isInstanceIdDuplicated) {
27+
instanceId = (Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)).toString();
28+
setAttributes({ instanceId });
29+
}
30+
31+
instanceIds.push(instanceId);
32+
}, []);
33+
34+
const parsedAttributes = parseAttributes(attributes);
1835
const blockProps: BlockProps = {
1936
focusable: true,
20-
attributes,
37+
attributes: parsedAttributes,
2138
iframeUrl: getIframeUrl(),
2239
};
2340

frontend/block/view.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { BlockProps, renderBlock } from "../app";
2+
import { parseAttributes } from "../components/block";
23

34
window.addEventListener('DOMContentLoaded', () => {
45
renderAllBlocks().catch(error => {
@@ -13,10 +14,16 @@ async function renderAllBlocks() {
1314
const containers = <HTMLCollectionOf<HTMLElement>>document.getElementsByClassName('wp-block-automattic-chatrix');
1415
for (const container of containers) {
1516
const config = getConfigFromDataAttribute(container);
17+
const attributes = parseAttributes(config.attributes);
1618
const props: BlockProps = {
17-
attributes: config.attributes,
19+
attributes: attributes,
1820
};
1921

22+
if (!attributes.instanceId) {
23+
// In earlier versions of the block, there was no instanceId attribute, so it can be undefined.
24+
console.warn('Chatrix block is missing the instanceId attribute. Re-save the page to fix this.');
25+
}
26+
2027
renderBlock(container, props);
2128
}
2229
}

frontend/components/block/attributes.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Unit, ValueWithUnit } from "./unit";
22

3-
interface Attributes {
3+
export interface BlockAttributes {
4+
instanceId: string,
45
defaultHomeserver?: string,
56
roomId?: string,
67
height?: Height,
@@ -10,15 +11,27 @@ interface Attributes {
1011
borderColor?: string,
1112
}
1213

13-
export function parseAttributes(attributes): Attributes {
14+
export function parseAttributes(attributes): BlockAttributes {
15+
const {
16+
instanceId,
17+
defaultHomeserver,
18+
roomId,
19+
height,
20+
borderWidth,
21+
borderRadius,
22+
borderStyle,
23+
borderColor,
24+
} = attributes;
25+
1426
return {
15-
defaultHomeserver: attributes.defaultHomeserver ?? '',
16-
roomId: attributes.roomId ?? '',
17-
height: attributes.height ? new Height(attributes.height.value, attributes.height.unit) : undefined,
18-
borderWidth: attributes.borderWidth ? new BorderWidth(attributes.borderWidth.value, attributes.borderWidth.unit) : undefined,
19-
borderRadius: attributes.borderRadius ? new BorderRadius(attributes.borderRadius.value, attributes.borderRadius.unit) : undefined,
20-
borderStyle: attributes.borderStyle,
21-
borderColor: attributes.borderColor,
27+
instanceId,
28+
defaultHomeserver: defaultHomeserver ?? '',
29+
roomId: roomId ?? '',
30+
height: height ? new Height(height.value, height.unit) : undefined,
31+
borderWidth: borderWidth ? new BorderWidth(borderWidth.value, borderWidth.unit) : undefined,
32+
borderRadius: borderRadius ? new BorderRadius(borderRadius.value, borderRadius.unit) : undefined,
33+
borderStyle,
34+
borderColor,
2235
};
2336
}
2437

frontend/components/block/block.tsx

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,39 @@
11
import { Chat, ChatProps } from "../chat";
2-
import { parseAttributes } from "./attributes";
2+
import { BlockAttributes } from "./attributes";
33

44
export interface BlockProps {
55
focusable?: boolean,
6-
attributes: object,
6+
attributes: BlockAttributes,
77
iframeUrl: URL,
88
}
99

1010
export function Block(props: BlockProps) {
11-
const attributes = parseAttributes(props.attributes);
11+
const { focusable, iframeUrl } = props;
12+
const {
13+
instanceId,
14+
defaultHomeserver,
15+
roomId,
16+
height,
17+
borderWidth,
18+
borderRadius,
19+
borderStyle,
20+
borderColor,
21+
} = props.attributes;
1222

1323
const style = {
14-
height: attributes.height ? attributes.height.toString() : '',
15-
borderWidth: attributes.borderWidth ? attributes.borderWidth.toString() : 0,
16-
borderRadius: attributes.borderRadius ? attributes.borderRadius.toString() : "",
17-
borderStyle: attributes.borderStyle ?? '',
18-
borderColor: attributes.borderColor ?? '',
24+
height: height ? height.toString() : '',
25+
borderWidth: borderWidth ? borderWidth.toString() : 0,
26+
borderRadius: borderRadius ? borderRadius.toString() : "",
27+
borderStyle: borderStyle ?? '',
28+
borderColor: borderColor ?? '',
1929
};
2030

2131
const chatProps: ChatProps = {
22-
focusable: props.focusable,
23-
iframeUrl: props.iframeUrl,
24-
defaultHomeserver: attributes.defaultHomeserver,
25-
roomId: attributes.roomId,
32+
focusable,
33+
iframeUrl,
34+
instanceId,
35+
defaultHomeserver,
36+
roomId,
2637
};
2738

2839
return (

frontend/components/chat/chat.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,33 @@ import { IframeUrl } from "./url";
44
export interface ChatProps {
55
focusable?: boolean,
66
iframeUrl: URL,
7+
instanceId: string,
78
defaultHomeserver?: string,
89
roomId?: string,
910
}
1011

1112
export function Chat(props: ChatProps) {
12-
const ref = props.focusable ? useFocusableIframe() : undefined;
13+
const {
14+
focusable,
15+
iframeUrl,
16+
instanceId,
17+
defaultHomeserver,
18+
roomId
19+
} = props;
1320

14-
const iframeUrl = new IframeUrl(props.iframeUrl, {
15-
defaultHomeserver: props.defaultHomeserver,
16-
roomId: props.roomId,
21+
const ref = focusable ? useFocusableIframe() : undefined;
22+
const url = new IframeUrl(iframeUrl, {
23+
instanceId,
24+
defaultHomeserver,
25+
roomId,
1726
});
1827

1928
return (
2029
<div className={"chatrix-component-chat"}>
2130
<iframe
2231
// @ts-ignore
2332
ref={ref}
24-
src={iframeUrl.toString()}
33+
src={url.toString()}
2534
></iframe>
2635
</div>
2736
);

frontend/components/chat/url.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const LOGIN_TOKEN_PARAM_NAME = "loginToken";
22

33
type IframeParams = {
4+
instanceId: string,
45
defaultHomeserver?: string
56
roomId?: string,
67
}

frontend/iframe/config/ConfigFactory.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export class ConfigFactory {
1212
};
1313

1414
return {
15+
instanceId: getQueryParam("instanceId") ?? "",
1516
defaultHomeserver: getQueryParam("defaultHomeserver") ?? "",
1617
roomId: getQueryParam("roomId") ?? "",
1718
themeManifests: [

frontend/iframe/config/IConfig.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
export interface IConfig {
2+
/**
3+
* Unique identifier for this instance of the client.
4+
* When multiple clients are embedded on the same page, this is used to distinguish them.
5+
*/
6+
instanceId: string;
7+
28
/**
39
* The default homeserver used by Hydrogen; autofilled in the login UI.
410
* eg: https://matrix.org

frontend/iframe/platform/History.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { History as BaseHistory } from "hydrogen-web/src/platform/web/dom/History";
22

33
export class History extends BaseHistory {
4+
private readonly _instanceId: string;
45
private _lastSessionHash: string | null | undefined;
56

6-
constructor() {
7+
constructor(instanceId: string) {
78
super();
9+
this._instanceId = instanceId;
810
}
911

1012
get() {
@@ -21,16 +23,20 @@ export class History extends BaseHistory {
2123
}
2224

2325
onSubscribeFirst() {
24-
this._lastSessionHash = window.localStorage?.getItem("chatrix_last_url_hash");
26+
this._lastSessionHash = window.localStorage?.getItem(this.urlHashKey);
2527
// @ts-ignore
2628
window.addEventListener('hashchange', this);
2729
}
2830

2931
_storeHash(hash) {
30-
window.localStorage?.setItem("chatrix_last_url_hash", hash);
32+
window.localStorage?.setItem(this.urlHashKey, hash);
3133
}
3234

3335
replaceUrlSilently(url) {
3436
super.replaceUrlSilently(url);
3537
}
38+
39+
private get urlHashKey(): string {
40+
return `chatrix_${this._instanceId}_last_url_hash`;
41+
}
3642
}

0 commit comments

Comments
 (0)