mirror of
https://github.com/wassname/talk.git
synced 2026-08-05 13:30:26 +08:00
Stream should work outside of iframe for debugging
This commit is contained in:
@@ -9,7 +9,7 @@ import { Environment } from "relay-runtime";
|
||||
import { BrowserInfo } from "talk-framework/lib/browserInfo";
|
||||
import { PostMessageService } from "talk-framework/lib/postMessage";
|
||||
import { RestClient } from "talk-framework/lib/rest";
|
||||
import { PymStorage } from "talk-framework/lib/storage";
|
||||
import { PromisifiedStorage } from "talk-framework/lib/storage";
|
||||
import { UIContext } from "talk-ui/components";
|
||||
import { ClickFarAwayRegister } from "talk-ui/components/ClickOutside";
|
||||
|
||||
@@ -30,10 +30,10 @@ export interface TalkContext {
|
||||
sessionStorage: Storage;
|
||||
|
||||
/** Session Storage over pym */
|
||||
pymLocalStorage?: PymStorage;
|
||||
pymLocalStorage?: PromisifiedStorage;
|
||||
|
||||
/** Session storage over pym */
|
||||
pymSessionStorage?: PymStorage;
|
||||
pymSessionStorage?: PromisifiedStorage;
|
||||
|
||||
/** media query values for testing purposes */
|
||||
mediaQueryValues?: MediaQueryMatchers;
|
||||
|
||||
@@ -9,6 +9,7 @@ import { getBrowserInfo } from "talk-framework/lib/browserInfo";
|
||||
import { LOCAL_ID } from "talk-framework/lib/relay";
|
||||
import {
|
||||
createLocalStorage,
|
||||
createPromisifiedStorage,
|
||||
createPymStorage,
|
||||
createSessionStorage,
|
||||
} from "talk-framework/lib/storage";
|
||||
@@ -57,6 +58,14 @@ export const timeagoFormatter: Formatter = (value, unit, suffix) => {
|
||||
);
|
||||
};
|
||||
|
||||
function areWeInIframe() {
|
||||
try {
|
||||
return window.self !== window.top;
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `createContext` manages the dependencies of our framework
|
||||
* and returns a `TalkContext` that can be passed to the
|
||||
@@ -69,6 +78,7 @@ export default async function createContext({
|
||||
pym,
|
||||
eventEmitter = new EventEmitter2({ wildcard: true }),
|
||||
}: CreateContextArguments): Promise<TalkContext> {
|
||||
const inIframe = areWeInIframe();
|
||||
// Initialize Relay.
|
||||
const source = new RecordSource();
|
||||
const tokenGetter: TokenGetter = () => {
|
||||
@@ -120,8 +130,12 @@ export default async function createContext({
|
||||
postMessage: new PostMessageService(),
|
||||
localStorage: createLocalStorage(),
|
||||
sessionStorage: createSessionStorage(),
|
||||
pymLocalStorage: pym && createPymStorage(pym, "localStorage"),
|
||||
pymSessionStorage: pym && createPymStorage(pym, "sessionStorage"),
|
||||
pymLocalStorage:
|
||||
(pym && (inIframe && createPymStorage(pym, "localStorage"))) ||
|
||||
createPromisifiedStorage(createLocalStorage("talkPym")),
|
||||
pymSessionStorage:
|
||||
(pym && (inIframe && createPymStorage(pym, "sessionStorage"))) ||
|
||||
createPromisifiedStorage(createSessionStorage("talkPym")),
|
||||
browserInfo: getBrowserInfo(),
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ it("should set and unset values", () => {
|
||||
storage.setItem("test", "value");
|
||||
expect(storage.getItem("test")).toBe("value");
|
||||
storage.removeItem("test");
|
||||
expect(storage.getItem("test")).toBeUndefined();
|
||||
expect(storage.getItem("test")).toBeNull();
|
||||
});
|
||||
|
||||
it("should return length", () => {
|
||||
|
||||
@@ -28,7 +28,7 @@ class InMemoryStorage implements Storage {
|
||||
}
|
||||
|
||||
public getItem(key: string) {
|
||||
return this.storage[key];
|
||||
return this.storage[key] || null;
|
||||
}
|
||||
|
||||
public setItem(key: string, value: string) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import prefixStorage from "./prefixStorage";
|
||||
|
||||
export default function createLocalStorage(): Storage {
|
||||
return prefixStorage(window.localStorage, "talk");
|
||||
export default function createLocalStorage(prefix = "talk"): Storage {
|
||||
return prefixStorage(window.localStorage, prefix);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import createInMemoryStorage from "./InMemoryStorage";
|
||||
import createPromisifiedStorage from "./PromisifiedStorage";
|
||||
|
||||
it("should set and unset values", () => {
|
||||
const storage = createPromisifiedStorage(createInMemoryStorage());
|
||||
expect(storage.setItem("test", "value")).resolves.toBeUndefined();
|
||||
expect(storage.getItem("test")).resolves.toBe("value");
|
||||
storage.removeItem("test");
|
||||
expect(storage.getItem("test")).resolves.toBeUndefined();
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
export interface PromisifiedStorage {
|
||||
/**
|
||||
* value = storage[key]
|
||||
*/
|
||||
getItem(key: string): Promise<string | null>;
|
||||
/**
|
||||
* delete storage[key]
|
||||
*/
|
||||
removeItem(key: string): Promise<void>;
|
||||
/**
|
||||
* storage[key] = value
|
||||
*/
|
||||
setItem(key: string, value: string): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* BackedPromisifedStorage.
|
||||
*/
|
||||
class BackedPromisifedStorage implements PromisifiedStorage {
|
||||
private storage: Storage;
|
||||
|
||||
constructor(storage: Storage) {
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
public getItem(key: string) {
|
||||
return Promise.resolve(this.storage.getItem(key));
|
||||
}
|
||||
|
||||
public setItem(key: string, value: string) {
|
||||
return Promise.resolve(this.storage.setItem(key, value));
|
||||
}
|
||||
|
||||
public removeItem(key: string) {
|
||||
return Promise.resolve(this.storage.removeItem(key));
|
||||
}
|
||||
}
|
||||
|
||||
export default function createPromisifiedStorage(storage: Storage) {
|
||||
return new BackedPromisifedStorage(storage);
|
||||
}
|
||||
@@ -1,20 +1,7 @@
|
||||
import { Child, Parent } from "pym.js";
|
||||
import uuid from "uuid/v4";
|
||||
|
||||
export interface PymStorage {
|
||||
/**
|
||||
* value = storage[key]
|
||||
*/
|
||||
getItem(key: string): Promise<string | null>;
|
||||
/**
|
||||
* delete storage[key]
|
||||
*/
|
||||
removeItem(key: string): Promise<void>;
|
||||
/**
|
||||
* storage[key] = value
|
||||
*/
|
||||
setItem(key: string, value: string): Promise<void>;
|
||||
}
|
||||
import { PromisifiedStorage } from "./PromisifiedStorage";
|
||||
|
||||
type Pym = Child | Parent;
|
||||
|
||||
@@ -27,7 +14,7 @@ type Pym = Child | Parent;
|
||||
export default function createPymStorage(
|
||||
pym: Pym,
|
||||
type: "localStorage" | "sessionStorage"
|
||||
): PymStorage {
|
||||
): PromisifiedStorage {
|
||||
// A Map of requestID => {resolve, reject}
|
||||
const requests: Record<
|
||||
string,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import prefixStorage from "./prefixStorage";
|
||||
|
||||
export default function createSessionStorage(): Storage {
|
||||
return prefixStorage(window.sessionStorage, "talk");
|
||||
export default function createSessionStorage(prefix = "talk"): Storage {
|
||||
return prefixStorage(window.sessionStorage, prefix);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
export { default as createInMemoryStorage } from "./InMemoryStorage";
|
||||
export { default as createLocalStorage } from "./LocalStorage";
|
||||
export { default as createSessionStorage } from "./SessionStorage";
|
||||
export { default as createPymStorage, PymStorage } from "./PymStorage";
|
||||
export { default as createPymStorage } from "./PymStorage";
|
||||
export {
|
||||
default as createPromisifiedStorage,
|
||||
PromisifiedStorage,
|
||||
} from "./PromisifiedStorage";
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { commitLocalUpdate, Environment, RecordSource } from "relay-runtime";
|
||||
import { Environment, RecordSource } from "relay-runtime";
|
||||
|
||||
import { timeout } from "talk-common/utils";
|
||||
import { LOCAL_ID } from "talk-framework/lib/relay";
|
||||
import { createInMemoryStorage } from "talk-framework/lib/storage";
|
||||
import { createRelayEnvironment } from "talk-framework/testHelpers";
|
||||
@@ -16,7 +15,7 @@ beforeAll(() => {
|
||||
});
|
||||
});
|
||||
|
||||
it("Sets auth token", async () => {
|
||||
it("Sets auth token to localStorage", () => {
|
||||
const context = {
|
||||
localStorage: createInMemoryStorage(),
|
||||
};
|
||||
@@ -26,26 +25,32 @@ it("Sets auth token", async () => {
|
||||
expect(context.localStorage.getItem("authToken")).toEqual(authToken);
|
||||
});
|
||||
|
||||
it("Removes auth token from localStorage", async () => {
|
||||
it("Removes auth token from localStorage", () => {
|
||||
const context = {
|
||||
localStorage: createInMemoryStorage(),
|
||||
};
|
||||
localStorage.setItem("authToken", "tmp");
|
||||
commit(environment, { authToken: null }, context as any);
|
||||
expect(context.localStorage.getItem("authToken")).toBeUndefined();
|
||||
expect(context.localStorage.getItem("authToken")).toBeNull();
|
||||
});
|
||||
|
||||
it("Should call gc", async () => {
|
||||
it("Sets auth token to pymLocalStorage", async () => {
|
||||
const context = {
|
||||
pymLocalStorage: createInMemoryStorage(),
|
||||
localStorage: createInMemoryStorage(),
|
||||
};
|
||||
commitLocalUpdate(environment, store => {
|
||||
store.create("should-disappear", "tmp");
|
||||
});
|
||||
const authToken = null;
|
||||
expect(source.get("should-disappear")).not.toBeUndefined();
|
||||
const authToken = "auth token";
|
||||
commit(environment, { authToken }, context as any);
|
||||
await timeout();
|
||||
expect(source.get(LOCAL_ID)!.authToken).toEqual(authToken);
|
||||
expect(source.get("should-disappear")).toBeUndefined();
|
||||
expect(await context.pymLocalStorage.getItem("authToken")).toEqual(authToken);
|
||||
});
|
||||
|
||||
it("Removes auth token from pymLocalStorage", async () => {
|
||||
const context = {
|
||||
pymLocalStorage: createInMemoryStorage(),
|
||||
localStorage: createInMemoryStorage(),
|
||||
};
|
||||
localStorage.setItem("authToken", "tmp");
|
||||
commit(environment, { authToken: null }, context as any);
|
||||
expect(await context.pymLocalStorage.getItem("authToken")).toBeNull();
|
||||
});
|
||||
|
||||
@@ -13,15 +13,16 @@ export type SetAuthTokenMutation = (input: SetAuthTokenInput) => Promise<void>;
|
||||
export async function commit(
|
||||
environment: Environment,
|
||||
input: SetAuthTokenInput,
|
||||
{ localStorage }: TalkContext
|
||||
{ localStorage, pymLocalStorage }: TalkContext
|
||||
) {
|
||||
return commitLocalUpdate(environment, store => {
|
||||
const record = store.get(LOCAL_ID)!;
|
||||
record.setValue(input.authToken, "authToken");
|
||||
const storage = pymLocalStorage || localStorage;
|
||||
if (input.authToken) {
|
||||
localStorage.setItem("authToken", input.authToken);
|
||||
storage.setItem("authToken", input.authToken);
|
||||
} else {
|
||||
localStorage.removeItem("authToken");
|
||||
storage.removeItem("authToken");
|
||||
}
|
||||
// Increment auth revision to indicate a change in auth state.
|
||||
record.setValue(record.getValue("authRevision") + 1, "authRevision");
|
||||
|
||||
@@ -1,21 +1,8 @@
|
||||
import { PymStorage } from "talk-framework/lib/storage";
|
||||
|
||||
export class FakeStorage implements PymStorage {
|
||||
public store: Record<string, string> = {};
|
||||
|
||||
public setItem(key: string, value: string) {
|
||||
this.store[key] = value;
|
||||
return Promise.resolve();
|
||||
}
|
||||
public removeItem(key: string) {
|
||||
delete this.store[key];
|
||||
return Promise.resolve();
|
||||
}
|
||||
public getItem(key: string) {
|
||||
return Promise.resolve(this.store[key] || null);
|
||||
}
|
||||
}
|
||||
import {
|
||||
createInMemoryStorage,
|
||||
createPromisifiedStorage,
|
||||
} from "talk-framework/lib/storage";
|
||||
|
||||
export default function createFakePymStorage() {
|
||||
return new FakeStorage();
|
||||
return createPromisifiedStorage(createInMemoryStorage());
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { Component } from "react";
|
||||
|
||||
import { withContext } from "talk-framework/lib/bootstrap";
|
||||
import { BadUserInputError } from "talk-framework/lib/errors";
|
||||
import { PymStorage } from "talk-framework/lib/storage";
|
||||
import { PromisifiedStorage } from "talk-framework/lib/storage";
|
||||
import { PropTypesOf } from "talk-framework/types";
|
||||
|
||||
import PostCommentForm, {
|
||||
@@ -13,7 +13,7 @@ import { CreateCommentMutation, withCreateCommentMutation } from "../mutations";
|
||||
interface InnerProps {
|
||||
createComment: CreateCommentMutation;
|
||||
assetID: string;
|
||||
pymSessionStorage: PymStorage;
|
||||
pymSessionStorage: PromisifiedStorage;
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { graphql } from "react-relay";
|
||||
import { withContext } from "talk-framework/lib/bootstrap";
|
||||
import { BadUserInputError } from "talk-framework/lib/errors";
|
||||
import { withFragmentContainer } from "talk-framework/lib/relay";
|
||||
import { PymStorage } from "talk-framework/lib/storage";
|
||||
import { PromisifiedStorage } from "talk-framework/lib/storage";
|
||||
import { PropTypesOf } from "talk-framework/types";
|
||||
import { ReplyCommentFormContainer_asset as AssetData } from "talk-stream/__generated__/ReplyCommentFormContainer_asset.graphql";
|
||||
import { ReplyCommentFormContainer_comment as CommentData } from "talk-stream/__generated__/ReplyCommentFormContainer_comment.graphql";
|
||||
@@ -17,7 +17,7 @@ import { CreateCommentMutation, withCreateCommentMutation } from "../mutations";
|
||||
|
||||
interface InnerProps {
|
||||
createComment: CreateCommentMutation;
|
||||
pymSessionStorage: PymStorage;
|
||||
pymSessionStorage: PromisifiedStorage;
|
||||
comment: CommentData;
|
||||
asset: AssetData;
|
||||
onClose?: () => void;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { Environment, RecordSource } from "relay-runtime";
|
||||
|
||||
import { timeout } from "talk-common/utils";
|
||||
import { TalkContext } from "talk-framework/lib/bootstrap";
|
||||
import { LOCAL_ID } from "talk-framework/lib/relay";
|
||||
import { createInMemoryStorage } from "talk-framework/lib/storage";
|
||||
import {
|
||||
createInMemoryStorage,
|
||||
createPromisifiedStorage,
|
||||
} from "talk-framework/lib/storage";
|
||||
import { createRelayEnvironment } from "talk-framework/testHelpers";
|
||||
|
||||
import initLocalState from "./initLocalState";
|
||||
@@ -19,14 +23,18 @@ beforeEach(() => {
|
||||
});
|
||||
|
||||
it("init local state", async () => {
|
||||
await initLocalState(environment, {
|
||||
localStorage: createInMemoryStorage(),
|
||||
} as any);
|
||||
const context: Partial<TalkContext> = {
|
||||
pymLocalStorage: createPromisifiedStorage(createInMemoryStorage()),
|
||||
};
|
||||
await initLocalState(environment, context as any);
|
||||
await timeout();
|
||||
expect(JSON.stringify(source.toJSON(), null, 2)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("set assetID from query", async () => {
|
||||
const context: Partial<TalkContext> = {
|
||||
pymLocalStorage: createPromisifiedStorage(createInMemoryStorage()),
|
||||
};
|
||||
const assetID = "asset-id";
|
||||
const previousLocation = location.toString();
|
||||
const previousState = window.history.state;
|
||||
@@ -35,14 +43,15 @@ it("set assetID from query", async () => {
|
||||
document.title,
|
||||
`http://localhost/?assetID=${assetID}`
|
||||
);
|
||||
await initLocalState(environment, {
|
||||
localStorage: createInMemoryStorage(),
|
||||
} as any);
|
||||
await initLocalState(environment, context as any);
|
||||
expect(source.get(LOCAL_ID)!.assetID).toBe(assetID);
|
||||
window.history.replaceState(previousState, document.title, previousLocation);
|
||||
});
|
||||
|
||||
it("set commentID from query", async () => {
|
||||
const context: Partial<TalkContext> = {
|
||||
pymLocalStorage: createPromisifiedStorage(createInMemoryStorage()),
|
||||
};
|
||||
const commentID = "comment-id";
|
||||
const previousLocation = location.toString();
|
||||
const previousState = window.history.state;
|
||||
@@ -51,18 +60,17 @@ it("set commentID from query", async () => {
|
||||
document.title,
|
||||
`http://localhost/?commentID=${commentID}`
|
||||
);
|
||||
await initLocalState(environment, {
|
||||
localStorage: createInMemoryStorage(),
|
||||
} as any);
|
||||
await initLocalState(environment, context as any);
|
||||
expect(source.get(LOCAL_ID)!.commentID).toBe(commentID);
|
||||
window.history.replaceState(previousState, document.title, previousLocation);
|
||||
});
|
||||
|
||||
it("set authToken from localStorage", async () => {
|
||||
const context: Partial<TalkContext> = {
|
||||
pymLocalStorage: createPromisifiedStorage(createInMemoryStorage()),
|
||||
};
|
||||
const authToken = "auth-token";
|
||||
const localStorage = createInMemoryStorage();
|
||||
localStorage.setItem("authToken", authToken);
|
||||
await initLocalState(environment, { localStorage } as any);
|
||||
context.pymLocalStorage!.setItem("authToken", authToken);
|
||||
await initLocalState(environment, context as any);
|
||||
expect(source.get(LOCAL_ID)!.authToken).toBe(authToken);
|
||||
localStorage.removeItem("authToken");
|
||||
});
|
||||
|
||||
@@ -20,9 +20,9 @@ import {
|
||||
*/
|
||||
export default async function initLocalState(
|
||||
environment: Environment,
|
||||
{ localStorage }: TalkContext
|
||||
{ pymLocalStorage }: TalkContext
|
||||
) {
|
||||
const authToken = await localStorage.getItem("authToken");
|
||||
const authToken = await pymLocalStorage!.getItem("authToken");
|
||||
|
||||
commitLocalUpdate(environment, s => {
|
||||
// TODO: (cvle) move local, auth token and network initialization to framework.
|
||||
|
||||
Reference in New Issue
Block a user