mirror of
https://github.com/penpot/penpot.git
synced 2025-05-14 11:16:38 +02:00
♻️ Refactor html5 history.
This commit is contained in:
parent
d856b9aae3
commit
7fe7c3da6c
9 changed files with 114 additions and 128 deletions
|
@ -20,7 +20,6 @@
|
||||||
[uxbox.main.ui.modal :refer [modal]]
|
[uxbox.main.ui.modal :refer [modal]]
|
||||||
[uxbox.main.worker]
|
[uxbox.main.worker]
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.html.history :as html-history]
|
|
||||||
[uxbox.util.i18n :as i18n]
|
[uxbox.util.i18n :as i18n]
|
||||||
[uxbox.util.theme :as theme]
|
[uxbox.util.theme :as theme]
|
||||||
[uxbox.util.router :as rt]
|
[uxbox.util.router :as rt]
|
||||||
|
@ -29,38 +28,33 @@
|
||||||
|
|
||||||
(declare reinit)
|
(declare reinit)
|
||||||
|
|
||||||
(defn- on-navigate
|
(defn on-navigate
|
||||||
[router path]
|
[router path]
|
||||||
(let [match (rt/match router path)
|
(let [match (rt/match router path)
|
||||||
profile (:profile storage)]
|
profile (:profile storage)]
|
||||||
(cond
|
(cond
|
||||||
(and (= path "") (not profile))
|
(and (= path "") (not profile))
|
||||||
(st/emit! (rt/nav :login))
|
(rt/nav :login)
|
||||||
|
|
||||||
(and (= path "") profile)
|
(and (= path "") profile)
|
||||||
(st/emit! (rt/nav :dashboard-team {:team-id (:default-team-id profile)}))
|
(rt/nav :dashboard-team {:team-id (:default-team-id profile)})
|
||||||
|
|
||||||
(nil? match)
|
(nil? match)
|
||||||
(st/emit! (rt/nav :not-found))
|
(rt/nav :not-found)
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! #(assoc % :route match)))))
|
#(assoc % :route match))))
|
||||||
|
|
||||||
(defn init-ui
|
(defn init-ui
|
||||||
[]
|
[]
|
||||||
(let [router (rt/init ui/routes)
|
(st/emit! (rt/initialize-router ui/routes)
|
||||||
cpath (deref html-history/path)]
|
(rt/initialize-history on-navigate))
|
||||||
|
|
||||||
(st/emit! #(assoc % :router router))
|
|
||||||
(add-watch html-history/path ::main #(on-navigate router %4))
|
|
||||||
|
|
||||||
(when (:profile storage)
|
(when (:profile storage)
|
||||||
(st/emit! udu/fetch-profile))
|
(st/emit! udu/fetch-profile))
|
||||||
|
|
||||||
(mf/mount (mf/element ui/app) (dom/get-element "app"))
|
(mf/mount (mf/element ui/app) (dom/get-element "app"))
|
||||||
(mf/mount (mf/element modal) (dom/get-element "modal"))
|
(mf/mount (mf/element modal) (dom/get-element "modal")))
|
||||||
|
|
||||||
(on-navigate router cpath)))
|
|
||||||
|
|
||||||
(defn ^:export init
|
(defn ^:export init
|
||||||
[]
|
[]
|
||||||
|
@ -73,7 +67,6 @@
|
||||||
|
|
||||||
(defn reinit
|
(defn reinit
|
||||||
[]
|
[]
|
||||||
(remove-watch html-history/path ::main)
|
|
||||||
(mf/unmount (dom/get-element "app"))
|
(mf/unmount (dom/get-element "app"))
|
||||||
(mf/unmount (dom/get-element "modal"))
|
(mf/unmount (dom/get-element "modal"))
|
||||||
(init-ui))
|
(init-ui))
|
||||||
|
|
|
@ -83,7 +83,7 @@
|
||||||
(group-by :backend (vals db)))))
|
(group-by :backend (vals db)))))
|
||||||
(add-watch fontsdb "main"
|
(add-watch fontsdb "main"
|
||||||
(fn [_ _ _ db]
|
(fn [_ _ _ db]
|
||||||
(ts/schedule-on-idle #(materialize-fontsview db))))
|
(ts/schedule #(materialize-fontsview db))))
|
||||||
|
|
||||||
(defn register!
|
(defn register!
|
||||||
[backend fonts]
|
[backend fonts]
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
(:require
|
(:require
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[uxbox.util.timers :refer [schedule-on-idle]]))
|
[uxbox.util.timers :refer [schedule]]))
|
||||||
|
|
||||||
(mf/defc chunked-list
|
(mf/defc chunked-list
|
||||||
[{:keys [items children initial-size chunk-size]
|
[{:keys [items children initial-size chunk-size]
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
:pending-num (- pending-num chunk-size)}))
|
:pending-num (- pending-num chunk-size)}))
|
||||||
(after-render [state]
|
(after-render [state]
|
||||||
(when (pos? (:pending-num @state))
|
(when (pos? (:pending-num @state))
|
||||||
(let [sem (schedule-on-idle (fn [] (swap! state update-state)))]
|
(let [sem (schedule (fn [] (swap! state update-state)))]
|
||||||
#(rx/cancel! sem))))]
|
#(rx/cancel! sem))))]
|
||||||
|
|
||||||
(let [initial (mf/use-memo initial-state)
|
(let [initial (mf/use-memo initial-state)
|
||||||
|
|
49
frontend/src/uxbox/util/browser_history.js
Normal file
49
frontend/src/uxbox/util/browser_history.js
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
* defined by the Mozilla Public License, v. 2.0.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
goog.provide("uxbox.util.browser_history");
|
||||||
|
goog.require("goog.history.Html5History");
|
||||||
|
|
||||||
|
|
||||||
|
goog.scope(function() {
|
||||||
|
const self = uxbox.util.browser_history;
|
||||||
|
const Html5History = goog.history.Html5History;
|
||||||
|
|
||||||
|
class TokenTransformer {
|
||||||
|
retrieveToken(pathPrefix, location) {
|
||||||
|
return location.pathname.substr(pathPrefix.length) + location.search;
|
||||||
|
}
|
||||||
|
|
||||||
|
createUrl(token, pathPrefix, location) {
|
||||||
|
return pathPrefix + token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.create = function() {
|
||||||
|
const instance = new Html5History(null, new TokenTransformer());
|
||||||
|
instance.setUseFragment(true);
|
||||||
|
return instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.enable_BANG_ = function(instance) {
|
||||||
|
instance.setEnabled(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
self.disable_BANG_ = function(instance) {
|
||||||
|
instance.setEnabled(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
self.set_token_BANG_ = function(instance, token) {
|
||||||
|
instance.setToken(token);
|
||||||
|
}
|
||||||
|
});
|
|
@ -9,7 +9,7 @@
|
||||||
(:require
|
(:require
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[uxbox.util.timers :refer [schedule-on-idle]]))
|
[uxbox.util.timers :refer [schedule]]))
|
||||||
|
|
||||||
;; TODO: this file is DEPRECATED (pending deletion)
|
;; TODO: this file is DEPRECATED (pending deletion)
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
:pending-num (- pending-num chunk-size)}))
|
:pending-num (- pending-num chunk-size)}))
|
||||||
(after-render [state]
|
(after-render [state]
|
||||||
(when (pos? (:pending-num @state))
|
(when (pos? (:pending-num @state))
|
||||||
(let [sem (schedule-on-idle (fn [] (swap! state update-state)))]
|
(let [sem (schedule (fn [] (swap! state update-state)))]
|
||||||
#(rx/cancel! sem))))]
|
#(rx/cancel! sem))))]
|
||||||
|
|
||||||
(let [initial (mf/use-memo initial-state)
|
(let [initial (mf/use-memo initial-state)
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
/**
|
|
||||||
* TokenTransformer
|
|
||||||
*
|
|
||||||
* @author Paul Anderson <paul@andersonpaul.com>, 2018
|
|
||||||
* @license BSD License <https://opensource.org/licenses/BSD-2-Clause>
|
|
||||||
*/
|
|
||||||
|
|
||||||
goog.provide('uxbox.util.html.TokenTransformer');
|
|
||||||
goog.require('goog.history.Html5History');
|
|
||||||
|
|
||||||
goog.scope(function() {
|
|
||||||
/**
|
|
||||||
* A goog.history.Html5History.TokenTransformer implementation that
|
|
||||||
* includes the query string in the token.
|
|
||||||
*
|
|
||||||
* The implementation of token<->url transforms in
|
|
||||||
* `goog.history.Html5History`, when useFragment is false and no custom
|
|
||||||
* transformer is supplied, assumes that a token is equivalent to
|
|
||||||
* `window.location.pathname` minus any configured path prefix. Since
|
|
||||||
* bide allows constructing urls that include a query string, we want
|
|
||||||
* to be able to store those as tokens.
|
|
||||||
*
|
|
||||||
* Addresses funcool/bide#15.
|
|
||||||
*
|
|
||||||
* @constructor
|
|
||||||
* @implements {goog.history.Html5History.TokenTransformer}
|
|
||||||
*/
|
|
||||||
uxbox.util.html.TokenTransformer = function () {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a history token given the path prefix and
|
|
||||||
* `window.location` object.
|
|
||||||
*
|
|
||||||
* @param {string} pathPrefix The path prefix to use when storing token
|
|
||||||
* in a path; always begin with a slash.
|
|
||||||
* @param {Location} location The `window.location` object.
|
|
||||||
* Treat this object as read-only.
|
|
||||||
* @return {string} token The history token.
|
|
||||||
*/
|
|
||||||
uxbox.util.html.TokenTransformer.prototype.retrieveToken = function(pathPrefix, location) {
|
|
||||||
return location.pathname.substr(pathPrefix.length) + location.search;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a URL to be pushed into HTML5 history stack when storing
|
|
||||||
* token without using hash fragment.
|
|
||||||
*
|
|
||||||
* @param {string} token The history token.
|
|
||||||
* @param {string} pathPrefix The path prefix to use when storing token
|
|
||||||
* in a path; always begin with a slash.
|
|
||||||
* @param {Location} location The `window.location` object.
|
|
||||||
* Treat this object as read-only.
|
|
||||||
* @return {string} url The complete URL string from path onwards
|
|
||||||
* (without {@code protocol://host:port} part); must begin with a
|
|
||||||
* slash.
|
|
||||||
*/
|
|
||||||
uxbox.util.html.TokenTransformer.prototype.createUrl = function(token, pathPrefix, location) {
|
|
||||||
return pathPrefix + token;
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,31 +0,0 @@
|
||||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
;;
|
|
||||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
|
||||||
|
|
||||||
(ns uxbox.util.html.history
|
|
||||||
"A singleton abstraction for the html5 fragment based history."
|
|
||||||
(:require [goog.events :as e])
|
|
||||||
(:import uxbox.util.html.TokenTransformer
|
|
||||||
goog.history.Html5History
|
|
||||||
goog.history.EventType))
|
|
||||||
|
|
||||||
(defonce ^:private +instance+
|
|
||||||
(doto (Html5History. nil (TokenTransformer.))
|
|
||||||
(.setUseFragment true)
|
|
||||||
(.setEnabled true)))
|
|
||||||
|
|
||||||
(defonce path (atom (.getToken +instance+)))
|
|
||||||
|
|
||||||
(defonce ^:private +instance-sem+
|
|
||||||
(e/listen +instance+ EventType.NAVIGATE
|
|
||||||
#(reset! path (.-token %))))
|
|
||||||
|
|
||||||
(defn set-path!
|
|
||||||
[path]
|
|
||||||
(.setToken +instance+ path))
|
|
||||||
|
|
||||||
(defn replace-path!
|
|
||||||
[path]
|
|
||||||
(.replaceToken +instance+ path))
|
|
|
@ -7,16 +7,19 @@
|
||||||
(ns uxbox.util.router
|
(ns uxbox.util.router
|
||||||
(:refer-clojure :exclude [resolve])
|
(:refer-clojure :exclude [resolve])
|
||||||
(:require
|
(:require
|
||||||
|
[beicon.core :as rx]
|
||||||
|
[rumext.alpha :as mf]
|
||||||
[reitit.core :as r]
|
[reitit.core :as r]
|
||||||
|
[goog.events :as e]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[uxbox.common.data :as d]
|
[uxbox.util.browser-history :as bhistory]
|
||||||
[uxbox.util.html.history :as html-history])
|
[uxbox.common.data :as d])
|
||||||
(:import
|
(:import
|
||||||
goog.Uri
|
goog.Uri
|
||||||
goog.Uri.QueryData))
|
goog.Uri.QueryData))
|
||||||
|
|
||||||
;; --- API
|
;; --- Router API
|
||||||
|
|
||||||
(defn- parse-query-data
|
(defn- parse-query-data
|
||||||
[^QueryData qdata]
|
[^QueryData qdata]
|
||||||
|
@ -50,10 +53,17 @@
|
||||||
(.setQueryData uri qdt)
|
(.setQueryData uri qdt)
|
||||||
(.toString uri))))))
|
(.toString uri))))))
|
||||||
|
|
||||||
(defn init
|
(defn create
|
||||||
[routes]
|
[routes]
|
||||||
(r/router routes))
|
(r/router routes))
|
||||||
|
|
||||||
|
(defn initialize-router
|
||||||
|
[routes]
|
||||||
|
(ptk/reify ::initialize-router
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(assoc state :router (create routes)))))
|
||||||
|
|
||||||
(defn query-params
|
(defn query-params
|
||||||
"Given goog.Uri, read query parameters into Clojure map."
|
"Given goog.Uri, read query parameters into Clojure map."
|
||||||
[^goog.Uri uri]
|
[^goog.Uri uri]
|
||||||
|
@ -63,13 +73,6 @@
|
||||||
(map (juxt keyword #(.get q %)))
|
(map (juxt keyword #(.get q %)))
|
||||||
(into {}))))
|
(into {}))))
|
||||||
|
|
||||||
(defn navigate!
|
|
||||||
([router id] (navigate! router id {} {}))
|
|
||||||
([router id params] (navigate! router id params {}))
|
|
||||||
([router id params qparams]
|
|
||||||
(-> (resolve router id params qparams)
|
|
||||||
(html-history/set-path!))))
|
|
||||||
|
|
||||||
(defn match
|
(defn match
|
||||||
"Given routing tree and current path, return match with possibly
|
"Given routing tree and current path, return match with possibly
|
||||||
coerced parameters. Return nil if no match found."
|
coerced parameters. Return nil if no match found."
|
||||||
|
@ -87,8 +90,10 @@
|
||||||
(deftype Navigate [id params qparams]
|
(deftype Navigate [id params qparams]
|
||||||
ptk/EffectEvent
|
ptk/EffectEvent
|
||||||
(effect [_ state stream]
|
(effect [_ state stream]
|
||||||
(let [router (:router state)]
|
(let [router (:router state)
|
||||||
(navigate! router id params qparams))))
|
history (:history state)
|
||||||
|
path (resolve router id params qparams)]
|
||||||
|
(bhistory/set-token! history path))))
|
||||||
|
|
||||||
(defn nav
|
(defn nav
|
||||||
([id] (nav id nil nil))
|
([id] (nav id nil nil))
|
||||||
|
@ -99,3 +104,30 @@
|
||||||
|
|
||||||
(def navigate nav)
|
(def navigate nav)
|
||||||
|
|
||||||
|
;; --- History API
|
||||||
|
|
||||||
|
(defn initialize-history
|
||||||
|
[on-change]
|
||||||
|
(ptk/reify ::initialize-history
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [history (bhistory/create)]
|
||||||
|
(assoc state :history history)))
|
||||||
|
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [stoper (rx/filter (ptk/type? ::initialize-history) stream)
|
||||||
|
history (:history state)
|
||||||
|
router (:router state)]
|
||||||
|
(->> (rx/create (fn [sink]
|
||||||
|
(let [key (e/listen history "navigate" #(sink (.-token %)))]
|
||||||
|
(bhistory/enable! history)
|
||||||
|
(fn []
|
||||||
|
(bhistory/disable! history)
|
||||||
|
(e/unlistenByKey key)))))
|
||||||
|
(rx/map #(on-change router %))
|
||||||
|
(rx/take-until stoper))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2016-2019 Andrey Antukh <niwi@niwi.nz>
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
(ns uxbox.util.timers
|
(ns uxbox.util.timers
|
||||||
(:require [beicon.core :as rx]))
|
(:require [beicon.core :as rx]))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue