aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/util/History.ts122
-rw-r--r--src/client/views/Main.tsx19
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx36
-rw-r--r--src/new_fields/Doc.ts7
5 files changed, 159 insertions, 27 deletions
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
new file mode 100644
index 000000000..92d2b2b44
--- /dev/null
+++ b/src/client/util/History.ts
@@ -0,0 +1,122 @@
+import { Doc, Opt, Field } from "../../new_fields/Doc";
+import { DocServer } from "../DocServer";
+import { Main } from "../views/Main";
+import { RouteStore } from "../../server/RouteStore";
+
+export namespace HistoryUtil {
+ export interface DocInitializerList {
+ [key: string]: string | number;
+ }
+
+ export interface DocUrl {
+ type: "doc";
+ docId: string;
+ initializers: {
+ [docId: string]: DocInitializerList;
+ };
+ }
+
+ export type ParsedUrl = DocUrl;
+
+ // const handlers: ((state: ParsedUrl | null) => void)[] = [];
+ function onHistory(e: PopStateEvent) {
+ if (window.location.pathname !== RouteStore.home) {
+ const url = e.state as ParsedUrl || parseUrl(window.location.pathname);
+ if (url) {
+ switch (url.type) {
+ case "doc":
+ onDocUrl(url);
+ break;
+ }
+ }
+ }
+ // for (const handler of handlers) {
+ // handler(e.state);
+ // }
+ }
+
+ export function pushState(state: ParsedUrl) {
+ history.pushState(state, "", createUrl(state));
+ }
+
+ export function replaceState(state: ParsedUrl) {
+ history.replaceState(state, "", createUrl(state));
+ }
+
+ function copyState(state: ParsedUrl): ParsedUrl {
+ return JSON.parse(JSON.stringify(state));
+ }
+
+ export function getState(): ParsedUrl {
+ return copyState(history.state);
+ }
+
+ // export function addHandler(handler: (state: ParsedUrl | null) => void) {
+ // handlers.push(handler);
+ // }
+
+ // export function removeHandler(handler: (state: ParsedUrl | null) => void) {
+ // const index = handlers.indexOf(handler);
+ // if (index !== -1) {
+ // handlers.splice(index, 1);
+ // }
+ // }
+
+ export function parseUrl(pathname: string): ParsedUrl | undefined {
+ let pathnameSplit = pathname.split("/");
+ if (pathnameSplit.length !== 2) {
+ return undefined;
+ }
+ const type = pathnameSplit[0];
+ const data = pathnameSplit[1];
+
+ if (type === "doc") {
+ const s = data.split("?");
+ if (s.length < 1 || s.length > 2) {
+ return undefined;
+ }
+ const docId = s[0];
+ const initializers = s.length === 2 ? JSON.parse(decodeURIComponent(s[1])) : {};
+ return {
+ type: "doc",
+ docId,
+ initializers
+ };
+ }
+
+ return undefined;
+ }
+
+ export function createUrl(params: ParsedUrl): string {
+ let baseUrl = DocServer.prepend(`/${params.type}`);
+ switch (params.type) {
+ case "doc":
+ const initializers = encodeURIComponent(JSON.stringify(params.initializers));
+ const id = params.docId;
+ let url = baseUrl + `/${id}`;
+ if (Object.keys(params.initializers).length) {
+ url += `?${initializers}`;
+ }
+ return url;
+ }
+ return "";
+ }
+
+ export async function initDoc(id: string, initializer: DocInitializerList) {
+ const doc = await DocServer.GetRefField(id);
+ if (!(doc instanceof Doc)) {
+ return;
+ }
+ Doc.assign(doc, initializer);
+ }
+
+ async function onDocUrl(url: DocUrl) {
+ const field = await DocServer.GetRefField(url.docId);
+ await Promise.all(Object.keys(url.initializers).map(id => initDoc(id, url.initializers[id])));
+ if (field instanceof Doc) {
+ Main.Instance.openWorkspace(field, true);
+ }
+ }
+
+ window.onpopstate = onHistory;
+}
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 385fc6ead..7aef7d3e5 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -31,6 +31,8 @@ import { Cast, FieldValue, StrCast } from '../../new_fields/Types';
import { DocServer } from '../DocServer';
import { listSpec } from '../../new_fields/Schema';
import { Id } from '../../new_fields/RefField';
+import { HistoryUtil } from '../util/History';
+
@observer
export class Main extends React.Component {
@@ -79,21 +81,6 @@ export class Main extends React.Component {
this.initAuthenticationRouters();
}
- componentDidMount() { window.onpopstate = this.onHistory; }
-
- componentWillUnmount() { window.onpopstate = null; }
-
- onHistory = () => {
- if (window.location.pathname !== RouteStore.home) {
- let pathname = window.location.pathname.split("/");
- DocServer.GetRefField(pathname[pathname.length - 1]).then(action((field: Opt<Field>) => {
- if (field instanceof Doc) {
- this.openWorkspace(field, true);
- }
- }));
- }
- }
-
initEventListeners = () => {
// window.addEventListener("pointermove", (e) => this.reportLocation(e))
window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler
@@ -149,7 +136,7 @@ export class Main extends React.Component {
openWorkspace = async (doc: Doc, fromHistory = false) => {
CurrentUserUtils.MainDocId = doc[Id];
this.mainContainer = doc;
- fromHistory || window.history.pushState(null, StrCast(doc.title), "/doc/" + doc[Id]);
+ fromHistory || HistoryUtil.pushState({ type: "doc", docId: doc[Id], initializers: {} });
const col = await Cast(CurrentUserUtils.UserDocument.optionalRightCollection, Doc);
// if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized)
setTimeout(async () => {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 6651a834d..58f1e33a1 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -190,7 +190,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
reactionDisposer?: Lambda;
componentDidMount: () => void = () => {
- console.log("Docking mount");
if (this._containerRef.current) {
this.reactionDisposer = reaction(
() => StrCast(this.props.Document.dockingConfig),
@@ -207,7 +206,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
}
componentWillUnmount: () => void = () => {
- console.log("Docking unmount");
try {
this._goldenLayout.unbind('itemDropped', this.itemDropped);
this._goldenLayout.unbind('tabCreated', this.tabCreated);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c36c708db..e60bb2fb2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -24,6 +24,7 @@ import { FieldValue, Cast, NumCast } from "../../../../new_fields/Types";
import { pageSchema } from "../../nodes/ImageBox";
import { Id } from "../../../../new_fields/RefField";
import { InkField, StrokeData } from "../../../../new_fields/InkField";
+import { HistoryUtil } from "../../../util/History";
export const panZoomSchema = createSchema({
panX: "number",
@@ -219,6 +220,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
setPan(panX: number, panY: number) {
+ this.panDisposer && clearTimeout(this.panDisposer);
+ this.props.Document.panTransformType = "None";
var scale = this.getLocalTransform().inverse().Scale;
const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY));
@@ -245,16 +248,37 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
doc.zIndex = docs.length + 1;
}
+ panDisposer?: NodeJS.Timeout;
focusDocument = (doc: Doc) => {
+ const panX = this.Document.panX;
+ const panY = this.Document.panY;
+ const id = this.Document[Id];
+ const state = HistoryUtil.getState();
+ // TODO This technically isn't correct if type !== "doc", as
+ // currently nothing is done, but we should probably push a new state
+ if (state.type === "doc" && panX !== undefined && panY !== undefined) {
+ const init = state.initializers[id];
+ if (!init) {
+ state.initializers[id] = {
+ panX, panY
+ };
+ HistoryUtil.pushState(state);
+ } else if (init.panX !== panX || init.panY !== panY) {
+ init.panX = panX;
+ init.panY = panY;
+ HistoryUtil.pushState(state);
+ }
+ }
SelectionManager.DeselectAll();
+ const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2;
+ const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2;
+ const newState = HistoryUtil.getState();
+ newState.initializers[id] = { panX: newPanX, panY: newPanY };
+ HistoryUtil.pushState(newState);
+ this.setPan(newPanX, newPanY);
this.props.Document.panTransformType = "Ease";
- this.setPan(
- NumCast(doc.x) + NumCast(doc.width) / 2,
- NumCast(doc.y) + NumCast(doc.height) / 2);
this.props.focus(this.props.Document);
- if (this.props.Document.panTransformType === "Ease") {
- setTimeout(() => this.props.Document.panTransformType = "None", 2000); // wait 3 seconds, then reset to false
- }
+ this.panDisposer = setTimeout(() => this.props.Document.panTransformType = "None", 2000); // wait 3 seconds, then reset to false
}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 89901490d..d6043ef7a 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -159,9 +159,10 @@ export namespace Doc {
for (const key in fields) {
if (fields.hasOwnProperty(key)) {
const value = fields[key];
- if (value !== undefined) {
- doc[key] = value;
- }
+ // Do we want to filter out undefineds?
+ // if (value !== undefined) {
+ doc[key] = value;
+ // }
}
}
return doc;