From b31afcfb4774f6a6f301db8bbbb98f46d2877d28 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 26 Nov 2024 10:15:55 +0100 Subject: [PATCH 1/9] :bug: Fix incorrect use of forward-ref on input* control component And in the derived input-tokens* component --- .../src/app/main/ui/ds/controls/input.cljs | 37 ++++++++++++------- .../components/controls/input_tokens.cljs | 13 +++---- .../app/main/ui/workspace/tokens/form.cljs | 2 +- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/frontend/src/app/main/ui/ds/controls/input.cljs b/frontend/src/app/main/ui/ds/controls/input.cljs index ae74123c3..0df0c0c6d 100644 --- a/frontend/src/app/main/ui/ds/controls/input.cljs +++ b/frontend/src/app/main/ui/ds/controls/input.cljs @@ -9,6 +9,7 @@ [app.common.data.macros :as dm] [app.main.style :as stl]) (:require + [app.common.data :as d] [app.main.ui.ds.foundations.assets.icon :refer [icon* icon-list]] [app.util.dom :as dom] [rumext.v2 :as mf])) @@ -25,17 +26,25 @@ {::mf/props :obj ::mf/forward-ref true ::mf/schema schema:input} - [{:keys [icon class type external-ref] :rest props}] - (let [ref (or external-ref (mf/use-ref)) - type (or type "text") - icon-class (stl/css-case :input true - :input-with-icon (some? icon)) - props (mf/spread-props props {:class icon-class :ref ref :type type}) - handle-icon-click (mf/use-fn (mf/deps ref) - (fn [_] - (let [input-node (mf/ref-val ref)] - (dom/select-node input-node) - (dom/focus! input-node))))] - [:> "span" {:class (dm/str class " " (stl/css :container))} - (when icon [:> icon* {:id icon :class (stl/css :icon) :on-click handle-icon-click}]) - [:> "input" props]])) + [{:keys [icon class type] :rest props} ref] + (let [ref (or ref (mf/use-ref)) + type (d/nilv type "text") + props (mf/spread-props props + :class (stl/css-case + :input true + :input-with-icon (some? icon)) + :ref ref + :type type) + + on-icon-click + (mf/use-fn + (mf/deps ref) + (fn [_event] + (let [input-node (mf/ref-val ref)] + (dom/select-node input-node) + (dom/focus! input-node))))] + + [:> :span {:class (dm/str class " " (stl/css :container))} + (when (some? icon) + [:> icon* {:id icon :class (stl/css :icon) :on-click on-icon-click}]) + [:> :input props]])) diff --git a/frontend/src/app/main/ui/workspace/tokens/components/controls/input_tokens.cljs b/frontend/src/app/main/ui/workspace/tokens/components/controls/input_tokens.cljs index dbb75a2b3..ad4e1ebce 100644 --- a/frontend/src/app/main/ui/workspace/tokens/components/controls/input_tokens.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/components/controls/input_tokens.cljs @@ -25,19 +25,18 @@ {::mf/props :obj ::mf/forward-ref true ::mf/schema schema::input-tokens} - [{:keys [class label external-ref id error value children] :rest props}] - (let [ref (or external-ref (mf/use-ref)) + [{:keys [class label id error value children] :rest props} ref] + (let [ref (or ref (mf/use-ref)) props (mf/spread-props props {:id id :type "text" :class (stl/css :input) :aria-invalid error :value value - :external-ref ref})] - [:div {:class (dm/str class " " - (stl/css-case :wrapper true - :input-error error))} + :ref ref})] + [:div {:class (dm/str class " " (stl/css-case :wrapper true + :input-error error))} [:label {:for id :class (stl/css :label)} label] [:div {:class (stl/css :input-wrapper)} - (when children + (when (some? children) [:div {:class (stl/css :input-swatch)} children]) [:> input* props]]])) diff --git a/frontend/src/app/main/ui/workspace/tokens/form.cljs b/frontend/src/app/main/ui/workspace/tokens/form.cljs index 748313baa..fcb34661d 100644 --- a/frontend/src/app/main/ui/workspace/tokens/form.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/form.cljs @@ -390,7 +390,7 @@ Token names should only contain letters and digits separated by . characters.")} :placeholder (tr "workspace.token.enter-token-value") :label (tr "workspace.token.token-value") :default-value @value-ref - :external-ref value-input-ref + :ref value-input-ref :on-change on-update-value :on-blur on-update-value} (when color? From b17d7c0289cc99226d709313956ff1d4f27658cb Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 26 Nov 2024 20:36:13 +0100 Subject: [PATCH 2/9] :recycle: Refactor dashboard bootstrap --- backend/src/app/rpc/commands/files.clj | 2 +- backend/src/app/rpc/commands/search.clj | 3 +- .../data/dashboard/get-project-files.json | 2 + .../playwright/data/dashboard/thumbnail.png | Bin 0 -> 1681 bytes frontend/playwright/ui/pages/DashboardPage.js | 4 + frontend/src/app/main.cljs | 10 +- frontend/src/app/main/data/auth.cljs | 317 +++++++++ frontend/src/app/main/data/dashboard.cljs | 663 ++++-------------- frontend/src/app/main/data/fonts.cljs | 48 +- frontend/src/app/main/data/team.cljs | 534 ++++++++++++++ frontend/src/app/main/data/users.cljs | 353 +--------- frontend/src/app/main/data/workspace.cljs | 39 +- frontend/src/app/main/errors.cljs | 4 +- frontend/src/app/main/refs.cljs | 73 +- frontend/src/app/main/ui.cljs | 130 ++-- frontend/src/app/main/ui/auth.cljs | 5 +- frontend/src/app/main/ui/auth/login.cljs | 8 +- frontend/src/app/main/ui/auth/register.cljs | 4 +- .../src/app/main/ui/auth/verify_token.cljs | 5 +- frontend/src/app/main/ui/context.cljs | 2 +- frontend/src/app/main/ui/dashboard.cljs | 195 +++--- .../app/main/ui/dashboard/change_owner.cljs | 4 +- .../src/app/main/ui/dashboard/file_menu.cljs | 2 +- frontend/src/app/main/ui/dashboard/files.cljs | 31 +- frontend/src/app/main/ui/dashboard/fonts.cljs | 38 +- frontend/src/app/main/ui/dashboard/grid.cljs | 80 +-- .../src/app/main/ui/dashboard/libraries.cljs | 46 +- .../src/app/main/ui/dashboard/projects.cljs | 77 +- .../src/app/main/ui/dashboard/search.cljs | 54 +- .../src/app/main/ui/dashboard/sidebar.cljs | 91 ++- frontend/src/app/main/ui/dashboard/team.cljs | 303 ++++---- .../src/app/main/ui/dashboard/team_form.cljs | 6 +- .../src/app/main/ui/dashboard/templates.cljs | 4 +- .../app/main/ui/onboarding/team_choice.cljs | 6 +- frontend/src/app/main/ui/routes.cljs | 8 +- .../src/app/main/ui/settings/options.cljs | 2 +- .../src/app/main/ui/settings/sidebar.cljs | 9 +- frontend/src/app/main/ui/static.cljs | 4 +- frontend/src/app/main/ui/workspace.cljs | 2 +- .../src/app/main/ui/workspace/main_menu.cljs | 4 +- .../main/ui/workspace/sidebar/options.cljs | 2 +- .../main/ui/workspace/sidebar/sitemap.cljs | 2 +- .../src/app/main/ui/workspace/viewport.cljs | 2 +- .../app/main/ui/workspace/viewport_wasm.cljs | 2 +- frontend/src/app/render.cljs | 4 +- 45 files changed, 1684 insertions(+), 1500 deletions(-) create mode 100644 frontend/playwright/data/dashboard/thumbnail.png create mode 100644 frontend/src/app/main/data/auth.cljs create mode 100644 frontend/src/app/main/data/team.cljs diff --git a/backend/src/app/rpc/commands/files.clj b/backend/src/app/rpc/commands/files.clj index 19b7637e0..a6c74b810 100644 --- a/backend/src/app/rpc/commands/files.clj +++ b/backend/src/app/rpc/commands/files.clj @@ -575,7 +575,7 @@ (if-let [media-id (:media-id row)] (-> row (dissoc :media-id) - (assoc :thumbnail-uri (resolve-public-uri media-id))) + (assoc :thumbnail-id media-id)) (dissoc row :media-id)))) (map #(assoc % :library-summary (get-library-summary cfg %))) (map #(dissoc % :data)))))) diff --git a/backend/src/app/rpc/commands/search.clj b/backend/src/app/rpc/commands/search.clj index 1a25a6dcf..801ff555b 100644 --- a/backend/src/app/rpc/commands/search.clj +++ b/backend/src/app/rpc/commands/search.clj @@ -9,7 +9,6 @@ [app.common.schema :as sm] [app.db :as db] [app.rpc :as-alias rpc] - [app.rpc.commands.files :refer [resolve-public-uri]] [app.rpc.doc :as-alias doc] [app.util.services :as sv])) @@ -61,7 +60,7 @@ (if-let [media-id (:media-id row)] (-> row (dissoc :media-id) - (assoc :thumbnail-uri (resolve-public-uri media-id))) + (assoc :thumbnail-id media-id)) (dissoc row :media-id)))))) (def ^:private schema:search-files diff --git a/frontend/playwright/data/dashboard/get-project-files.json b/frontend/playwright/data/dashboard/get-project-files.json index b0394aff1..3eec69caa 100644 --- a/frontend/playwright/data/dashboard/get-project-files.json +++ b/frontend/playwright/data/dashboard/get-project-files.json @@ -6,6 +6,7 @@ "~:modified-at": "~m1714045654874", "~:name": "New File 2", "~:revn": 1, + "~:thumbnail-id": "~u95d6fdd8-48d8-8148-8004-38af910d2dbe", "~:is-shared": false }, { @@ -15,6 +16,7 @@ "~:modified-at": "~m1713519762931", "~:name": "New File 1", "~:revn": 1, + "~:thumbnail-id": "~u95d6fdd8-48d8-8148-8004-38af910d2dbe", "~:is-shared": false } ] diff --git a/frontend/playwright/data/dashboard/thumbnail.png b/frontend/playwright/data/dashboard/thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..d03c7de5b9c59f7f6155e5471c2cdf208ccf64e0 GIT binary patch literal 1681 zcmeAS@N?(olHy`uVBq!ia0vp^e}H%e2OE$)vZc8VNHG=%xjQkeJ16rJ$YCjV@(kes zf*OvL4h9A`Yfl%)kczmsw|)IZvPE1UuHs8Ry8i$F_Z$Wj%6RT(_{b==+?sQ5s!iY9 z&+m3;t-Ukx_1n9DKNlUe4-XGNJ^l6h)4SG*$2`nrDPoy7Z{D=|{KxMcyt8!nR@EPe zA5QpvzKFAtTd1O@M&yt8pZ|a5<35O+T)7;`)byX<@1T2Z@tTH)2dwc67IFr&u;~BL zyuK&c!Qn%b{)G!InoLdNf2L&L(^OEXI4GWxaY%&mAa~s<)Au3*0(%~Co0uea0kzsc zomI}o!4da?Rce-jD^TnFqO;blAhVcweP<{EwfgUTHkS!#R!w7@nvWn*>++b-evCk~ z_Bl8UF9T@}URSw%)EUD+G}g1!|NVJ+)4JDJzJK6jlJ|P>vLs4Qu225gG3);OYqpnv zKmY#E#`%g32ljDr)WzRF9mQfMDDX$Ermlw;A}@IV{ocO*pX1qO`LB=rvYb1A{`AKm z*_MBQwwsFoxh(ys|NZ@GhaVO=n$2@laDPx@_y4bWNmG5}$_K@pc9v8!HPt_r=J@w% zQq%-xU?P8>Q?zm(C@1N!Y_tR=_H$~}UvUD{_SZ>LGe#ARh6B0j;l9A1UF*KeUvgl% u=k%b)PM`mH{OOk^Rj1?suh?JwS$c8P93f9e8)IO#!r> stream - (rx/filter (ptk/type? ::profile-fetched)) + (rx/filter du/profile-fetched?) (rx/take 1) (rx/map deref) (rx/mapcat (fn [profile] (if (du/is-authenticated? profile) - (rx/of (du/fetch-teams)) + (rx/of (du/initialize-profile profile)) (rx/empty)))) (rx/observe-on :async)))) @@ -92,6 +93,11 @@ (initialize-profile stream) + ;; Watch for profile deletion events + (->> stream + (rx/filter du/profile-deleted?) + (rx/map da/logged-out)) + ;; Once profile is fetched, initialize all penpot application ;; routes (->> stream diff --git a/frontend/src/app/main/data/auth.cljs b/frontend/src/app/main/data/auth.cljs new file mode 100644 index 000000000..4a04d0d33 --- /dev/null +++ b/frontend/src/app/main/data/auth.cljs @@ -0,0 +1,317 @@ +;; 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) KALEIDOS INC + +(ns app.main.data.auth + "Auth related data events" + (:require + [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.exceptions :as ex] + [app.common.schema :as sm] + [app.common.uuid :as uuid] + [app.main.data.events :as ev] + [app.main.data.notifications :as ntf] + [app.main.data.team :as dtm] + [app.main.data.users :as du] + [app.main.data.websocket :as ws] + [app.main.repo :as rp] + [app.util.i18n :as i18n :refer [tr]] + [app.util.router :as rt] + [app.util.storage :as storage] + [beicon.v2.core :as rx] + [potok.v2.core :as ptk])) + +;; --- HELPERS + +(defn is-authenticated? + [{:keys [id]}] + (and (uuid? id) (not= id uuid/zero))) + +;; --- EVENT: login + +(defn- logged-in + "This is the main event that is executed once we have logged in + profile. The profile can proceed from standard login or from + accepting invitation, or third party auth signup or singin." + [{:keys [props] :as profile}] + (letfn [(get-redirect-events [teams] + (if-let [redirect-href (:login-redirect storage/session)] + (binding [storage/*sync* true] + (swap! storage/session dissoc :login-redirect) + (if (= redirect-href (rt/get-current-href)) + (rx/of (rt/reload true)) + (rx/of (rt/nav-raw :href redirect-href)))) + (if-let [file-id (get props :welcome-file-id)] + (rx/of (rt/nav' :workspace {:project-id (:default-project-id profile) + :file-id file-id}) + (du/update-profile-props {:welcome-file-id nil})) + + (let [teams (into #{} (map :id) teams) + team-id (dtm/get-last-team-id) + team-id (if (and team-id (contains? teams team-id)) + team-id + (:default-team-id profile))] + (rx/of (rt/nav' :dashboard-projects {:team-id team-id}))))))] + + (ptk/reify ::logged-in + ev/Event + (-data [_] + {::ev/name "signin" + ::ev/type "identify" + :email (:email profile) + :auth-backend (:auth-backend profile) + :fullname (:fullname profile) + :is-muted (:is-muted profile) + :default-team-id (:default-team-id profile) + :default-project-id (:default-project-id profile)}) + + ptk/WatchEvent + (watch [_ _ stream] + (->> (rx/merge + (rx/of (du/initialize-profile profile) + (ws/initialize) + (dtm/fetch-teams)) + + (->> stream + (rx/filter (ptk/type? ::dtm/teams-fetched)) + (rx/take 1) + (rx/map deref) + (rx/mapcat get-redirect-events))) + + (rx/observe-on :async)))))) + +(declare login-from-register) + +(defn login + [{:keys [email password invitation-token] :as data}] + (ptk/reify ::login + ptk/WatchEvent + (watch [_ _ stream] + (let [{:keys [on-error on-success] + :or {on-error rx/throw + on-success identity}} (meta data) + + params {:email email + :password password + :invitation-token invitation-token}] + + ;; NOTE: We can't take the profile value from login because + ;; there are cases when login is successful but the cookie is + ;; not set properly (because of possible misconfiguration). + ;; So, we proceed to make an additional call to fetch the + ;; profile, and ensure that cookie is set correctly. If + ;; profile fetch is successful, we mark the user logged in, if + ;; the returned profile is an NOT authenticated profile, we + ;; proceed to logout and show an error message. + + (->> (rp/cmd! :login-with-password (d/without-nils params)) + (rx/merge-map (fn [data] + (rx/merge + (rx/of (du/fetch-profile)) + (->> stream + (rx/filter du/profile-fetched?) + (rx/take 1) + (rx/map deref) + (rx/filter (complement is-authenticated?)) + (rx/tap on-error) + (rx/map #(ex/raise :type :authentication)) + (rx/observe-on :async)) + + (->> stream + (rx/filter du/profile-fetched?) + (rx/take 1) + (rx/map deref) + (rx/filter is-authenticated?) + (rx/map (fn [profile] + (with-meta (merge data profile) + {::ev/source "login"}))) + (rx/tap on-success) + (rx/map logged-in) + (rx/observe-on :async))))) + (rx/catch on-error)))))) + +(def ^:private schema:login-with-ldap + [:map {:title "login-with-ldap"} + [:email ::sm/email] + [:password :string]]) + +(defn login-with-ldap + [params] + + (dm/assert! + "expected valid params" + (sm/check schema:login-with-ldap params)) + + (ptk/reify ::login-with-ldap + ptk/WatchEvent + (watch [_ _ _] + (let [{:keys [on-error on-success] + :or {on-error rx/throw + on-success identity}} (meta params)] + (->> (rp/cmd! :login-with-ldap params) + (rx/tap on-success) + (rx/map (fn [profile] + (-> profile + (with-meta {::ev/source "login-with-ldap"}) + (logged-in)))) + (rx/catch on-error)))))) + +(defn login-from-token + "Used mainly as flow continuation after token validation." + [{:keys [profile] :as tdata}] + (ptk/reify ::login-from-token + ptk/WatchEvent + (watch [_ _ _] + (->> (rx/of (logged-in (with-meta profile {::ev/source "login-with-token"}))) + ;; NOTE: we need this to be asynchronous because the effect + ;; should be called before proceed with the login process + (rx/observe-on :async))))) + +(defn login-from-register + "Event used mainly for mark current session as logged-in in after the + user successfully registered using third party auth provider (in this + case we dont need to verify the email)." + [] + (ptk/reify ::login-from-register + ptk/WatchEvent + (watch [_ _ stream] + (rx/merge + (rx/of (du/fetch-profile)) + (->> stream + (rx/filter du/profile-fetched?) + (rx/take 1) + (rx/map deref) + (rx/filter is-authenticated?) + (rx/map (fn [profile] + (with-meta profile + {::ev/source "register"}))) + (rx/map logged-in) + (rx/observe-on :async)))))) + +;; --- EVENT: logout + +(defn logged-out + [] + (ptk/reify ::logged-out + ptk/UpdateEvent + (update [_ state] + (select-keys state [:route :router :session-id :history])) + + ptk/WatchEvent + (watch [_ _ _] + (rx/merge + ;; NOTE: We need the `effect` of the current event to be + ;; executed before the redirect. + (->> (rx/of (rt/nav :auth-login)) + (rx/observe-on :async)) + (rx/of (ws/finalize)))) + + ptk/EffectEvent + (effect [_ _ _] + ;; We prefer to keek some stuff in the storage like the current-team-id and the profile + (swap! storage/user (constantly {}))))) + +(defn logout + [] + (ptk/reify ::logout + ev/Event + (-data [_] {}) + + ptk/WatchEvent + (watch [_ state _] + (let [profile-id (:profile-id state)] + (->> (rx/interval 500) + (rx/take 1) + (rx/mapcat (fn [_] + (->> (rp/cmd! :logout {:profile-id profile-id}) + (rx/delay-at-least 300) + (rx/catch (constantly (rx/of 1)))))) + (rx/map logged-out)))))) + +;; --- Update Profile + +(def ^:private + schema:request-profile-recovery + [:map {:title "request-profile-recovery" :closed true} + [:email ::sm/email]]) + +(defn request-profile-recovery + [data] + + (dm/assert! + "expected valid parameters" + (sm/check schema:request-profile-recovery data)) + + (ptk/reify ::request-profile-recovery + ptk/WatchEvent + (watch [_ _ _] + (let [{:keys [on-error on-success] + :or {on-error rx/throw + on-success identity}} (meta data)] + + (->> (rp/cmd! :request-profile-recovery data) + (rx/tap on-success) + (rx/catch on-error)))))) + +;; --- EVENT: recover-profile (Password) + +(def ^:private + schema:recover-profile + [:map {:title "recover-profile" :closed true} + [:password :string] + [:token :string]]) + +(defn recover-profile + [data] + (dm/assert! + "expected valid arguments" + (sm/check schema:recover-profile data)) + + (ptk/reify ::recover-profile + ptk/WatchEvent + (watch [_ _ _] + (let [{:keys [on-error on-success] + :or {on-error rx/throw + on-success identity}} (meta data)] + (->> (rp/cmd! :recover-profile data) + (rx/tap on-success) + (rx/catch on-error)))))) + +;; --- EVENT: crete-demo-profile + +(defn create-demo-profile + [] + (ptk/reify ::create-demo-profile + ptk/WatchEvent + (watch [_ _ _] + (->> (rp/cmd! :create-demo-profile {}) + (rx/map login))))) + +(defn show-redirect-error + "A helper event that interprets the OIDC redirect errors on the URI + and shows an appropriate error message using the notification + banners." + [error] + (ptk/reify ::show-redirect-error + ptk/WatchEvent + (watch [_ _ _] + (when-let [hint (case error + "registration-disabled" + (tr "errors.registration-disabled") + "profile-blocked" + (tr "errors.profile-blocked") + "auth-provider-not-allowed" + (tr "errors.auth-provider-not-allowed") + "email-domain-not-allowed" + (tr "errors.email-domain-not-allowed") + + ;; We explicitly do not show any error here, it a explicit user operation. + "unable-to-auth" + nil + + (tr "errors.generic"))] + + (rx/of (ntf/warn hint)))))) diff --git a/frontend/src/app/main/data/dashboard.cljs b/frontend/src/app/main/data/dashboard.cljs index e66187bea..1f727313d 100644 --- a/frontend/src/app/main/data/dashboard.cljs +++ b/frontend/src/app/main/data/dashboard.cljs @@ -12,16 +12,11 @@ [app.common.files.helpers :as cfh] [app.common.logging :as log] [app.common.schema :as sm] - [app.common.types.team :as ctt] - [app.common.uri :as u] [app.common.uuid :as uuid] - [app.config :as cf] [app.main.data.common :as dc] [app.main.data.events :as ev] [app.main.data.fonts :as df] - [app.main.data.media :as di] [app.main.data.modal :as modal] - [app.main.data.users :as du] [app.main.data.websocket :as dws] [app.main.features :as features] [app.main.repo :as rp] @@ -29,9 +24,8 @@ [app.util.i18n :as i18n :refer [tr]] [app.util.router :as rt] [app.util.sse :as sse] + [app.util.storage :as storage] [app.util.time :as dt] - [app.util.timers :as tm] - [app.util.webapi :as wapi] [beicon.v2.core :as rx] [clojure.set :as set] [potok.v2.core :as ptk])) @@ -43,143 +37,38 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (declare fetch-projects) -(declare fetch-team-members) (declare process-message) (defn initialize - [{:keys [id]}] - (dm/assert! (uuid? id)) + [] (ptk/reify ::initialize - ptk/UpdateEvent - (update [_ state] - (let [prev-team-id (:current-team-id state)] - (cond-> state - (not= prev-team-id id) - (-> (dissoc :current-team-initialized) - (dissoc :dashboard-files) - (dissoc :dashboard-projects) - (dissoc :dashboard-shared-files) - (dissoc :dashboard-recent-files) - (dissoc :dashboard-team-members) - (dissoc :dashboard-team-stats) - (assoc :current-team-id id) - (update :workspace-global dissoc :default-font))))) - ptk/WatchEvent (watch [_ state stream] - (let [stopper (rx/filter (ptk/type? ::finalize) stream) + (let [stopper (rx/filter (ptk/type? ::finalize) stream) profile-id (:profile-id state)] (->> (rx/merge - ;; fetch teams must be first in case the team doesn't exist - (ptk/watch (du/fetch-teams) state stream) - (ptk/watch (df/load-team-fonts id) state stream) - (ptk/watch (fetch-projects) state stream) - (ptk/watch (fetch-team-members) state stream) - (ptk/watch (du/fetch-users) state stream) - + (rx/of (fetch-projects) + (df/fetch-fonts)) (->> stream (rx/filter (ptk/type? ::dws/message)) (rx/map deref) (rx/filter (fn [{:keys [topic] :as msg}] (or (= topic uuid/zero) (= topic profile-id)))) - (rx/map process-message)) - - ;; Once the teams are fecthed, initialize features related - ;; to currently active team - (->> stream - (rx/filter (ptk/type? ::du/teams-fetched)) - (rx/observe-on :async) - (rx/mapcat deref) - (rx/filter #(= id (:id %))) - (rx/mapcat (fn [team] - (rx/of (du/set-current-team team) - #(assoc % :current-team-initialized true)))))) + (rx/map process-message) + (rx/ignore))) (rx/take-until stopper)))))) (defn finalize - [params] - (ptk/data-event ::finalize params)) + [] + (ptk/data-event ::finalize {})) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Data Fetching (context aware: current team) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; --- EVENT: fetch-team-members - -(defn team-members-fetched - [members] - (ptk/reify ::team-members-fetched - ptk/UpdateEvent - (update [_ state] - (assoc state :dashboard-team-members (d/index-by :id members))))) - -(defn fetch-team-members - ([] (fetch-team-members nil)) - ([team-id] - (ptk/reify ::fetch-team-members - ptk/WatchEvent - (watch [_ state _] - (let [team-id (or team-id (:current-team-id state))] - (assert (uuid? team-id) "expected team-id to be resolved") - (->> (rp/cmd! :get-team-members {:team-id team-id}) - (rx/map team-members-fetched))))))) - -;; --- EVENT: fetch-team-stats - -(defn team-stats-fetched - [stats] - (ptk/reify ::team-stats-fetched - ptk/UpdateEvent - (update [_ state] - (assoc state :dashboard-team-stats stats)))) - -(defn fetch-team-stats - [team-id] - (ptk/reify ::fetch-team-stats - ptk/WatchEvent - (watch [_ _ _] - (->> (rp/cmd! :get-team-stats {:team-id team-id}) - (rx/map team-stats-fetched))))) - -;; --- EVENT: fetch-team-invitations - -(defn team-invitations-fetched - [invitations] - (ptk/reify ::team-invitations-fetched - ptk/UpdateEvent - (update [_ state] - (assoc state :dashboard-team-invitations invitations)))) - -(defn fetch-team-invitations - [] - (ptk/reify ::fetch-team-invitations - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (->> (rp/cmd! :get-team-invitations {:team-id team-id}) - (rx/map team-invitations-fetched)))))) - -;; --- EVENT: fetch-team-webhooks - -(defn team-webhooks-fetched - [webhooks] - (ptk/reify ::team-webhooks-fetched - ptk/UpdateEvent - (update [_ state] - (assoc state :dashboard-team-webhooks webhooks)))) - -(defn fetch-team-webhooks - [] - (ptk/reify ::fetch-team-webhooks - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (->> (rp/cmd! :get-webhooks {:team-id team-id}) - (rx/map team-webhooks-fetched)))))) - ;; --- EVENT: fetch-projects (defn projects-fetched @@ -187,8 +76,10 @@ (ptk/reify ::projects-fetched ptk/UpdateEvent (update [_ state] - (let [projects (d/index-by :id projects)] - (assoc state :dashboard-projects projects))))) + (reduce (fn [state {:keys [id] :as project}] + (update-in state [:projects id] merge project)) + state + projects)))) (defn fetch-projects [] @@ -201,31 +92,28 @@ ;; --- EVENT: search -(defn search-result-fetched - [result] - (ptk/reify ::search-result-fetched - ptk/UpdateEvent - (update [_ state] - (assoc state :dashboard-search-result result)))) - -(def schema:search-params +(def ^:private schema:search-params [:map {:closed true} [:search-term [:maybe :string]]]) +(def ^:private check-search-params + (sm/check-fn schema:search-params)) + (defn search [params] - (dm/assert! schema:search-params params) - (ptk/reify ::search - ptk/UpdateEvent - (update [_ state] - (dissoc state :dashboard-search-result)) + (let [params (check-search-params params)] + (ptk/reify ::search + ptk/UpdateEvent + (update [_ state] + (dissoc state :search-result)) - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state) - params (assoc params :team-id team-id)] - (->> (rp/cmd! :search-files params) - (rx/map search-result-fetched)))))) + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state) + params (assoc params :team-id team-id)] + (->> (rp/cmd! :search-files params) + (rx/map (fn [result] + #(assoc % :search-result result))))))))) ;; --- EVENT: files @@ -241,11 +129,12 @@ ptk/UpdateEvent (update [_ state] (-> state - (update :dashboard-files - (fn [state] - (let [state (remove-project-files state)] - (reduce #(assoc %1 (:id %2) %2) state files)))) - (assoc-in [:dashboard-projects project-id :count] (count files))))))) + (update :files + (fn [files'] + (reduce #(assoc %1 (:id %2) %2) + (remove-project-files files') + files))) + (assoc-in [:projects project-id :count] (count files))))))) (defn fetch-files [{:keys [project-id] :as params}] @@ -264,19 +153,16 @@ ptk/UpdateEvent (update [_ state] (let [files (d/index-by :id files)] - (-> state - (assoc :dashboard-shared-files files) - (update :dashboard-files d/merge files)))))) + (assoc state :shared-files files))))) (defn fetch-shared-files - ([] (fetch-shared-files nil)) - ([team-id] - (ptk/reify ::fetch-shared-files - ptk/WatchEvent - (watch [_ state _] - (let [team-id (or team-id (:current-team-id state))] - (->> (rp/cmd! :get-team-shared-files {:team-id team-id}) - (rx/map shared-files-fetched))))))) + [] + (ptk/reify ::fetch-shared-files + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state)] + (->> (rp/cmd! :get-team-shared-files {:team-id team-id}) + (rx/map shared-files-fetched)))))) ;; --- EVENT: recent-files @@ -287,8 +173,8 @@ (update [_ state] (let [files (d/index-by :id files)] (-> state - (assoc :dashboard-recent-files files) - (update :dashboard-files d/merge files)))))) + (assoc :recent-files files) + (update :files d/merge files)))))) (defn fetch-recent-files [] @@ -325,27 +211,22 @@ (ptk/reify ::clear-file-select ptk/UpdateEvent (update [_ state] - (update state :dashboard-local - assoc :selected-files #{} - :selected-project nil - :menu-open false - :menu-pos nil)))) + (-> state + (dissoc :selected-files) + (dissoc :selected-project) + (update :dashboard-local dissoc :menu-open :menu-pos))))) (defn toggle-file-select [{:keys [id project-id] :as file}] (ptk/reify ::toggle-file-select ptk/UpdateEvent (update [_ state] - (let [selected-project-id (get-in state [:dashboard-local :selected-project])] + (let [selected-project-id (get state :selected-project)] (if (or (nil? selected-project-id) (= selected-project-id project-id)) - (update state :dashboard-local - (fn [local] - (-> local - (update :selected-files #(if (contains? % id) - (disj % id) - (conj % id))) - (assoc :selected-project project-id)))) + (-> state + (update :selected-files #(if (contains? % id) (disj % id) (conj % id))) + (assoc :selected-project project-id)) state))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -399,320 +280,6 @@ ;; Data Modification ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; --- EVENT: create-team - -(defn team-created - [team] - (ptk/reify ::team-created - IDeref - (-deref [_] team))) - -(defn create-team - [{:keys [name] :as params}] - (dm/assert! (string? name)) - (ptk/reify ::create-team - ptk/WatchEvent - (watch [it state _] - (let [{:keys [on-success on-error] - :or {on-success identity - on-error rx/throw}} (meta params) - features (features/get-enabled-features state) - params {:name name :features features}] - (->> (rp/cmd! :create-team (with-meta params (meta it))) - (rx/tap on-success) - (rx/map team-created) - (rx/catch on-error)))))) - -;; --- EVENT: create-team-with-invitations - -(defn create-team-with-invitations - [{:keys [name emails role] :as params}] - (ptk/reify ::create-team-with-invitations - ptk/WatchEvent - (watch [it state _] - (let [{:keys [on-success on-error] - :or {on-success identity - on-error rx/throw}} (meta params) - features (features/get-enabled-features state) - params {:name name - :emails emails - :role role - :features features}] - (->> (rp/cmd! :create-team-with-invitations (with-meta params (meta it))) - (rx/tap on-success) - (rx/map team-created) - (rx/catch on-error)))))) - -;; --- EVENT: update-team - -(defn update-team - [{:keys [id name] :as params}] - (ptk/reify ::update-team - ptk/UpdateEvent - (update [_ state] - (-> state - (assoc-in [:teams id :name] name) - (assoc-in [:team :name] name))) - - ptk/WatchEvent - (watch [_ _ _] - (->> (rp/cmd! :update-team params) - (rx/ignore))))) - -(defn update-team-photo - [file] - (dm/assert! - "expected a valid blob for `file` param" - (di/blob? file)) - (ptk/reify ::update-team-photo - ptk/WatchEvent - (watch [_ state _] - (let [on-success di/notify-finished-loading - on-error #(do (di/notify-finished-loading) - (di/process-error %)) - team-id (:current-team-id state) - prepare #(hash-map :file % :team-id team-id)] - - (di/notify-start-loading) - (->> (rx/of file) - (rx/map di/validate-file) - (rx/map prepare) - (rx/mapcat #(rp/cmd! :update-team-photo %)) - (rx/tap on-success) - (rx/mapcat (fn [_] - (rx/of (du/fetch-teams) - (ptk/data-event ::ev/event - {::ev/name "update-team-photo" - :team-id team-id})))) - (rx/catch on-error)))))) - -(defn update-team-member-role - [{:keys [role member-id] :as params}] - (dm/assert! (uuid? member-id)) - (dm/assert! (contains? ctt/valid-roles role)) - - (ptk/reify ::update-team-member-role - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state) - params (assoc params :team-id team-id)] - (->> (rp/cmd! :update-team-member-role params) - (rx/mapcat (fn [_] - (rx/of (fetch-team-members) - (du/fetch-teams) - (ptk/data-event ::ev/event - {::ev/name "update-team-member-role" - :team-id team-id - :role role - :member-id member-id}))))))))) - -(defn delete-team-member - [{:keys [member-id] :as params}] - (dm/assert! (uuid? member-id)) - (ptk/reify ::delete-team-member - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state) - params (assoc params :team-id team-id)] - (->> (rp/cmd! :delete-team-member params) - (rx/mapcat (fn [_] - (rx/of (fetch-team-members) - (du/fetch-teams) - (ptk/data-event ::ev/event - {::ev/name "delete-team-member" - :team-id team-id - :member-id member-id}))))))))) - -(defn leave-team - [{:keys [reassign-to] :as params}] - (dm/assert! (or (nil? reassign-to) - (uuid? reassign-to))) - - (ptk/reify ::leave-team - ptk/WatchEvent - (watch [_ state _] - (let [{:keys [on-success on-error] - :or {on-success identity - on-error rx/throw}} (meta params) - team-id (:current-team-id state) - params (cond-> {:id team-id} - (uuid? reassign-to) - (assoc :reassign-to reassign-to))] - (->> (rp/cmd! :leave-team params) - (rx/tap #(tm/schedule on-success)) - (rx/map (fn [_] - (ptk/data-event ::ev/event - {::ev/name "leave-team" - :reassign-to reassign-to - :team-id team-id}))) - (rx/catch on-error)))))) - -(defn invite-team-members - [{:keys [emails role team-id resend?] :as params}] - (dm/assert! (keyword? role)) - (dm/assert! (uuid? team-id)) - - (dm/assert! - "expected a valid set of emails" - (sm/check-set-of-emails! emails)) - - (ptk/reify ::invite-team-members - ev/Event - (-data [_] - {:role role - :team-id team-id - :resend resend?}) - - ptk/WatchEvent - (watch [it _ _] - (let [{:keys [on-success on-error] - :or {on-success identity - on-error rx/throw}} (meta params) - params (dissoc params :resend?)] - (->> (rp/cmd! :create-team-invitations (with-meta params (meta it))) - (rx/tap on-success) - (rx/catch on-error)))))) - - -(defn copy-invitation-link - [{:keys [email team-id] :as params}] - (dm/assert! - "expected a valid email" - (sm/check-email! email)) - - (dm/assert! (uuid? team-id)) - - (ptk/reify ::copy-invitation-link - IDeref - (-deref [_] {:email email :team-id team-id}) - - ptk/WatchEvent - (watch [_ state _] - (let [{:keys [on-success on-error] - :or {on-success identity - on-error rx/throw}} (meta params) - router (:router state)] - - (->> (rp/cmd! :get-team-invitation-token params) - (rx/map (fn [params] - (rt/resolve router :auth-verify-token {} params))) - (rx/map (fn [fragment] - (assoc cf/public-uri :fragment fragment))) - (rx/tap (fn [uri] - (wapi/write-to-clipboard (str uri)))) - (rx/tap on-success) - (rx/ignore) - (rx/catch on-error)))))) - - -(defn update-team-invitation-role - [{:keys [email team-id role] :as params}] - (dm/assert! - "expected a valid email" - (sm/check-email! email)) - - (dm/assert! (uuid? team-id)) - (dm/assert! (contains? ctt/valid-roles role)) - - (ptk/reify ::update-team-invitation-role - IDeref - (-deref [_] {:role role}) - - ptk/WatchEvent - (watch [_ _ _] - (let [{:keys [on-success on-error] - :or {on-success identity - on-error rx/throw}} (meta params)] - (->> (rp/cmd! :update-team-invitation-role params) - (rx/tap on-success) - (rx/catch on-error)))))) - -(defn delete-team-invitation - [{:keys [email team-id] :as params}] - (dm/assert! (sm/check-email! email)) - (dm/assert! (uuid? team-id)) - (ptk/reify ::delete-team-invitation - ptk/WatchEvent - (watch [_ _ _] - (let [{:keys [on-success on-error] - :or {on-success identity - on-error rx/throw}} (meta params)] - (->> (rp/cmd! :delete-team-invitation params) - (rx/tap on-success) - (rx/catch on-error)))))) - -(defn delete-team-webhook - [{:keys [id] :as params}] - (dm/assert! (uuid? id)) - (ptk/reify ::delete-team-webhook - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state) - params (assoc params :team-id team-id) - {:keys [on-success on-error] - :or {on-success identity - on-error rx/throw}} (meta params)] - (->> (rp/cmd! :delete-webhook params) - (rx/tap on-success) - (rx/catch on-error)))))) - -(def valid-mtypes - #{"application/json" - "application/x-www-form-urlencoded" - "application/transit+json"}) - -(defn update-team-webhook - [{:keys [id uri mtype is-active] :as params}] - (dm/assert! (uuid? id)) - (dm/assert! (contains? valid-mtypes mtype)) - (dm/assert! (boolean? is-active)) - (dm/assert! (u/uri? uri)) - (ptk/reify ::update-team-webhook - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state) - params (assoc params :team-id team-id) - {:keys [on-success on-error] - :or {on-success identity - on-error rx/throw}} (meta params)] - (->> (rp/cmd! :update-webhook params) - (rx/tap on-success) - (rx/catch on-error)))))) - -(defn create-team-webhook - [{:keys [uri mtype is-active] :as params}] - (dm/assert! (contains? valid-mtypes mtype)) - (dm/assert! (boolean? is-active)) - (dm/assert! (u/uri? uri)) - - (ptk/reify ::create-team-webhook - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state) - params (-> params - (assoc :team-id team-id) - (update :uri str)) - {:keys [on-success on-error] - :or {on-success identity - on-error rx/throw}} (meta params)] - (->> (rp/cmd! :create-webhook params) - (rx/tap on-success) - (rx/catch on-error)))))) - -;; --- EVENT: delete-team - -(defn delete-team - [{:keys [id] :as params}] - (ptk/reify ::delete-team - ptk/WatchEvent - (watch [_ _ _] - (let [{:keys [on-success on-error] - :or {on-success identity - on-error rx/throw}} (meta params)] - (->> (rp/cmd! :delete-team {:id id}) - (rx/tap on-success) - (rx/catch on-error)))))) - ;; --- EVENT: create-project (defn- project-created @@ -724,7 +291,7 @@ ptk/UpdateEvent (update [_ state] (-> state - (assoc-in [:dashboard-projects id] project) + (assoc-in [:projects id] project) (assoc-in [:dashboard-local :project-for-edit] id))))) (defn create-project @@ -732,7 +299,7 @@ (ptk/reify ::create-project ptk/WatchEvent (watch [_ state _] - (let [projects (get state :dashboard-projects) + (let [projects (get state :projects) unames (cfh/get-used-names projects) name (cfh/generate-unique-name unames (str (tr "dashboard.new-project-prefix") " 1")) team-id (:current-team-id state) @@ -753,7 +320,7 @@ (ptk/reify ::project-duplicated ptk/UpdateEvent (update [_ state] - (assoc-in state [:dashboard-projects id] project)))) + (assoc-in state [:projects id] project)))) (defn duplicate-project [{:keys [id name] :as params}] @@ -803,11 +370,11 @@ (ptk/reify ::toggle-project-pin ptk/UpdateEvent (update [_ state] - (assoc-in state [:dashboard-projects id :is-pinned] (not is-pinned))) + (assoc-in state [:projects id :is-pinned] (not is-pinned))) ptk/WatchEvent (watch [_ state _] - (let [project (get-in state [:dashboard-projects id]) + (let [project (get-in state [:projects id]) params (select-keys project [:id :is-pinned :team-id])] (->> (rp/cmd! :update-project-pin params) (rx/ignore)))))) @@ -820,7 +387,7 @@ ptk/UpdateEvent (update [_ state] (-> state - (update-in [:dashboard-projects id :name] (constantly name)) + (update-in [:projects id :name] (constantly name)) (update :dashboard-local dissoc :project-for-edit))) ptk/WatchEvent @@ -836,7 +403,7 @@ (ptk/reify ::delete-project ptk/UpdateEvent (update [_ state] - (update state :dashboard-projects dissoc id)) + (update state :projects dissoc id)) ptk/WatchEvent (watch [_ _ _] @@ -850,7 +417,7 @@ (ptk/reify ::file-deleted ptk/UpdateEvent (update [_ state] - (update-in state [:dashboard-projects project-id :count] dec)))) + (update-in state [:projects project-id :count] dec)))) (defn delete-file [{:keys [id project-id] :as params}] @@ -858,9 +425,9 @@ ptk/UpdateEvent (update [_ state] (-> state - (d/update-when :dashboard-files dissoc id) - (d/update-when :dashboard-shared-files dissoc id) - (d/update-when :dashboard-recent-files dissoc id))) + (d/update-when :files dissoc id) + (d/update-when :shared-files dissoc id) + (d/update-when :recent-files dissoc id))) ptk/WatchEvent (watch [_ state _] @@ -882,9 +449,9 @@ ptk/UpdateEvent (update [_ state] (-> state - (d/update-in-when [:dashboard-files id :name] (constantly name)) - (d/update-in-when [:dashboard-shared-files id :name] (constantly name)) - (d/update-in-when [:dashboard-recent-files id :name] (constantly name)))) + (d/update-in-when [:files id :name] (constantly name)) + (d/update-in-when [:shared-files id :name] (constantly name)) + (d/update-in-when [:recent-files id :name] (constantly name)))) ptk/WatchEvent (watch [_ _ _] @@ -906,10 +473,10 @@ ptk/UpdateEvent (update [_ state] (-> state - (d/update-in-when [:dashboard-files id :is-shared] (constantly is-shared)) - (d/update-in-when [:dashboard-recent-files id :is-shared] (constantly is-shared)) + (d/update-in-when [:files id :is-shared] (constantly is-shared)) + (d/update-in-when [:recent-files id :is-shared] (constantly is-shared)) (cond-> (not is-shared) - (d/update-when :dashboard-shared-files dissoc id)))) + (d/update-when :shared-files dissoc id)))) ptk/WatchEvent (watch [_ _ _] @@ -928,8 +495,8 @@ (= file-id (:id %)) (assoc :thumbnail-id thumbnail-id)))))] (-> state - (d/update-in-when [:dashboard-files file-id] assoc :thumbnail-id thumbnail-id) - (d/update-in-when [:dashboard-recent-files file-id] assoc :thumbnail-id thumbnail-id) + (d/update-in-when [:files file-id] assoc :thumbnail-id thumbnail-id) + (d/update-in-when [:recent-files file-id] assoc :thumbnail-id thumbnail-id) (d/update-when :dashboard-search-result update-search-files)))))) ;; --- EVENT: create-file @@ -946,9 +513,9 @@ ptk/UpdateEvent (update [_ state] (-> state - (assoc-in [:dashboard-files id] file) - (assoc-in [:dashboard-recent-files id] file) - (update-in [:dashboard-projects project-id :count] inc))))) + (assoc-in [:files id] file) + (assoc-in [:recent-files id] file) + (update-in [:projects project-id :count] inc))))) (defn create-file [{:keys [project-id name] :as params}] @@ -963,7 +530,7 @@ :or {on-success identity on-error rx/throw}} (meta params) - files (get state :dashboard-files) + files (get state :files) unames (cfh/get-used-names files) name (or name (cfh/generate-unique-name unames (str (tr "dashboard.new-file-prefix") " 1"))) features (-> (features/get-team-enabled-features state) @@ -1015,14 +582,14 @@ ptk/UpdateEvent (update [_ state] - (let [origin-project (get-in state [:dashboard-files (first ids) :project-id]) + (let [origin-project (get-in state [:files (first ids) :project-id]) update-project (fn [project delta op] (-> project (update :count #(op % (count ids))) (assoc :modified-at (dt/plus (dt/now) {:milliseconds delta}))))] (-> state - (d/update-in-when [:dashboard-projects origin-project] update-project 0 -) - (d/update-in-when [:dashboard-projects project-id] update-project 10 +)))) + (d/update-in-when [:projects origin-project] update-project 0 -) + (d/update-in-when [:projects project-id] update-project 10 +)))) ptk/WatchEvent (watch [_ _ _] @@ -1077,19 +644,14 @@ (defn go-to-files - ([project-id] - (ptk/reify ::go-to-files-1 + ([project-id] (go-to-files project-id nil)) + ([project-id team-id] + (ptk/reify ::go-to-files ptk/WatchEvent (watch [_ state _] - (let [team-id (:current-team-id state)] + (let [team-id (or team-id (:current-team-id state))] (rx/of (rt/nav :dashboard-files {:team-id team-id - :project-id project-id})))))) - ([team-id project-id] - (ptk/reify ::go-to-files-2 - ptk/WatchEvent - (watch [_ _ _] - (rx/of (rt/nav :dashboard-files {:team-id team-id - :project-id project-id})))))) + :project-id project-id}))))))) (defn go-to-search ([] (go-to-search nil)) @@ -1112,21 +674,36 @@ (dom/focus! (dom/get-element "search-input")))))) (defn go-to-projects - ([] - (ptk/reify ::go-to-projects-0 - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (rx/of (rt/nav :dashboard-projects {:team-id team-id})))))) - ([team-id] - (ptk/reify ::go-to-projects-1 - ptk/UpdateEvent - (update [_ state] - ;; FIXME: Remove on 2.5 - (assoc state :current-team-id team-id)) - ptk/WatchEvent - (watch [_ _ _] - (rx/of (rt/nav :dashboard-projects {:team-id team-id})))))) + [team-id] + (ptk/reify ::go-to-projects + ptk/WatchEvent + (watch [_ state _] + (let [team-id (or team-id (:current-team-id state))] + (rx/of (rt/nav :dashboard-projects {:team-id team-id})))))) + +(defn go-to-default-team + "High-level component for redirect to the current profile default + team and hide all modals" + [] + (ptk/reify ::go-to-default-team + ptk/WatchEvent + (watch [_ state _] + (let [team-id (dm/get-in state [:profile :default-team-id])] + (rx/of (go-to-projects team-id) + (modal/hide)))))) + + +(defn go-to-current-team + "High-level component for redirect to the current profile default + team and hide all modals" + [] + (ptk/reify ::go-to-current-team + ptk/WatchEvent + (watch [_ state _] + (let [team-id (or (::current-team-id storage/user) + (dm/get-in state [:profile :default-team-id]))] + (rx/of (go-to-projects team-id) + (modal/hide)))))) (defn go-to-team-members [] @@ -1166,7 +743,7 @@ ptk/WatchEvent (watch [_ state _] (let [team-id (:current-team-id state) - projects (:dashboard-projects state) + projects (:projects state) default-project (d/seek :is-default (vals projects))] (when default-project (rx/of (rt/nav :dashboard-files {:team-id team-id @@ -1190,10 +767,10 @@ pparams (:path-params route) in-project? (contains? pparams :project-id) name (if in-project? - (let [files (get state :dashboard-files) + (let [files (get state :files) unames (cfh/get-used-names files)] (cfh/generate-unique-name unames (str (tr "dashboard.new-file-prefix") " 1"))) - (let [projects (get state :dashboard-projects) + (let [projects (get state :projects) unames (cfh/get-used-names projects)] (cfh/generate-unique-name unames (str (tr "dashboard.new-project-prefix") " 1")))) params (if in-project? @@ -1203,7 +780,7 @@ :team-id team-id}) action-name (if in-project? :create-file :create-project) action (if in-project? file-created project-created) - can-edit? (dm/get-in state [:permissions :can-edit])] + can-edit? (dm/get-in state [:teams team-id :permissions :can-edit])] (when can-edit? (->> (rp/cmd! action-name params) @@ -1214,9 +791,9 @@ (ptk/reify ::open-selected-file ptk/WatchEvent (watch [_ state _] - (let [files (get-in state [:dashboard-local :selected-files])] + (let [[file-id :as files] (get state :selected-files)] (if (= 1 (count files)) - (let [file (get-in state [:dashboard-files (first files)])] + (let [file (dm/get-in state [files file-id])] (rx/of (go-to-workspace file))) (rx/empty)))))) diff --git a/frontend/src/app/main/data/fonts.cljs b/frontend/src/app/main/data/fonts.cljs index 87f670903..2daf207c6 100644 --- a/frontend/src/app/main/data/fonts.cljs +++ b/frontend/src/app/main/data/fonts.cljs @@ -59,10 +59,10 @@ (adapt-font-id [variant] (update variant :font-id #(str "custom-" %)))] - (ptk/reify ::team-fonts-loaded + (ptk/reify ::fonts-loaded ptk/UpdateEvent (update [_ state] - (assoc state :dashboard-fonts (d/index-by :id fonts))) + (assoc state :fonts (d/index-by :id fonts))) ptk/EffectEvent (effect [_ _ _] @@ -72,13 +72,14 @@ (mapv prepare-font))] (fonts/register! :custom fonts)))))) -(defn load-team-fonts - [team-id] +(defn fetch-fonts + [] (ptk/reify ::load-team-fonts ptk/WatchEvent - (watch [_ _ _] - (->> (rp/cmd! :get-font-variants {:team-id team-id}) - (rx/map fonts-fetched))))) + (watch [_ state _] + (let [team-id (:current-team-id state)] + (->> (rp/cmd! :get-font-variants {:team-id team-id}) + (rx/map fonts-fetched)))))) (defn process-upload "Given a seq of blobs and the team id, creates a ready-to-use fonts @@ -90,12 +91,15 @@ variant (or (.getEnglishName ^js font "preferredSubfamily") (.getEnglishName ^js font "fontSubfamily")) - ;; Vertical metrics determine the baseline in a text and the space between lines of text. - ;; For historical reasons, there are three pairs of ascender/descender values, known as hhea, OS/2 and uSWin metrics. - ;; Depending on the font, operating system and application a different set will be used to render text on the screen. - ;; On Mac, Safari and Chrome use the hhea values to render text. Firefox will respect the useTypoMetrics setting and will use the OS/2 if it is set. - ;; If the useTypoMetrics is not set, Firefox will also use metrics from the hhea table. - ;; On Windows, all browsers use the usWin metrics, but respect the useTypoMetrics setting and if set will use the OS/2 values. + ;; Vertical metrics determine the baseline in a text and the space between lines of + ;; text. For historical reasons, there are three pairs of ascender/descender + ;; values, known as hhea, OS/2 and uSWin metrics. Depending on the font, operating + ;; system and application a different set will be used to render text on the + ;; screen. On Mac, Safari and Chrome use the hhea values to render text. Firefox + ;; will respect the useTypoMetrics setting and will use the OS/2 if it is set. If + ;; the useTypoMetrics is not set, Firefox will also use metrics from the hhea + ;; table. On Windows, all browsers use the usWin metrics, but respect the + ;; useTypoMetrics setting and if set will use the OS/2 values. hhea-ascender (abs (-> ^js font .-tables .-hhea .-ascender)) hhea-descender (abs (-> ^js font .-tables .-hhea .-descender)) @@ -239,7 +243,7 @@ (ptk/reify ::add-font ptk/UpdateEvent (update [_ state] - (update state :dashboard-fonts assoc (:id font) font)) + (update state :fonts assoc (:id font) font)) ptk/WatchEvent (watch [_ state _] @@ -260,13 +264,10 @@ (update [_ state] ;; Update all variants that has the same font-id with the new ;; name in the local state. - (update state :dashboard-fonts - (fn [fonts] - (d/mapm (fn [_ font] - (cond-> font - (= id (:font-id font)) - (assoc :font-family name))) - fonts)))) + (update state :fonts update-vals (fn [font] + (cond-> font + (= id (:font-id font)) + (assoc :font-family name))))) ptk/WatchEvent (watch [_ state _] @@ -285,10 +286,11 @@ ptk/UpdateEvent (update [_ state] - (update state :dashboard-fonts + (update state :fonts (fn [variants] (d/removem (fn [[_id variant]] (= (:font-id variant) font-id)) variants)))) + ptk/WatchEvent (watch [_ state _] (let [team-id (:current-team-id state)] @@ -305,7 +307,7 @@ (ptk/reify ::delete-font-variants ptk/UpdateEvent (update [_ state] - (update state :dashboard-fonts + (update state :fonts (fn [variants] (d/removem (fn [[_ variant]] (= (:id variant) id)) diff --git a/frontend/src/app/main/data/team.cljs b/frontend/src/app/main/data/team.cljs new file mode 100644 index 000000000..74805e380 --- /dev/null +++ b/frontend/src/app/main/data/team.cljs @@ -0,0 +1,534 @@ +;; 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) KALEIDOS INC + +(ns app.main.data.team + (:require + [app.common.data.macros :as dm] + [app.common.logging :as log] + [app.common.schema :as sm] + [app.common.types.team :as ctt] + [app.common.uri :as u] + [app.config :as cf] + [app.main.data.events :as ev] + [app.main.data.media :as di] + [app.main.features :as features] + [app.main.repo :as rp] + [app.util.router :as rt] + [app.util.storage :as storage] + [app.util.webapi :as wapi] + [beicon.v2.core :as rx] + [potok.v2.core :as ptk])) + +(log/set-level! :warn) + +(defn get-last-team-id + "Get last accessed team id" + [] + (::current-team-id storage/global)) + +(defn teams-fetched + [teams] + (ptk/reify ::teams-fetched + IDeref + (-deref [_] teams) + + ptk/UpdateEvent + (update [_ state] + (reduce (fn [state {:keys [id] :as team}] + (update-in state [:teams id] merge team)) + state + teams)))) + +(defn fetch-teams + [] + (ptk/reify ::fetch-teams + ptk/WatchEvent + (watch [_ _ _] + (->> (rp/cmd! :get-teams) + (rx/map teams-fetched))))) + +;; --- EVENT: fetch-members + +(defn- members-fetched + [team-id members] + (ptk/reify ::members-fetched + ptk/UpdateEvent + (update [_ state] + (update-in state [:teams team-id] assoc :members members)))) + +(defn fetch-members + [] + (ptk/reify ::fetch-members + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state)] + (->> (rp/cmd! :get-team-members {:team-id team-id}) + (rx/map (partial members-fetched team-id))))))) + +(defn- invitations-fetched + [team-id invitations] + (ptk/reify ::invitations-fetched + ptk/UpdateEvent + (update [_ state] + (update-in state [:teams team-id] assoc :invitations invitations)))) + +(defn fetch-invitations + [] + (ptk/reify ::fetch-invitations + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state)] + (->> (rp/cmd! :get-team-invitations {:team-id team-id}) + (rx/map (partial invitations-fetched team-id))))))) + +(defn set-current-team + [{:keys [id permissions features] :as team}] + (ptk/reify ::set-current-team + ptk/UpdateEvent + (update [_ state] + (-> state + ;; FIXME: redundant operation, only necessary on workspace + ;; until workspace initialization is refactored + (update-in [:teams id] merge team) + (assoc :permissions permissions) + ;; FIXME: this is a redundant operation that only needed by + ;; workspace; ti will not be needed after workspace + ;; bootstrap & urls refactor + (assoc :current-team-id id))) + + ptk/WatchEvent + (watch [_ _ _] + (rx/of (features/initialize (or features #{})))) + + ptk/EffectEvent + (effect [_ _ _] + (swap! storage/global assoc ::current-team-id id)))) + +(defn- team-initialized + [] + (ptk/reify ::team-initialized + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state) + teams (get state :teams) + team (get teams team-id)] + (rx/of (set-current-team team) + (fetch-members)))))) + +(defn initialize-team + [team-id] + (ptk/reify ::initialize-team + ptk/UpdateEvent + (update [_ state] + (assoc state :current-team-id team-id)) + + ptk/WatchEvent + (watch [_ _ stream] + (let [stopper (rx/filter (ptk/type? ::finalize) stream)] + (->> (rx/merge + (rx/of (fetch-teams)) + (->> stream + (rx/filter (ptk/type? ::teams-fetched)) + (rx/observe-on :async) + (rx/map team-initialized))) + (rx/take-until stopper)))))) + +(defn finalize-team + [team-id] + (ptk/reify ::finalize-team + ptk/UpdateEvent + (update [_ state] + (let [team-id' (get state :current-team-id)] + (if (= team-id' team-id) + (-> state + (dissoc :current-team-id) + (dissoc :fonts)) + state))))) + +;; --- ROLES + +(defn update-member-role + [{:keys [role member-id] :as params}] + (dm/assert! (uuid? member-id)) + (dm/assert! (contains? ctt/valid-roles role)) + + (ptk/reify ::update-member-role + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state) + params (assoc params :team-id team-id)] + (->> (rp/cmd! :update-team-member-role params) + (rx/mapcat (fn [_] + (rx/of (fetch-members) + (fetch-teams) + (ptk/data-event ::ev/event + {::ev/name "update-team-member-role" + :team-id team-id + :role role + :member-id member-id}))))))))) + +(defn delete-member + [{:keys [member-id] :as params}] + (dm/assert! (uuid? member-id)) + (ptk/reify ::delete-member + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state) + params (assoc params :team-id team-id)] + (->> (rp/cmd! :delete-team-member params) + (rx/mapcat (fn [_] + (rx/of (fetch-members) + (fetch-teams) + (ptk/data-event ::ev/event + {::ev/name "delete-team-member" + :team-id team-id + :member-id member-id}))))))))) + + +(defn- stats-fetched + [team-id stats] + (ptk/reify ::stats-fetched + ptk/UpdateEvent + (update [_ state] + (update-in state [:teams team-id] assoc :stats stats)))) + +(defn fetch-stats + [] + (ptk/reify ::fetch-stats + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state)] + (->> (rp/cmd! :get-team-stats {:team-id team-id}) + (rx/map (partial stats-fetched team-id))))))) + +(defn- webhooks-fetched + [team-id webhooks] + (ptk/reify ::webhooks-fetched + ptk/UpdateEvent + (update [_ state] + (update-in state [:team-id team-id] assoc :webhooks webhooks)))) + +(defn fetch-webhooks + [] + (ptk/reify ::fetch-webhooks + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state)] + (->> (rp/cmd! :get-webhooks {:team-id team-id}) + (rx/map (partial webhooks-fetched team-id))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Data Modification +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn update-team-photo + [file] + (dm/assert! + "expected a valid blob for `file` param" + (di/blob? file)) + (ptk/reify ::update-team-photo + ptk/WatchEvent + (watch [_ state _] + (let [on-success di/notify-finished-loading + on-error #(do (di/notify-finished-loading) + (di/process-error %)) + team-id (:current-team-id state) + prepare #(hash-map :file % :team-id team-id)] + + (di/notify-start-loading) + (->> (rx/of file) + (rx/map di/validate-file) + (rx/map prepare) + (rx/mapcat #(rp/cmd! :update-team-photo %)) + (rx/tap on-success) + (rx/mapcat (fn [_] + (rx/of (fetch-teams) + (ptk/data-event ::ev/event + {::ev/name "update-team-photo" + :team-id team-id})))) + (rx/catch on-error)))))) + + +;; --- EVENT: create-team + +(defn- team-created + [team] + (ptk/reify ::team-created + IDeref + (-deref [_] team))) + +(defn create-team + [{:keys [name] :as params}] + (dm/assert! (string? name)) + (ptk/reify ::create-team + ptk/WatchEvent + (watch [it state _] + (let [{:keys [on-success on-error] + :or {on-success identity + on-error rx/throw}} (meta params) + features (features/get-enabled-features state) + params {:name name :features features}] + (->> (rp/cmd! :create-team (with-meta params (meta it))) + (rx/tap on-success) + (rx/map team-created) + (rx/catch on-error)))))) + +;; --- EVENT: create-team-with-invitations + +(defn create-team-with-invitations + [{:keys [name emails role] :as params}] + (ptk/reify ::create-team-with-invitations + ptk/WatchEvent + (watch [it state _] + (let [{:keys [on-success on-error] + :or {on-success identity + on-error rx/throw}} (meta params) + features (features/get-enabled-features state) + params {:name name + :emails emails + :role role + :features features}] + (->> (rp/cmd! :create-team-with-invitations (with-meta params (meta it))) + (rx/tap on-success) + (rx/map team-created) + (rx/catch on-error)))))) + +(defn update-team + [{:keys [id name] :as params}] + (ptk/reify ::update-team + ptk/UpdateEvent + (update [_ state] + (assoc-in state [:teams id :name] name)) + + ptk/WatchEvent + (watch [_ _ _] + (->> (rp/cmd! :update-team params) + (rx/ignore))))) + +(defn- team-leaved + [{:keys [id] :as params}] + (ptk/reify ::team-leaved + IDeref + (-deref [_] params) + + ptk/UpdateEvent + (update [_ state] + (update state :teams dissoc id)) + + ptk/EffectEvent + (effect [_ state _] + (let [teams (get state :teams)] + (when-let [ctid (::current-team-id storage/user)] + (when-not (contains? teams ctid) + (swap! storage/user dissoc ::current-team-id))))))) + +(defn leave-current-team + "High-level event for leave team, mainly executed from the + dashboard. It automatically redirects user to the default team, once + the team-leave operation succeed" + [{:keys [reassign-to] :as params}] + + (when reassign-to + (assert (uuid? reassign-to) "expect a valid uuid for `reassign-to`")) + + (ptk/reify ::leave-team + ptk/WatchEvent + (watch [_ state _] + (let [team-id (get state :current-team-id) + params (assoc params :id team-id) + + {:keys [on-error on-success] + :or {on-success rx/empty + on-error rx/throw}} + (meta params)] + + (->> (rp/cmd! :leave-team params) + (rx/mapcat + (fn [_] + (rx/merge + (rx/of (team-leaved params) + (fetch-teams) + (ptk/data-event ::ev/event + {::ev/name "leave-team" + :reassign-to reassign-to + :team-id team-id})) + (on-success)))) + (rx/catch on-error)))))) + +(defn create-invitations + [{:keys [emails role team-id resend?] :as params}] + (dm/assert! (keyword? role)) + (dm/assert! (uuid? team-id)) + + (dm/assert! + "expected a valid set of emails" + (sm/check-set-of-emails! emails)) + + (ptk/reify ::create-invitations + ev/Event + (-data [_] + {:role role + :team-id team-id + :resend resend?}) + + ptk/WatchEvent + (watch [it _ _] + (let [{:keys [on-success on-error] + :or {on-success identity + on-error rx/throw}} (meta params) + params (dissoc params :resend?)] + (->> (rp/cmd! :create-team-invitations (with-meta params (meta it))) + (rx/tap on-success) + (rx/catch on-error)))))) + +(defn copy-invitation-link + [{:keys [email team-id] :as params}] + (dm/assert! + "expected a valid email" + (sm/check-email! email)) + + (dm/assert! (uuid? team-id)) + + (ptk/reify ::copy-invitation-link + IDeref + (-deref [_] {:email email :team-id team-id}) + + ptk/WatchEvent + (watch [_ state _] + (let [{:keys [on-success on-error] + :or {on-success identity + on-error rx/throw}} (meta params) + router (:router state)] + + (->> (rp/cmd! :get-team-invitation-token params) + (rx/map (fn [params] + (rt/resolve router :auth-verify-token {} params))) + (rx/map (fn [fragment] + (assoc cf/public-uri :fragment fragment))) + (rx/tap (fn [uri] + (wapi/write-to-clipboard (str uri)))) + (rx/tap on-success) + (rx/ignore) + (rx/catch on-error)))))) + +(defn update-invitation-role + [{:keys [email team-id role] :as params}] + (dm/assert! + "expected a valid email" + (sm/check-email! email)) + + (dm/assert! (uuid? team-id)) + (dm/assert! (contains? ctt/valid-roles role)) + + (ptk/reify ::update-invitation-role + IDeref + (-deref [_] {:role role}) + + ptk/WatchEvent + (watch [_ _ _] + (let [{:keys [on-success on-error] + :or {on-success identity + on-error rx/throw}} (meta params)] + (->> (rp/cmd! :update-team-invitation-role params) + (rx/tap on-success) + (rx/catch on-error)))))) + +(defn delete-invitation + [{:keys [email team-id] :as params}] + (dm/assert! (sm/check-email! email)) + (dm/assert! (uuid? team-id)) + (ptk/reify ::delete-invitation + ptk/WatchEvent + (watch [_ _ _] + (let [{:keys [on-success on-error] + :or {on-success identity + on-error rx/throw}} (meta params)] + (->> (rp/cmd! :delete-team-invitation params) + (rx/tap on-success) + (rx/catch on-error)))))) + +(defn delete-team + [{:keys [id] :as params}] + (ptk/reify ::delete-team + ptk/WatchEvent + (watch [_ _ _] + (let [{:keys [on-success on-error] + :or {on-success rx/empty + on-error rx/throw}} + (meta params)] + + (->> (rp/cmd! :delete-team {:id id}) + (rx/mapcat on-success) + (rx/catch on-error)))))) + +(defn delete-webhook + [{:keys [id] :as params}] + (dm/assert! (uuid? id)) + + (ptk/reify ::delete-webhook + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state) + params (assoc params :team-id team-id) + {:keys [on-success on-error] + :or {on-success identity + on-error rx/throw}} (meta params)] + (->> (rp/cmd! :delete-webhook params) + (rx/tap on-success) + (rx/catch on-error)))))) + +(def valid-mtypes + #{"application/json" + "application/x-www-form-urlencoded" + "application/transit+json"}) + +(defn update-webhook + [{:keys [id uri mtype is-active] :as params}] + (dm/assert! (uuid? id)) + (dm/assert! (contains? valid-mtypes mtype)) + (dm/assert! (boolean? is-active)) + (dm/assert! (u/uri? uri)) + + (ptk/reify ::update-webhook + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state) + params (assoc params :team-id team-id) + {:keys [on-success on-error] + :or {on-success rx/empty + on-error rx/throw}} (meta params)] + (->> (rp/cmd! :update-webhook params) + (rx/mapcat (fn [_] + (rx/concat + (on-success) + (rx/of (fetch-webhooks))))) + (rx/catch on-error)))))) + +(defn create-webhook + [{:keys [uri mtype is-active] :as params}] + (dm/assert! (contains? valid-mtypes mtype)) + (dm/assert! (boolean? is-active)) + (dm/assert! (u/uri? uri)) + + (ptk/reify ::create-webhook + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state) + params (-> params + (assoc :team-id team-id) + (update :uri str)) + {:keys [on-success on-error] + :or {on-success rx/empty + on-error rx/throw}} (meta params)] + (->> (rp/cmd! :create-webhook params) + (rx/mapcat (fn [_] + (rx/concat + (on-success) + (rx/of (fetch-webhooks))))) + (rx/catch on-error)))))) + + + diff --git a/frontend/src/app/main/data/users.cljs b/frontend/src/app/main/data/users.cljs index 237cdde46..8ace629bc 100644 --- a/frontend/src/app/main/data/users.cljs +++ b/frontend/src/app/main/data/users.cljs @@ -8,19 +8,16 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.common.exceptions :as ex] [app.common.schema :as sm] [app.common.spec :as us] [app.common.uuid :as uuid] [app.config :as cf] [app.main.data.events :as ev] [app.main.data.media :as di] - [app.main.data.notifications :as ntf] - [app.main.data.websocket :as ws] - [app.main.features :as features] + [app.main.data.team :as-alias dtm] [app.main.repo :as rp] - [app.plugins.register :as register] - [app.util.i18n :as i18n :refer [tr]] + [app.plugins.register :as plugins.register] + [app.util.i18n :as i18n] [app.util.router :as rt] [app.util.storage :as storage] [beicon.v2.core :as rx] @@ -40,7 +37,7 @@ [:lang {:optional true} :string] [:theme {:optional true} :string]]) -(def check-profile! +(def check-profile (sm/check-fn schema:profile)) ;; --- HELPERS @@ -49,98 +46,31 @@ [{:keys [id]}] (and (uuid? id) (not= id uuid/zero))) -(defn get-current-team-id - [profile] - (let [team-id (::current-team-id storage/user)] - (or team-id (:default-team-id profile)))) - -(defn set-current-team! - [team-id] - (if (nil? team-id) - (swap! storage/user dissoc ::current-team-id) - (swap! storage/user assoc ::current-team-id team-id))) - -;; --- EVENT: fetch-teams - -(defn teams-fetched - [teams] - (ptk/reify ::teams-fetched - IDeref - (-deref [_] teams) - - ptk/UpdateEvent - (update [_ state] - (assoc state :teams (d/index-by :id teams))) - - ptk/EffectEvent - (effect [_ _ _] - ;; Check if current team-id is part of available teams - ;; if not, dissoc it from storage. - - (let [ids (into #{} (map :id) teams)] - (when-let [ctid (::current-team-id storage/user)] - (when-not (contains? ids ctid) - (swap! storage/user dissoc ::current-team-id))))))) - -(defn fetch-teams - [] - (ptk/reify ::fetch-teams - ptk/WatchEvent - (watch [_ _ _] - (->> (rp/cmd! :get-teams) - (rx/map teams-fetched))))) - -(defn set-current-team - [team] - (ptk/reify ::set-current-team - ptk/UpdateEvent - (update [_ state] - (-> state - (assoc :team team) - (assoc :permissions (:permissions team)) - (assoc :current-team-id (:id team)))) - - ptk/WatchEvent - (watch [_ _ _] - (rx/of (features/initialize (:features team #{})))) - - ptk/EffectEvent - (effect [_ _ _] - (set-current-team! (:id team))))) - ;; --- EVENT: fetch-profile -(declare logout) - -(def profile-fetched? - (ptk/type? ::profile-fetched)) - -(defn profile-fetched +(defn initialize-profile + "Initialize profile state, only logged-in profile data should be + passed to this event" [{:keys [id] :as profile}] - (ptk/reify ::profile-fetched + (ptk/reify ::initialize-profile IDeref (-deref [_] profile) ptk/UpdateEvent (update [_ state] - (cond-> state - (is-authenticated? profile) - (-> (assoc :profile-id id) - (assoc :profile profile)))) + (-> state + (assoc :profile-id id) + (assoc :profile profile))) ptk/EffectEvent (effect [_ state _] - (let [profile (:profile state) - email (:email profile) - previous-profile (:profile storage/user) - previous-email (:email previous-profile)] - (when profile - (swap! storage/user assoc :profile profile) - (i18n/set-locale! (:lang profile)) - (when (not= previous-email email) - (set-current-team! nil)) + (let [profile (:profile state)] + (swap! storage/user assoc :profile profile) + (i18n/set-locale! (:lang profile)) + (plugins.register/init))))) - (register/init)))))) +(def profile-fetched? + (ptk/type? ::profile-fetched)) (defn- on-fetch-profile-exception [cause] @@ -160,213 +90,9 @@ ptk/WatchEvent (watch [_ _ _] (->> (rp/cmd! :get-profile) - (rx/map profile-fetched) + (rx/map (partial ptk/data-event ::profile-fetched)) (rx/catch on-fetch-profile-exception))))) -;; --- EVENT: login - -(defn- logged-in - "This is the main event that is executed once we have logged in - profile. The profile can proceed from standard login or from - accepting invitation, or third party auth signup or singin." - [profile] - (letfn [(get-redirect-events [] - (let [team-id (get-current-team-id profile) - welcome-file-id (dm/get-in profile [:props :welcome-file-id]) - redirect-href (:login-redirect @storage/session) - current-href (rt/get-current-href)] - - (cond - (some? redirect-href) - (binding [storage/*sync* true] - (swap! storage/session dissoc :login-redirect) - (if (= current-href redirect-href) - (rx/of (rt/reload true)) - (rx/of (rt/nav-raw :href redirect-href)))) - - (some? welcome-file-id) - (rx/of (rt/nav' :workspace {:project-id (:default-project-id profile) - :file-id welcome-file-id}) - (update-profile-props {:welcome-file-id nil})) - - :else - (rx/of (rt/nav' :dashboard-projects {:team-id team-id})))))] - - (ptk/reify ::logged-in - ev/Event - (-data [_] - {::ev/name "signin" - ::ev/type "identify" - :email (:email profile) - :auth-backend (:auth-backend profile) - :fullname (:fullname profile) - :is-muted (:is-muted profile) - :default-team-id (:default-team-id profile) - :default-project-id (:default-project-id profile)}) - - ptk/WatchEvent - (watch [_ _ _] - (when (is-authenticated? profile) - (->> (rx/concat - (rx/of (profile-fetched profile) - (fetch-teams) - (ws/initialize)) - (get-redirect-events)) - (rx/observe-on :async))))))) - -(declare login-from-register) - -(defn login - [{:keys [email password invitation-token] :as data}] - (ptk/reify ::login - ptk/WatchEvent - (watch [_ _ stream] - (let [{:keys [on-error on-success] - :or {on-error rx/throw - on-success identity}} (meta data) - - params {:email email - :password password - :invitation-token invitation-token}] - - ;; NOTE: We can't take the profile value from login because - ;; there are cases when login is successful but the cookie is - ;; not set properly (because of possible misconfiguration). - ;; So, we proceed to make an additional call to fetch the - ;; profile, and ensure that cookie is set correctly. If - ;; profile fetch is successful, we mark the user logged in, if - ;; the returned profile is an NOT authenticated profile, we - ;; proceed to logout and show an error message. - - (->> (rp/cmd! :login-with-password (d/without-nils params)) - (rx/merge-map (fn [data] - (rx/merge - (rx/of (fetch-profile)) - (->> stream - (rx/filter profile-fetched?) - (rx/take 1) - (rx/map deref) - (rx/filter (complement is-authenticated?)) - (rx/tap on-error) - (rx/map #(ex/raise :type :authentication)) - (rx/observe-on :async)) - - (->> stream - (rx/filter profile-fetched?) - (rx/take 1) - (rx/map deref) - (rx/filter is-authenticated?) - (rx/map (fn [profile] - (with-meta (merge data profile) - {::ev/source "login"}))) - (rx/tap on-success) - (rx/map logged-in) - (rx/observe-on :async))))) - (rx/catch on-error)))))) - -(def ^:private schema:login-with-ldap - [:map {:title "login-with-ldap"} - [:email ::sm/email] - [:password :string]]) - -(defn login-with-ldap - [params] - - (dm/assert! - "expected valid params" - (sm/check schema:login-with-ldap params)) - - (ptk/reify ::login-with-ldap - ptk/WatchEvent - (watch [_ _ _] - (let [{:keys [on-error on-success] - :or {on-error rx/throw - on-success identity}} (meta params)] - (->> (rp/cmd! :login-with-ldap params) - (rx/tap on-success) - (rx/map (fn [profile] - (-> profile - (with-meta {::ev/source "login-with-ldap"}) - (logged-in)))) - (rx/catch on-error)))))) - -(defn login-from-token - "Used mainly as flow continuation after token validation." - [{:keys [profile] :as tdata}] - (ptk/reify ::login-from-token - ptk/WatchEvent - (watch [_ _ _] - (->> (rx/of (logged-in (with-meta profile {::ev/source "login-with-token"}))) - ;; NOTE: we need this to be asynchronous because the effect - ;; should be called before proceed with the login process - (rx/observe-on :async))) - - ptk/EffectEvent - (effect [_ _ _] - (set-current-team! nil)))) - -(defn login-from-register - "Event used mainly for mark current session as logged-in in after the - user successfully registered using third party auth provider (in this - case we dont need to verify the email)." - [] - (ptk/reify ::login-from-register - ptk/WatchEvent - (watch [_ _ stream] - (rx/merge - (rx/of (fetch-profile)) - (->> stream - (rx/filter (ptk/type? ::profile-fetched)) - (rx/take 1) - (rx/map deref) - (rx/map (fn [profile] - (with-meta profile - {::ev/source "register"}))) - (rx/map logged-in) - (rx/observe-on :async)))))) - -;; --- EVENT: logout - -(defn logged-out - ([] (logged-out {})) - ([_params] - (ptk/reify ::logged-out - ptk/UpdateEvent - (update [_ state] - (select-keys state [:route :router :session-id :history])) - - ptk/WatchEvent - (watch [_ _ _] - (rx/merge - ;; NOTE: We need the `effect` of the current event to be - ;; executed before the redirect. - (->> (rx/of (rt/nav :auth-login)) - (rx/observe-on :async)) - (rx/of (ws/finalize)))) - - ptk/EffectEvent - (effect [_ _ _] - ;; We prefer to keek some stuff in the storage like the current-team-id and the profile - (swap! storage/user (constantly {})))))) - -(defn logout - ([] (logout {})) - ([params] - (ptk/reify ::logout - ev/Event - (-data [_] {}) - - ptk/WatchEvent - (watch [_ state _] - (let [profile-id (:profile-id state)] - (->> (rx/interval 500) - (rx/take 1) - (rx/mapcat (fn [_] - (->> (rp/cmd! :logout {:profile-id profile-id}) - (rx/delay-at-least 300) - (rx/catch (constantly (rx/of 1)))))) - (rx/map #(logged-out params)))))))) - ;; --- Update Profile (defn persist-profile @@ -386,7 +112,7 @@ [data] (dm/assert! "expected valid profile data" - (check-profile! data)) + (check-profile data)) (ptk/reify ::update-profile ptk/WatchEvent @@ -589,6 +315,9 @@ ;; --- EVENT: request-account-deletion +(def profile-deleted? + (ptk/type? ::profile-deleted)) + (defn request-account-deletion [params] (ptk/reify ::request-account-deletion @@ -599,7 +328,8 @@ on-success identity}} (meta params)] (->> (rp/cmd! :delete-profile {}) (rx/tap on-success) - (rx/map logged-out) + (rx/map (fn [_] + (ptk/data-event ::profile-deleted params))) (rx/catch on-error) (rx/delay-at-least 300)))))) @@ -652,16 +382,6 @@ (rx/tap on-success) (rx/catch on-error)))))) -;; --- EVENT: crete-demo-profile - -(defn create-demo-profile - [] - (ptk/reify ::create-demo-profile - ptk/WatchEvent - (watch [_ _ _] - (->> (rp/cmd! :create-demo-profile {}) - (rx/map login))))) - ;; --- EVENT: fetch-team-webhooks (defn access-tokens-fetched @@ -716,28 +436,3 @@ (rx/tap on-success) (rx/catch on-error)))))) -(defn show-redirect-error - "A helper event that interprets the OIDC redirect errors on the URI - and shows an appropriate error message using the notification - banners." - [error] - (ptk/reify ::show-redirect-error - ptk/WatchEvent - (watch [_ _ _] - (when-let [hint (case error - "registration-disabled" - (tr "errors.registration-disabled") - "profile-blocked" - (tr "errors.profile-blocked") - "auth-provider-not-allowed" - (tr "errors.auth-provider-not-allowed") - "email-domain-not-allowed" - (tr "errors.email-domain-not-allowed") - - ;; We explicitly do not show any error here, it a explicit user operation. - "unable-to-auth" - nil - - (tr "errors.generic"))] - - (rx/of (ntf/warn hint)))))) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index e2ad1f6d8..526727345 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -43,6 +43,7 @@ [app.main.data.notifications :as ntf] [app.main.data.persistence :as dps] [app.main.data.plugins :as dp] + [app.main.data.team :as dtm] [app.main.data.users :as du] [app.main.data.workspace.bool :as dwb] [app.main.data.workspace.collapse :as dwco] @@ -167,6 +168,7 @@ (->> (rx/concat ;; Initialize notifications + ;; FIXME: this should not be initialized here looks like (rx/of (dwn/initialize team-id file-id) (dwsl/initialize)) @@ -174,11 +176,11 @@ ;; fully loadad before mark workspace as initialized (rx/merge (->> stream - (rx/filter (ptk/type? ::df/team-fonts-loaded)) + (rx/filter (ptk/type? ::df/fonts-loaded)) (rx/take 1) (rx/ignore)) - (rx/of (df/load-team-fonts team-id)) + (rx/of (df/fetch-fonts)) ;; FIXME: move to bundle fetch stages @@ -275,20 +277,29 @@ ;; steps (->> (rx/from wasm/module) (rx/ignore)) + (->> (rp/cmd! :get-team {:id (:team-id project)}) (rx/mapcat (fn [team] (let [bundle {:team team :project project :file-id file-id :project-id project-id}] - (rx/of (du/set-current-team team) + ;; FIXME: this should not be handled here, pending + ;; refactor of urls and team initialization + ;; normalization + (rx/of (dtm/set-current-team team) (ptk/data-event ::bundle-stage-1 bundle))))))))) (rx/take-until (rx/filter (ptk/type? ::fetch-bundle) stream)))))) (defn- fetch-bundle-stage-2 - [{:keys [file-id project-id] :as bundle}] + [{:keys [file-id project-id project] :as bundle}] (ptk/reify ::fetch-bundle-stage-2 + ptk/UpdateEvent + (update [_ state] + (-> state + (update :projects assoc project-id project))) + ptk/WatchEvent (watch [_ state stream] (let [features (features/get-team-enabled-features state) @@ -419,7 +430,6 @@ :workspace-media-objects :workspace-persistence :workspace-presence - :workspace-project :workspace-ready? :workspace-undo) (update :workspace-global dissoc :read-only?) @@ -1148,9 +1158,9 @@ ptk/WatchEvent (watch [_ state _] - (let [project-id (get-in state [:workspace-project :id]) - file-id (get-in state [:workspace-file :id]) - page-id (get state :current-page-id) + (let [project-id (:current-project-id state) + file-id (:current-file-id state) + page-id (:current-page-id state) pparams {:file-id file-id :project-id project-id} qparams {:page-id page-id :layout (name layout)}] (rx/of (rt/nav :workspace pparams qparams)))))) @@ -1300,9 +1310,10 @@ (let [components-v2 (features/active-feature? state "components/v2")] (if components-v2 (rx/of (go-to-main-instance nil component-id)) - (let [project-id (get-in state [:workspace-project :id]) - file-id (get-in state [:workspace-file :id]) - page-id (get state :current-page-id) + (let [file-id (:current-file-id state) + project-id (:current-project-id state) + page-id (:current-page-id state) + pparams {:file-id file-id :project-id project-id} qparams {:page-id page-id :layout :assets}] (rx/of (rt/nav :workspace pparams qparams) @@ -1322,9 +1333,9 @@ (ptk/reify ::show-component-in-assets ptk/WatchEvent (watch [_ state _] - (let [project-id (get-in state [:workspace-project :id]) - file-id (get-in state [:workspace-file :id]) - page-id (get state :current-page-id) + (let [project-id (:current-project-id state) + file-id (:current-file-id state) + page-id (:current-page-id state) pparams {:file-id file-id :project-id project-id} qparams {:page-id page-id :layout :assets} component-path (cfh/split-path (get-in state [:workspace-data :components component-id :path])) diff --git a/frontend/src/app/main/errors.cljs b/frontend/src/app/main/errors.cljs index 7a4f2e2e4..2a59336ed 100644 --- a/frontend/src/app/main/errors.cljs +++ b/frontend/src/app/main/errors.cljs @@ -10,9 +10,9 @@ [app.common.exceptions :as ex] [app.common.pprint :as pp] [app.common.schema :as sm] + [app.main.data.auth :as da] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] - [app.main.data.users :as du] [app.main.store :as st] [app.util.globals :as glob] [app.util.i18n :refer [tr]] @@ -116,7 +116,7 @@ (if show-oops? (st/async-emit! (rt/assign-exception e)) (do - (st/emit! (du/logout {:capture-redirect true})) + (st/emit! (da/logout)) (ts/schedule 500 #(st/emit! (ntf/warn msg))))))) ;; Error that happens on an active business model validation does not diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 4ac723f5c..cc7e193a8 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -31,10 +31,14 @@ (l/derived :profile st/state)) (def team - (l/derived :team st/state)) + (l/derived (fn [state] + (let [team-id (:current-team-id state) + teams (:teams state)] + (get teams team-id))) + st/state)) (def permissions - (l/derived :permissions st/state)) + (l/derived :permissions team)) (def teams (l/derived :teams st/state)) @@ -54,68 +58,37 @@ (def persistence (l/derived :persistence st/state)) -;; ---- Dashboard refs +(def projects + (l/derived :projects st/state)) -(def dashboard-local - (l/derived :dashboard-local st/state)) +(def files + (l/derived :files st/state)) -(def dashboard-fonts - (l/derived :dashboard-fonts st/state)) +(def shared-files + (l/derived :shared-files st/state)) -(def dashboard-projects - (l/derived :dashboard-projects st/state)) - -(def dashboard-files - (l/derived :dashboard-files st/state)) - -(def dashboard-shared-files - (l/derived :dashboard-shared-files st/state)) - -(def dashboard-search-result - (l/derived :dashboard-search-result st/state)) - -(def dashboard-team-stats - (l/derived :dashboard-team-stats st/state)) - -(def dashboard-team-members - (l/derived :dashboard-team-members st/state)) - -(def dashboard-team-invitations - (l/derived :dashboard-team-invitations st/state)) - -(def dashboard-team-webhooks - (l/derived :dashboard-team-webhooks st/state)) - -(def dashboard-selected-project - (l/derived (fn [state] - (dm/get-in state [:dashboard-local :selected-project])) - st/state)) - -(defn- dashboard-extract-selected +(defn extract-selected-files [files selected] (let [get-file #(get files %) sim-file #(select-keys % [:id :name :project-id :is-shared]) xform (comp (keep get-file) (map sim-file))] - (->> (into #{} xform selected) + (->> (sequence xform selected) (d/index-by :id)))) -(def dashboard-selected-search +(def selected-files (l/derived (fn [state] - ;; we need to this because :dashboard-search-result is a list - ;; of maps and we need a map of maps (using :id as key). - (let [files (d/index-by :id (:dashboard-search-result state))] - (->> (dm/get-in state [:dashboard-local :selected-files]) - (dashboard-extract-selected files)))) + (let [selected (get state :selected-files) + files (get state :files)] + (extract-selected-files files selected))) st/state)) -(def dashboard-selected-files - (l/derived (fn [state] - (->> (dm/get-in state [:dashboard-local :selected-files]) - (dashboard-extract-selected (:dashboard-files state)))) - st/state)) +(def selected-project + (l/derived :selected-project st/state)) -;; ---- Workspace refs + +(def dashboard-local + (l/derived :dashboard-local st/state)) (def render-state (l/derived :render-state st/state)) diff --git a/frontend/src/app/main/ui.cljs b/frontend/src/app/main/ui.cljs index 17ce25d75..90605632d 100644 --- a/frontend/src/app/main/ui.cljs +++ b/frontend/src/app/main/ui.cljs @@ -7,7 +7,9 @@ (ns app.main.ui (:require [app.config :as cf] + [app.main.data.team :as dtm] [app.main.refs :as refs] + [app.main.store :as st] [app.main.ui.context :as ctx] [app.main.ui.debug.icons-preview :refer [icons-preview]] [app.main.ui.error-boundary :refer [error-boundary*]] @@ -34,7 +36,7 @@ (mf/lazy-component app.main.ui.viewer/viewer)) (def dashboard-page - (mf/lazy-component app.main.ui.dashboard/dashboard)) + (mf/lazy-component app.main.ui.dashboard/dashboard*)) (def settings-page (mf/lazy-component app.main.ui.settings/settings)) @@ -42,12 +44,33 @@ (def workspace-page (mf/lazy-component app.main.ui.workspace/workspace)) -(mf/defc main-page +(mf/defc team-container* + {::mf/props :obj + ::mf/private true} + [{:keys [team-id children]}] + + (mf/with-effect [team-id] + (st/emit! (dtm/initialize-team team-id)) + (fn [] + (st/emit! (dtm/finalize-team team-id)))) + + (let [team (mf/deref refs/team)] + (when (= team-id (:id team)) + [:& (mf/provider ctx/current-team-id) {:value team-id} + [:& (mf/provider ctx/permissions) {:value (:permissions team)} + ;; The `:key` is mandatory here because we want to reinitialize + ;; all dom tree instead of simple rerender. + [:* {:key (str team-id)} children]]]))) + +(mf/defc page-container* {::mf/props :obj ::mf/private true} [{:keys [route profile]}] (let [{:keys [data params]} route - props (get profile :props) + props (get profile :props) + route-name (get data :name) + + show-question-modal? (and (contains? cf/flags :onboarding) (not (:onboarding-viewed props)) @@ -72,7 +95,7 @@ (not= "0.0" (:main cf/version)))] [:& (mf/provider ctx/current-route) {:value route} - (case (:name data) + (case route-name (:auth-login :auth-register :auth-register-validate @@ -105,26 +128,66 @@ :dashboard-team-invitations :dashboard-team-webhooks :dashboard-team-settings) - [:? - #_[:& app.main.ui.releases/release-notes-modal {:version "2.3"}] - #_[:& app.main.ui.onboarding/onboarding-templates-modal] - #_[:& app.main.ui.onboarding/onboarding-modal] - #_[:& app.main.ui.onboarding.team-choice/onboarding-team-modal] + (let [team-id (some-> params :path :team-id uuid) + project-id (some-> params :path :project-id uuid) + search-term (some-> params :query :search-term) + plugin-url (some-> params :query :plugin)] - (cond - show-question-modal? - [:& questions-modal] + [:? + #_[:& app.main.ui.releases/release-notes-modal {:version "2.3"}] + #_[:& app.main.ui.onboarding/onboarding-templates-modal] + #_[:& app.main.ui.onboarding/onboarding-modal] + #_[:& app.main.ui.onboarding.team-choice/onboarding-team-modal] - show-newsletter-modal? - [:& onboarding-newsletter] + (cond + show-question-modal? + [:& questions-modal] - show-team-modal? - [:& onboarding-team-modal {:go-to-team? true}] + show-newsletter-modal? + [:& onboarding-newsletter] + + show-team-modal? + [:& onboarding-team-modal {:go-to-team? true}] + + show-release-modal? + [:& release-notes-modal {:version (:main cf/version)}]) + + [:> team-container* {:team-id team-id} + [:> dashboard-page {:profile profile + :route-name route-name + :team-id team-id + :search-term search-term + :plugin-url plugin-url + :project-id project-id}]]]) + + :workspace + (let [project-id (some-> params :path :project-id uuid) + file-id (some-> params :path :file-id uuid) + page-id (some-> params :query :page-id uuid) + layout (some-> params :query :layout keyword)] + [:? {} + (when (cf/external-feature-flag "onboarding-03" "test") + (cond + show-question-modal? + [:& questions-modal] + + show-newsletter-modal? + [:& onboarding-newsletter] + + show-team-modal? + [:& onboarding-team-modal {:go-to-team? false}] + + show-release-modal? + [:& release-notes-modal {:version (:main cf/version)}])) + + [:* + [:& workspace-page {:project-id project-id + :file-id file-id + :page-id page-id + :layout-name layout + :key file-id}]]]) - show-release-modal? - [:& release-notes-modal {:version (:main cf/version)}]) - [:& dashboard-page {:route route :profile profile}]] :viewer (let [{:keys [query-params path-params]} route {:keys [index share-id section page-id interactions-mode frame-id share] @@ -151,37 +214,12 @@ :frame-id frame-id :share share}])]) - :workspace - (let [project-id (some-> params :path :project-id uuid) - file-id (some-> params :path :file-id uuid) - page-id (some-> params :query :page-id uuid) - layout (some-> params :query :layout keyword)] - [:? {} - (when (cf/external-feature-flag "onboarding-03" "test") - (cond - show-question-modal? - [:& questions-modal] - - show-newsletter-modal? - [:& onboarding-newsletter] - - show-team-modal? - [:& onboarding-team-modal {:go-to-team? false}] - - show-release-modal? - [:& release-notes-modal {:version (:main cf/version)}])) - - [:& workspace-page {:project-id project-id - :file-id file-id - :page-id page-id - :layout-name layout - :key file-id}]]) - :frame-preview [:& frame-preview/frame-preview] nil)])) + (mf/defc app [] (let [route (mf/deref refs/route) @@ -199,4 +237,4 @@ [:> error-boundary* {:fallback static/internal-error*} [:& notifications/current-notification] (when route - [:& main-page {:route route :profile profile}])])]])) + [:> page-container* {:route route :profile profile}])])]])) diff --git a/frontend/src/app/main/ui/auth.cljs b/frontend/src/app/main/ui/auth.cljs index 1b5fb62b4..7a5acbd44 100644 --- a/frontend/src/app/main/ui/auth.cljs +++ b/frontend/src/app/main/ui/auth.cljs @@ -8,7 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] - [app.main.data.users :as du] + [app.main.data.auth :as da] [app.main.store :as st] [app.main.ui.auth.login :refer [login-page]] [app.main.ui.auth.recovery :refer [recovery-page]] @@ -19,7 +19,6 @@ [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) - (mf/defc auth {::mf/props :obj} [{:keys [route]}] @@ -35,7 +34,7 @@ (mf/with-effect [error] (when error - (st/emit! (du/show-redirect-error error)))) + (st/emit! (da/show-redirect-error error)))) [:main {:class (stl/css :auth-section)} (when show-login-icon diff --git a/frontend/src/app/main/ui/auth/login.cljs b/frontend/src/app/main/ui/auth/login.cljs index 901f0dd58..13513d46a 100644 --- a/frontend/src/app/main/ui/auth/login.cljs +++ b/frontend/src/app/main/ui/auth/login.cljs @@ -10,8 +10,8 @@ [app.common.logging :as log] [app.common.schema :as sm] [app.config :as cf] + [app.main.data.auth :as da] [app.main.data.notifications :as ntf] - [app.main.data.users :as du] [app.main.repo :as rp] [app.main.store :as st] [app.main.ui.components.button-link :as bl] @@ -43,7 +43,7 @@ (defn create-demo-profile [] - (st/emit! (du/create-demo-profile))) + (st/emit! (da/create-demo-profile))) (defn- store-login-redirect [save-login-redirect] @@ -140,7 +140,7 @@ (let [params (with-meta (:clean-data @form) {:on-error on-error :on-success on-success})] - (st/emit! (du/login params))))) + (st/emit! (da/login params))))) on-submit-ldap (mf/use-callback @@ -154,7 +154,7 @@ params (with-meta params {:on-error on-error :on-success on-success})] - (st/emit! (du/login-with-ldap params))))) + (st/emit! (da/login-with-ldap params))))) default-recovery-req (mf/use-fn diff --git a/frontend/src/app/main/ui/auth/register.cljs b/frontend/src/app/main/ui/auth/register.cljs index 98cee17f1..7f2907182 100644 --- a/frontend/src/app/main/ui/auth/register.cljs +++ b/frontend/src/app/main/ui/auth/register.cljs @@ -10,8 +10,8 @@ [app.common.data.macros :as dm] [app.common.schema :as sm] [app.config :as cf] + [app.main.data.auth :as da] [app.main.data.notifications :as ntf] - [app.main.data.users :as du] [app.main.repo :as rp] [app.main.store :as st] [app.main.ui.auth.login :as login] @@ -194,7 +194,7 @@ (st/emit! (rt/nav :auth-verify-token {} {:token token}))) (:is-active params) - (st/emit! (du/login-from-register)) + (st/emit! (da/login-from-register)) :else (do diff --git a/frontend/src/app/main/ui/auth/verify_token.cljs b/frontend/src/app/main/ui/auth/verify_token.cljs index 9e8bdbbd5..6ec884ed5 100644 --- a/frontend/src/app/main/ui/auth/verify_token.cljs +++ b/frontend/src/app/main/ui/auth/verify_token.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.auth.verify-token (:require + [app.main.data.auth :as da] [app.main.data.notifications :as ntf] [app.main.data.users :as du] [app.main.repo :as rp] @@ -25,7 +26,7 @@ [data] (let [msg (tr "dashboard.notifications.email-verified-successfully")] (ts/schedule 1000 #(st/emit! (ntf/success msg))) - (st/emit! (du/login-from-token data)))) + (st/emit! (da/login-from-token data)))) (defmethod handle-token :change-email [_data] @@ -36,7 +37,7 @@ (defmethod handle-token :auth [tdata] - (st/emit! (du/login-from-token tdata))) + (st/emit! (da/login-from-token tdata))) (defmethod handle-token :team-invitation [tdata] diff --git a/frontend/src/app/main/ui/context.cljs b/frontend/src/app/main/ui/context.cljs index d192558a3..09f12f995 100644 --- a/frontend/src/app/main/ui/context.cljs +++ b/frontend/src/app/main/ui/context.cljs @@ -33,4 +33,4 @@ (def is-component? (mf/create-context false)) (def sidebar (mf/create-context nil)) -(def team-permissions (mf/create-context nil)) +(def permissions (mf/create-context nil)) diff --git a/frontend/src/app/main/ui/dashboard.cljs b/frontend/src/app/main/ui/dashboard.cljs index 9efd0635a..b5862ed63 100644 --- a/frontend/src/app/main/ui/dashboard.cljs +++ b/frontend/src/app/main/ui/dashboard.cljs @@ -7,9 +7,7 @@ (ns app.main.ui.dashboard (:require-macros [app.main.style :as stl]) (:require - [app.common.data :as d] [app.common.data.macros :as dm] - [app.common.spec :as us] [app.config :as cf] [app.main.data.dashboard :as dd] [app.main.data.dashboard.shortcuts :as sc] @@ -20,15 +18,15 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.context :as ctx] - [app.main.ui.dashboard.files :refer [files-section]] - [app.main.ui.dashboard.fonts :refer [fonts-page font-providers-page]] + [app.main.ui.dashboard.files :refer [files-section*]] + [app.main.ui.dashboard.fonts :refer [fonts-page* font-providers-page*]] [app.main.ui.dashboard.import] - [app.main.ui.dashboard.libraries :refer [libraries-page]] - [app.main.ui.dashboard.projects :refer [projects-section]] - [app.main.ui.dashboard.search :refer [search-page]] - [app.main.ui.dashboard.sidebar :refer [sidebar]] - [app.main.ui.dashboard.team :refer [team-settings-page team-members-page team-invitations-page team-webhooks-page]] - [app.main.ui.dashboard.templates :refer [templates-section]] + [app.main.ui.dashboard.libraries :refer [libraries-page*]] + [app.main.ui.dashboard.projects :refer [projects-section*]] + [app.main.ui.dashboard.search :refer [search-page*]] + [app.main.ui.dashboard.sidebar :refer [sidebar*]] + [app.main.ui.dashboard.team :refer [team-settings-page* team-members-page* team-invitations-page* webhooks-page*]] + [app.main.ui.dashboard.templates :refer [templates-section*]] [app.main.ui.hooks :as hooks] [app.main.ui.workspace.plugins] [app.plugins.register :as preg] @@ -42,27 +40,13 @@ [potok.v2.core :as ptk] [rumext.v2 :as mf])) -(defn ^boolean uuid-str? - [s] - (and (string? s) - (boolean (re-seq us/uuid-rx s)))) - -(defn- parse-params - [route] - (let [search-term (get-in route [:params :query :search-term]) - team-id (get-in route [:params :path :team-id]) - project-id (get-in route [:params :path :project-id])] - (cond-> {:search-term search-term} - (uuid-str? team-id) - (assoc :team-id (uuid team-id)) - - (uuid-str? project-id) - (assoc :project-id (uuid project-id))))) - -(mf/defc dashboard-content - [{:keys [team projects project section search-term profile] :as props}] +(mf/defc dashboard-content* + {::mf/props :obj + ::mf/private true} + [{:keys [team projects project section search-term profile default-project]}] (let [container (mf/use-ref) content-width (mf/use-state 0) + project-id (:id project) team-id (:id team) @@ -72,10 +56,7 @@ file-menu-open? (:menu-open dashboard-local) default-project-id - (mf/with-memo [projects] - (->> (vals projects) - (d/seek :is-default) - (:id))) + (get default-project :id) on-resize (mf/use-fn @@ -88,7 +69,7 @@ (mf/use-fn #(st/emit! (dd/clear-selected-files))) - show-templates + show-templates? (and (contains? cf/flags :dashboard-templates-section) (:can-edit permissions))] @@ -105,59 +86,62 @@ (case section :dashboard-projects [:* - [:& projects-section + [:> projects-section* {:team team :projects projects - :profile profile - :default-project-id default-project-id}] + :profile profile}] - (when show-templates - [:& templates-section {:profile profile - :project-id project-id - :team-id team-id - :default-project-id default-project-id - :content-width @content-width}])] + (when ^boolean show-templates? + [:> templates-section* + {:profile profile + :project-id project-id + :team-id team-id + :default-project-id default-project-id + :content-width @content-width}])] :dashboard-fonts - [:& fonts-page {:team team}] + [:> fonts-page* {:team team}] :dashboard-font-providers - [:& font-providers-page {:team team}] + [:> font-providers-page* {:team team}] :dashboard-files (when project [:* - [:& files-section {:team team :project project}] - (when show-templates - [:& templates-section {:profile profile - :team-id team-id - :project-id project-id - :default-project-id default-project-id - :content-width @content-width}])]) + [:> files-section* {:team team + :project project}] + (when ^boolean show-templates? + [:> templates-section* + {:profile profile + :team-id team-id + :project-id project-id + :default-project-id default-project-id + :content-width @content-width}])]) :dashboard-search - [:& search-page {:team team - :search-term search-term}] + [:> search-page* {:team team + :search-term search-term}] :dashboard-libraries - [:& libraries-page {:team team}] + [:> libraries-page* {:team team + :default-project default-project}] :dashboard-team-members - [:& team-members-page {:team team :profile profile}] + [:> team-members-page* {:team team :profile profile}] :dashboard-team-invitations - [:& team-invitations-page {:team team}] + [:> team-invitations-page* {:team team}] :dashboard-team-webhooks - [:& team-webhooks-page {:team team}] + [:> webhooks-page* {:team team}] :dashboard-team-settings - [:& team-settings-page {:team team :profile profile}] + [:> team-settings-page* {:team team :profile profile}] nil)])) (def ref:dashboard-initialized - (l/derived :current-team-initialized st/state)) + (l/derived :team-initialized st/state)) (defn use-plugin-register [plugin-url team-id project-id] @@ -218,33 +202,29 @@ (fn [_] (st/emit! (notif/error "The plugin URL is incorrect"))))))))) -(mf/defc dashboard +(mf/defc dashboard* {::mf/props :obj} - [{:keys [route profile]}] - (let [section (get-in route [:data :name]) - params (parse-params route) + [{:keys [profile project-id team-id search-term plugin-url route-name]}] + (let [team (mf/deref refs/team) + projects (mf/deref refs/projects) - project-id (:project-id params) + project (get projects project-id) + projects (mf/with-memo [projects team-id] + (->> (vals projects) + (filterv #(= team-id (:team-id %))))) - team-id (:team-id params) - search-term (:search-term params) - - plugin-url (-> route :query-params :plugin) - - team (mf/deref refs/team) - projects (mf/deref refs/dashboard-projects) - project (get projects project-id) - - default-project (->> projects vals (d/seek :is-default)) - - initialized? (mf/deref ref:dashboard-initialized)] + default-project + (mf/with-memo [projects] + (->> projects + (filter :is-default) + (first)))] (hooks/use-shortcuts ::dashboard sc/shortcuts) - (mf/with-effect [team-id] - (st/emit! (dd/initialize {:id team-id})) + (mf/with-effect [] + (st/emit! (dd/initialize)) (fn [] - (st/emit! (dd/finalize {:id team-id})))) + (st/emit! (dd/finalize)))) (mf/with-effect [] (let [key (events/listen goog/global "keydown" @@ -257,31 +237,30 @@ (use-plugin-register plugin-url team-id (:id default-project)) - [:& (mf/provider ctx/current-team-id) {:value team-id} - [:& (mf/provider ctx/current-project-id) {:value project-id} - [:& (mf/provider ctx/team-permissions) {:value (:permissions team)} - ;; NOTE: dashboard events and other related functions assumes - ;; that the team is a implicit context variable that is - ;; available using react context or accessing - ;; the :current-team-id on the state. We set the key to the - ;; team-id because we want to completely refresh all the - ;; components on team change. Many components assumes that the - ;; team is already set so don't put the team into mf/deps. - (when (and team initialized?) - [:main {:class (stl/css :dashboard) - :key (:id team)} - [:& sidebar - {:team team - :projects projects - :project project - :profile profile - :section section - :search-term search-term}] - (when (and team profile (seq projects)) - [:& dashboard-content - {:projects projects - :profile profile - :project project - :section section - :search-term search-term - :team team}])])]]])) + [:& (mf/provider ctx/current-project-id) {:value project-id} + ;; NOTE: dashboard events and other related functions assumes + ;; that the team is a implicit context variable that is + ;; available using react context or accessing + ;; the :current-team-id on the state. We set the key to the + ;; team-id because we want to completely refresh all the + ;; components on team change. Many components assumes that the + ;; team is already set so don't put the team into mf/deps. + [:main {:class (stl/css :dashboard) + :key (dm/str (:id team))} + [:> sidebar* + {:team team + :projects projects + :project project + :default-project default-project + :profile profile + :section route-name + :search-term search-term}] + (when (seq projects) + [:> dashboard-content* + {:projects projects + :profile profile + :project project + :default-project default-project + :section route-name + :search-term search-term + :team team}])]])) diff --git a/frontend/src/app/main/ui/dashboard/change_owner.cljs b/frontend/src/app/main/ui/dashboard/change_owner.cljs index d87056e00..2118b96dd 100644 --- a/frontend/src/app/main/ui/dashboard/change_owner.cljs +++ b/frontend/src/app/main/ui/dashboard/change_owner.cljs @@ -9,7 +9,6 @@ (:require [app.common.schema :as sm] [app.main.data.modal :as modal] - [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.forms :as fm] [app.main.ui.icons :as i] @@ -25,8 +24,7 @@ ::mf/register-as :leave-and-reassign} [{:keys [profile team accept]}] (let [form (fm/use-form :schema schema:leave-modal-form :initial {}) - members-map (mf/deref refs/dashboard-team-members) - members (vals members-map) + members (get team :members) options (into [{:value "" diff --git a/frontend/src/app/main/ui/dashboard/file_menu.cljs b/frontend/src/app/main/ui/dashboard/file_menu.cljs index 9a0926110..6ce4ff2ab 100644 --- a/frontend/src/app/main/ui/dashboard/file_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/file_menu.cljs @@ -134,7 +134,7 @@ (st/emit! (ntf/success (tr "dashboard.success-move-files"))) (st/emit! (ntf/success (tr "dashboard.success-move-file")))) (if (or navigate (not= team-id current-team-id)) - (st/emit! (dd/go-to-files team-id project-id)) + (st/emit! (dd/go-to-files project-id team-id)) (st/emit! (dd/fetch-recent-files) (dd/clear-selected-files)))) diff --git a/frontend/src/app/main/ui/dashboard/files.cljs b/frontend/src/app/main/ui/dashboard/files.cljs index 019e7b502..f7e0ccb96 100644 --- a/frontend/src/app/main/ui/dashboard/files.cljs +++ b/frontend/src/app/main/ui/dashboard/files.cljs @@ -126,25 +126,31 @@ :on-menu-close on-menu-close :on-import on-import}])]])) -(mf/defc files-section +(mf/defc files-section* {::mf/props :obj} [{:keys [project team]}] - (let [files-map (mf/deref refs/dashboard-files) - can-edit? (-> team :permissions :can-edit) - project-id (:id project) - is-draft-proyect (:is-default project) + (let [files (mf/deref refs/files) + project-id (get project :id) - [rowref limit] (hooks/use-dynamic-grid-item-width) + files (mf/with-memo [project-id files] + (->> (vals files) + (filter #(= project-id (:project-id %))) + (sort-by :modified-at) + (reverse))) - files (mf/with-memo [project-id files-map] - (->> (vals files-map) - (filter #(= project-id (:project-id %))) - (sort-by :modified-at) - (reverse))) - file-count (or (count files) 0) + + can-edit? (-> team :permissions :can-edit) + project-id (:id project) + is-draft-proyect (:is-default project) + + [rowref limit] (hooks/use-dynamic-grid-item-width) + + file-count (or (count files) 0) empty-state-viewer (and (not can-edit?) (= 0 file-count)) + selected-files (mf/deref refs/selected-files) + on-file-created (mf/use-fn (fn [data] @@ -191,6 +197,7 @@ (tr "dashboard.empty-placeholder-files-subtitle"))}] [:& grid {:project project :files files + :selected-files selected-files :can-edit can-edit? :origin :files :create-fn create-file diff --git a/frontend/src/app/main/ui/dashboard/fonts.cljs b/frontend/src/app/main/ui/dashboard/fonts.cljs index fecf68eb0..7ae210b5f 100644 --- a/frontend/src/app/main/ui/dashboard/fonts.cljs +++ b/frontend/src/app/main/ui/dashboard/fonts.cljs @@ -11,7 +11,6 @@ [app.common.media :as cm] [app.main.data.fonts :as df] [app.main.data.modal :as modal] - [app.main.refs :as refs] [app.main.repo :as rp] [app.main.store :as st] [app.main.ui.components.context-menu-a11y :refer [context-menu*]] @@ -24,6 +23,7 @@ [app.util.keyboard :as kbd] [beicon.v2.core :as rx] [cuerdas.core :as str] + [okulary.core :as l] [rumext.v2 :as mf])) (defn- use-page-title @@ -42,7 +42,7 @@ (and (contains? font :font-family-tmp) (str/blank? (:font-family-tmp font)))) -(mf/defc header +(mf/defc header* {::mf/props :obj ::mf/memo true ::mf/private true} @@ -52,7 +52,7 @@ [:div#dashboard-fonts-title {:class (stl/css :dashboard-title)} [:h1 (tr "labels.fonts")]]]) -(mf/defc font-variant-display-name +(mf/defc font-variant-display-name* {::mf/props :obj ::mf/private true} [{:keys [variant]}] @@ -61,10 +61,10 @@ (when (not= "normal" (:font-style variant)) [:span " " (str/capital (:font-style variant))])]) -(mf/defc uploaded-fonts +(mf/defc uploaded-fonts* {::mf/props :obj ::mf/private true} - [{:keys [team installed-fonts] :as props}] + [{:keys [team installed-fonts]}] (let [fonts* (mf/use-state {}) fonts (deref fonts*) font-vals (mf/with-memo [fonts] @@ -219,7 +219,7 @@ :default-value (:font-family item)}]] [:div {:class (stl/css :table-field :variants)} [:span {:class (stl/css :label)} - [:& font-variant-display-name {:variant item}]]] + [:> font-variant-display-name* {:variant item}]]] [:div {:class (stl/css :table-field :filenames)} (for [item (:names item)] @@ -364,7 +364,7 @@ :inhert-variant (not can-edit)) :key (dm/str id)} [:span {:class (stl/css :label)} - [:& font-variant-display-name {:variant item}]] + [:> font-variant-display-name* {:variant item}]] (when can-edit [:span {:class (stl/css :icon :close) @@ -396,8 +396,9 @@ :on-delete on-delete-font :on-edit on-edit}]]))])) -(mf/defc installed-fonts - [{:keys [fonts can-edit] :as props}] +(mf/defc installed-fonts* + {::mf/props :obj} + [{:keys [fonts can-edit]}] (let [sterm (mf/use-state "") matches? @@ -445,26 +446,27 @@ :subtitle (tr "dashboard.fonts.empty-placeholder-viewer-sub") :type 2}]))])) +(def ^:private ref:fonts + (l/derived :fonts st/state)) -(mf/defc fonts-page +(mf/defc fonts-page* {::mf/props :obj} [{:keys [team]}] - (let [fonts (mf/deref refs/dashboard-fonts) + (let [fonts (mf/deref ref:fonts) permissions (:permissions team) can-edit (:can-edit permissions)] [:* - [:& header {:team team :section :fonts}] + [:> header* {:team team :section :fonts}] [:section {:class (stl/css :dashboard-container :dashboard-fonts)} (when ^boolean can-edit - [:& uploaded-fonts {:team team :installed-fonts fonts}]) - [:& installed-fonts {:team team :fonts fonts :can-edit can-edit}]]])) + [:> uploaded-fonts* {:team team :installed-fonts fonts}]) + [:> installed-fonts* + {:team team :fonts fonts :can-edit can-edit}]]])) -(mf/defc font-providers-page +(mf/defc font-providers-page* {::mf/props :obj} [{:keys [team]}] [:* - [:& header {:team team :section :providers}] + [:> header* {:team team :section :providers}] [:section {:class (stl/css :dashboard-container)} [:span "font providers"]]]) - - diff --git a/frontend/src/app/main/ui/dashboard/grid.cljs b/frontend/src/app/main/ui/dashboard/grid.cljs index 4c72b6852..f73bebd11 100644 --- a/frontend/src/app/main/ui/dashboard/grid.cljs +++ b/frontend/src/app/main/ui/dashboard/grid.cljs @@ -71,11 +71,19 @@ (rx/mapcat thr/render) (rx/mapcat (partial persist-thumbnail file-id revn)))) -(mf/defc grid-item-thumbnail - {::mf/wrap-props false} - [{:keys [file-id revn thumbnail-id background-color can-edit]}] - (let [container (mf/use-ref) - visible? (h/use-visible container :once? true)] +(mf/defc grid-item-thumbnail* + {::mf/props :obj + ::mf/private true} + [{:keys [can-edit file]}] + (let [file-id (get file :id) + revn (get file :revn) + thumbnail-id (get file :thumbnail-id) + + ;; FIXME: revisit maybe bug + bg-color (dm/get-in file [:data :options :background]) + + container (mf/use-ref) + visible? (h/use-visible container :once? true)] (mf/with-effect [file-id revn visible? thumbnail-id] (when (and visible? (not thumbnail-id)) @@ -89,7 +97,7 @@ :message (ex-message cause))))))) [:div {:class (stl/css :grid-item-th) - :style {:background-color background-color} + :style {:background-color bg-color} :ref container} (when visible? (if thumbnail-id @@ -108,10 +116,9 @@ (def ^:private menu-icon (i/icon-xref :menu (stl/css :menu-icon))) -(mf/defc grid-item-library - {::mf/wrap [mf/memo]} - [{:keys [file] :as props}] - +(mf/defc grid-item-library* + {::mf/props :obj} + [{:keys [file]}] (mf/with-effect [file] (when file (let [font-ids (map :font-id (get-in file [:library-summary :typographies :sample] []))] @@ -231,16 +238,12 @@ (dom/set-text! counter-el (str file-count)) counter-el)) -(mf/defc grid-item - {:wrap [mf/memo]} - [{:keys [file origin library-view? can-edit] :as props}] +(mf/defc grid-item* + {::mf/props :obj} + [{:keys [file origin can-edit selected-files]}] (let [file-id (:id file) - ;; FIXME: this breaks react hooks rule, hooks should never to - ;; be in a conditional code - selected-files (if (= origin :search) - (mf/deref refs/dashboard-selected-search) - (mf/deref refs/dashboard-selected-files)) + is-library-view (= origin :libraries) dashboard-local (mf/deref refs/dashboard-local) file-menu-open? (:menu-open dashboard-local) @@ -354,9 +357,12 @@ (on-select event)) ;; TODO Fix this )))] - [:li {:class (stl/css-case :grid-item true :project-th true :library library-view?)} + [:li {:class (stl/css-case :grid-item true + :project-th true + :library is-library-view)} [:div - {:class (stl/css-case :selected selected? :library library-view?) + {:class (stl/css-case :selected selected? + :library is-library-view) :ref node-ref :role "button" :title (:name file) @@ -369,16 +375,11 @@ [:div {:class (stl/css :overlay)}] - (if library-view? - [:& grid-item-library {:file file}] - [:& grid-item-thumbnail - {:file-id (:id file) - :can-edit can-edit - :revn (:revn file) - :thumbnail-id (:thumbnail-id file) - :background-color (dm/get-in file [:data :options :background])}]) + (if ^boolean is-library-view + [:> grid-item-library* {:file file}] + [:> grid-item-thumbnail* {:file file :can-edit can-edit}]) - (when (and (:is-shared file) (not library-view?)) + (when (and (:is-shared file) (not is-library-view)) [:div {:class (stl/css :item-badge)} i/library]) [:div {:class (stl/css :info-wrapper)} @@ -417,7 +418,8 @@ :parent-id (dm/str file-id "-action-menu")}]])]]]]])) (mf/defc grid - [{:keys [files project origin limit library-view? create-fn can-edit] :as props}] + {::mf/props :obj} + [{:keys [files project origin limit create-fn can-edit selected-files]}] (let [dragging? (mf/use-state false) project-id (:id project) node-ref (mf/use-var nil) @@ -484,13 +486,12 @@ (when @dragging? [:li {:class (stl/css :grid-item)}]) (for [item slice] - [:& grid-item + [:> grid-item* {:file item - :key (:id item) - :navigate? true + :key (dm/str (:id item)) :origin origin - :can-edit can-edit - :library-view? library-view?}])]) + :selected-files selected-files + :can-edit can-edit}])]) :else [:& empty-placeholder @@ -510,13 +511,12 @@ [:li {:class (stl/css :grid-item :dragged)}]) (for [item (take limit files)] - [:& grid-item + [:> grid-item* {:id (:id item) :file item :selected-files selected-files :can-edit can-edit - :key (:id item) - :navigate? false}])])) + :key (dm/str (:id item))}])])) (mf/defc line-grid [{:keys [project team files limit create-fn can-edit] :as props}] @@ -524,8 +524,8 @@ project-id (:id project) team-id (:id team) - selected-files (mf/deref refs/dashboard-selected-files) - selected-project (mf/deref refs/dashboard-selected-project) + selected-files (mf/deref refs/selected-files) + selected-project (mf/deref refs/selected-project) on-finish-import (mf/use-fn diff --git a/frontend/src/app/main/ui/dashboard/libraries.cljs b/frontend/src/app/main/ui/dashboard/libraries.cljs index e883c5f0c..f1d428e93 100644 --- a/frontend/src/app/main/ui/dashboard/libraries.cljs +++ b/frontend/src/app/main/ui/dashboard/libraries.cljs @@ -7,9 +7,7 @@ (ns app.main.ui.dashboard.libraries (:require-macros [app.main.style :as stl]) (:require - [app.common.data :as d] [app.main.data.dashboard :as dd] - [app.main.features :as features] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.dashboard.grid :refer [grid]] @@ -18,35 +16,32 @@ [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) -(mf/defc libraries-page +(mf/defc libraries-page* {::mf/props :obj} - [{:keys [team] :as props}] - (let [files-map (mf/deref refs/dashboard-shared-files) - projects (mf/deref refs/dashboard-projects) - can-edit (-> team :permissions :can-edit) + [{:keys [team default-project]}] + (let [files + (mf/deref refs/shared-files) - default-project (->> projects vals (d/seek :is-default)) + files + (mf/with-memo [files] + (->> (vals files) + (sort-by :modified-at) + (reverse))) - files (mf/with-memo [files-map] - (if (nil? files-map) - nil - (->> (vals files-map) - (sort-by :modified-at) - (reverse)))) + can-edit + (-> team :permissions :can-edit) - components-v2 (features/use-feature "components/v2") - - [rowref limit] (hooks/use-dynamic-grid-item-width 350)] + [rowref limit] + (hooks/use-dynamic-grid-item-width 350)] (mf/with-effect [team] - (when team - (let [tname (if (:is-default team) - (tr "dashboard.your-penpot") - (:name team))] - (dom/set-html-title (tr "title.dashboard.shared-libraries" tname))))) + (let [tname (if (:is-default team) + (tr "dashboard.your-penpot") + (:name team))] + (dom/set-html-title (tr "title.dashboard.shared-libraries" tname)))) - (mf/with-effect [] - (st/emit! (dd/fetch-shared-files (:id team)) + (mf/with-effect [team] + (st/emit! (dd/fetch-shared-files) (dd/clear-selected-files))) [:* @@ -58,6 +53,5 @@ :project default-project :origin :libraries :limit limit - :can-edit can-edit - :library-view? components-v2}]]])) + :can-edit can-edit}]]])) diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs index 611f205cf..37cab4ad2 100644 --- a/frontend/src/app/main/ui/dashboard/projects.cljs +++ b/frontend/src/app/main/ui/dashboard/projects.cljs @@ -43,8 +43,10 @@ (def ^:private menu-icon (i/icon-xref :menu (stl/css :menu-icon))) -(mf/defc header - {::mf/wrap [mf/memo]} +(mf/defc header* + {::mf/wrap [mf/memo] + ::mf/props :obj + ::mf/private true} [{:keys [can-edit]}] (let [on-click (mf/use-fn #(st/emit! (dd/create-project)))] [:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"} @@ -96,27 +98,30 @@ :aria-label (tr "labels.close")} close-icon]])) -(def builtin-templates - (l/derived :builtin-templates st/state)) - -(mf/defc project-item - [{:keys [project first? team files can-edit] :as props}] +(mf/defc project-item* + {::mf/props :obj + ::mf/private true} + [{:keys [project is-first team files can-edit]}] (let [locale (mf/deref i18n/locale) - file-count (or (:count project) 0) + project-id (:id project) - is-draft-proyect (:is-default project) team-id (:id team) - empty-state-viewer (and (not can-edit) - (= 0 file-count)) + + file-count (or (:count project) 0) + is-draft? (:is-default project) + empty? (and (not can-edit) + (= 0 file-count)) dstate (mf/deref refs/dashboard-local) edit-id (:project-for-edit dstate) local (mf/use-state {:menu-open false :menu-pos nil - :edition? (= (:id project) edit-id)}) + :edition (= (:id project) edit-id)}) + + [rowref limit] + (hooks/use-dynamic-grid-item-width) - [rowref limit] (hooks/use-dynamic-grid-item-width) on-nav (mf/use-fn (mf/deps project-id team-id) @@ -152,7 +157,7 @@ (mf/use-fn #(swap! local assoc :menu-open false)) on-edit-open - (mf/use-fn #(swap! local assoc :edition? true)) + (mf/use-fn #(swap! local assoc :edition true)) on-edit (mf/use-fn @@ -162,7 +167,7 @@ (when-not (str/empty? name) (st/emit! (-> (dd/rename-project (assoc project :name name)) (with-meta {::ev/origin "dashboard"})))) - (swap! local assoc :edition? false)))) + (swap! local assoc :edition false)))) on-file-created (mf/use-fn @@ -212,10 +217,10 @@ (on-menu-click event)))) title-width (/ 100 limit)] - [:article {:class (stl/css-case :dashboard-project-row true :first first?)} + [:article {:class (stl/css-case :dashboard-project-row true :first is-first)} [:header {:class (stl/css :project)} [:div {:class (stl/css :project-name-wrapper)} - (if (:edition? @local) + (if (:edition @local) [:& inline-edition {:content (:name project) :on-end on-edit}] [:h2 {:on-click on-nav @@ -231,7 +236,6 @@ [:div {:class (stl/css :info-wrapper)} - ;; We group these two spans under a div to avoid having extra space between them. [:div [:span {:class (stl/css :info)} (str (tr "labels.num-of-files" (i18n/c file-count)))] @@ -274,13 +278,13 @@ :on-import on-import}])]]] [:div {:class (stl/css :grid-container) :ref rowref} - (if empty-state-viewer - [:> empty-placeholder* {:title (if is-draft-proyect + (if ^boolean empty? + [:> empty-placeholder* {:title (if ^boolean is-draft? (tr "dashboard.empty-placeholder-drafts-title") (tr "dashboard.empty-placeholder-files-title")) :class (stl/css :placeholder-placement) :type 1 - :subtitle (if is-draft-proyect + :subtitle (if ^boolean is-draft? (tr "dashboard.empty-placeholder-drafts-subtitle") (tr "dashboard.empty-placeholder-files-subtitle"))}] @@ -303,16 +307,19 @@ [:span {:class (stl/css :placeholder-label)} (tr "dashboard.show-all-files")] show-more-icon])])) -(def ref:recent-files - (l/derived :dashboard-recent-files st/state)) +(def ^:private ref:recent-files + (l/derived :recent-files st/state)) -(mf/defc projects-section +(mf/defc projects-section* {::mf/props :obj} [{:keys [team projects profile]}] - (let [projects (->> (vals projects) - (sort-by :modified-at) - (reverse)) + (let [projects + (mf/with-memo [projects] + (->> projects + (sort-by :modified-at) + (reverse))) + recent-map (mf/deref ref:recent-files) permisions (:permissions team) @@ -334,7 +341,7 @@ ::ev/origin "dashboard"}))))] (mf/with-effect [show-team-hero?] - (swap! storage/global assoc ::show-team-hero show-team-hero?)) + (swap! storage/global assoc ::show-eam-hero show-team-hero?)) (mf/with-effect [team] (let [tname (if (:is-default team) @@ -348,7 +355,7 @@ (when (seq projects) [:* - [:& header {:can-edit can-edit}] + [:> header* {:can-edit can-edit}] [:div {:class (stl/css :projects-container)} [:* (when (and show-team-hero? @@ -368,9 +375,9 @@ (->> (vals recent-map) (filterv #(= id (:project-id %))) (sort-by :modified-at #(compare %2 %1))))] - [:& project-item {:project project - :team team - :files files - :can-edit can-edit - :first? (= project (first projects)) - :key id}]))]]]]))) + [:> project-item* {:project project + :team team + :files files + :can-edit can-edit + :is-first (= project (first projects)) + :key id}]))]]]]))) diff --git a/frontend/src/app/main/ui/dashboard/search.cljs b/frontend/src/app/main/ui/dashboard/search.cljs index 862fc700a..0ad67d06a 100644 --- a/frontend/src/app/main/ui/dashboard/search.cljs +++ b/frontend/src/app/main/ui/dashboard/search.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.dashboard.search (:require-macros [app.main.style :as stl]) (:require + [app.common.data :as d] [app.main.data.dashboard :as dd] [app.main.refs :as refs] [app.main.store :as st] @@ -15,28 +16,43 @@ [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] + [okulary.core :as l] [rumext.v2 :as mf])) -(mf/defc search-page - [{:keys [team search-term] :as props}] - (let [search-term (or search-term "") - result (mf/deref refs/dashboard-search-result) - [rowref limit] (hooks/use-dynamic-grid-item-width)] +(def ^:private ref:search-result + (l/derived :search-result st/state)) - (mf/use-effect - (mf/deps team) - (fn [] - (when team - (let [tname (if (:is-default team) - (tr "dashboard.your-penpot") - (:name team))] - (dom/set-html-title (tr "title.dashboard.search" tname)))))) +(def ^:private ref:selected + (l/derived (fn [state] + ;; we need to this because :dashboard-search-result is a list + ;; of maps and we need a map of maps (using :id as key). + (let [files (d/index-by :id (:search-result state))] + (->> (get state :selected-files) + (refs/extract-selected-files files)))) + st/state)) + +(mf/defc search-page* + {::mf/props :obj} + [{:keys [team search-term]}] + (let [search-term (d/nilv search-term "") + + result (mf/deref ref:search-result) + selected (mf/deref ref:selected) + + [rowref limit] + (hooks/use-dynamic-grid-item-width)] + + (mf/with-effect [team] + (when team + (let [tname (if (:is-default team) + (tr "dashboard.your-penpot") + (:name team))] + (dom/set-html-title (tr "title.dashboard.search" tname))))) + + (mf/with-effect [search-term] + (st/emit! (dd/search {:search-term search-term}) + (dd/clear-selected-files))) - (mf/use-effect - (mf/deps search-term) - (fn [] - (st/emit! (dd/search {:search-term search-term}) - (dd/clear-selected-files)))) [:* [:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"} [:div#dashboard-search-title {:class (stl/css :dashboard-title)} @@ -62,6 +78,6 @@ :else [:& grid {:files result - :hide-new? true + :selected-files selected :origin :search :limit limit}])]])) diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs index 2e53b3bf7..db0219ee9 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs @@ -7,15 +7,15 @@ (ns app.main.ui.dashboard.sidebar (:require-macros [app.main.style :as stl]) (:require - [app.common.data :as d] [app.common.data.macros :as dm] [app.common.spec :as us] [app.config :as cf] + [app.main.data.auth :as da] [app.main.data.dashboard :as dd] [app.main.data.events :as ev] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] - [app.main.data.users :as du] + [app.main.data.team :as dtm] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]] @@ -29,7 +29,6 @@ [app.util.dom.dnd :as dnd] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.object :as obj] [app.util.router :as rt] [app.util.timers :as ts] [beicon.v2.core :as rx] @@ -80,6 +79,7 @@ :dragging? false}) local @local* + on-click (mf/use-fn (mf/deps item) @@ -348,15 +348,17 @@ go-webhooks #(st/emit! (dd/go-to-team-webhooks)) go-settings #(st/emit! (dd/go-to-team-settings)) - members-map (mf/deref refs/dashboard-team-members) - members (vals members-map) - can-rename? (or (get-in team [:permissions :is-owner]) (get-in team [:permissions :is-admin])) + members (get team :members) + permissions (get team :permissions) + can-rename? (or (:is-owner permissions) + (:is-admin permissions)) on-success (fn [] + ;; FIXME: this should be handled in the event, not here (st/emit! (dd/go-to-projects (:default-team-id profile)) (modal/hide) - (du/fetch-teams))) + (dtm/fetch-teams))) on-error (fn [{:keys [code] :as error}] @@ -377,15 +379,15 @@ (mf/deps on-success on-error) (fn [member-id] (let [params (cond-> {} (uuid? member-id) (assoc :reassign-to member-id))] - (st/emit! (dd/leave-team (with-meta params - {:on-success on-success - :on-error on-error})))))) + (st/emit! (dtm/leave-current-team (with-meta params + {:on-success on-success + :on-error on-error})))))) delete-fn (mf/use-fn (mf/deps team on-success on-error) (fn [] - (st/emit! (dd/delete-team (with-meta team {:on-success on-success - :on-error on-error}))))) + (st/emit! (dtm/delete-team (with-meta team {:on-success on-success + :on-error on-error}))))) on-rename-clicked (mf/use-fn (mf/deps team) @@ -406,7 +408,7 @@ (mf/use-fn (mf/deps team profile leave-fn) (fn [] - (st/emit! (dd/fetch-team-members (:id team)) + (st/emit! (dtm/fetch-members) (modal/show {:type :leave-and-reassign :profile profile @@ -590,6 +592,10 @@ (when (get-in team [:permissions :is-owner]) "teams-options-delete-team")] + + ;; _ (prn "--------------- sidebar-team-switch") + ;; _ (app.common.pprint/pprint teams) + handle-show-team-click (fn [event] (dom/stop-propagation event) @@ -679,12 +685,12 @@ [:& team-options-dropdown {:team team :profile profile}]]])) -(mf/defc sidebar-content - [{:keys [projects profile section team project search-term] :as props}] +(mf/defc sidebar-content* + {::mf/private true + ::mf/props :obj} + [{:keys [projects profile section team project search-term default-project] :as props}] (let [default-project-id - (->> (vals projects) - (d/seek :is-default) - (:id)) + (get default-project :id) projects? (= section :dashboard-projects) fonts? (= section :dashboard-fonts) @@ -763,7 +769,7 @@ (dom/focus! libs-title) (dom/set-attribute! libs-title "tabindex" "-1"))))))) pinned-projects - (->> (vals projects) + (->> projects (remove :is-default) (filter :is-pinned))] @@ -826,11 +832,12 @@ pin-icon [:span {:class (stl/css :empty-text)} (tr "dashboard.no-projects-placeholder")]])]])) -(mf/defc profile-section - [{:keys [profile team] :as props}] +(mf/defc profile-section* + {::mf/props :obj} + [{:keys [profile team]}] (let [show* (mf/use-state false) show (deref show*) - photo (cf/resolve-profile-photo-url profile) + photo (cf/resolve-profile-photo-url profile) on-click (mf/use-fn @@ -875,14 +882,13 @@ (when (kbd/enter? event) (reset! show* true)))) - handle-close + on-close (fn [event] (dom/stop-propagation event) (reset! show* false)) handle-key-down-profile (mf/use-fn - (mf/deps on-click) (fn [event] (when (kbd/enter? event) (on-click :settings-profile event)))) @@ -910,34 +916,27 @@ (show-release-notes)))) handle-feedback-click - (mf/use-fn - (mf/deps on-click) - #(on-click :settings-feedback %)) + (mf/use-fn #(on-click :settings-feedback %)) handle-feedback-keydown (mf/use-fn - (mf/deps on-click) (fn [event] (when (kbd/enter? event) (on-click :settings-feedback event)))) handle-logout-click (mf/use-fn - (mf/deps on-click) - #(on-click (du/logout) %)) + #(on-click (da/logout) %)) handle-logout-keydown (mf/use-fn - (mf/deps on-click) (fn [event] (when (kbd/enter? event) - (on-click (du/logout) event)))) + (on-click (da/logout) event)))) handle-set-profile (mf/use-fn - (mf/deps on-click) - (fn [event] - (on-click :settings-profile event)))] + #(on-click :settings-profile %))] [:* (when (and team profile) @@ -959,7 +958,9 @@ :alt (:fullname profile)}] [:span {:class (stl/css :profile-fullname)} (:fullname profile)]] - [:& dropdown-menu {:on-close handle-close :show show :list-class (stl/css :profile-dropdown)} + [:& dropdown-menu {:on-close on-close + :show show + :list-class (stl/css :profile-dropdown)} [:li {:tab-index (if show "0" "-1") :class (stl/css :profile-dropdown-item) :on-click handle-set-profile @@ -1045,15 +1046,13 @@ :show? show-comments? :on-show-comments handle-show-comments}])]])) -(mf/defc sidebar - {::mf/wrap-props false +(mf/defc sidebar* + {::mf/props :obj ::mf/wrap [mf/memo]} - [props] - (let [team (obj/get props "team") - profile (obj/get props "profile")] - [:nav {:class (stl/css :dashboard-sidebar) :data-testid "dashboard-sidebar"} - [:> sidebar-content props] - [:& profile-section - {:profile profile - :team team}]])) + [{:keys [team profile] :as props}] + [:nav {:class (stl/css :dashboard-sidebar) :data-testid "dashboard-sidebar"} + [:> sidebar-content* props] + [:> profile-section* + {:profile profile + :team team}]]) diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index 431dd2711..20eb49cec 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -15,7 +15,7 @@ [app.main.data.events :as ev] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] - [app.main.data.users :as du] + [app.main.data.team :as dtm] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] @@ -31,6 +31,7 @@ [app.util.i18n :as i18n :refer [tr]] [beicon.v2.core :as rx] [cuerdas.core :as str] + [okulary.core :as l] [rumext.v2 :as mf])) (def ^:private arrow-icon @@ -139,12 +140,12 @@ ::mf/register-as :invite-members ::mf/props :obj} [{:keys [team origin invite-email]}] - (let [members-map (mf/deref refs/dashboard-team-members) - perms (:permissions team) + (let [members (get team :members) + perms (get team :permissions) + team-id (get team :id) roles (mf/with-memo [perms] (get-available-roles perms)) - team-id (:id team) initial (mf/with-memo [team-id invite-email] (if invite-email @@ -156,7 +157,7 @@ error-text (mf/use-state "") current-data-emails (into #{} (dm/get-in @form [:clean-data :emails])) - current-members-emails (into #{} (map (comp :email second)) members-map) + current-members-emails (into #{} (map :email) members) on-success (fn [_form {:keys [total]}] @@ -164,8 +165,8 @@ (st/emit! (ntf/success (tr "notifications.invitation-email-sent")))) (st/emit! (modal/hide) - (dd/fetch-team-members) - (dd/fetch-team-invitations))) + (dtm/fetch-members) + (dtm/fetch-invitations))) on-error (fn [_form cause] @@ -198,11 +199,11 @@ (let [params (:clean-data @form) mdata {:on-success (partial on-success form) :on-error (partial on-error form)}] - (st/emit! (-> (dd/invite-team-members (with-meta params mdata)) + (st/emit! (-> (dtm/create-invitations (with-meta params mdata)) (with-meta {::ev/origin origin})) - (dd/fetch-team-invitations) - (dd/fetch-team-members (:id team)))))] - + ;; FIXME: looks duplicate + (dtm/fetch-invitations) + (dtm/fetch-members))))] [:div {:class (stl/css-case :modal-team-container true :modal-team-container-workspace (= origin :workspace) @@ -338,7 +339,8 @@ (when is-you? [:li {:on-click on-leave :class (stl/css :action-dropdown-item) - :key "is-you-option"} (tr "dashboard.leave-team")]) + :key "is-you-option"} + (tr "dashboard.leave-team")]) (when (and can-delete? (not is-you?) (not (and is-owner? (not owner?)))) [:li {:on-click on-delete :class (stl/css :action-dropdown-item) @@ -346,18 +348,18 @@ (defn- set-role! [member-id role] (let [params {:member-id member-id :role role}] - (st/emit! (dd/update-team-member-role params)))) + (st/emit! (dtm/update-member-role params)))) -(mf/defc team-member +(mf/defc team-member* {::mf/wrap [mf/memo] ::mf/props :obj} - [{:keys [team member members profile]}] + [{:keys [team member total-members profile]}] - (let [member-id (:id member) + (let [member-id (:id member) on-set-admin (mf/use-fn (mf/deps member-id) (partial set-role! member-id :admin)) on-set-editor (mf/use-fn (mf/deps member-id) (partial set-role! member-id :editor)) on-set-viewer (mf/use-fn (mf/deps member-id) (partial set-role! member-id :viewer)) - owner? (dm/get-in team [:permissions :is-owner]) + owner? (dm/get-in team [:permissions :is-owner]) on-set-owner (mf/use-fn @@ -373,18 +375,12 @@ (st/emit! (modal/show params))))) on-success - (mf/use-fn - (mf/deps profile) - (fn [] - (st/emit! (dd/go-to-projects (:default-team-id profile)) - (modal/hide) - (du/fetch-teams)))) + (mf/use-fn (fn [] (rx/of (dd/go-to-default-team)))) on-error (mf/use-fn (fn [{:keys [code] :as error}] (condp = code - :no-enough-members-for-leave (rx/of (ntf/error (tr "errors.team-leave.insufficient-members"))) @@ -400,17 +396,17 @@ (mf/use-fn (mf/deps team on-success on-error) (fn [] - (st/emit! (dd/delete-team (with-meta team {:on-success on-success - :on-error on-error}))))) + (st/emit! (dtm/delete-team (with-meta team {:on-success on-success + :on-error on-error}))))) on-leave-accepted (mf/use-fn (mf/deps on-success on-error) (fn [member-id] (let [params (cond-> {} (uuid? member-id) (assoc :reassign-to member-id))] - (st/emit! (dd/leave-team (with-meta params - {:on-success on-success - :on-error on-error})))))) + (st/emit! (dtm/leave-current-team (with-meta params + {:on-success on-success + :on-error on-error})))))) on-leave-and-close (mf/use-fn @@ -428,7 +424,7 @@ (mf/use-fn (mf/deps profile team on-leave-accepted) (fn [] - (st/emit! (dd/fetch-team-members (:id team)) + (st/emit! (dtm/fetch-members) (modal/show {:type :leave-and-reassign :profile profile @@ -450,7 +446,7 @@ (mf/use-fn (mf/deps member-id) (fn [] - (let [on-accept #(st/emit! (dd/delete-team-member {:member-id member-id})) + (let [on-accept #(st/emit! (dtm/delete-member {:member-id member-id})) params {:type :confirm :title (tr "modals.delete-team-member-confirm.title") :message (tr "modals.delete-team-member-confirm.message") @@ -459,7 +455,7 @@ (st/emit! (modal/show params))))) on-leave' - (cond (= 1 (count members)) on-leave-and-close + (cond (= 1 total-members) on-leave-and-close (= true owner?) on-change-owner-and-leave :else on-leave)] @@ -483,16 +479,26 @@ :on-delete on-delete :on-leave on-leave'}]]])) -(mf/defc team-members - {::mf/props :obj} - [{:keys [members-map team profile]}] - (let [members (mf/with-memo [members-map] - (->> (vals members-map) - (sort-by :created-at) - (remove :is-owner))) - owner (mf/with-memo [members-map] - (->> (vals members-map) - (d/seek :is-owner)))] +(mf/defc team-members* + {::mf/props :obj + ::mf/private true} + [{:keys [team profile]}] + (let [members (get team :members) + + total-members + (count members) + + + owner + (mf/with-memo [members] + (d/seek :is-owner members)) + + members + (mf/with-memo [team] + (->> (:members team) + (sort-by :created-at) + (remove :is-owner) + (vec)))] [:div {:class (stl/css :dashboard-table :team-members)} [:div {:class (stl/css :table-header)} @@ -500,42 +506,39 @@ [:div {:class (stl/css :table-field :title-field-role)} (tr "labels.role")]] [:div {:class (stl/css :table-rows)} - [:& team-member + [:> team-member* {:member owner :team team :profile profile - :members members-map}] + :total-members total-members}] (for [item members] - [:& team-member + [:> team-member* {:member item :team team :profile profile - :key (:id item) - :members members-map}])]])) + :key (dm/str (:id item)) + :total-members total-members}])]])) -(mf/defc team-members-page +(mf/defc team-members-page* {::mf/props :obj} [{:keys [team profile]}] - (let [members-map (mf/deref refs/dashboard-team-members)] + (mf/with-effect [team] + (dom/set-html-title + (tr "title.team-members" + (if (:is-default team) + (tr "dashboard.your-penpot") + (:name team))))) - (mf/with-effect [team] - (dom/set-html-title - (tr "title.team-members" - (if (:is-default team) - (tr "dashboard.your-penpot") - (:name team))))) + (mf/with-effect [] + (st/emit! (dtm/fetch-members))) - (mf/with-effect [team] - (st/emit! (dd/fetch-team-members (:id team)))) - - [:* - [:& header {:section :dashboard-team-members :team team}] - [:section {:class (stl/css :dashboard-container :dashboard-team-members)} - [:& team-members - {:profile profile - :team team - :members-map members-map}]]])) + [:* + [:& header {:section :dashboard-team-members :team team}] + [:section {:class (stl/css :dashboard-container :dashboard-team-members)} + [:> team-members* + {:profile profile + :team team}]]]) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; INVITATIONS SECTION @@ -587,8 +590,9 @@ :on-click on-change'} (tr "labels.viewer")]]]])) -(mf/defc invitation-actions - {::mf/props :obj} +(mf/defc invitation-actions* + {::mf/props :obj + ::mf/private true} [{:keys [invitation team-id]}] (let [show? (mf/use-state false) @@ -622,15 +626,15 @@ (mf/deps email team-id) (fn [] (let [params {:email email :team-id team-id} - mdata {:on-success #(st/emit! (dd/fetch-team-invitations))}] - (st/emit! (dd/delete-team-invitation (with-meta params mdata)))))) + mdata {:on-success #(st/emit! (dtm/fetch-invitations))}] + (st/emit! (dtm/delete-invitation (with-meta params mdata)))))) on-resend-success (mf/use-fn (fn [] (st/emit! (ntf/success (tr "notifications.invitation-email-sent")) (modal/hide) - (dd/fetch-team-invitations)))) + (dtm/fetch-invitations)))) on-resend (mf/use-fn @@ -643,7 +647,7 @@ {:on-success on-resend-success :on-error on-error})] (st/emit! - (-> (dd/invite-team-members params) + (-> (dtm/create-invitations params) (with-meta {::ev/origin :team})))))) on-copy-success @@ -660,7 +664,7 @@ {:on-success on-copy-success :on-error on-error})] (st/emit! - (-> (dd/copy-invitation-link params) + (-> (dtm/copy-invitation-link params) (with-meta {::ev/origin :team})))))) on-hide (mf/use-fn #(reset! show? false)) @@ -694,17 +698,19 @@ role (:role invitation) status (if expired? :expired :pending) type (if expired? :warning :default) - badge-content (if (= status :expired) - (tr "labels.expired-invitation") - (tr "labels.pending-invitation")) + + badge-content + (if (= status :expired) + (tr "labels.expired-invitation") + (tr "labels.pending-invitation")) on-change-role (mf/use-fn (mf/deps email team-id) (fn [role _event] (let [params {:email email :team-id team-id :role role} - mdata {:on-success #(st/emit! (dd/fetch-team-invitations))}] - (st/emit! (dd/update-team-invitation-role (with-meta params mdata))))))] + mdata {:on-success #(st/emit! (dtm/fetch-invitations))}] + (st/emit! (dtm/update-invitation-role (with-meta params mdata))))))] [:div {:class (stl/css :table-row :table-row-invitations)} [:div {:class (stl/css :table-field :field-email)} email] @@ -720,25 +726,36 @@ [:& badge-notification {:type type :content badge-content}]] [:div {:class (stl/css :table-field :field-actions)} - (when can-invite - [:& invitation-actions + (when ^boolean can-invite + [:> invitation-actions* {:invitation invitation :team-id team-id}])]])) -(mf/defc empty-invitation-table - [{:keys [can-invite] :as props}] +(mf/defc empty-invitation-table* + {::mf/props :obj + ::mf/private true} + [{:keys [can-invite]}] [:div {:class (stl/css :empty-invitations)} [:span (tr "labels.no-invitations")] - (when can-invite + (when ^boolean can-invite [:> i18n/tr-html* {:content (tr "labels.no-invitations-hint") :tag-name "span"}])]) -(mf/defc invitation-section - [{:keys [team invitations] :as props}] - (let [owner? (dm/get-in team [:permissions :is-owner]) - admin? (dm/get-in team [:permissions :is-admin]) - can-invite (or owner? admin?) - team-id (:id team)] +(def ^:private ref:invitations + (l/derived :invitations st/state)) + +(mf/defc invitation-section* + {::mf/props :obj + ::mf/private true} + [{:keys [team]}] + (let [permissions (get team :permissions) + invitations (mf/deref ref:invitations) + + team-id (get team :id) + + owner? (get permissions :is-owner) + admin? (get permissions :is-admin) + can-invite? (or owner? admin?)] [:div {:class (stl/css :invitations)} [:div {:class (stl/css :table-header)} @@ -746,38 +763,34 @@ [:div {:class (stl/css :title-field-role)} (tr "labels.role")] [:div {:class (stl/css :title-field-status)} (tr "labels.status")]] (if (empty? invitations) - [:& empty-invitation-table {:can-invite can-invite}] + [:> empty-invitation-table* {:can-invite can-invite?}] [:div {:class (stl/css :table-rows)} (for [invitation invitations] [:> invitation-row* {:key (:email invitation) :invitation invitation - :can-invite can-invite + :can-invite can-invite? :team-id team-id}])])])) -(mf/defc team-invitations-page +(mf/defc team-invitations-page* + {::mf/props :obj} [{:keys [team]}] - (let [invitations (mf/deref refs/dashboard-team-invitations)] - (mf/with-effect [team] - (dom/set-html-title - (tr "title.team-invitations" - (if (:is-default team) - (tr "dashboard.your-penpot") - (:name team))))) + (mf/with-effect [team] + (dom/set-html-title + (tr "title.team-invitations" + (if (:is-default team) + (tr "dashboard.your-penpot") + (:name team))))) - (mf/with-effect [] - (st/emit! (dd/fetch-team-invitations))) + (mf/with-effect [] + (st/emit! (dtm/fetch-invitations))) - [:* - [:& header {:section :dashboard-team-invitations - :team team}] - [:section {:class (stl/css :dashboard-team-invitations)} - ;; TODO: We should consider adding a "loading state" here - ;; with an (if (nil? invitations) [:& loading-state] [:& invitations]) - (when-not (nil? invitations) - [:& invitation-section {:team team - :invitations invitations}])]])) + [:* + [:& header {:section :dashboard-team-invitations + :team team}] + [:section {:class (stl/css :dashboard-team-invitations)} + [:> invitation-section* {:team team}]]]) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; WEBHOOKS SECTION @@ -811,9 +824,8 @@ (mf/use-fn (fn [_] (let [message (tr "dashboard.webhooks.create.success")] - (st/emit! (dd/fetch-team-webhooks) - (ntf/success message) - (modal/hide))))) + (rx/of (ntf/success message) + (modal/hide))))) on-error (mf/use-fn @@ -846,7 +858,7 @@ params {:uri (:uri cdata) :mtype (:mtype cdata) :is-active (:is-active cdata)}] - (st/emit! (dd/create-team-webhook + (st/emit! (dtm/create-webhook (with-meta params mdata)))))) on-update-submit @@ -855,7 +867,7 @@ (let [params (:clean-data @form) mdata {:on-success (partial on-success form) :on-error (partial on-error form)}] - (st/emit! (dd/update-team-webhook + (st/emit! (dtm/update-webhook (with-meta params mdata)))))) on-submit @@ -910,7 +922,7 @@ (tr "modals.edit-webhook.submit-label") (tr "modals.create-webhook.submit-label"))}]]]]]])) -(mf/defc webhooks-hero +(mf/defc webhooks-hero* {::mf/props :obj} [] [:div {:class (stl/css :webhooks-hero-container)} @@ -922,7 +934,7 @@ :on-click #(st/emit! (modal/show :webhook {}))} (tr "dashboard.webhooks.create")]]) -(mf/defc webhook-actions +(mf/defc webhook-actions* {::mf/props :obj ::mf/private true} [{:keys [on-edit on-delete can-edit]}] @@ -945,8 +957,10 @@ :class (stl/css :menu-disabled)} [:> icon* {:id "menu"}]]))) -(mf/defc webhook-item - {::mf/wrap [mf/memo]} +(mf/defc webhook-item* + {::mf/wrap [mf/memo] + ::mf/props :obj + ::mf/private true} [{:keys [webhook permissions]}] (let [error-code (:error-code webhook) id (:id webhook) @@ -966,8 +980,8 @@ (mf/deps id) (fn [] (let [params {:id id} - mdata {:on-success #(st/emit! (dd/fetch-team-webhooks))}] - (st/emit! (dd/delete-team-webhook (with-meta params mdata)))))) + mdata {:on-success #(st/emit! (dtm/fetch-webhooks))}] + (st/emit! (dtm/delete-webhook (with-meta params mdata)))))) on-delete (mf/use-fn @@ -1005,22 +1019,29 @@ (tr "labels.active") (tr "labels.inactive"))]] [:div {:class (stl/css :table-field :actions)} - [:& webhook-actions + [:> webhook-actions* {:on-edit on-edit :on-delete on-delete :can-edit can-edit}]]])) -(mf/defc webhooks-list - {::mf/props :obj} +(mf/defc webhooks-list* + {::mf/props :obj + ::mf/private true} [{:keys [webhooks permissions]}] [:div {:class (stl/css :table-rows :webhook-table)} (for [webhook webhooks] - [:& webhook-item {:webhook webhook :key (:id webhook) :permissions permissions}])]) + [:> webhook-item* + {:webhook webhook + :key (dm/str (:id webhook)) + :permissions permissions}])]) -(mf/defc team-webhooks-page +(def ^:private ref:webhooks + (l/derived :webhooks st/state)) + +(mf/defc webhooks-page* {::mf/props :obj} [{:keys [team]}] - (let [webhooks (mf/deref refs/dashboard-team-webhooks)] + (let [webhooks (mf/deref ref:webhooks)] (mf/with-effect [team] (dom/set-html-title @@ -1030,33 +1051,34 @@ (:name team))))) (mf/with-effect [team] - (st/emit! (dd/fetch-team-webhooks))) + (st/emit! (dtm/fetch-webhooks))) [:* [:& header {:team team :section :dashboard-team-webhooks}] [:section {:class (stl/css :dashboard-container :dashboard-team-webhooks)} [:* - [:& webhooks-hero] + [:> webhooks-hero* {}] (if (empty? webhooks) [:div {:class (stl/css :webhooks-empty)} [:div (tr "dashboard.webhooks.empty.no-webhooks")] [:div (tr "dashboard.webhooks.empty.add-one")]] - [:& webhooks-list {:webhooks webhooks :permissions (:permissions team)}])]]])) + [:> webhooks-list* + {:webhooks webhooks + :permissions (:permissions team)}])]]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SETTINGS SECTION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(mf/defc team-settings-page +(mf/defc team-settings-page* {::mf/props :obj} [{:keys [team]}] (let [finput (mf/use-ref) - members-map (mf/deref refs/dashboard-team-members) - owner (->> (vals members-map) - (d/seek :is-owner)) + members (get team :members) + stats (get team :stats) - stats (mf/deref refs/dashboard-team-stats) + owner (d/seek :is-owner members) permissions (:permissions team) can-edit (or (:is-owner permissions) @@ -1067,8 +1089,7 @@ on-file-selected (fn [file] - (st/emit! (dd/update-team-photo file)))] - + (st/emit! (dtm/update-team-photo file)))] (mf/with-effect [team] (dom/set-html-title (tr "title.team-settings" @@ -1076,11 +1097,9 @@ (tr "dashboard.your-penpot") (:name team))))) - - (mf/with-effect [team] - (let [team-id (:id team)] - (st/emit! (dd/fetch-team-members team-id) - (dd/fetch-team-stats team-id)))) + (mf/with-effect [] + (st/emit! (dtm/fetch-members) + (dtm/fetch-stats))) [:* [:& header {:section :dashboard-team-settings :team team}] @@ -1116,7 +1135,7 @@ [:div {:class (stl/css :block-content)} user-icon [:span {:class (stl/css :block-text)} - (tr "dashboard.num-of-members" (count members-map))]]] + (tr "dashboard.num-of-members" (count members))]]] [:div {:class (stl/css :block)} [:div {:class (stl/css :block-label)} diff --git a/frontend/src/app/main/ui/dashboard/team_form.cljs b/frontend/src/app/main/ui/dashboard/team_form.cljs index 631b17556..8250d9f8c 100644 --- a/frontend/src/app/main/ui/dashboard/team_form.cljs +++ b/frontend/src/app/main/ui/dashboard/team_form.cljs @@ -8,10 +8,10 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.schema :as sm] - [app.main.data.dashboard :as dd] [app.main.data.events :as ev] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] + [app.main.data.team :as dtm] [app.main.store :as st] [app.main.ui.components.forms :as fm] [app.main.ui.icons :as i] @@ -51,7 +51,7 @@ (let [mdata {:on-success (partial on-create-success form) :on-error (partial on-error form)} params {:name (get-in @form [:clean-data :name])}] - (st/emit! (-> (dd/create-team (with-meta params mdata)) + (st/emit! (-> (dtm/create-team (with-meta params mdata)) (with-meta {::ev/origin :dashboard}))))) (defn- on-update-submit @@ -59,7 +59,7 @@ (let [mdata {:on-success (partial on-update-success form) :on-error (partial on-error form)} team (get @form :clean-data)] - (st/emit! (dd/update-team (with-meta team mdata)) + (st/emit! (dtm/update-team (with-meta team mdata)) (modal/hide)))) (defn- on-submit diff --git a/frontend/src/app/main/ui/dashboard/templates.cljs b/frontend/src/app/main/ui/dashboard/templates.cljs index f410c332d..d94b55192 100644 --- a/frontend/src/app/main/ui/dashboard/templates.cljs +++ b/frontend/src/app/main/ui/dashboard/templates.cljs @@ -157,8 +157,8 @@ [:div {:class (stl/css :template-link-title)} (tr "dashboard.libraries-and-templates")] [:div {:class (stl/css :template-link-text)} (tr "dashboard.libraries-and-templates.explore")]]]]]])) -(mf/defc templates-section - {::mf/wrap-props false} +(mf/defc templates-section* + {::mf/props :obj} [{:keys [default-project-id profile project-id team-id]}] (let [templates (mf/deref builtin-templates) templates (mf/with-memo [templates] diff --git a/frontend/src/app/main/ui/onboarding/team_choice.cljs b/frontend/src/app/main/ui/onboarding/team_choice.cljs index 0fe0f961b..59f453985 100644 --- a/frontend/src/app/main/ui/onboarding/team_choice.cljs +++ b/frontend/src/app/main/ui/onboarding/team_choice.cljs @@ -9,8 +9,8 @@ (:require [app.common.data.macros :as dm] [app.common.schema :as sm] - [app.main.data.dashboard :as dd] [app.main.data.events :as ev] + [app.main.data.team :as dtm] [app.main.data.users :as du] [app.main.store :as st] [app.main.ui.components.forms :as fm] @@ -117,7 +117,7 @@ (let [mdata {:on-success on-success :on-error on-error} params {:name name}] - (st/emit! (-> (dd/create-team (with-meta params mdata)) + (st/emit! (-> (dtm/create-team (with-meta params mdata)) (with-meta {::ev/origin :onboarding-without-invitations})) (ptk/data-event ::ev/event {::ev/name "onboarding-step" @@ -133,7 +133,7 @@ (let [mdata {:on-success on-success :on-error on-error}] - (st/emit! (-> (dd/create-team-with-invitations (with-meta params mdata)) + (st/emit! (-> (dtm/create-team-with-invitations (with-meta params mdata)) (with-meta {::ev/origin :onboarding-with-invitations})) (ptk/data-event ::ev/event {::ev/name "onboarding-step" diff --git a/frontend/src/app/main/ui/routes.cljs b/frontend/src/app/main/ui/routes.cljs index a43038b73..74f10126f 100644 --- a/frontend/src/app/main/ui/routes.cljs +++ b/frontend/src/app/main/ui/routes.cljs @@ -11,7 +11,7 @@ [app.common.uri :as u] [app.common.uuid :as uuid] [app.config :as cf] - [app.main.data.users :as du] + [app.main.data.team :as dtm] [app.main.repo :as rp] [app.main.store :as st] [app.util.router :as rt] @@ -119,7 +119,11 @@ (st/emit! (rt/nav :auth-login)) empty-path? - (st/emit! (rt/nav :dashboard-projects {:team-id (du/get-current-team-id profile)} (u/query-string->map qs))) + (let [team-id (or (dtm/get-last-team-id) + (:default-team-id profile))] + (st/emit! (rt/nav :dashboard-projects + {:team-id team-id} + (u/query-string->map qs)))) :else (st/emit! (rt/assign-exception {:type :not-found}))))))))) diff --git a/frontend/src/app/main/ui/settings/options.cljs b/frontend/src/app/main/ui/settings/options.cljs index e1d49acfd..2ea4a4408 100644 --- a/frontend/src/app/main/ui/settings/options.cljs +++ b/frontend/src/app/main/ui/settings/options.cljs @@ -24,7 +24,7 @@ (defn- on-success [profile] (st/emit! (ntf/success (tr "notifications.profile-saved")) - (du/profile-fetched profile))) + (du/initialize-profile profile))) (defn- on-submit [form _event] diff --git a/frontend/src/app/main/ui/settings/sidebar.cljs b/frontend/src/app/main/ui/settings/sidebar.cljs index 651129a36..0cc08fe1e 100644 --- a/frontend/src/app/main/ui/settings/sidebar.cljs +++ b/frontend/src/app/main/ui/settings/sidebar.cljs @@ -10,9 +10,9 @@ [app.config :as cf] [app.main.data.events :as ev] [app.main.data.modal :as modal] - [app.main.data.users :as du] + [app.main.data.team :as dtm] [app.main.store :as st] - [app.main.ui.dashboard.sidebar :refer [profile-section]] + [app.main.ui.dashboard.sidebar :refer [profile-section*]] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] @@ -58,7 +58,8 @@ options? (= section :settings-options) feedback? (= section :settings-feedback) access-tokens? (= section :settings-access-tokens) - team-id (du/get-current-team-id profile) + team-id (or (dtm/get-last-team-id) + (:default-team-id profile)) go-dashboard (mf/use-fn @@ -119,5 +120,5 @@ [:div {:class (stl/css :dashboard-sidebar :settings)} [:& sidebar-content {:profile profile :section section}] - [:& profile-section {:profile profile}]]) + [:> profile-section* {:profile profile}]]) diff --git a/frontend/src/app/main/ui/static.cljs b/frontend/src/app/main/ui/static.cljs index 50a3141e8..4efa39b07 100644 --- a/frontend/src/app/main/ui/static.cljs +++ b/frontend/src/app/main/ui/static.cljs @@ -19,7 +19,7 @@ [app.main.ui.auth.login :refer [login-methods]] [app.main.ui.auth.recovery-request :refer [recovery-request-page recovery-sent-page]] [app.main.ui.auth.register :as register] - [app.main.ui.dashboard.sidebar :refer [sidebar]] + [app.main.ui.dashboard.sidebar :refer [sidebar*]] [app.main.ui.ds.foundations.assets.icon :refer [icon*]] [app.main.ui.ds.foundations.assets.raw-svg :refer [raw-svg*]] [app.main.ui.icons :as i] @@ -267,7 +267,7 @@ [:div {:class (stl/css :dashboard)} [:div {:class (stl/css :dashboard-sidebar)} - [:& sidebar + [:> sidebar* {:team nil :projects [] :project (:default-project-id profile) diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs index b88f4fed9..d5104699c 100644 --- a/frontend/src/app/main/ui/workspace.cljs +++ b/frontend/src/app/main/ui/workspace.cljs @@ -214,7 +214,7 @@ [:& (mf/provider ctx/components-v2) {:value components-v2?} [:& (mf/provider ctx/design-tokens) {:value design-tokens?} [:& (mf/provider ctx/workspace-read-only?) {:value read-only?} - [:& (mf/provider ctx/team-permissions) {:value permissions} + [:& (mf/provider ctx/permissions) {:value permissions} [:section {:class (stl/css :workspace) :style {:background-color background-color :touch-action "none"}} diff --git a/frontend/src/app/main/ui/workspace/main_menu.cljs b/frontend/src/app/main/ui/workspace/main_menu.cljs index f002f90a2..4dc887990 100644 --- a/frontend/src/app/main/ui/workspace/main_menu.cljs +++ b/frontend/src/app/main/ui/workspace/main_menu.cljs @@ -425,7 +425,7 @@ (let [select-all (mf/use-fn #(st/emit! (dw/select-all))) undo (mf/use-fn #(st/emit! dwu/undo)) redo (mf/use-fn #(st/emit! dwu/redo)) - perms (mf/use-ctx ctx/team-permissions) + perms (mf/use-ctx ctx/permissions) can-edit (:can-edit perms)] [:& dropdown-menu {:show true @@ -488,7 +488,7 @@ frames (->> (cfh/get-immediate-children objects uuid/zero) (filterv cfh/frame-shape?)) - perms (mf/use-ctx ctx/team-permissions) + perms (mf/use-ctx ctx/permissions) can-edit (:can-edit perms) on-remove-shared diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs index a63558346..3d61627ed 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs @@ -134,7 +134,7 @@ ::mf/props :obj} [{:keys [selected shapes shapes-with-children page-id file-id on-change-section on-expand]}] (let [objects (mf/deref refs/workspace-page-objects) - permissions (mf/use-ctx ctx/team-permissions) + permissions (mf/use-ctx ctx/permissions) selected-shapes (into [] (keep (d/getf objects)) selected) first-selected-shape (first selected-shapes) diff --git a/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs b/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs index 4d3ba4692..f30af0735 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs @@ -207,7 +207,7 @@ (st/emit! (dw/create-page {:file-id file-id :project-id project-id})) (-> event dom/get-current-target dom/blur!))) read-only? (mf/use-ctx ctx/workspace-read-only?) - permissions (mf/use-ctx ctx/team-permissions)] + permissions (mf/use-ctx ctx/permissions)] [:div {:class (stl/css :sitemap) :style #js {"--height" (str size "px")}} diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index a89eab0c6..c839f6d91 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -96,7 +96,7 @@ vbox' (mf/use-debounce 100 vbox) - permissions (mf/use-ctx ctx/team-permissions) + permissions (mf/use-ctx ctx/permissions) read-only? (mf/use-ctx ctx/workspace-read-only?) ;; DEREFS diff --git a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs index 579b27c10..2f95a851e 100644 --- a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs +++ b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs @@ -94,7 +94,7 @@ show-distances? picking-color?]} wglobal - permissions (mf/use-ctx ctx/team-permissions) + permissions (mf/use-ctx ctx/permissions) read-only? (mf/use-ctx ctx/workspace-read-only?) ;; DEREFS diff --git a/frontend/src/app/render.cljs b/frontend/src/app/render.cljs index 215f45648..d8a3d8650 100644 --- a/frontend/src/app/render.cljs +++ b/frontend/src/app/render.cljs @@ -14,7 +14,7 @@ [app.common.types.components-list :as ctkl] [app.common.uri :as u] [app.main.data.fonts :as df] - [app.main.data.users :as du] + [app.main.data.team :as dtm] [app.main.features :as features] [app.main.render :as render] [app.main.repo :as repo] @@ -37,7 +37,7 @@ (watch [_ _ _] (->> (repo/cmd! :get-team {:file-id file-id}) (rx/mapcat (fn [team] - (rx/of (du/set-current-team team) + (rx/of (dtm/set-current-team team) (ptk/data-event ::team-fetched team)))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; From 1de35b2e3a612cff7a079c9dffdddae28cae3744 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 27 Nov 2024 09:24:27 +0100 Subject: [PATCH 3/9] :sparkles: Disable wasm loading if feature is disabled --- frontend/src/app/main/data/team.cljs | 2 +- frontend/src/app/main/data/workspace.cljs | 50 ++++++++++++----------- frontend/src/app/render_wasm/api.cljs | 27 ++++++------ 3 files changed, 42 insertions(+), 37 deletions(-) diff --git a/frontend/src/app/main/data/team.cljs b/frontend/src/app/main/data/team.cljs index 74805e380..6cbccc054 100644 --- a/frontend/src/app/main/data/team.cljs +++ b/frontend/src/app/main/data/team.cljs @@ -127,7 +127,7 @@ ptk/WatchEvent (watch [_ _ stream] - (let [stopper (rx/filter (ptk/type? ::finalize) stream)] + (let [stopper (rx/filter (ptk/type? ::finalize-team) stream)] (->> (rx/merge (rx/of (fetch-teams)) (->> stream diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 526727345..0580ce640 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -267,30 +267,34 @@ [project-id file-id] (ptk/reify ::fetch-bundle-stage-1 ptk/WatchEvent - (watch [_ _ stream] - (->> (rp/cmd! :get-project {:id project-id}) - (rx/mapcat (fn [project] - (rx/concat - ;; Wait the wasm module to be loaded or failed to - ;; load. We need to wait the promise to be resolved - ;; before continue with the next workspace loading - ;; steps - (->> (rx/from wasm/module) - (rx/ignore)) + (watch [_ state stream] + (let [render-wasm? (features/active-feature? state "render-wasm/v1")] + (->> (rp/cmd! :get-project {:id project-id}) + (rx/mapcat (fn [project] + (rx/concat + ;; Wait the wasm module to be loaded or failed to + ;; load. We need to wait the promise to be resolved + ;; before continue with the next workspace loading + ;; steps - (->> (rp/cmd! :get-team {:id (:team-id project)}) - (rx/mapcat (fn [team] - (let [bundle {:team team - :project project - :file-id file-id - :project-id project-id}] - ;; FIXME: this should not be handled here, pending - ;; refactor of urls and team initialization - ;; normalization - (rx/of (dtm/set-current-team team) - (ptk/data-event ::bundle-stage-1 bundle))))))))) - (rx/take-until - (rx/filter (ptk/type? ::fetch-bundle) stream)))))) + (if ^boolean render-wasm? + (->> (rx/from @wasm/module) + (rx/ignore)) + (rx/empty)) + + (->> (rp/cmd! :get-team {:id (:team-id project)}) + (rx/mapcat (fn [team] + (let [bundle {:team team + :project project + :file-id file-id + :project-id project-id}] + ;; FIXME: this should not be handled here, pending + ;; refactor of urls and team initialization + ;; normalization + (rx/of (dtm/set-current-team team) + (ptk/data-event ::bundle-stage-1 bundle))))))))) + (rx/take-until + (rx/filter (ptk/type? ::fetch-bundle) stream))))))) (defn- fetch-bundle-stage-2 [{:keys [file-id project-id project] :as bundle}] diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index 65a5a7fc8..54171edf1 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -239,16 +239,17 @@ (set! (.-height canvas) (* dpr (.-clientHeight ^js canvas)))) (defonce module - (if (exists? js/dynamicImport) - (let [uri (cf/resolve-static-asset "js/render_wasm.js")] - (->> (js/dynamicImport (str uri)) - (p/mcat (fn [module] - (let [default (unchecked-get module "default")] - (default)))) - (p/fmap (fn [module] - (set! internal-module module) - true)) - (p/merr (fn [cause] - (js/console.error cause) - (p/resolved false))))) - (p/resolved false))) + (delay + (if (exists? js/dynamicImport) + (let [uri (cf/resolve-static-asset "js/render_wasm.js")] + (->> (js/dynamicImport (str uri)) + (p/mcat (fn [module] + (let [default (unchecked-get module "default")] + (default)))) + (p/fmap (fn [module] + (set! internal-module module) + true)) + (p/merr (fn [cause] + (js/console.error cause) + (p/resolved false))))) + (p/resolved false)))) From 838fb060391799212824bddb318f49b56c17a08d Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 26 Nov 2024 20:29:20 +0100 Subject: [PATCH 4/9] :recycle: Rename data.users to data.profile --- frontend/src/app/main.cljs | 2 +- frontend/src/app/main/data/auth.cljs | 2 +- frontend/src/app/main/data/dashboard/shortcuts.cljs | 2 +- frontend/src/app/main/data/events.cljs | 2 +- frontend/src/app/main/data/{users.cljs => profile.cljs} | 2 +- frontend/src/app/main/data/workspace.cljs | 2 +- frontend/src/app/main/data/workspace/shortcuts.cljs | 2 +- frontend/src/app/main/ui/auth/recovery.cljs | 2 +- frontend/src/app/main/ui/auth/recovery_request.cljs | 2 +- frontend/src/app/main/ui/auth/verify_token.cljs | 2 +- frontend/src/app/main/ui/onboarding/newsletter.cljs | 2 +- frontend/src/app/main/ui/onboarding/questions.cljs | 2 +- frontend/src/app/main/ui/onboarding/team_choice.cljs | 2 +- frontend/src/app/main/ui/releases.cljs | 2 +- frontend/src/app/main/ui/settings/access_tokens.cljs | 2 +- frontend/src/app/main/ui/settings/change_email.cljs | 2 +- frontend/src/app/main/ui/settings/delete_account.cljs | 2 +- frontend/src/app/main/ui/settings/options.cljs | 2 +- frontend/src/app/main/ui/settings/password.cljs | 2 +- frontend/src/app/main/ui/settings/profile.cljs | 2 +- frontend/src/app/main/ui/workspace/libraries.cljs | 2 +- frontend/src/app/main/ui/workspace/main_menu.cljs | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) rename frontend/src/app/main/data/{users.cljs => profile.cljs} (99%) diff --git a/frontend/src/app/main.cljs b/frontend/src/app/main.cljs index a01ffdb09..191848d10 100644 --- a/frontend/src/app/main.cljs +++ b/frontend/src/app/main.cljs @@ -12,7 +12,7 @@ [app.config :as cf] [app.main.data.auth :as da] [app.main.data.events :as ev] - [app.main.data.users :as du] + [app.main.data.profile :as du] [app.main.data.websocket :as ws] [app.main.errors] [app.main.features :as feat] diff --git a/frontend/src/app/main/data/auth.cljs b/frontend/src/app/main/data/auth.cljs index 4a04d0d33..10f70ba1a 100644 --- a/frontend/src/app/main/data/auth.cljs +++ b/frontend/src/app/main/data/auth.cljs @@ -14,8 +14,8 @@ [app.common.uuid :as uuid] [app.main.data.events :as ev] [app.main.data.notifications :as ntf] + [app.main.data.profile :as du] [app.main.data.team :as dtm] - [app.main.data.users :as du] [app.main.data.websocket :as ws] [app.main.repo :as rp] [app.util.i18n :as i18n :refer [tr]] diff --git a/frontend/src/app/main/data/dashboard/shortcuts.cljs b/frontend/src/app/main/data/dashboard/shortcuts.cljs index 98d987c11..0a00a5a1e 100644 --- a/frontend/src/app/main/data/dashboard/shortcuts.cljs +++ b/frontend/src/app/main/data/dashboard/shortcuts.cljs @@ -8,8 +8,8 @@ (:require [app.main.data.dashboard :as dd] [app.main.data.events :as ev] + [app.main.data.profile :as du] [app.main.data.shortcuts :as ds] - [app.main.data.users :as du] [app.main.store :as st])) (def shortcuts diff --git a/frontend/src/app/main/data/events.cljs b/frontend/src/app/main/data/events.cljs index 27001909a..ff7260a91 100644 --- a/frontend/src/app/main/data/events.cljs +++ b/frontend/src/app/main/data/events.cljs @@ -182,7 +182,7 @@ (rx/filter #(pos? (count %))) (rx/debounce 2000)) (->> stream - (rx/filter (ptk/type? :app.main.data.users/logout)) + (rx/filter (ptk/type? :app.main.data.profile/logout)) (rx/observe-on :async))) (rx/map (fn [_] (into [] (take max-buffer-size) @buffer))) diff --git a/frontend/src/app/main/data/users.cljs b/frontend/src/app/main/data/profile.cljs similarity index 99% rename from frontend/src/app/main/data/users.cljs rename to frontend/src/app/main/data/profile.cljs index 8ace629bc..c6d0e5a56 100644 --- a/frontend/src/app/main/data/users.cljs +++ b/frontend/src/app/main/data/profile.cljs @@ -4,7 +4,7 @@ ;; ;; Copyright (c) KALEIDOS INC -(ns app.main.data.users +(ns app.main.data.profile (:require [app.common.data :as d] [app.common.data.macros :as dm] diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 0580ce640..861044f0d 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -43,8 +43,8 @@ [app.main.data.notifications :as ntf] [app.main.data.persistence :as dps] [app.main.data.plugins :as dp] + [app.main.data.profile :as du] [app.main.data.team :as dtm] - [app.main.data.users :as du] [app.main.data.workspace.bool :as dwb] [app.main.data.workspace.collapse :as dwco] [app.main.data.workspace.drawing :as dwd] diff --git a/frontend/src/app/main/data/workspace/shortcuts.cljs b/frontend/src/app/main/data/workspace/shortcuts.cljs index 3e87114af..842f9e33d 100644 --- a/frontend/src/app/main/data/workspace/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/shortcuts.cljs @@ -12,8 +12,8 @@ [app.main.data.modal :as modal] [app.main.data.plugins :as dpl] [app.main.data.preview :as dp] + [app.main.data.profile :as du] [app.main.data.shortcuts :as ds] - [app.main.data.users :as du] [app.main.data.workspace :as dw] [app.main.data.workspace.colors :as mdc] [app.main.data.workspace.drawing :as dwd] diff --git a/frontend/src/app/main/ui/auth/recovery.cljs b/frontend/src/app/main/ui/auth/recovery.cljs index cc567d310..85c2af513 100644 --- a/frontend/src/app/main/ui/auth/recovery.cljs +++ b/frontend/src/app/main/ui/auth/recovery.cljs @@ -9,7 +9,7 @@ (:require [app.common.schema :as sm] [app.main.data.notifications :as ntf] - [app.main.data.users :as du] + [app.main.data.profile :as du] [app.main.store :as st] [app.main.ui.components.forms :as fm] [app.util.i18n :as i18n :refer [tr]] diff --git a/frontend/src/app/main/ui/auth/recovery_request.cljs b/frontend/src/app/main/ui/auth/recovery_request.cljs index afb240647..a76c76e9f 100644 --- a/frontend/src/app/main/ui/auth/recovery_request.cljs +++ b/frontend/src/app/main/ui/auth/recovery_request.cljs @@ -9,7 +9,7 @@ (:require [app.common.schema :as sm] [app.main.data.notifications :as ntf] - [app.main.data.users :as du] + [app.main.data.profile :as du] [app.main.store :as st] [app.main.ui.components.forms :as fm] [app.main.ui.components.link :as lk] diff --git a/frontend/src/app/main/ui/auth/verify_token.cljs b/frontend/src/app/main/ui/auth/verify_token.cljs index 6ec884ed5..57483ed31 100644 --- a/frontend/src/app/main/ui/auth/verify_token.cljs +++ b/frontend/src/app/main/ui/auth/verify_token.cljs @@ -8,7 +8,7 @@ (:require [app.main.data.auth :as da] [app.main.data.notifications :as ntf] - [app.main.data.users :as du] + [app.main.data.profile :as du] [app.main.repo :as rp] [app.main.store :as st] [app.main.ui.ds.product.loader :refer [loader*]] diff --git a/frontend/src/app/main/ui/onboarding/newsletter.cljs b/frontend/src/app/main/ui/onboarding/newsletter.cljs index 48c3db10f..c7339ac5a 100644 --- a/frontend/src/app/main/ui/onboarding/newsletter.cljs +++ b/frontend/src/app/main/ui/onboarding/newsletter.cljs @@ -9,7 +9,7 @@ (:require [app.main.data.events :as-alias ev] [app.main.data.notifications :as ntf] - [app.main.data.users :as du] + [app.main.data.profile :as du] [app.main.store :as st] [app.main.ui.icons :as i] [app.util.dom :as dom] diff --git a/frontend/src/app/main/ui/onboarding/questions.cljs b/frontend/src/app/main/ui/onboarding/questions.cljs index 5e223f69f..cdcedcb03 100644 --- a/frontend/src/app/main/ui/onboarding/questions.cljs +++ b/frontend/src/app/main/ui/onboarding/questions.cljs @@ -12,7 +12,7 @@ [app.common.data.macros :as dm] [app.common.schema :as sm] [app.main.data.events :as-alias ev] - [app.main.data.users :as du] + [app.main.data.profile :as du] [app.main.store :as st] [app.main.ui.components.forms :as fm] [app.main.ui.icons :as i] diff --git a/frontend/src/app/main/ui/onboarding/team_choice.cljs b/frontend/src/app/main/ui/onboarding/team_choice.cljs index 59f453985..8e5fde18c 100644 --- a/frontend/src/app/main/ui/onboarding/team_choice.cljs +++ b/frontend/src/app/main/ui/onboarding/team_choice.cljs @@ -10,8 +10,8 @@ [app.common.data.macros :as dm] [app.common.schema :as sm] [app.main.data.events :as ev] + [app.main.data.profile :as du] [app.main.data.team :as dtm] - [app.main.data.users :as du] [app.main.store :as st] [app.main.ui.components.forms :as fm] [app.main.ui.icons :as i] diff --git a/frontend/src/app/main/ui/releases.cljs b/frontend/src/app/main/ui/releases.cljs index d07f2d95f..aa03e0754 100644 --- a/frontend/src/app/main/ui/releases.cljs +++ b/frontend/src/app/main/ui/releases.cljs @@ -7,7 +7,7 @@ (ns app.main.ui.releases (:require [app.main.data.modal :as modal] - [app.main.data.users :as du] + [app.main.data.profile :as du] [app.main.store :as st] [app.main.ui.releases.common :as rc] [app.main.ui.releases.v1-10] diff --git a/frontend/src/app/main/ui/settings/access_tokens.cljs b/frontend/src/app/main/ui/settings/access_tokens.cljs index deae25f54..b555e47e5 100644 --- a/frontend/src/app/main/ui/settings/access_tokens.cljs +++ b/frontend/src/app/main/ui/settings/access_tokens.cljs @@ -10,7 +10,7 @@ [app.common.schema :as sm] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] - [app.main.data.users :as du] + [app.main.data.profile :as du] [app.main.store :as st] [app.main.ui.components.context-menu-a11y :refer [context-menu*]] [app.main.ui.components.forms :as fm] diff --git a/frontend/src/app/main/ui/settings/change_email.cljs b/frontend/src/app/main/ui/settings/change_email.cljs index da5e13779..040364cff 100644 --- a/frontend/src/app/main/ui/settings/change_email.cljs +++ b/frontend/src/app/main/ui/settings/change_email.cljs @@ -10,7 +10,7 @@ [app.common.schema :as sm] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] - [app.main.data.users :as du] + [app.main.data.profile :as du] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.forms :as fm] diff --git a/frontend/src/app/main/ui/settings/delete_account.cljs b/frontend/src/app/main/ui/settings/delete_account.cljs index 4836465bb..3e87531fd 100644 --- a/frontend/src/app/main/ui/settings/delete_account.cljs +++ b/frontend/src/app/main/ui/settings/delete_account.cljs @@ -9,7 +9,7 @@ (:require [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] - [app.main.data.users :as du] + [app.main.data.profile :as du] [app.main.store :as st] [app.main.ui.icons :as i] [app.main.ui.notifications.context-notification :refer [context-notification]] diff --git a/frontend/src/app/main/ui/settings/options.cljs b/frontend/src/app/main/ui/settings/options.cljs index 2ea4a4408..1331d4772 100644 --- a/frontend/src/app/main/ui/settings/options.cljs +++ b/frontend/src/app/main/ui/settings/options.cljs @@ -8,7 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.main.data.notifications :as ntf] - [app.main.data.users :as du] + [app.main.data.profile :as du] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.forms :as fm] diff --git a/frontend/src/app/main/ui/settings/password.cljs b/frontend/src/app/main/ui/settings/password.cljs index ac93d6599..5de6d7b65 100644 --- a/frontend/src/app/main/ui/settings/password.cljs +++ b/frontend/src/app/main/ui/settings/password.cljs @@ -9,7 +9,7 @@ (:require [app.common.schema :as sm] [app.main.data.notifications :as ntf] - [app.main.data.users :as udu] + [app.main.data.profile :as udu] [app.main.store :as st] [app.main.ui.components.forms :as fm] [app.util.dom :as dom] diff --git a/frontend/src/app/main/ui/settings/profile.cljs b/frontend/src/app/main/ui/settings/profile.cljs index e59116203..613208b88 100644 --- a/frontend/src/app/main/ui/settings/profile.cljs +++ b/frontend/src/app/main/ui/settings/profile.cljs @@ -11,7 +11,7 @@ [app.config :as cf] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] - [app.main.data.users :as du] + [app.main.data.profile :as du] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.file-uploader :refer [file-uploader]] diff --git a/frontend/src/app/main/ui/workspace/libraries.cljs b/frontend/src/app/main/ui/workspace/libraries.cljs index 26ae8ca01..ead9db735 100644 --- a/frontend/src/app/main/ui/workspace/libraries.cljs +++ b/frontend/src/app/main/ui/workspace/libraries.cljs @@ -16,7 +16,7 @@ [app.common.uuid :as uuid] [app.config :as cf] [app.main.data.modal :as modal] - [app.main.data.users :as du] + [app.main.data.profile :as du] [app.main.data.workspace.colors :as mdc] [app.main.data.workspace.libraries :as dwl] [app.main.refs :as refs] diff --git a/frontend/src/app/main/ui/workspace/main_menu.cljs b/frontend/src/app/main/ui/workspace/main_menu.cljs index 4dc887990..a0b8a6306 100644 --- a/frontend/src/app/main/ui/workspace/main_menu.cljs +++ b/frontend/src/app/main/ui/workspace/main_menu.cljs @@ -18,8 +18,8 @@ [app.main.data.exports.files :as fexp] [app.main.data.modal :as modal] [app.main.data.plugins :as dp] + [app.main.data.profile :as du] [app.main.data.shortcuts :as scd] - [app.main.data.users :as du] [app.main.data.workspace :as dw] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.shortcuts :as sc] From c6b96a81f15e97693808f09befa031040e9f48ea Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 27 Nov 2024 08:51:42 +0100 Subject: [PATCH 5/9] :recycle: Rename data.events to data.event --- frontend/src/app/main.cljs | 2 +- frontend/src/app/main/data/auth.cljs | 2 +- frontend/src/app/main/data/comments.cljs | 2 +- frontend/src/app/main/data/dashboard.cljs | 2 +- frontend/src/app/main/data/dashboard/shortcuts.cljs | 2 +- frontend/src/app/main/data/{events.cljs => event.cljs} | 2 +- frontend/src/app/main/data/exports/assets.cljs | 2 +- frontend/src/app/main/data/exports/files.cljs | 2 +- frontend/src/app/main/data/fonts.cljs | 2 +- frontend/src/app/main/data/modal.cljs | 2 +- frontend/src/app/main/data/profile.cljs | 2 +- frontend/src/app/main/data/team.cljs | 2 +- frontend/src/app/main/data/viewer.cljs | 2 +- frontend/src/app/main/data/workspace.cljs | 2 +- frontend/src/app/main/data/workspace/colors.cljs | 2 +- frontend/src/app/main/data/workspace/comments.cljs | 2 +- frontend/src/app/main/data/workspace/guides.cljs | 2 +- frontend/src/app/main/data/workspace/interactions.cljs | 2 +- frontend/src/app/main/data/workspace/layout.cljs | 2 +- frontend/src/app/main/data/workspace/libraries.cljs | 2 +- frontend/src/app/main/data/workspace/selection.cljs | 2 +- frontend/src/app/main/data/workspace/shape_layout.cljs | 2 +- frontend/src/app/main/data/workspace/shapes.cljs | 2 +- frontend/src/app/main/data/workspace/shortcuts.cljs | 2 +- frontend/src/app/main/data/workspace/texts.cljs | 2 +- frontend/src/app/main/data/workspace/versions.cljs | 2 +- frontend/src/app/main/repo.cljs | 2 +- frontend/src/app/main/ui/components/copy_button.cljs | 2 +- frontend/src/app/main/ui/dashboard.cljs | 2 +- frontend/src/app/main/ui/dashboard/comments.cljs | 2 +- frontend/src/app/main/ui/dashboard/file_menu.cljs | 2 +- frontend/src/app/main/ui/dashboard/files.cljs | 2 +- frontend/src/app/main/ui/dashboard/import.cljs | 2 +- frontend/src/app/main/ui/dashboard/projects.cljs | 2 +- frontend/src/app/main/ui/dashboard/sidebar.cljs | 2 +- frontend/src/app/main/ui/dashboard/team.cljs | 2 +- frontend/src/app/main/ui/dashboard/team_form.cljs | 2 +- frontend/src/app/main/ui/dashboard/templates.cljs | 2 +- frontend/src/app/main/ui/onboarding/newsletter.cljs | 2 +- frontend/src/app/main/ui/onboarding/questions.cljs | 2 +- frontend/src/app/main/ui/onboarding/team_choice.cljs | 2 +- frontend/src/app/main/ui/settings/sidebar.cljs | 2 +- frontend/src/app/main/ui/static.cljs | 2 +- frontend/src/app/main/ui/viewer/comments.cljs | 2 +- frontend/src/app/main/ui/viewer/inspect/code.cljs | 2 +- frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs | 2 +- frontend/src/app/main/ui/viewer/share_link.cljs | 2 +- frontend/src/app/main/ui/workspace/color_palette.cljs | 2 +- frontend/src/app/main/ui/workspace/colorpicker.cljs | 2 +- frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs | 2 +- frontend/src/app/main/ui/workspace/comments.cljs | 2 +- frontend/src/app/main/ui/workspace/context_menu.cljs | 2 +- frontend/src/app/main/ui/workspace/main_menu.cljs | 2 +- frontend/src/app/main/ui/workspace/palette.cljs | 2 +- frontend/src/app/main/ui/workspace/plugins.cljs | 2 +- frontend/src/app/main/ui/workspace/right_header.cljs | 2 +- frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs | 2 +- .../src/app/main/ui/workspace/sidebar/assets/components.cljs | 2 +- .../src/app/main/ui/workspace/sidebar/assets/file_library.cljs | 2 +- frontend/src/app/main/ui/workspace/sidebar/assets/graphics.cljs | 2 +- .../src/app/main/ui/workspace/sidebar/assets/typographies.cljs | 2 +- .../ui/workspace/sidebar/options/menus/layout_container.cljs | 2 +- frontend/src/app/main/ui/workspace/text_palette.cljs | 2 +- frontend/src/app/main/ui/workspace/top_toolbar.cljs | 2 +- frontend/src/app/util/router.cljs | 2 +- 65 files changed, 65 insertions(+), 65 deletions(-) rename frontend/src/app/main/data/{events.cljs => event.cljs} (99%) diff --git a/frontend/src/app/main.cljs b/frontend/src/app/main.cljs index 191848d10..05d5aed59 100644 --- a/frontend/src/app/main.cljs +++ b/frontend/src/app/main.cljs @@ -11,7 +11,7 @@ [app.common.uuid :as uuid] [app.config :as cf] [app.main.data.auth :as da] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.profile :as du] [app.main.data.websocket :as ws] [app.main.errors] diff --git a/frontend/src/app/main/data/auth.cljs b/frontend/src/app/main/data/auth.cljs index 10f70ba1a..de5ec9a69 100644 --- a/frontend/src/app/main/data/auth.cljs +++ b/frontend/src/app/main/data/auth.cljs @@ -12,7 +12,7 @@ [app.common.exceptions :as ex] [app.common.schema :as sm] [app.common.uuid :as uuid] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.notifications :as ntf] [app.main.data.profile :as du] [app.main.data.team :as dtm] diff --git a/frontend/src/app/main/data/comments.cljs b/frontend/src/app/main/data/comments.cljs index d4333d594..d3e75929a 100644 --- a/frontend/src/app/main/data/comments.cljs +++ b/frontend/src/app/main/data/comments.cljs @@ -12,7 +12,7 @@ [app.common.schema :as sm] [app.common.types.shape-tree :as ctst] [app.common.uuid :as uuid] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.workspace.state-helpers :as wsh] [app.main.repo :as rp] [beicon.v2.core :as rx] diff --git a/frontend/src/app/main/data/dashboard.cljs b/frontend/src/app/main/data/dashboard.cljs index 1f727313d..6a4bb8679 100644 --- a/frontend/src/app/main/data/dashboard.cljs +++ b/frontend/src/app/main/data/dashboard.cljs @@ -14,7 +14,7 @@ [app.common.schema :as sm] [app.common.uuid :as uuid] [app.main.data.common :as dc] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.fonts :as df] [app.main.data.modal :as modal] [app.main.data.websocket :as dws] diff --git a/frontend/src/app/main/data/dashboard/shortcuts.cljs b/frontend/src/app/main/data/dashboard/shortcuts.cljs index 0a00a5a1e..d7a4b9959 100644 --- a/frontend/src/app/main/data/dashboard/shortcuts.cljs +++ b/frontend/src/app/main/data/dashboard/shortcuts.cljs @@ -7,7 +7,7 @@ (ns app.main.data.dashboard.shortcuts (:require [app.main.data.dashboard :as dd] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.profile :as du] [app.main.data.shortcuts :as ds] [app.main.store :as st])) diff --git a/frontend/src/app/main/data/events.cljs b/frontend/src/app/main/data/event.cljs similarity index 99% rename from frontend/src/app/main/data/events.cljs rename to frontend/src/app/main/data/event.cljs index ff7260a91..ff5250be7 100644 --- a/frontend/src/app/main/data/events.cljs +++ b/frontend/src/app/main/data/event.cljs @@ -4,7 +4,7 @@ ;; ;; Copyright (c) KALEIDOS INC -(ns app.main.data.events +(ns app.main.data.event (:require ["ua-parser-js" :as ua] [app.common.data :as d] diff --git a/frontend/src/app/main/data/exports/assets.cljs b/frontend/src/app/main/data/exports/assets.cljs index 686b22780..ca1d41183 100644 --- a/frontend/src/app/main/data/exports/assets.cljs +++ b/frontend/src/app/main/data/exports/assets.cljs @@ -7,7 +7,7 @@ (ns app.main.data.exports.assets (:require [app.common.uuid :as uuid] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.persistence :as dwp] [app.main.data.workspace.state-helpers :as wsh] diff --git a/frontend/src/app/main/data/exports/files.cljs b/frontend/src/app/main/data/exports/files.cljs index 439dcaa97..56ab281a7 100644 --- a/frontend/src/app/main/data/exports/files.cljs +++ b/frontend/src/app/main/data/exports/files.cljs @@ -10,7 +10,7 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.schema :as sm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.features :as features] [app.main.repo :as rp] diff --git a/frontend/src/app/main/data/fonts.cljs b/frontend/src/app/main/data/fonts.cljs index 2daf207c6..e8503ea59 100644 --- a/frontend/src/app/main/data/fonts.cljs +++ b/frontend/src/app/main/data/fonts.cljs @@ -12,7 +12,7 @@ [app.common.logging :as log] [app.common.media :as cm] [app.common.uuid :as uuid] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.notifications :as ntf] [app.main.fonts :as fonts] [app.main.repo :as rp] diff --git a/frontend/src/app/main/data/modal.cljs b/frontend/src/app/main/data/modal.cljs index 1055014c2..27500359f 100644 --- a/frontend/src/app/main/data/modal.cljs +++ b/frontend/src/app/main/data/modal.cljs @@ -8,7 +8,7 @@ (:refer-clojure :exclude [update]) (:require [app.common.uuid :as uuid] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.store :as st] [cljs.core :as c] [potok.v2.core :as ptk])) diff --git a/frontend/src/app/main/data/profile.cljs b/frontend/src/app/main/data/profile.cljs index c6d0e5a56..9cbaaa94c 100644 --- a/frontend/src/app/main/data/profile.cljs +++ b/frontend/src/app/main/data/profile.cljs @@ -12,7 +12,7 @@ [app.common.spec :as us] [app.common.uuid :as uuid] [app.config :as cf] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.media :as di] [app.main.data.team :as-alias dtm] [app.main.repo :as rp] diff --git a/frontend/src/app/main/data/team.cljs b/frontend/src/app/main/data/team.cljs index 6cbccc054..2fcb4aef1 100644 --- a/frontend/src/app/main/data/team.cljs +++ b/frontend/src/app/main/data/team.cljs @@ -12,7 +12,7 @@ [app.common.types.team :as ctt] [app.common.uri :as u] [app.config :as cf] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.media :as di] [app.main.features :as features] [app.main.repo :as rp] diff --git a/frontend/src/app/main/data/viewer.cljs b/frontend/src/app/main/data/viewer.cljs index f247e70f5..c0637e420 100644 --- a/frontend/src/app/main/data/viewer.cljs +++ b/frontend/src/app/main/data/viewer.cljs @@ -16,7 +16,7 @@ [app.common.types.shape-tree :as ctt] [app.common.types.shape.interactions :as ctsi] [app.main.data.comments :as dcm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.fonts :as df] [app.main.features :as features] [app.main.repo :as rp] diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 861044f0d..145855fff 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -37,7 +37,7 @@ [app.config :as cf] [app.main.data.changes :as dch] [app.main.data.comments :as dcm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.fonts :as df] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] diff --git a/frontend/src/app/main/data/workspace/colors.cljs b/frontend/src/app/main/data/workspace/colors.cljs index b11632496..709cc739d 100644 --- a/frontend/src/app/main/data/workspace/colors.cljs +++ b/frontend/src/app/main/data/workspace/colors.cljs @@ -16,7 +16,7 @@ [app.common.types.shape :refer [check-stroke!]] [app.common.types.shape.shadow :refer [check-shadow!]] [app.main.broadcast :as mbc] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as md] [app.main.data.workspace.layout :as layout] [app.main.data.workspace.libraries :as dwl] diff --git a/frontend/src/app/main/data/workspace/comments.cljs b/frontend/src/app/main/data/workspace/comments.cljs index 1e023740e..6664f69cc 100644 --- a/frontend/src/app/main/data/workspace/comments.cljs +++ b/frontend/src/app/main/data/workspace/comments.cljs @@ -14,7 +14,7 @@ [app.common.types.shape-tree :as ctst] [app.main.data.changes :as dch] [app.main.data.comments :as dcm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.workspace :as dw] [app.main.data.workspace.common :as dwco] [app.main.data.workspace.drawing :as dwd] diff --git a/frontend/src/app/main/data/workspace/guides.cljs b/frontend/src/app/main/data/workspace/guides.cljs index 6547d5772..4ecfaba63 100644 --- a/frontend/src/app/main/data/workspace/guides.cljs +++ b/frontend/src/app/main/data/workspace/guides.cljs @@ -12,7 +12,7 @@ [app.common.geom.shapes :as gsh] [app.common.types.page :as ctp] [app.main.data.changes :as dwc] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.workspace.state-helpers :as wsh] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) diff --git a/frontend/src/app/main/data/workspace/interactions.cljs b/frontend/src/app/main/data/workspace/interactions.cljs index 5610f8a54..978413941 100644 --- a/frontend/src/app/main/data/workspace/interactions.cljs +++ b/frontend/src/app/main/data/workspace/interactions.cljs @@ -17,7 +17,7 @@ [app.common.types.shape.interactions :as ctsi] [app.common.uuid :as uuid] [app.main.data.changes :as dch] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.undo :as dwu] diff --git a/frontend/src/app/main/data/workspace/layout.cljs b/frontend/src/app/main/data/workspace/layout.cljs index 1fb219863..c99deb871 100644 --- a/frontend/src/app/main/data/workspace/layout.cljs +++ b/frontend/src/app/main/data/workspace/layout.cljs @@ -9,7 +9,7 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.util.storage :as storage] [clojure.set :as set] [potok.v2.core :as ptk])) diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index fa16fb9a8..f5ee8fe4d 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -27,7 +27,7 @@ [app.config :as cf] [app.main.data.changes :as dch] [app.main.data.comments :as dc] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] [app.main.data.workspace :as-alias dw] diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index cc20ee803..7125453b5 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -18,7 +18,7 @@ [app.common.types.component :as ctk] [app.common.uuid :as uuid] [app.main.data.changes :as dch] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as md] [app.main.data.workspace.collapse :as dwc] [app.main.data.workspace.specialized-panel :as-alias dwsp] diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index 3f7440c06..5ca5ba95c 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -21,7 +21,7 @@ [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid] [app.main.data.changes :as dch] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.workspace.colors :as cl] [app.main.data.workspace.grid-layout.editor :as dwge] [app.main.data.workspace.modifiers :as dwm] diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs index c489b07dd..3433c6a1c 100644 --- a/frontend/src/app/main/data/workspace/shapes.cljs +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -18,7 +18,7 @@ [app.common.types.shape-tree :as ctst] [app.main.data.changes :as dch] [app.main.data.comments :as dc] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.workspace.edition :as dwe] [app.main.data.workspace.selection :as dws] [app.main.data.workspace.state-helpers :as wsh] diff --git a/frontend/src/app/main/data/workspace/shortcuts.cljs b/frontend/src/app/main/data/workspace/shortcuts.cljs index 842f9e33d..64f987d98 100644 --- a/frontend/src/app/main/data/workspace/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/shortcuts.cljs @@ -7,7 +7,7 @@ (ns app.main.data.workspace.shortcuts (:require [app.common.data.macros :as dm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.exports.assets :as de] [app.main.data.modal :as modal] [app.main.data.plugins :as dpl] diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index 20e24611b..4d7f5cf39 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -17,7 +17,7 @@ [app.common.text :as txt] [app.common.types.modifiers :as ctm] [app.common.uuid :as uuid] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.workspace.common :as dwc] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.modifiers :as dwm] diff --git a/frontend/src/app/main/data/workspace/versions.cljs b/frontend/src/app/main/data/workspace/versions.cljs index a9de40a13..a5dfdc2da 100644 --- a/frontend/src/app/main/data/workspace/versions.cljs +++ b/frontend/src/app/main/data/workspace/versions.cljs @@ -8,7 +8,7 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.persistence :as dwp] [app.main.data.workspace :as dw] [app.main.refs :as refs] diff --git a/frontend/src/app/main/repo.cljs b/frontend/src/app/main/repo.cljs index 485495701..2c5247c28 100644 --- a/frontend/src/app/main/repo.cljs +++ b/frontend/src/app/main/repo.cljs @@ -11,7 +11,7 @@ [app.common.transit :as t] [app.common.uri :as u] [app.config :as cf] - [app.main.data.events :as-alias ev] + [app.main.data.event :as-alias ev] [app.util.http :as http] [app.util.sse :as sse] [beicon.v2.core :as rx] diff --git a/frontend/src/app/main/ui/components/copy_button.cljs b/frontend/src/app/main/ui/components/copy_button.cljs index 912fcb9f9..a767d931b 100644 --- a/frontend/src/app/main/ui/components/copy_button.cljs +++ b/frontend/src/app/main/ui/components/copy_button.cljs @@ -8,7 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] - [app.main.data.events :as-alias ev] + [app.main.data.event :as-alias ev] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.timers :as tm] diff --git a/frontend/src/app/main/ui/dashboard.cljs b/frontend/src/app/main/ui/dashboard.cljs index b5862ed63..470b6553d 100644 --- a/frontend/src/app/main/ui/dashboard.cljs +++ b/frontend/src/app/main/ui/dashboard.cljs @@ -11,7 +11,7 @@ [app.config :as cf] [app.main.data.dashboard :as dd] [app.main.data.dashboard.shortcuts :as sc] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.notifications :as notif] [app.main.data.plugins :as dp] diff --git a/frontend/src/app/main/ui/dashboard/comments.cljs b/frontend/src/app/main/ui/dashboard/comments.cljs index 7a34437cc..75ebd8e29 100644 --- a/frontend/src/app/main/ui/dashboard/comments.cljs +++ b/frontend/src/app/main/ui/dashboard/comments.cljs @@ -8,7 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.main.data.comments :as dcm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.workspace.comments :as dwcm] [app.main.refs :as refs] [app.main.store :as st] diff --git a/frontend/src/app/main/ui/dashboard/file_menu.cljs b/frontend/src/app/main/ui/dashboard/file_menu.cljs index 6ce4ff2ab..c210efe08 100644 --- a/frontend/src/app/main/ui/dashboard/file_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/file_menu.cljs @@ -9,7 +9,7 @@ [app.config :as cf] [app.main.data.common :as dcm] [app.main.data.dashboard :as dd] - [app.main.data.events :as-alias ev] + [app.main.data.event :as-alias ev] [app.main.data.exports.files :as fexp] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] diff --git a/frontend/src/app/main/ui/dashboard/files.cljs b/frontend/src/app/main/ui/dashboard/files.cljs index f7e0ccb96..a1d6bedf5 100644 --- a/frontend/src/app/main/ui/dashboard/files.cljs +++ b/frontend/src/app/main/ui/dashboard/files.cljs @@ -8,7 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.main.data.dashboard :as dd] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.dashboard.grid :refer [grid]] diff --git a/frontend/src/app/main/ui/dashboard/import.cljs b/frontend/src/app/main/ui/dashboard/import.cljs index 21f793c7e..c9fb7df2b 100644 --- a/frontend/src/app/main/ui/dashboard/import.cljs +++ b/frontend/src/app/main/ui/dashboard/import.cljs @@ -11,7 +11,7 @@ [app.common.data.macros :as dm] [app.common.logging :as log] [app.main.data.dashboard :as dd] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] [app.main.errors :as errors] diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs index 37cab4ad2..082bfd7d1 100644 --- a/frontend/src/app/main/ui/dashboard/projects.cljs +++ b/frontend/src/app/main/ui/dashboard/projects.cljs @@ -9,7 +9,7 @@ (:require [app.common.geom.point :as gpt] [app.main.data.dashboard :as dd] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.refs :as refs] [app.main.store :as st] diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs index db0219ee9..1c341fdc2 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs @@ -12,7 +12,7 @@ [app.config :as cf] [app.main.data.auth :as da] [app.main.data.dashboard :as dd] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] [app.main.data.team :as dtm] diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index 20eb49cec..dd63d2441 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -12,7 +12,7 @@ [app.common.schema :as sm] [app.config :as cfg] [app.main.data.dashboard :as dd] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] [app.main.data.team :as dtm] diff --git a/frontend/src/app/main/ui/dashboard/team_form.cljs b/frontend/src/app/main/ui/dashboard/team_form.cljs index 8250d9f8c..04b3deb68 100644 --- a/frontend/src/app/main/ui/dashboard/team_form.cljs +++ b/frontend/src/app/main/ui/dashboard/team_form.cljs @@ -8,7 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.schema :as sm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] [app.main.data.team :as dtm] diff --git a/frontend/src/app/main/ui/dashboard/templates.cljs b/frontend/src/app/main/ui/dashboard/templates.cljs index d94b55192..81e27a432 100644 --- a/frontend/src/app/main/ui/dashboard/templates.cljs +++ b/frontend/src/app/main/ui/dashboard/templates.cljs @@ -10,7 +10,7 @@ [app.common.data.macros :as dm] [app.config :as cf] [app.main.data.dashboard :as dd] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.refs :as refs] [app.main.store :as st] diff --git a/frontend/src/app/main/ui/onboarding/newsletter.cljs b/frontend/src/app/main/ui/onboarding/newsletter.cljs index c7339ac5a..a501659f2 100644 --- a/frontend/src/app/main/ui/onboarding/newsletter.cljs +++ b/frontend/src/app/main/ui/onboarding/newsletter.cljs @@ -7,7 +7,7 @@ (ns app.main.ui.onboarding.newsletter (:require-macros [app.main.style :as stl]) (:require - [app.main.data.events :as-alias ev] + [app.main.data.event :as-alias ev] [app.main.data.notifications :as ntf] [app.main.data.profile :as du] [app.main.store :as st] diff --git a/frontend/src/app/main/ui/onboarding/questions.cljs b/frontend/src/app/main/ui/onboarding/questions.cljs index cdcedcb03..6f5592db5 100644 --- a/frontend/src/app/main/ui/onboarding/questions.cljs +++ b/frontend/src/app/main/ui/onboarding/questions.cljs @@ -11,7 +11,7 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.schema :as sm] - [app.main.data.events :as-alias ev] + [app.main.data.event :as-alias ev] [app.main.data.profile :as du] [app.main.store :as st] [app.main.ui.components.forms :as fm] diff --git a/frontend/src/app/main/ui/onboarding/team_choice.cljs b/frontend/src/app/main/ui/onboarding/team_choice.cljs index 8e5fde18c..b799dd646 100644 --- a/frontend/src/app/main/ui/onboarding/team_choice.cljs +++ b/frontend/src/app/main/ui/onboarding/team_choice.cljs @@ -9,7 +9,7 @@ (:require [app.common.data.macros :as dm] [app.common.schema :as sm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.profile :as du] [app.main.data.team :as dtm] [app.main.store :as st] diff --git a/frontend/src/app/main/ui/settings/sidebar.cljs b/frontend/src/app/main/ui/settings/sidebar.cljs index 0cc08fe1e..4f5d4dcac 100644 --- a/frontend/src/app/main/ui/settings/sidebar.cljs +++ b/frontend/src/app/main/ui/settings/sidebar.cljs @@ -8,7 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.config :as cf] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.team :as dtm] [app.main.store :as st] diff --git a/frontend/src/app/main/ui/static.cljs b/frontend/src/app/main/ui/static.cljs index 4efa39b07..565894f7b 100644 --- a/frontend/src/app/main/ui/static.cljs +++ b/frontend/src/app/main/ui/static.cljs @@ -12,7 +12,7 @@ [app.common.pprint :as pp] [app.common.uri :as u] [app.main.data.common :as dc] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.refs :as refs] [app.main.repo :as rp] [app.main.store :as st] diff --git a/frontend/src/app/main/ui/viewer/comments.cljs b/frontend/src/app/main/ui/viewer/comments.cljs index a84f44b7d..1d3c7e000 100644 --- a/frontend/src/app/main/ui/viewer/comments.cljs +++ b/frontend/src/app/main/ui/viewer/comments.cljs @@ -14,7 +14,7 @@ [app.common.geom.rect :as grc] [app.common.geom.shapes :as gsh] [app.main.data.comments :as dcm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.comments :as cmt] diff --git a/frontend/src/app/main/ui/viewer/inspect/code.cljs b/frontend/src/app/main/ui/viewer/inspect/code.cljs index abfc29b90..2a1870070 100644 --- a/frontend/src/app/main/ui/viewer/inspect/code.cljs +++ b/frontend/src/app/main/ui/viewer/inspect/code.cljs @@ -13,7 +13,7 @@ [app.common.geom.shapes :as gsh] [app.common.types.shape-tree :as ctst] [app.config :as cfg] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.fonts :as fonts] [app.main.refs :as refs] [app.main.store :as st] diff --git a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs index d3e5bd480..8d2548007 100644 --- a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs +++ b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs @@ -9,7 +9,7 @@ (:require [app.common.data.macros :as dm] [app.common.types.component :as ctk] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.shape-icon :as sir] diff --git a/frontend/src/app/main/ui/viewer/share_link.cljs b/frontend/src/app/main/ui/viewer/share_link.cljs index 1f86f0ae2..0bac1bac4 100644 --- a/frontend/src/app/main/ui/viewer/share_link.cljs +++ b/frontend/src/app/main/ui/viewer/share_link.cljs @@ -12,7 +12,7 @@ [app.common.logging :as log] [app.config :as cf] [app.main.data.common :as dc] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] [app.main.refs :as refs] diff --git a/frontend/src/app/main/ui/workspace/color_palette.cljs b/frontend/src/app/main/ui/workspace/color_palette.cljs index f1b0a2fb0..e12ec9c23 100644 --- a/frontend/src/app/main/ui/workspace/color_palette.cljs +++ b/frontend/src/app/main/ui/workspace/color_palette.cljs @@ -8,7 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.workspace.colors :as mdc] [app.main.data.workspace.libraries :as dwl] [app.main.refs :as refs] diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index 46d006b60..28fd4a0f6 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -13,7 +13,7 @@ [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.config :as cfg] - [app.main.data.events :as-alias ev] + [app.main.data.event :as-alias ev] [app.main.data.modal :as modal] [app.main.data.shortcuts :as dsc] [app.main.data.workspace.colors :as dc] diff --git a/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs b/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs index c38dbb19b..66120bfdd 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs @@ -10,7 +10,7 @@ [app.common.colors :as c] [app.common.data :as d] [app.common.data.macros :as dm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.workspace :as dw] [app.main.data.workspace.colors :as mdc] [app.main.refs :as refs] diff --git a/frontend/src/app/main/ui/workspace/comments.cljs b/frontend/src/app/main/ui/workspace/comments.cljs index 66ccc1e3d..c34f44ad0 100644 --- a/frontend/src/app/main/ui/workspace/comments.cljs +++ b/frontend/src/app/main/ui/workspace/comments.cljs @@ -8,7 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.main.data.comments :as dcm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.workspace :as dw] [app.main.data.workspace.comments :as dwcm] [app.main.refs :as refs] diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index dd33626c0..67c9ad974 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -16,7 +16,7 @@ [app.common.types.page :as ctp] [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.shortcuts :as scd] [app.main.data.workspace :as dw] diff --git a/frontend/src/app/main/ui/workspace/main_menu.cljs b/frontend/src/app/main/ui/workspace/main_menu.cljs index a0b8a6306..f30710419 100644 --- a/frontend/src/app/main/ui/workspace/main_menu.cljs +++ b/frontend/src/app/main/ui/workspace/main_menu.cljs @@ -13,7 +13,7 @@ [app.common.uuid :as uuid] [app.config :as cf] [app.main.data.common :as dcm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.exports.assets :as de] [app.main.data.exports.files :as fexp] [app.main.data.modal :as modal] diff --git a/frontend/src/app/main/ui/workspace/palette.cljs b/frontend/src/app/main/ui/workspace/palette.cljs index d643ecb8d..37085382c 100644 --- a/frontend/src/app/main/ui/workspace/palette.cljs +++ b/frontend/src/app/main/ui/workspace/palette.cljs @@ -9,7 +9,7 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.workspace :as dw] [app.main.data.workspace.colors :as mdc] [app.main.data.workspace.shortcuts :as sc] diff --git a/frontend/src/app/main/ui/workspace/plugins.cljs b/frontend/src/app/main/ui/workspace/plugins.cljs index c0ad1c580..bebfdbb98 100644 --- a/frontend/src/app/main/ui/workspace/plugins.cljs +++ b/frontend/src/app/main/ui/workspace/plugins.cljs @@ -10,7 +10,7 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.config :as cfg] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.plugins :as dp] [app.main.refs :as refs] diff --git a/frontend/src/app/main/ui/workspace/right_header.cljs b/frontend/src/app/main/ui/workspace/right_header.cljs index 5c1cc9df0..d308b4d13 100644 --- a/frontend/src/app/main/ui/workspace/right_header.cljs +++ b/frontend/src/app/main/ui/workspace/right_header.cljs @@ -8,7 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.config :as cf] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.shortcuts :as scd] [app.main.data.workspace :as dw] diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs index 3391e47d5..f3dfccfd8 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs @@ -10,7 +10,7 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.workspace :as dw] [app.main.data.workspace.colors :as dc] diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs index 77974c20a..65e1e7d23 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs @@ -12,7 +12,7 @@ [app.common.files.helpers :as cfh] [app.common.media :as cm] [app.common.types.file :as ctf] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.workspace :as dw] [app.main.data.workspace.libraries :as dwl] diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs index 88a548384..678c1a714 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs @@ -9,7 +9,7 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.workspace :as dw] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.undo :as dwu] diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/graphics.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/graphics.cljs index a1bb804cb..fe49a5fa3 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/graphics.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/graphics.cljs @@ -12,7 +12,7 @@ [app.common.files.helpers :as cfh] [app.common.media :as cm] [app.config :as cf] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.workspace :as dw] [app.main.data.workspace.libraries :as dwl] diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.cljs index 728c95814..ecb553e0c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.cljs @@ -10,7 +10,7 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.workspace :as dw] [app.main.data.workspace.libraries :as dwl] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs index 937771564..57cf42d9d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs @@ -12,7 +12,7 @@ [app.common.math :as mth] [app.common.types.shape.layout :as ctl] [app.config :as cf] - [app.main.data.events :as-alias ev] + [app.main.data.event :as-alias ev] [app.main.data.workspace :as udw] [app.main.data.workspace.grid-layout.editor :as dwge] [app.main.data.workspace.shape-layout :as dwsl] diff --git a/frontend/src/app/main/ui/workspace/text_palette.cljs b/frontend/src/app/main/ui/workspace/text_palette.cljs index f508747f5..9cb76e456 100644 --- a/frontend/src/app/main/ui/workspace/text_palette.cljs +++ b/frontend/src/app/main/ui/workspace/text_palette.cljs @@ -8,7 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.workspace.texts :as dwt] [app.main.fonts :as f] [app.main.refs :as refs] diff --git a/frontend/src/app/main/ui/workspace/top_toolbar.cljs b/frontend/src/app/main/ui/workspace/top_toolbar.cljs index eb19fc6f7..21a53d8fc 100644 --- a/frontend/src/app/main/ui/workspace/top_toolbar.cljs +++ b/frontend/src/app/main/ui/workspace/top_toolbar.cljs @@ -11,7 +11,7 @@ [app.common.geom.point :as gpt] [app.common.media :as cm] [app.config :as cf] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.workspace :as dw] [app.main.data.workspace.common :as dwc] diff --git a/frontend/src/app/util/router.cljs b/frontend/src/app/util/router.cljs index 1a92d4ec7..880d61997 100644 --- a/frontend/src/app/util/router.cljs +++ b/frontend/src/app/util/router.cljs @@ -10,7 +10,7 @@ [app.common.data.macros :as dm] [app.common.uri :as u] [app.config :as cf] - [app.main.data.events :as ev] + [app.main.data.event :as ev] [app.util.browser-history :as bhistory] [app.util.dom :as dom] [app.util.globals :as globals] From f31e2b72e6eb75b63b1bcdc21633b75b00931c8f Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 26 Nov 2024 20:34:57 +0100 Subject: [PATCH 6/9] :sparkles: Adjust data profile alias --- frontend/src/app/main.cljs | 18 +++++++++--------- frontend/src/app/main/data/auth.cljs | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/frontend/src/app/main.cljs b/frontend/src/app/main.cljs index 05d5aed59..d266422f6 100644 --- a/frontend/src/app/main.cljs +++ b/frontend/src/app/main.cljs @@ -12,7 +12,7 @@ [app.config :as cf] [app.main.data.auth :as da] [app.main.data.event :as ev] - [app.main.data.profile :as du] + [app.main.data.profile :as dp] [app.main.data.websocket :as ws] [app.main.errors] [app.main.features :as feat] @@ -67,14 +67,14 @@ authenticated user; proceed to fetch teams." [stream] (rx/merge - (rx/of (du/fetch-profile)) + (rx/of (dp/fetch-profile)) (->> stream - (rx/filter du/profile-fetched?) + (rx/filter dp/profile-fetched?) (rx/take 1) (rx/map deref) (rx/mapcat (fn [profile] - (if (du/is-authenticated? profile) - (rx/of (du/initialize-profile profile)) + (if (dp/is-authenticated? profile) + (rx/of (dp/initialize-profile profile)) (rx/empty)))) (rx/observe-on :async)))) @@ -95,22 +95,22 @@ ;; Watch for profile deletion events (->> stream - (rx/filter du/profile-deleted?) + (rx/filter dp/profile-deleted?) (rx/map da/logged-out)) ;; Once profile is fetched, initialize all penpot application ;; routes (->> stream - (rx/filter du/profile-fetched?) + (rx/filter dp/profile-fetched?) (rx/take 1) (rx/map #(rt/init-routes))) ;; Once profile fetched and the current user is authenticated, ;; proceed to initialize the websockets connection. (->> stream - (rx/filter du/profile-fetched?) + (rx/filter dp/profile-fetched?) (rx/map deref) - (rx/filter du/is-authenticated?) + (rx/filter dp/is-authenticated?) (rx/take 1) (rx/map #(ws/initialize))))))) diff --git a/frontend/src/app/main/data/auth.cljs b/frontend/src/app/main/data/auth.cljs index de5ec9a69..d25359073 100644 --- a/frontend/src/app/main/data/auth.cljs +++ b/frontend/src/app/main/data/auth.cljs @@ -14,7 +14,7 @@ [app.common.uuid :as uuid] [app.main.data.event :as ev] [app.main.data.notifications :as ntf] - [app.main.data.profile :as du] + [app.main.data.profile :as dp] [app.main.data.team :as dtm] [app.main.data.websocket :as ws] [app.main.repo :as rp] @@ -47,7 +47,7 @@ (if-let [file-id (get props :welcome-file-id)] (rx/of (rt/nav' :workspace {:project-id (:default-project-id profile) :file-id file-id}) - (du/update-profile-props {:welcome-file-id nil})) + (dp/update-profile-props {:welcome-file-id nil})) (let [teams (into #{} (map :id) teams) team-id (dtm/get-last-team-id) @@ -71,7 +71,7 @@ ptk/WatchEvent (watch [_ _ stream] (->> (rx/merge - (rx/of (du/initialize-profile profile) + (rx/of (dp/initialize-profile profile) (ws/initialize) (dtm/fetch-teams)) @@ -110,9 +110,9 @@ (->> (rp/cmd! :login-with-password (d/without-nils params)) (rx/merge-map (fn [data] (rx/merge - (rx/of (du/fetch-profile)) + (rx/of (dp/fetch-profile)) (->> stream - (rx/filter du/profile-fetched?) + (rx/filter dp/profile-fetched?) (rx/take 1) (rx/map deref) (rx/filter (complement is-authenticated?)) @@ -121,7 +121,7 @@ (rx/observe-on :async)) (->> stream - (rx/filter du/profile-fetched?) + (rx/filter dp/profile-fetched?) (rx/take 1) (rx/map deref) (rx/filter is-authenticated?) @@ -179,9 +179,9 @@ ptk/WatchEvent (watch [_ _ stream] (rx/merge - (rx/of (du/fetch-profile)) + (rx/of (dp/fetch-profile)) (->> stream - (rx/filter du/profile-fetched?) + (rx/filter dp/profile-fetched?) (rx/take 1) (rx/map deref) (rx/filter is-authenticated?) From b2f02de5c15cbc9c3f68d8bc6db498cf4f601cac Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 27 Nov 2024 09:17:39 +0100 Subject: [PATCH 7/9] :bug: Fix incorrect uuid on e2e dashboard mock files --- frontend/playwright/data/dashboard/create-project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/playwright/data/dashboard/create-project.json b/frontend/playwright/data/dashboard/create-project.json index 92566a65f..2d9bb26dd 100644 --- a/frontend/playwright/data/dashboard/create-project.json +++ b/frontend/playwright/data/dashboard/create-project.json @@ -1,6 +1,6 @@ { "~:id": "~ue5a24d1b-ef1e-812f-8004-52bab84be6f7", - "~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6", + "~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d", "~:created-at": "~m1715266551088", "~:modified-at": "~m1715266551088", "~:is-default": false, From 3e090b126ef190e745e0ccdd0f9d4d104384ef7a Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 3 Dec 2024 18:23:41 +0100 Subject: [PATCH 8/9] :recycle: Refactor application routing Mainly removes an inconsistent use of path params and normalize all routes to use query params for make it extensible without breaking urls. --- backend/src/app/rpc/commands/viewer.clj | 2 +- common/src/app/common/schema.cljc | 3 + common/src/app/common/text.cljc | 1 - common/src/app/common/uuid.cljc | 9 +- .../data/get-teams-role-viewer.json | 23 + frontend/playwright/data/get-teams.json | 23 + frontend/playwright/ui/pages/DashboardPage.js | 24 +- frontend/playwright/ui/pages/ViewerPage.js | 2 +- frontend/playwright/ui/pages/WorkspacePage.js | 11 +- .../ui/specs/workspace-viewer-role.spec.js | 6 +- frontend/src/app/main/data/auth.cljs | 10 +- frontend/src/app/main/data/comments.cljs | 15 + frontend/src/app/main/data/common.cljs | 163 +++- frontend/src/app/main/data/dashboard.cljs | 190 +---- .../app/main/data/dashboard/shortcuts.cljs | 7 +- frontend/src/app/main/data/notifications.cljs | 2 +- frontend/src/app/main/data/profile.cljs | 2 +- frontend/src/app/main/data/project.cljs | 80 ++ frontend/src/app/main/data/team.cljs | 33 +- frontend/src/app/main/data/viewer.cljs | 168 ++--- .../src/app/main/data/viewer/shortcuts.cljs | 3 +- frontend/src/app/main/data/workspace.cljs | 702 ++++++------------ .../src/app/main/data/workspace/comments.cljs | 43 +- .../app/main/data/workspace/libraries.cljs | 75 +- .../main/data/workspace/notifications.cljs | 6 +- .../app/main/data/workspace/shape_layout.cljs | 12 +- .../app/main/data/workspace/shortcuts.cljs | 16 +- .../main/data/workspace/state_helpers.cljs | 4 - .../src/app/main/data/workspace/texts.cljs | 2 +- .../src/app/main/data/workspace/undo.cljs | 16 +- .../src/app/main/data/workspace/versions.cljs | 220 +++--- frontend/src/app/main/errors.cljs | 6 +- frontend/src/app/main/refs.cljs | 79 +- frontend/src/app/{util => main}/router.cljs | 65 +- frontend/src/app/main/store.cljs | 2 +- frontend/src/app/main/ui.cljs | 166 ++++- frontend/src/app/main/ui/auth/login.cljs | 6 +- frontend/src/app/main/ui/auth/recovery.cljs | 2 +- .../app/main/ui/auth/recovery_request.cljs | 2 +- frontend/src/app/main/ui/auth/register.cljs | 10 +- .../src/app/main/ui/auth/verify_token.cljs | 14 +- frontend/src/app/main/ui/comments.cljs | 18 +- frontend/src/app/main/ui/dashboard.cljs | 27 +- .../src/app/main/ui/dashboard/comments.cljs | 6 +- .../src/app/main/ui/dashboard/file_menu.cljs | 13 +- frontend/src/app/main/ui/dashboard/files.cljs | 27 +- frontend/src/app/main/ui/dashboard/grid.cljs | 16 +- .../src/app/main/ui/dashboard/libraries.cljs | 3 +- .../app/main/ui/dashboard/project_menu.cljs | 17 +- .../src/app/main/ui/dashboard/projects.cljs | 25 +- .../src/app/main/ui/dashboard/sidebar.cljs | 131 ++-- frontend/src/app/main/ui/dashboard/team.cljs | 12 +- .../src/app/main/ui/dashboard/team_form.cljs | 14 +- .../src/app/main/ui/dashboard/templates.cljs | 8 +- .../app/main/ui/onboarding/team_choice.cljs | 4 +- frontend/src/app/main/ui/routes.cljs | 78 +- frontend/src/app/main/ui/settings.cljs | 2 +- .../src/app/main/ui/settings/sidebar.cljs | 6 +- frontend/src/app/main/ui/static.cljs | 9 +- frontend/src/app/main/ui/viewer.cljs | 19 +- frontend/src/app/main/ui/viewer/comments.cljs | 15 +- frontend/src/app/main/ui/viewer/header.cljs | 5 +- .../src/app/main/ui/viewer/inspect/code.cljs | 16 +- .../src/app/main/ui/viewer/interactions.cljs | 4 +- frontend/src/app/main/ui/viewer/shapes.cljs | 16 +- .../src/app/main/ui/viewer/share_link.cljs | 15 +- frontend/src/app/main/ui/workspace.cljs | 71 +- .../src/app/main/ui/workspace/comments.cljs | 32 +- .../app/main/ui/workspace/left_header.cljs | 17 +- .../src/app/main/ui/workspace/libraries.cljs | 67 +- .../src/app/main/ui/workspace/main_menu.cljs | 6 +- .../src/app/main/ui/workspace/presence.cljs | 16 +- .../app/main/ui/workspace/right_header.cljs | 3 +- .../src/app/main/ui/workspace/sidebar.cljs | 50 +- .../app/main/ui/workspace/sidebar/assets.cljs | 7 +- .../ui/workspace/sidebar/assets/common.cljs | 12 +- .../workspace/sidebar/assets/components.cljs | 8 +- .../sidebar/assets/file_library.cljs | 52 +- .../sidebar/options/menus/component.cljs | 1 - .../sidebar/options/menus/typography.cljs | 4 +- .../main/ui/workspace/sidebar/sitemap.cljs | 3 +- .../main/ui/workspace/sidebar/versions.cljs | 35 +- .../main/ui/workspace/viewport/comments.cljs | 4 +- .../main/ui/workspace/viewport/presence.cljs | 4 +- .../main/ui/workspace/viewport/widgets.cljs | 3 +- frontend/src/app/plugins/api.cljs | 6 +- frontend/src/app/plugins/file.cljs | 18 +- frontend/src/app/plugins/page.cljs | 4 +- frontend/src/app/util/dom.cljs | 8 + frontend/src/debug.cljs | 3 +- 90 files changed, 1617 insertions(+), 1548 deletions(-) create mode 100644 frontend/playwright/data/get-teams-role-viewer.json create mode 100644 frontend/playwright/data/get-teams.json create mode 100644 frontend/src/app/main/data/project.cljs rename frontend/src/app/{util => main}/router.cljs (76%) diff --git a/backend/src/app/rpc/commands/viewer.clj b/backend/src/app/rpc/commands/viewer.clj index 9d15b3e8f..641d564af 100644 --- a/backend/src/app/rpc/commands/viewer.clj +++ b/backend/src/app/rpc/commands/viewer.clj @@ -77,7 +77,7 @@ :share-links links :libraries libs :file file - :team team + :team (assoc team :permissions perms) :permissions perms})) (def schema:get-view-only-bundle diff --git a/common/src/app/common/schema.cljc b/common/src/app/common/schema.cljc index 6c1ab3746..eaa4fffbd 100644 --- a/common/src/app/common/schema.cljc +++ b/common/src/app/common/schema.cljc @@ -1010,6 +1010,9 @@ (def valid-safe-number? (lazy-validator ::safe-number)) +(def valid-text? + (validator ::text)) + (def check-safe-int! (check-fn ::safe-int)) diff --git a/common/src/app/common/text.cljc b/common/src/app/common/text.cljc index 3a7fdec93..ad86914ac 100644 --- a/common/src/app/common/text.cljc +++ b/common/src/app/common/text.cljc @@ -412,7 +412,6 @@ (recur (when continue? (rest styles)) taking? to result)) result)))) - (defn content->text "Given a root node of a text content extracts the texts with its associated styles" [content] diff --git a/common/src/app/common/uuid.cljc b/common/src/app/common/uuid.cljc index fd72bdc56..b7b49e2c1 100644 --- a/common/src/app/common/uuid.cljc +++ b/common/src/app/common/uuid.cljc @@ -18,11 +18,18 @@ java.nio.ByteBuffer))) (defn uuid - "Parse string uuid representation into proper UUID instance." + "Creates an UUID instance from string, expectes valid uuid strings, + the existense of validation is implementation detail" [s] #?(:clj (UUID/fromString s) :cljs (c/uuid s))) +(defn parse + "Parse string uuid representation into proper UUID instance, validates input" + [s] + #?(:clj (UUID/fromString s) + :cljs (c/parse-uuid s))) + (defn next [] #?(:clj (UUIDv8/create) diff --git a/frontend/playwright/data/get-teams-role-viewer.json b/frontend/playwright/data/get-teams-role-viewer.json new file mode 100644 index 000000000..25ca63ccf --- /dev/null +++ b/frontend/playwright/data/get-teams-role-viewer.json @@ -0,0 +1,23 @@ +[{ + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:permissions": { + "~:type": "~:membership", + "~:is-owner": false, + "~:is-admin": false, + "~:can-edit": false + }, + "~:name": "Default", + "~:modified-at": "~m1713533116375", + "~:id": "~uc7ce0794-0992-8105-8004-38e630f7920a", + "~:created-at": "~m1713533116375", + "~:is-default": true +}] diff --git a/frontend/playwright/data/get-teams.json b/frontend/playwright/data/get-teams.json new file mode 100644 index 000000000..b572a093a --- /dev/null +++ b/frontend/playwright/data/get-teams.json @@ -0,0 +1,23 @@ +[{ + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:permissions": { + "~:type": "~:membership", + "~:is-owner": true, + "~:is-admin": true, + "~:can-edit": true + }, + "~:name": "Default", + "~:modified-at": "~m1713533116375", + "~:id": "~uc7ce0794-0992-8105-8004-38e630f7920a", + "~:created-at": "~m1713533116375", + "~:is-default": true +}] diff --git a/frontend/playwright/ui/pages/DashboardPage.js b/frontend/playwright/ui/pages/DashboardPage.js index c29469c59..3f7faa604 100644 --- a/frontend/playwright/ui/pages/DashboardPage.js +++ b/frontend/playwright/ui/pages/DashboardPage.js @@ -211,60 +211,64 @@ export class DashboardPage extends BaseWebSocketPage { async goToDashboard() { await this.page.goto( - `#/dashboard/team/${DashboardPage.anyTeamId}/projects`, + `#/dashboard/recent?team-id=${DashboardPage.anyTeamId}`, ); await expect(this.mainHeading).toBeVisible(); } async goToSecondTeamDashboard() { await this.page.goto( - `#/dashboard/team/${DashboardPage.secondTeamId}/projects`, + `#/dashboard/recent?team-id=${DashboardPage.secondTeamId}`, ); } async goToSecondTeamMembersSection() { await this.page.goto( - `#/dashboard/team/${DashboardPage.secondTeamId}/members`, + `#/dashboard/members?team-id=${DashboardPage.secondTeamId}`, ); } async goToSecondTeamInvitationsSection() { await this.page.goto( - `#/dashboard/team/${DashboardPage.secondTeamId}/invitations`, + `#/dashboard/invitations?team-id=${DashboardPage.secondTeamId}`, ); } async goToSecondTeamWebhooksSection() { await this.page.goto( - `#/dashboard/team/${DashboardPage.secondTeamId}/webhooks`, + `#/dashboard/webhooks?team-id=${DashboardPage.secondTeamId}`, ); } async goToSecondTeamWebhooksSection() { await this.page.goto( - `#/dashboard/team/${DashboardPage.secondTeamId}/webhooks`, + `#/dashboard/webhooks?team-id=${DashboardPage.secondTeamId}`, ); } async goToSecondTeamSettingsSection() { await this.page.goto( - `#/dashboard/team/${DashboardPage.secondTeamId}/settings`, + `#/dashboard/settings?team-id=${DashboardPage.secondTeamId}`, ); } async goToSearch() { - await this.page.goto(`#/dashboard/team/${DashboardPage.anyTeamId}/search`); + await this.page.goto( + `#/dashboard/search?team-id=${DashboardPage.anyTeamId}`, + ); } async goToDrafts() { await this.page.goto( - `#/dashboard/team/${DashboardPage.anyTeamId}/projects/${DashboardPage.draftProjectId}`, + `#/dashboard/files?team-id=${DashboardPage.anyTeamId}&project-id=${DashboardPage.draftProjectId}`, ); await expect(this.mainHeading).toHaveText("Drafts"); } async goToFonts() { - await this.page.goto(`#/dashboard/team/${DashboardPage.anyTeamId}/fonts`); + await this.page.goto( + `#/dashboard/fonts?team-id=${DashboardPage.anyTeamId}`, + ); await expect(this.mainHeading).toHaveText("Fonts"); } diff --git a/frontend/playwright/ui/pages/ViewerPage.js b/frontend/playwright/ui/pages/ViewerPage.js index 41fd45a23..311c0c45f 100644 --- a/frontend/playwright/ui/pages/ViewerPage.js +++ b/frontend/playwright/ui/pages/ViewerPage.js @@ -85,7 +85,7 @@ export class ViewerPage extends BaseWebSocketPage { pageId = ViewerPage.anyPageId, } = {}) { await this.page.goto( - `/#/view/${fileId}?page-id=${pageId}§ion=interactions&index=0`, + `/#/view?file-id=${fileId}&page-id=${pageId}§ion=interactions&index=0`, ); this.#ws = await this.waitForNotificationsWebSocket(); diff --git a/frontend/playwright/ui/pages/WorkspacePage.js b/frontend/playwright/ui/pages/WorkspacePage.js index 71c20b639..d0d29b531 100644 --- a/frontend/playwright/ui/pages/WorkspacePage.js +++ b/frontend/playwright/ui/pages/WorkspacePage.js @@ -36,6 +36,14 @@ export class WorkspacePage extends BaseWebSocketPage { "get-team?id=*", "workspace/get-team-default.json", ); + await BaseWebSocketPage.mockRPC(page, "get-teams", "get-teams.json"); + + await BaseWebSocketPage.mockRPC( + page, + "get-team-members?team-id=*", + "logged-in-user/get-team-members-your-penpot.json", + ); + await BaseWebSocketPage.mockRPC( page, "get-profiles-for-file-comments?file-id=*", @@ -43,6 +51,7 @@ export class WorkspacePage extends BaseWebSocketPage { ); } + static anyTeamId = "c7ce0794-0992-8105-8004-38e630f7920a"; static anyProjectId = "c7ce0794-0992-8105-8004-38e630f7920b"; static anyFileId = "c7ce0794-0992-8105-8004-38f280443849"; static anyPageId = "c7ce0794-0992-8105-8004-38f28044384a"; @@ -83,7 +92,7 @@ export class WorkspacePage extends BaseWebSocketPage { pageId = WorkspacePage.anyPageId, } = {}) { await this.page.goto( - `/#/workspace/${WorkspacePage.anyProjectId}/${fileId}?page-id=${pageId}`, + `/#/workspace?team-id=${WorkspacePage.anyTeamId}&file-id=${fileId}&page-id=${pageId}`, ); this.#ws = await this.waitForNotificationsWebSocket(); diff --git a/frontend/playwright/ui/specs/workspace-viewer-role.spec.js b/frontend/playwright/ui/specs/workspace-viewer-role.spec.js index 9830d39b5..d78c3a37d 100644 --- a/frontend/playwright/ui/specs/workspace-viewer-role.spec.js +++ b/frontend/playwright/ui/specs/workspace-viewer-role.spec.js @@ -7,11 +7,7 @@ test.beforeEach(async ({ page }) => { const workspacePage = new WorkspacePage(page); await workspacePage.setupEmptyFile(page); - await WorkspacePage.mockRPC( - page, - "get-team?id=*", - "workspace/get-team-role-viewer.json", - ); + await WorkspacePage.mockRPC(page, "get-teams", "get-teams-role-viewer.json"); await workspacePage.goToWorkspace(); }); diff --git a/frontend/src/app/main/data/auth.cljs b/frontend/src/app/main/data/auth.cljs index d25359073..dd8a39879 100644 --- a/frontend/src/app/main/data/auth.cljs +++ b/frontend/src/app/main/data/auth.cljs @@ -12,14 +12,15 @@ [app.common.exceptions :as ex] [app.common.schema :as sm] [app.common.uuid :as uuid] + [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.data.notifications :as ntf] [app.main.data.profile :as dp] [app.main.data.team :as dtm] [app.main.data.websocket :as ws] [app.main.repo :as rp] + [app.main.router :as rt] [app.util.i18n :as i18n :refer [tr]] - [app.util.router :as rt] [app.util.storage :as storage] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -45,8 +46,9 @@ (rx/of (rt/reload true)) (rx/of (rt/nav-raw :href redirect-href)))) (if-let [file-id (get props :welcome-file-id)] - (rx/of (rt/nav' :workspace {:project-id (:default-project-id profile) - :file-id file-id}) + (rx/of (dcm/go-to-workspace + :file-id file-id + :team-id (:default-team-id profile)) (dp/update-profile-props {:welcome-file-id nil})) (let [teams (into #{} (map :id) teams) @@ -54,7 +56,7 @@ team-id (if (and team-id (contains? teams team-id)) team-id (:default-team-id profile))] - (rx/of (rt/nav' :dashboard-projects {:team-id team-id}))))))] + (rx/of (dcm/go-to-dashboard-recent {:team-id team-id}))))))] (ptk/reify ::logged-in ev/Event diff --git a/frontend/src/app/main/data/comments.cljs b/frontend/src/app/main/data/comments.cljs index d3e75929a..3dc86b5a3 100644 --- a/frontend/src/app/main/data/comments.cljs +++ b/frontend/src/app/main/data/comments.cljs @@ -603,3 +603,18 @@ (filter (fn [comment] (some #(= % (:frame-id comment)) frame-ids?))) (map update-comment-thread-frame) (rx/from)))))) + +(defn fetch-profiles + "Fetch or refresh all profile data for comments of the current file" + [] + (ptk/reify ::fetch-comments-profiles + ptk/WatchEvent + (watch [_ state _] + (let [file-id (:current-file-id state) + share-id (or (-> state :viewer-local :share-id) + (:current-share-id state))] + (->> (rp/cmd! :get-profiles-for-file-comments {:file-id file-id :share-id share-id}) + (rx/map (fn [profiles] + #(update % :profiles merge (d/index-by :id profiles))))))))) + + diff --git a/frontend/src/app/main/data/common.cljs b/frontend/src/app/main/data/common.cljs index 35c582c6a..925c31467 100644 --- a/frontend/src/app/main/data/common.cljs +++ b/frontend/src/app/main/data/common.cljs @@ -14,14 +14,18 @@ [app.common.types.team :as ctt] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] + [app.main.data.persistence :as-alias dps] [app.main.features :as features] [app.main.repo :as rp] + [app.main.router :as rt] [app.main.store :as st] + [app.util.dom :as-alias dom] [app.util.i18n :refer [tr]] - [app.util.router :as rt] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) +(declare go-to-dashboard-recent) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SHARE LINK ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -233,12 +237,165 @@ (watch [_ state _] (when (= :removed change) (let [message (tr "dashboard.removed-from-team" team-name) - profile (:profile state)] + team-id (-> state :profile :default-team-id)] (rx/concat - (rx/of (rt/nav :dashboard-projects {:team-id (:default-team-id profile)})) + (rx/of (go-to-dashboard-recent :team-id team-id)) (->> (rx/of (ntf/info message)) ;; Delay so the navigation can finish (rx/delay 250)))))))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; NAVEGATION EVENTS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn go-to-feedback + [] + (ptk/reify ::go-to-feedback + ptk/WatchEvent + (watch [_ _ _] + (rx/of (rt/nav :settings-feedback {} + ::rt/new-window true + ::rt/window-name "penpot-feedback"))))) + +(defn go-to-dashboard-files + [& {:keys [project-id team-id] :as options}] + (ptk/reify ::go-to-dashboard-files + ptk/WatchEvent + (watch [_ state _] + (let [profile (get state :profile) + team-id (or team-id (:current-team-id state)) + project-id (if (= project-id :default) + (:default-project-id profile) + project-id) + + params {:team-id team-id + :project-id project-id}] + (rx/of (rt/nav :dashboard-files params options)))))) + +(defn go-to-dashboard-search + [& {:keys [term] :as options}] + (ptk/reify ::go-to-dashboard-search + ptk/WatchEvent + (watch [_ state stream] + (let [team-id (:current-team-id state)] + (rx/merge + (->> (rx/of (rt/nav :dashboard-search + {:team-id team-id + :search-term term}) + (modal/hide)) + (rx/observe-on :async)) + + (->> stream + (rx/filter (ptk/type? ::rt/navigated)) + (rx/take 1) + (rx/map (fn [_] + (ptk/event ::dom/focus-element + {:name "search-input"}))))))))) + +(defn go-to-dashboard-libraries + [& {:keys [team-id] :as options}] + (ptk/reify ::go-to-dashboard-libraries + ptk/WatchEvent + (watch [_ state _] + (let [team-id (or team-id (:current-team-id state))] + (rx/of (rt/nav :dashboard-libraries {:team-id team-id})))))) +(defn go-to-dashboard-fonts + [& {:keys [team-id] :as options}] + (ptk/reify ::go-to-dashboard-fonts + ptk/WatchEvent + (watch [_ state _] + (let [team-id (or team-id (:current-team-id state))] + (rx/of (rt/nav :dashboard-libraries {:team-id team-id})))))) + +(defn go-to-dashboard-recent + [& {:keys [team-id] :as options}] + (ptk/reify ::go-to-dashboard-recent + ptk/WatchEvent + (watch [_ state _] + (let [profile (get state :profile) + team-id (cond + (= :default team-id) + (:default-team-id profile) + + (uuid? team-id) + team-id + + :else + (:current-team-id state)) + params {:team-id team-id}] + (rx/of (modal/hide) + (rt/nav :dashboard-recent params options)))))) + +(defn go-to-dashboard-members + [& {:as options}] + (ptk/reify ::go-to-dashboard-members + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state)] + (rx/of (rt/nav :dashboard-members {:team-id team-id})))))) + +(defn go-to-dashboard-invitations + [& {:as options}] + (ptk/reify ::go-to-dashboard-invitations + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state)] + (rx/of (rt/nav :dashboard-invitations {:team-id team-id})))))) + +(defn go-to-dashboard-webhooks + [& {:as options}] + (ptk/reify ::go-to-dashboard-webhooks + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state)] + (rx/of (rt/nav :dashboard-webhooks {:team-id team-id})))))) + +(defn go-to-dashboard-settings + [& {:as options}] + (ptk/reify ::go-to-dashboard-settings + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state)] + (rx/of (rt/nav :dashboard-settings {:team-id team-id})))))) + +(defn go-to-workspace + [& {:keys [team-id file-id page-id layout] :as options}] + (ptk/reify ::go-to-workspace + ptk/WatchEvent + (watch [_ state _] + (let [team-id (or team-id (:current-team-id state)) + file-id (or file-id (:current-file-id state)) + ;: FIXME: why not :current-page-id + page-id (or page-id + (dm/get-in state [:workspace-data :pages 0])) + params (-> (rt/get-params state) + (assoc :team-id team-id) + (assoc :file-id file-id) + (assoc :page-id page-id) + (assoc :layout layout) + (d/without-nils))] + (rx/of (rt/nav :workspace params options)))))) + +(defn go-to-viewer + [& {:keys [file-id page-id section frame-id index] :as options}] + (ptk/reify ::go-to-viewer + ptk/WatchEvent + (watch [_ state _] + (let [page-id (or page-id (:current-page-id state)) + file-id (or file-id (:current-file-id state)) + section (or section :interactions) + params {:file-id file-id + :page-id page-id + :section section + :frame-id frame-id + :index index} + params (d/without-nils params) + name (dm/str "viewer-" file-id) + options (merge {::rt/new-window true + ::rt/window-name name} + options)] + (rx/of ::dps/force-persist + (rt/nav :viewer params options)))))) + diff --git a/frontend/src/app/main/data/dashboard.cljs b/frontend/src/app/main/data/dashboard.cljs index 6a4bb8679..4f606716c 100644 --- a/frontend/src/app/main/data/dashboard.cljs +++ b/frontend/src/app/main/data/dashboard.cljs @@ -13,18 +13,15 @@ [app.common.logging :as log] [app.common.schema :as sm] [app.common.uuid :as uuid] - [app.main.data.common :as dc] + [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.data.fonts :as df] [app.main.data.modal :as modal] [app.main.data.websocket :as dws] [app.main.features :as features] [app.main.repo :as rp] - [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] - [app.util.router :as rt] [app.util.sse :as sse] - [app.util.storage :as storage] [app.util.time :as dt] [beicon.v2.core :as rx] [clojure.set :as set] @@ -115,55 +112,6 @@ (rx/map (fn [result] #(assoc % :search-result result))))))))) -;; --- EVENT: files - -(defn files-fetched - [project-id files] - (letfn [(remove-project-files [files] - (reduce-kv (fn [result id file] - (cond-> result - (= (:project-id file) project-id) (dissoc id))) - files - files))] - (ptk/reify ::files-fetched - ptk/UpdateEvent - (update [_ state] - (-> state - (update :files - (fn [files'] - (reduce #(assoc %1 (:id %2) %2) - (remove-project-files files') - files))) - (assoc-in [:projects project-id :count] (count files))))))) - -(defn fetch-files - [{:keys [project-id] :as params}] - (dm/assert! (uuid? project-id)) - (ptk/reify ::fetch-files - ptk/WatchEvent - (watch [_ _ _] - (->> (rp/cmd! :get-project-files {:project-id project-id}) - (rx/map #(files-fetched project-id %)))))) - -;; --- EVENT: shared-files - -(defn shared-files-fetched - [files] - (ptk/reify ::shared-files-fetched - ptk/UpdateEvent - (update [_ state] - (let [files (d/index-by :id files)] - (assoc state :shared-files files))))) - -(defn fetch-shared-files - [] - (ptk/reify ::fetch-shared-files - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (->> (rp/cmd! :get-team-shared-files {:team-id team-id}) - (rx/map shared-files-fetched)))))) - ;; --- EVENT: recent-files (defn recent-files-fetched @@ -630,133 +578,6 @@ (rx/tap on-success) (rx/catch on-error)))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Navigation -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn go-to-workspace - [{:keys [id project-id] :as file}] - (ptk/reify ::go-to-workspace - ptk/WatchEvent - (watch [_ _ _] - (let [pparams {:project-id project-id :file-id id}] - (rx/of (rt/nav :workspace pparams)))))) - - -(defn go-to-files - ([project-id] (go-to-files project-id nil)) - ([project-id team-id] - (ptk/reify ::go-to-files - ptk/WatchEvent - (watch [_ state _] - (let [team-id (or team-id (:current-team-id state))] - (rx/of (rt/nav :dashboard-files {:team-id team-id - :project-id project-id}))))))) - -(defn go-to-search - ([] (go-to-search nil)) - ([term] - (ptk/reify ::go-to-search - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (if (empty? term) - (do - (dom/focus! (dom/get-element "search-input")) - (rx/of (rt/nav :dashboard-search - {:team-id team-id}))) - (rx/of (rt/nav :dashboard-search - {:team-id team-id} - {:search-term term}))))) - - ptk/EffectEvent - (effect [_ _ _] - (dom/focus! (dom/get-element "search-input")))))) - -(defn go-to-projects - [team-id] - (ptk/reify ::go-to-projects - ptk/WatchEvent - (watch [_ state _] - (let [team-id (or team-id (:current-team-id state))] - (rx/of (rt/nav :dashboard-projects {:team-id team-id})))))) - -(defn go-to-default-team - "High-level component for redirect to the current profile default - team and hide all modals" - [] - (ptk/reify ::go-to-default-team - ptk/WatchEvent - (watch [_ state _] - (let [team-id (dm/get-in state [:profile :default-team-id])] - (rx/of (go-to-projects team-id) - (modal/hide)))))) - - -(defn go-to-current-team - "High-level component for redirect to the current profile default - team and hide all modals" - [] - (ptk/reify ::go-to-current-team - ptk/WatchEvent - (watch [_ state _] - (let [team-id (or (::current-team-id storage/user) - (dm/get-in state [:profile :default-team-id]))] - (rx/of (go-to-projects team-id) - (modal/hide)))))) - -(defn go-to-team-members - [] - (ptk/reify ::go-to-team-members - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (rx/of (rt/nav :dashboard-team-members {:team-id team-id})))))) - -(defn go-to-team-invitations - [] - (ptk/reify ::go-to-team-invitations - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (rx/of (rt/nav :dashboard-team-invitations {:team-id team-id})))))) - -(defn go-to-team-webhooks - [] - (ptk/reify ::go-to-team-webhooks - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (rx/of (rt/nav :dashboard-team-webhooks {:team-id team-id})))))) - -(defn go-to-team-settings - [] - (ptk/reify ::go-to-team-settings - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (rx/of (rt/nav :dashboard-team-settings {:team-id team-id})))))) - -(defn go-to-drafts - [] - (ptk/reify ::go-to-drafts - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state) - projects (:projects state) - default-project (d/seek :is-default (vals projects))] - (when default-project - (rx/of (rt/nav :dashboard-files {:team-id team-id - :project-id (:id default-project)}))))))) - -(defn go-to-libs - [] - (ptk/reify ::go-to-libs - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (rx/of (rt/nav :dashboard-libraries {:team-id team-id})))))) - (defn create-element [] (ptk/reify ::create-element @@ -793,8 +614,7 @@ (watch [_ state _] (let [[file-id :as files] (get state :selected-files)] (if (= 1 (count files)) - (let [file (dm/get-in state [files file-id])] - (rx/of (go-to-workspace file))) + (rx/of (dcm/go-to-workspace :file-id file-id)) (rx/empty)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -806,13 +626,13 @@ (ptk/reify ::handle-change-team-role ptk/WatchEvent (watch [_ _ _] - (rx/of (dc/change-team-role params) + (rx/of (dcm/change-team-role params) (modal/hide))))) (defn- process-message [{:keys [type] :as msg}] (case type - :notification (dc/handle-notification msg) + :notification (dcm/handle-notification msg) :team-role-change (handle-change-team-role msg) - :team-membership-change (dc/team-membership-change msg) + :team-membership-change (dcm/team-membership-change msg) nil)) diff --git a/frontend/src/app/main/data/dashboard/shortcuts.cljs b/frontend/src/app/main/data/dashboard/shortcuts.cljs index d7a4b9959..93183ef4b 100644 --- a/frontend/src/app/main/data/dashboard/shortcuts.cljs +++ b/frontend/src/app/main/data/dashboard/shortcuts.cljs @@ -6,6 +6,7 @@ (ns app.main.data.dashboard.shortcuts (:require + [app.main.data.common :as dcm] [app.main.data.dashboard :as dd] [app.main.data.event :as ev] [app.main.data.profile :as du] @@ -16,17 +17,17 @@ {:go-to-search {:tooltip (ds/meta "F") :command (ds/c-mod "f") :subsections [:navigation-dashboard] - :fn #(st/emit! (dd/go-to-search))} + :fn #(st/emit! (dcm/go-to-dashboard-search))} :go-to-drafts {:tooltip "G D" :command "g d" :subsections [:navigation-dashboard] - :fn #(st/emit! (dd/go-to-drafts))} + :fn #(st/emit! (dcm/go-to-dashboard-files :project-id :default))} :go-to-libs {:tooltip "G L" :command "g l" :subsections [:navigation-dashboard] - :fn #(st/emit! (dd/go-to-libs))} + :fn #(st/emit! (dcm/go-to-dashboard-libraries))} :create-new-project {:tooltip "+" :command "+" diff --git a/frontend/src/app/main/data/notifications.cljs b/frontend/src/app/main/data/notifications.cljs index c58fb4c60..d65ea5486 100644 --- a/frontend/src/app/main/data/notifications.cljs +++ b/frontend/src/app/main/data/notifications.cljs @@ -64,7 +64,7 @@ (rx/merge (let [stopper (rx/filter (ptk/type? ::hide) stream)] (->> stream - (rx/filter (ptk/type? :app.util.router/navigate)) + (rx/filter (ptk/type? :app.main.router/navigate)) (rx/map (fn [_] (hide))) (rx/take-until stopper))) (when (:timeout data) diff --git a/frontend/src/app/main/data/profile.cljs b/frontend/src/app/main/data/profile.cljs index 9cbaaa94c..6c43354fa 100644 --- a/frontend/src/app/main/data/profile.cljs +++ b/frontend/src/app/main/data/profile.cljs @@ -16,9 +16,9 @@ [app.main.data.media :as di] [app.main.data.team :as-alias dtm] [app.main.repo :as rp] + [app.main.router :as rt] [app.plugins.register :as plugins.register] [app.util.i18n :as i18n] - [app.util.router :as rt] [app.util.storage :as storage] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) diff --git a/frontend/src/app/main/data/project.cljs b/frontend/src/app/main/data/project.cljs new file mode 100644 index 000000000..74a927d8d --- /dev/null +++ b/frontend/src/app/main/data/project.cljs @@ -0,0 +1,80 @@ +;; 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) KALEIDOS INC + +(ns app.main.data.project + (:require + [app.common.data :as d] + [app.common.logging :as log] + [app.main.repo :as rp] + [beicon.v2.core :as rx] + [potok.v2.core :as ptk])) + +(log/set-level! :warn) + +(defn- project-fetched + [{:keys [id] :as project}] + (ptk/reify ::project-fetched + ptk/UpdateEvent + (update [_ state] + (update-in state [:projects id] merge project)))) + +(defn fetch-project + "Fetch or refresh a single project" + ([] (fetch-project)) + ([project-id] + (assert (uuid? project-id) "expected a valid uuid for `project-id`") + + (ptk/reify ::fetch-project + ptk/WatchEvent + (watch [_ state _] + (let [project-id (or project-id (:current-project-id state))] + (->> (rp/cmd! :get-project {:id project-id}) + (rx/map project-fetched))))))) + +(defn initialize-project + [project-id] + (ptk/reify ::initialize-project + ptk/UpdateEvent + (update [_ state] + (assoc state :current-project-id project-id)) + + ptk/WatchEvent + (watch [_ _ _] + (rx/of (fetch-project project-id))))) + +(defn finalize-project + [project-id] + (ptk/reify ::finalize-project + ptk/UpdateEvent + (update [_ state] + (let [project-id' (get state :current-project-id)] + (if (= project-id' project-id) + (dissoc state :current-project-id) + state))))) + + +(defn- files-fetched + [project-id files] + (ptk/reify ::files-fetched + ptk/UpdateEvent + (update [_ state] + (-> state + (update :files merge (d/index-by :id files)) + (d/update-in-when [:projects project-id] (fn [project] + (assoc project :count (count files)))))))) + +(defn fetch-files + [project-id] + (assert (uuid? project-id) "expected valid uuid for `project-id`") + (ptk/reify ::fetch-files + ptk/WatchEvent + (watch [_ _ _] + (->> (rp/cmd! :get-project-files {:project-id project-id}) + (rx/map (partial files-fetched project-id)))))) + + + + diff --git a/frontend/src/app/main/data/team.cljs b/frontend/src/app/main/data/team.cljs index 2fcb4aef1..b145b07cc 100644 --- a/frontend/src/app/main/data/team.cljs +++ b/frontend/src/app/main/data/team.cljs @@ -6,6 +6,7 @@ (ns app.main.data.team (:require + [app.common.data :as d] [app.common.data.macros :as dm] [app.common.logging :as log] [app.common.schema :as sm] @@ -16,7 +17,7 @@ [app.main.data.media :as di] [app.main.features :as features] [app.main.repo :as rp] - [app.util.router :as rt] + [app.main.router :as rt] [app.util.storage :as storage] [app.util.webapi :as wapi] [beicon.v2.core :as rx] @@ -57,7 +58,9 @@ (ptk/reify ::members-fetched ptk/UpdateEvent (update [_ state] - (update-in state [:teams team-id] assoc :members members)))) + (-> state + (update-in [:teams team-id] assoc :members members) + (update :profiles merge (d/index-by :id members)))))) (defn fetch-members [] @@ -145,6 +148,7 @@ (if (= team-id' team-id) (-> state (dissoc :current-team-id) + (dissoc :shared-files) (dissoc :fonts)) state))))) @@ -220,6 +224,26 @@ (->> (rp/cmd! :get-webhooks {:team-id team-id}) (rx/map (partial webhooks-fetched team-id))))))) +(defn- shared-files-fetched + [files] + (ptk/reify ::shared-files-fetched + ptk/UpdateEvent + (update [_ state] + (let [files (d/index-by :id files)] + (assoc state :shared-files files))))) + +(defn fetch-shared-files + "Event mainly used for fetch a list of shared libraries for a team, + this list does not includes the content of the library per se. It + is used mainly for show available libraries and a summary of it." + [] + (ptk/reify ::fetch-shared-files + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state)] + (->> (rp/cmd! :get-team-shared-files {:team-id team-id}) + (rx/map shared-files-fetched)))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Data Modification ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -334,7 +358,7 @@ (when reassign-to (assert (uuid? reassign-to) "expect a valid uuid for `reassign-to`")) - (ptk/reify ::leave-team + (ptk/reify ::leave-current-team ptk/WatchEvent (watch [_ state _] (let [team-id (get state :current-team-id) @@ -405,7 +429,7 @@ (->> (rp/cmd! :get-team-invitation-token params) (rx/map (fn [params] - (rt/resolve router :auth-verify-token {} params))) + (rt/resolve router :auth-verify-token params))) (rx/map (fn [fragment] (assoc cf/public-uri :fragment fragment))) (rx/tap (fn [uri] @@ -532,3 +556,4 @@ + diff --git a/frontend/src/app/main/data/viewer.cljs b/frontend/src/app/main/data/viewer.cljs index c0637e420..409c1819d 100644 --- a/frontend/src/app/main/data/viewer.cljs +++ b/frontend/src/app/main/data/viewer.cljs @@ -15,13 +15,15 @@ [app.common.transit :as t] [app.common.types.shape-tree :as ctt] [app.common.types.shape.interactions :as ctsi] - [app.main.data.comments :as dcm] + [app.common.uuid :as uuid] + [app.main.data.comments :as dcmt] + [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.data.fonts :as df] [app.main.features :as features] [app.main.repo :as rp] + [app.main.router :as rt] [app.util.globals :as ug] - [app.util.router :as rt] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -32,7 +34,7 @@ {:zoom 1 :fullscreen? false :interactions-mode :show-on-click - :interactions-show? false + :show-interactions false :comments-mode :all :comments-show :unresolved :selected #{} @@ -55,7 +57,7 @@ [:page-id {:optional true} ::sm/uuid]]) (defn initialize - [{:keys [file-id share-id interactions-show?] :as params}] + [{:keys [file-id share-id] :as params}] (dm/assert! "expected valid params" (sm/check schema:initialize params)) @@ -65,13 +67,13 @@ (update [_ state] (-> state (assoc :current-file-id file-id) + (assoc :current-share-id share-id) (update :viewer-local (fn [lstate] (if (nil? lstate) default-local-state lstate))) - (assoc-in [:viewer-local :share-id] share-id) - (assoc-in [:viewer-local :interactions-show?] interactions-show?))) + (assoc-in [:viewer-local :share-id] share-id))) ptk/WatchEvent (watch [_ state _] @@ -256,12 +258,9 @@ ptk/WatchEvent (watch [_ state _] (let [zoom-type (get-in state [:viewer-local :zoom-type]) - route (:route state) - screen (-> route :data :name keyword) - qparams (:query-params route) - pparams (:path-params route)] + params (rt/get-params state)] - (rx/of (rt/nav screen pparams (assoc qparams :zoom zoom-type))))))) + (rx/of (rt/nav :viewer (assoc params :zoom zoom-type))))))) (def increase-zoom (ptk/reify ::increase-zoom @@ -293,14 +292,17 @@ (ptk/reify ::zoom-to-fit ptk/UpdateEvent (update [_ state] - (let [srect (as-> (get-in state [:route :query-params :page-id]) % - (get-in state [:viewer :pages % :frames]) - (nth % (get-in state [:route :query-params :index])) - (get % :selrect)) - orig-size (get-in state [:viewer-local :viewport-size]) - wdiff (/ (:width orig-size) (:width srect)) - hdiff (/ (:height orig-size) (:height srect)) - minzoom (min wdiff hdiff)] + (let [params (rt/get-params state) + page-id (some-> (:page-id params) uuid/parse) + index (some-> (:index params) parse-long) + + frames (dm/get-in state [:viewer :pages page-id :frames]) + srect (-> (nth frames index) + (get :selrect)) + osize (dm/get-in state [:viewer-local :viewport-size]) + wdiff (/ (:width osize) (:width srect)) + hdiff (/ (:height osize) (:height srect)) + minzoom (min wdiff hdiff)] (-> state (assoc-in [:viewer-local :zoom] minzoom) (assoc-in [:viewer-local :zoom-type] :fit)))) @@ -312,17 +314,25 @@ (ptk/reify ::zoom-to-fill ptk/UpdateEvent (update [_ state] - (let [srect (as-> (get-in state [:route :query-params :page-id]) % - (get-in state [:viewer :pages % :frames]) - (nth % (get-in state [:route :query-params :index])) - (get % :selrect)) - orig-size (get-in state [:viewer-local :viewport-size]) - wdiff (/ (:width orig-size) (:width srect)) - hdiff (/ (:height orig-size) (:height srect)) - maxzoom (max wdiff hdiff)] + + (let [params (rt/get-params state) + page-id (some-> (:page-id params) uuid/parse) + index (some-> (:index params) parse-long) + + frames (dm/get-in state [:viewer :pages page-id :frames]) + srect (-> (nth frames index) + (get :selrect)) + + osize (dm/get-in state [:viewer-local :viewport-size]) + + wdiff (/ (:width osize) (:width srect)) + hdiff (/ (:height osize) (:height srect)) + + maxzoom (max wdiff hdiff)] (-> state (assoc-in [:viewer-local :zoom] maxzoom) (assoc-in [:viewer-local :zoom-type] :fill)))) + ptk/WatchEvent (watch [_ _ _] (rx/of update-zoom-querystring)))) @@ -376,16 +386,15 @@ (-> state (dissoc :viewer-animations) (assoc :viewer-overlays []))) + ptk/WatchEvent (watch [_ state _] - (let [route (:route state) - qparams (:query-params route) - pparams (:path-params route) - index (:index qparams)] + (let [params (rt/get-params state) + index (some-> params :index parse-long)] (when (pos? index) (rx/of - (dcm/close-thread) - (rt/nav :viewer pparams (assoc qparams :index (dec index))))))))) + (dcmt/close-thread) + (rt/nav :viewer (assoc params :index (dec index))))))))) (def select-next-frame (ptk/reify ::select-next-frame @@ -396,30 +405,25 @@ (assoc :viewer-overlays []))) ptk/WatchEvent (watch [_ state _] - (let [route (:route state) - pparams (:path-params route) - qparams (:query-params route) - - page-id (:page-id qparams) - index (:index qparams) + (let [params (rt/get-params state) + index (some-> params :index parse-long) + page-id (some-> params :page-id parse-uuid) total (count (get-in state [:viewer :pages page-id :frames]))] (when (< index (dec total)) (rx/of - (dcm/close-thread) - (rt/nav :viewer pparams (assoc qparams :index (inc index))))))))) + (dcmt/close-thread) + (rt/nav :viewer params (assoc params :index (inc index))))))))) (def select-first-frame (ptk/reify ::select-first-frame ptk/WatchEvent (watch [_ state _] - (let [route (:route state) - qparams (:query-params route) - pparams (:path-params route)] + (let [params (rt/get-params state)] (rx/of - (dcm/close-thread) - (rt/nav :viewer pparams (assoc qparams :index 0))))))) + (dcmt/close-thread) + (rt/nav :viewer (assoc params :index 0))))))) (def valid-interaction-modes #{:hide :show :show-on-click}) @@ -434,17 +438,14 @@ (update [_ state] (-> state (assoc-in [:viewer-local :interactions-mode] mode) - (assoc-in [:viewer-local :interactions-show?] (case mode - :hide false - :show true - :show-on-click false)))) + (assoc-in [:viewer-local :show-interactions] (case mode + :hide false + :show true + :show-on-click false)))) ptk/WatchEvent (watch [_ state _] - (let [route (:route state) - screen (-> route :data :name keyword) - qparams (:query-params route) - pparams (:path-params route)] - (rx/of (rt/nav screen pparams (assoc qparams :interactions-mode mode))))))) + (let [params (rt/get-params state)] + (rx/of (rt/nav :viewer (assoc params :interactions-mode mode))))))) (declare flash-done) @@ -453,7 +454,7 @@ (ptk/reify ::flash-interactions ptk/UpdateEvent (update [_ state] - (assoc-in state [:viewer-local :interactions-show?] true)) + (assoc-in state [:viewer-local :show-interactions] true)) ptk/WatchEvent (watch [_ _ stream] @@ -466,7 +467,7 @@ (ptk/reify ::flash-done ptk/UpdateEvent (update [_ state] - (assoc-in state [:viewer-local :interactions-show?] false)))) + (assoc-in state [:viewer-local :show-interactions] false)))) (defn set-nav-scroll [scroll] @@ -500,11 +501,8 @@ ptk/WatchEvent (watch [_ state _] - (let [route (:route state) - screen (-> route :data :name keyword) - qparams (:query-params route) - pparams (:path-params route)] - (rx/of (rt/nav screen pparams (assoc qparams :index index))))))) + (let [params (rt/get-params state)] + (rx/of (rt/nav :viewer (assoc params :index index))))))) (defn go-to-frame ([frame-id] @@ -573,10 +571,8 @@ ptk/WatchEvent (watch [_ state _] - (let [route (:route state) - pparams (:path-params route) - qparams (:query-params route)] - (rx/of (rt/nav :viewer pparams (assoc qparams :section section))))))) + (let [params (rt/get-params state)] + (rx/of (rt/nav :viewer (assoc params :section section))))))) ;; --- Overlays @@ -771,9 +767,8 @@ (ptk/reify ::go-to-dashboard ptk/WatchEvent (watch [_ state _] - (let [team-id (get-in state [:viewer :project :team-id]) - params {:team-id team-id}] - (rx/of (rt/nav :dashboard-projects params)))))) + (let [team-id (get-in state [:viewer :project :team-id])] + (rx/of (dcm/go-to-dashboard-recent :team-id team-id)))))) (defn go-to-page [page-id] @@ -784,13 +779,10 @@ ptk/WatchEvent (watch [_ state _] - (let [route (:route state) - pparams (:path-params route) - qparams (-> (:query-params route) - (assoc :index 0) - (assoc :page-id page-id)) - rname (get-in route [:data :name])] - (rx/of (rt/nav rname pparams qparams)))))) + (let [params (-> (rt/get-params state) + (assoc :index 0) + (assoc :page-id page-id))] + (rx/of (rt/nav :viewer params)))))) (defn go-to-workspace ([] (go-to-workspace nil)) @@ -798,14 +790,16 @@ (ptk/reify ::go-to-workspace ptk/WatchEvent (watch [_ state _] - (let [route (:route state) - project-id (get-in state [:viewer :project :id]) - file-id (get-in state [:viewer :file :id]) - saved-page-id (get-in route [:query-params :page-id]) - pparams {:project-id project-id :file-id file-id} - qparams {:page-id (or page-id saved-page-id)}] - (rx/of (rt/nav-new-window* - {:rname :workspace - :path-params pparams - :query-params qparams - :name (str "workspace-" file-id)}))))))) + (let [params (rt/get-params state) + file-id (get-in state [:viewer :file :id]) + team-id (get-in state [:viewer :project :team-id]) + page-id (or page-id + (some-> (:page-id params) uuid/parse)) + params {:page-id page-id + :file-id file-id + :team-id team-id} + name (dm/str "workspace-" file-id)] + + (rx/of (rt/nav :workspace params + ::rt/new-window true + ::rt/window-name name))))))) diff --git a/frontend/src/app/main/data/viewer/shortcuts.cljs b/frontend/src/app/main/data/viewer/shortcuts.cljs index f6ac67296..3fa3165ea 100644 --- a/frontend/src/app/main/data/viewer/shortcuts.cljs +++ b/frontend/src/app/main/data/viewer/shortcuts.cljs @@ -6,6 +6,7 @@ (ns app.main.data.viewer.shortcuts (:require + [app.main.data.common :as dcm] [app.main.data.shortcuts :as ds] [app.main.data.viewer :as dv] [app.main.store :as st])) @@ -69,7 +70,7 @@ :open-workspace {:tooltip "G W" :command "g w" :subsections [:navigation-viewer] - :fn #(st/emit! (dv/go-to-workspace))}}) + :fn #(st/emit! (dcm/go-to-workspace))}}) (defn get-tooltip [shortcut] (assert (contains? shortcuts shortcut) (str shortcut)) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 145855fff..5cbc687e7 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -36,17 +36,18 @@ [app.common.uuid :as uuid] [app.config :as cf] [app.main.data.changes :as dch] - [app.main.data.comments :as dcm] + [app.main.data.comments :as dcmt] + [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.data.fonts :as df] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] - [app.main.data.persistence :as dps] [app.main.data.plugins :as dp] [app.main.data.profile :as du] - [app.main.data.team :as dtm] + [app.main.data.project :as dpj] [app.main.data.workspace.bool :as dwb] [app.main.data.workspace.collapse :as dwco] + [app.main.data.workspace.colors :as dwcl] [app.main.data.workspace.drawing :as dwd] [app.main.data.workspace.edition :as dwe] [app.main.data.workspace.fix-broken-shapes :as fbs] @@ -75,6 +76,7 @@ [app.main.features :as features] [app.main.features.pointer-map :as fpmap] [app.main.repo :as rp] + [app.main.router :as rt] [app.main.streams :as ms] [app.main.worker :as uw] [app.render-wasm :as wasm] @@ -82,7 +84,6 @@ [app.util.globals :as ug] [app.util.http :as http] [app.util.i18n :as i18n :refer [tr]] - [app.util.router :as rt] [app.util.storage :as storage] [app.util.timers :as tm] [app.util.webapi :as wapi] @@ -101,12 +102,15 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (declare ^:private workspace-initialized) +(declare ^:private fetch-libraries) (declare ^:private libraries-fetched) -(declare go-to-layout) +(declare ^:private preload-data-uris) + +;; (declare go-to-layout) ;; --- Initialize Workspace -(defn initialize-layout +(defn initialize-workspace-layout [lname] (ptk/reify ::initialize-layout ptk/UpdateEvent @@ -121,105 +125,45 @@ (rx/of (layout/ensure-layout lname)) (rx/of (layout/ensure-layout :layers)))))) -(defn- workspace-initialized - [] - (ptk/reify ::workspace-initialized - ptk/UpdateEvent - (update [_ state] - (-> state - (assoc :workspace-undo {}) - (assoc :workspace-ready? true))) +(defn- datauri->blob-uri + [uri] + (->> (http/send! {:uri uri + :response-type :blob + :method :get}) + (rx/map :body) + (rx/map (fn [blob] (wapi/create-uri blob))))) - ptk/WatchEvent - (watch [_ state _] - (rx/of - (when (and (not (boolean (-> state :profile :props :v2-info-shown))) - (features/active-feature? state "components/v2")) - (modal/show :v2-info {})) - (dp/check-open-plugin) - (fdf/fix-deleted-fonts) - (fbs/fix-broken-shapes))))) +(defn- get-file-object-thumbnails + [file-id] + (->> (rp/cmd! :get-file-object-thumbnails {:file-id file-id}) + (rx/mapcat (fn [thumbnails] + (->> (rx/from thumbnails) + (rx/mapcat (fn [[k v]] + ;; we only need to fetch the thumbnail if + ;; it is a data:uri, otherwise we can just + ;; use the value as is. + (if (str/starts-with? v "data:") + (->> (datauri->blob-uri v) + (rx/map (fn [uri] [k uri]))) + (rx/of [k v]))))))) + (rx/reduce conj {}))) -(defn- workspace-data-loaded - [data] - (ptk/reify ::workspace-data-loaded - ptk/UpdateEvent - (update [_ state] - (let [data (d/removem (comp t/pointer? val) data)] - (assoc state :workspace-data data))))) - -(defn- bundle-fetched - [{:keys [features file thumbnails project team team-users comments-users]}] - (ptk/reify ::bundle-fetched - ptk/UpdateEvent - (update [_ state] - (-> state - (assoc :users (d/index-by :id team-users)) - (assoc :workspace-thumbnails thumbnails) - (assoc :workspace-file (dissoc file :data)) - (assoc :workspace-project project) - (assoc :current-file-comments-users (d/index-by :id comments-users)))) - - ptk/WatchEvent - (watch [_ _ stream] - (let [team-id (:id team) - file-id (:id file) - stopper (rx/filter (ptk/type? ::bundle-fetched) stream)] - - (->> (rx/concat - ;; Initialize notifications - ;; FIXME: this should not be initialized here looks like - (rx/of (dwn/initialize team-id file-id) - (dwsl/initialize)) - - ;; Load team fonts. We must ensure custom fonts are - ;; fully loadad before mark workspace as initialized - (rx/merge - (->> stream - (rx/filter (ptk/type? ::df/fonts-loaded)) - (rx/take 1) - (rx/ignore)) - - (rx/of (df/fetch-fonts)) - - ;; FIXME: move to bundle fetch stages - - ;; Load main file - (->> (fpmap/resolve-file file) - (rx/map :data) - (rx/mapcat (fn [{:keys [pages-index] :as data}] - (->> (rx/from (seq pages-index)) - (rx/mapcat - (fn [[id page]] - (let [page (update page :objects ctst/start-page-index)] - (->> (uw/ask! {:cmd :initialize-page-index :page page}) - (rx/map (fn [_] [id page])))))) - (rx/reduce conj {}) - (rx/map (fn [pages-index] - (assoc data :pages-index pages-index)))))) - (rx/map workspace-data-loaded)) - - ;; Load libraries - (->> (rp/cmd! :get-file-libraries {:file-id file-id}) - (rx/mapcat (fn [libraries] - (rx/merge - (->> (rx/from libraries) - (rx/merge-map - (fn [{:keys [id synced-at]}] - (->> (rp/cmd! :get-file {:id id :features features}) - (rx/map #(assoc % :synced-at synced-at))))) - (rx/merge-map fpmap/resolve-file) - (rx/reduce conj []) - (rx/map libraries-fetched)) - (->> (rx/from libraries) - (rx/map :id) - (rx/mapcat (fn [file-id] - (rp/cmd! :get-file-object-thumbnails {:file-id file-id :tag "component"}))) - (rx/map dwl/library-thumbnails-fetched))))))) - - (rx/of (with-meta (workspace-initialized) - {:file-id file-id}))) - (rx/take-until stopper)))))) +(defn- resolve-file + [file] + (->> (fpmap/resolve-file file) + (rx/map :data) + (rx/mapcat + (fn [{:keys [pages-index] :as data}] + (->> (rx/from (seq pages-index)) + (rx/mapcat + (fn [[id page]] + (let [page (update page :objects ctst/start-page-index)] + (->> (uw/ask! {:cmd :initialize-page-index :page page}) + (rx/map (fn [_] [id page])))))) + (rx/reduce conj {}) + (rx/map (fn [pages-index] + (let [data (assoc data :pages-index pages-index)] + (assoc file :data (d/removem (comp t/pointer? val) data)))))))))) (defn- libraries-fetched [libraries] @@ -240,193 +184,180 @@ (rx/concat (rx/timer 1000) (rx/of (dwl/notify-sync-file file-id)))))))) -(defn- datauri->blob-uri - [uri] - (->> (http/send! {:uri uri - :response-type :blob - :method :get}) - (rx/map :body) - (rx/map (fn [blob] (wapi/create-uri blob))))) - -(defn- fetch-file-object-thumbnails +(defn- fetch-libraries [file-id] - (->> (rp/cmd! :get-file-object-thumbnails {:file-id file-id}) - (rx/mapcat (fn [thumbnails] - (->> (rx/from thumbnails) - (rx/mapcat (fn [[k v]] - ;; we only need to fetch the thumbnail if - ;; it is a data:uri, otherwise we can just - ;; use the value as is. - (if (str/starts-with? v "data:") - (->> (datauri->blob-uri v) - (rx/map (fn [uri] [k uri]))) - (rx/of [k v]))))))) - (rx/reduce conj {}))) - -(defn- fetch-bundle-stage-1 - [project-id file-id] - (ptk/reify ::fetch-bundle-stage-1 + (ptk/reify ::fetch-libries ptk/WatchEvent - (watch [_ state stream] - (let [render-wasm? (features/active-feature? state "render-wasm/v1")] - (->> (rp/cmd! :get-project {:id project-id}) - (rx/mapcat (fn [project] - (rx/concat - ;; Wait the wasm module to be loaded or failed to - ;; load. We need to wait the promise to be resolved - ;; before continue with the next workspace loading - ;; steps + (watch [_ state _] + (let [features (features/get-team-enabled-features state)] + (->> (rp/cmd! :get-file-libraries {:file-id file-id}) + (rx/mapcat + (fn [libraries] + (rx/merge + (->> (rx/from libraries) + (rx/merge-map + (fn [{:keys [id synced-at]}] + (->> (rp/cmd! :get-file {:id id :features features}) + (rx/map #(assoc % :synced-at synced-at))))) + (rx/merge-map resolve-file) + (rx/reduce conj []) + (rx/map libraries-fetched)) + (->> (rx/from libraries) + (rx/map :id) + (rx/mapcat (fn [file-id] + (rp/cmd! :get-file-object-thumbnails {:file-id file-id :tag "component"}))) + (rx/map dwl/library-thumbnails-fetched)))))))))) - (if ^boolean render-wasm? - (->> (rx/from @wasm/module) - (rx/ignore)) - (rx/empty)) - - (->> (rp/cmd! :get-team {:id (:team-id project)}) - (rx/mapcat (fn [team] - (let [bundle {:team team - :project project - :file-id file-id - :project-id project-id}] - ;; FIXME: this should not be handled here, pending - ;; refactor of urls and team initialization - ;; normalization - (rx/of (dtm/set-current-team team) - (ptk/data-event ::bundle-stage-1 bundle))))))))) - (rx/take-until - (rx/filter (ptk/type? ::fetch-bundle) stream))))))) - -(defn- fetch-bundle-stage-2 - [{:keys [file-id project-id project] :as bundle}] - (ptk/reify ::fetch-bundle-stage-2 +(defn- workspace-initialized + [] + (ptk/reify ::workspace-initialized ptk/UpdateEvent (update [_ state] (-> state - (update :projects assoc project-id project))) + (assoc :workspace-undo {}) + (assoc :workspace-ready true))) ptk/WatchEvent - (watch [_ state stream] - (let [features (features/get-team-enabled-features state) + (watch [_ state _] + (rx/of + (when (and (not (boolean (-> state :profile :props :v2-info-shown))) + (features/active-feature? state "components/v2")) + (modal/show :v2-info {})) + (dp/check-open-plugin) + (fdf/fix-deleted-fonts) + (fbs/fix-broken-shapes))))) - ;; WTF is this? - share-id (-> state :viewer-local :share-id)] - (->> (rx/zip (rp/cmd! :get-file {:id file-id :features features :project-id project-id}) - (fetch-file-object-thumbnails file-id) - (rp/cmd! :get-team-users {:file-id file-id}) - (rp/cmd! :get-profiles-for-file-comments {:file-id file-id :share-id share-id})) - (rx/take 1) - (rx/map (fn [[file thumbnails team-users comments-users]] - (let [bundle (-> bundle - (assoc :file file) - (assoc :features features) - (assoc :thumbnails thumbnails) - (assoc :team-users team-users) - (assoc :comments-users comments-users))] - (ptk/data-event ::bundle-stage-2 bundle)))) - (rx/take-until - (rx/filter (ptk/type? ::fetch-bundle) stream))))))) +(defn- bundle-fetched + [{:keys [features file thumbnails]}] + (ptk/reify ::bundle-fetched + IDeref + (-deref [_] + {:features features + :file file + :thumbnails thumbnails}) -(declare go-to-component) + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc :thumbnails thumbnails) + (assoc :workspace-file (dissoc file :data)) + (assoc :workspace-data (:data file)))) + + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state) + file-id (:id file)] + + (rx/of (dwn/initialize team-id file-id) + (dwsl/initialize-shape-layout) + (fetch-libraries file-id)))))) (defn- fetch-bundle "Multi-stage file bundle fetch coordinator" - [project-id file-id] + [file-id] (ptk/reify ::fetch-bundle ptk/WatchEvent (watch [_ state stream] - (->> (rx/merge - (rx/of (fetch-bundle-stage-1 project-id file-id)) + (let [features (features/get-team-enabled-features state) + render-wasm? (contains? features "render-wasm/v1") + stopper-s (rx/filter (ptk/type? ::finalize-workspace) stream)] - (->> stream - (rx/filter (ptk/type? ::bundle-stage-1)) - (rx/observe-on :async) - (rx/map deref) - (rx/map fetch-bundle-stage-2)) + (->> (rx/concat + ;; Firstly load wasm module if it is enabled and fonts + (rx/merge + (if ^boolean render-wasm? + (->> (rx/from @wasm/module) + (rx/ignore)) + (rx/empty)) - (->> stream - (rx/filter (ptk/type? ::bundle-stage-2)) - (rx/observe-on :async) - (rx/map deref) - (rx/map bundle-fetched)) + (->> stream + (rx/filter (ptk/type? ::df/fonts-loaded)) + (rx/take 1) + (rx/ignore)) + (rx/of (df/fetch-fonts))) - (when-let [component-id (get-in state [:route :query-params :component-id])] - (->> stream - (rx/filter (ptk/type? ::workspace-initialized)) - (rx/observe-on :async) + ;; Then fetch file and thumbnails + (->> (rx/zip (rp/cmd! :get-file {:id file-id :features features}) + (get-file-object-thumbnails file-id)) (rx/take 1) - (rx/map #(go-to-component (uuid/uuid component-id)))))) + (rx/mapcat + (fn [[file thumbnails]] + (->> (resolve-file file) + (rx/map (fn [file] + {:file file + :features features + :thumbnails thumbnails}))))) + (rx/map bundle-fetched))) - (rx/take-until - (rx/filter (ptk/type? ::fetch-bundle) stream)))))) + (rx/take-until stopper-s)))))) -(defn initialize-file - [project-id file-id] - (dm/assert! (uuid? project-id)) - (dm/assert! (uuid? file-id)) +(defn initialize-workspace + [file-id] + (assert (uuid? file-id) "expected valud uuid for `file-id`") - (ptk/reify ::initialize-file + (ptk/reify ::initialize-workspace ptk/UpdateEvent (update [_ state] (assoc state :recent-colors (:recent-colors storage/user) - :workspace-ready? false + :workspace-ready false :current-file-id file-id - :current-project-id project-id :workspace-presence {})) ptk/WatchEvent - (watch [_ _ stream] - (log/debug :hint "initialize-file" :file-id file-id) - (let [stoper-s (rx/filter (ptk/type? ::finalize-file) stream)] - (rx/merge - (rx/of (ntf/hide) - ;; We initialize the features without knowning the - ;; team specific features in this step. - (features/initialize) - (dcm/retrieve-comment-threads file-id) - (fetch-bundle project-id file-id)) + (watch [_ state stream] + (log/debug :hint "initialize-workspace" :file-id file-id) + (let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream) + rparams (rt/get-params state)] - (->> stream - (rx/filter dch/commit?) - (rx/map deref) - (rx/mapcat (fn [{:keys [save-undo? undo-changes redo-changes undo-group tags stack-undo?]}] - (if (and save-undo? (seq undo-changes)) - (let [entry {:undo-changes undo-changes - :redo-changes redo-changes - :undo-group undo-group - :tags tags}] - (rx/of (dwu/append-undo entry stack-undo?))) - (rx/empty)))) + (->> (rx/merge + (rx/of (ntf/hide) + (dcmt/retrieve-comment-threads file-id) + (dcmt/fetch-profiles) + (fetch-bundle file-id)) - (rx/take-until stoper-s))))) + (->> stream + (rx/filter (ptk/type? ::bundle-fetched)) + (rx/take 1) + (rx/map deref) + (rx/mapcat (fn [{:keys [file]}] + (rx/of (dpj/initialize-project (:project-id file)) + (-> (workspace-initialized) + (with-meta {:file-id file-id})))))) + + (when-let [component-id (some-> rparams :component-id parse-uuid)] + (->> stream + (rx/filter (ptk/type? ::workspace-initialized)) + (rx/observe-on :async) + (rx/take 1) + (rx/map #(dwl/go-to-local-component :id component-id)))) + + (->> stream + (rx/filter dch/commit?) + (rx/map deref) + (rx/mapcat (fn [{:keys [save-undo? undo-changes redo-changes undo-group tags stack-undo?]}] + (if (and save-undo? (seq undo-changes)) + (let [entry {:undo-changes undo-changes + :redo-changes redo-changes + :undo-group undo-group + :tags tags}] + (rx/of (dwu/append-undo entry stack-undo?))) + (rx/empty)))))) + (rx/take-until stoper-s)))) ptk/EffectEvent (effect [_ _ _] (let [name (dm/str "workspace-" file-id)] (unchecked-set ug/global "name" name))))) -(defn reload-file - [] - (ptk/reify ::reload-file - ptk/WatchEvent - (watch [_ state _] - (let [file-id (:current-file-id state) - project-id (:current-project-id state)] - (rx/of (initialize-file project-id file-id)))))) - -;; We need to inject this so there are no cycles -(set! app.main.data.workspace.notifications/reload-file reload-file) -(set! app.main.errors/reload-file reload-file) - -(defn finalize-file - [_project-id file-id] +(defn finalize-workspace + [file-id] (ptk/reify ::finalize-file ptk/UpdateEvent (update [_ state] (-> state (dissoc :current-file-id - :current-project-id :workspace-data :workspace-editor-state :workspace-file @@ -434,22 +365,37 @@ :workspace-media-objects :workspace-persistence :workspace-presence - :workspace-ready? + :workspace-ready :workspace-undo) (update :workspace-global dissoc :read-only?) (assoc-in [:workspace-global :options-mode] :design))) ptk/WatchEvent - (watch [_ _ _] - (rx/of (dwn/finalize file-id) - (dwsl/finalize))))) + (watch [_ state _] + (let [project-id (:current-project-id state)] -(declare go-to-page) -(declare ^:private preload-data-uris) + (rx/of (dwn/finalize file-id) + (dpj/finalize-project project-id) + (dwsl/finalize-shape-layout) + (dwcl/stop-picker) + (modal/hide) + (ntf/hide)))))) + +(defn- reload-current-file + [] + (ptk/reify ::reload-current-file + ptk/WatchEvent + (watch [_ state _] + (let [file-id (:current-file-id state)] + (rx/of (initialize-workspace file-id)))))) + +;; Make this event callable through dynamic resolution +(defmethod ptk/resolve ::reload-current-file [_ _] (reload-current-file)) (defn initialize-page [page-id] - (dm/assert! (uuid? page-id)) + (assert (uuid? page-id) "expected valid uuid for `page-id`") + (ptk/reify ::initialize-page ptk/UpdateEvent (update [_ state] @@ -464,30 +410,20 @@ ;; FIXME: this should be done on `initialize-layout` (?) (update :workspace-layout layout/load-layout-flags) - (update :workspace-global layout/load-layout-state) - - (update :workspace-global assoc :background-color (-> page :options :background)) - (update-in [:route :params :query] assoc :page-id (dm/str id)))) + (update :workspace-global layout/load-layout-state))) state)) ptk/WatchEvent (watch [_ state _] - ;; NOTE: there are cases between files navigation when this - ;; event is emmited but the page-index is still not loaded, so - ;; we only need to proceed when page-index is properly loaded - (when-let [pindex (-> state :workspace-data :pages-index)] - (if (contains? pindex page-id) - (let [file-id (:current-file-id state)] - (rx/of (preload-data-uris page-id) - (dwth/watch-state-changes file-id page-id) - (dwl/watch-component-changes))) - (let [page-id (dm/get-in state [:workspace-data :pages 0])] - (rx/of (go-to-page page-id)))))))) + (let [file-id (:current-file-id state)] + (rx/of (preload-data-uris page-id) + (dwth/watch-state-changes file-id page-id) + (dwl/watch-component-changes)))))) (defn finalize-page [page-id] - (dm/assert! (uuid? page-id)) + (assert (uuid? page-id) "expected valid uuid for `page-id`") (ptk/reify ::finalize-page ptk/UpdateEvent (update [_ state] @@ -683,7 +619,7 @@ (rx/of (dch/commit-changes changes) (when (= id (:current-page-id state)) - go-to-file)))))) + (go-to-file))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; WORKSPACE File Actions @@ -847,7 +783,7 @@ (let [selected (wsh/lookup-selected state) id (first selected)] (when (= (count selected) 1) - (rx/of (go-to-layout :layers) + (rx/of (dcm/go-to-workspace :layout :layers) (start-rename-shape id))))))) ;; --- Shape Vertical Ordering @@ -1111,76 +1047,17 @@ (rx/of (dwsh/update-shapes selected #(assoc % :proportion-lock true))) (rx/of (dwsh/update-shapes selected #(update % :proportion-lock not)))))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Navigation -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - (defn workspace-focus-lost [] (ptk/reify ::workspace-focus-lost ptk/UpdateEvent (update [_ state] + ;; FIXME: remove the `?` from show-distances? (assoc-in state [:workspace-global :show-distances?] false)))) -(defn navigate-to-project - [project-id] - (ptk/reify ::navigate-to-project - ptk/WatchEvent - (watch [_ state _] - (let [page-ids (get-in state [:projects project-id :pages]) - params {:project project-id :page (first page-ids)}] - (rx/of (rt/nav :workspace/page params)))))) - -(defn go-to-page - ([] - (ptk/reify ::go-to-page - ptk/WatchEvent - (watch [_ state _] - (let [project-id (:current-project-id state) - file-id (:current-file-id state) - page-id (get-in state [:workspace-data :pages 0]) - - pparams {:file-id file-id :project-id project-id} - qparams {:page-id page-id}] - (rx/of (rt/nav' :workspace pparams qparams)))))) - ([page-id] - (dm/assert! (uuid? page-id)) - (ptk/reify ::go-to-page-2 - ptk/WatchEvent - (watch [_ state _] - (let [project-id (:current-project-id state) - file-id (:current-file-id state) - pparams {:file-id file-id :project-id project-id} - qparams {:page-id page-id}] - (rx/of (rt/nav :workspace pparams qparams))))))) - -(defn go-to-layout - [layout] - (ptk/reify ::go-to-layout - IDeref - (-deref [_] {:layout layout}) - - ptk/WatchEvent - (watch [_ state _] - (let [project-id (:current-project-id state) - file-id (:current-file-id state) - page-id (:current-page-id state) - pparams {:file-id file-id :project-id project-id} - qparams {:page-id page-id :layout (name layout)}] - (rx/of (rt/nav :workspace pparams qparams)))))) - -(defn navigate-to-library - "Open a new tab, and navigate to the workspace with the provided file" - [library-id] - (ptk/reify ::navigate-to-file - ptk/WatchEvent - (watch [_ state _] - (when-let [file (dm/get-in state [:workspace-libraries library-id])] - (let [params {:rname :workspace - :path-params {:project-id (:project-id file) - :file-id (:id file)} - :query-params {:page-id (dm/get-in file [:data :pages 0])}}] - (rx/of (rt/nav-new-window* params))))))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Navigation +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn set-assets-section-open [file-id section open?] @@ -1242,111 +1119,18 @@ (update-in state [:workspace-assets :selected] dissoc file-id) (update state :workspace-assets dissoc :selected)))))) -(defn go-to-main-instance - [file-id component-id] - (dm/assert! - "expected uuid type for `file-id` parameter (nilable)" - (or (nil? file-id) - (uuid? file-id))) - - (dm/assert! - "expected uuid type for `component-id` parameter" - (uuid? component-id)) - - (ptk/reify ::go-to-main-instance - ptk/WatchEvent - (watch [_ state stream] - (let [current-file-id (:current-file-id state) - current-page-id (:current-page-id state) - current-project-id (:current-project-id state) - file-id (or file-id current-file-id) - - select-and-zoom - (fn [shape-id] - (rx/of (dws/select-shapes (d/ordered-set shape-id)) - dwz/zoom-to-selected-shape)) - - redirect-to-page - (fn [page-id shape-id] - (rx/concat - (rx/of (go-to-page page-id)) - (->> stream - (rx/filter (ptk/type? ::initialize-page)) - (rx/take 1) - (rx/observe-on :async)) - (select-and-zoom shape-id))) - - redirect-to-file - (fn [file-id page-id] - (let [pparams {:file-id file-id :project-id current-project-id} - qparams {:page-id page-id}] - (rx/merge - (rx/of (rt/nav :workspace pparams qparams)) - (->> stream - (rx/filter (ptk/type? ::workspace-initialized)) - (rx/map meta) - (rx/filter #(= file-id (:file-id %))) - (rx/take 1) - (rx/observe-on :async) - (rx/map #(go-to-main-instance file-id component-id))))))] - - (if (= file-id current-file-id) - (let [component (dm/get-in state [:workspace-data :components component-id]) - page-id (:main-instance-page component) - shape-id (:main-instance-id component)] - (when (some? page-id) - (if (= page-id current-page-id) - (select-and-zoom shape-id) - (redirect-to-page page-id shape-id)))) - - (let [component (dm/get-in state [:workspace-libraries file-id :data :components component-id])] - (some->> (:main-instance-page component) - (redirect-to-file file-id)))))))) - -(defn go-to-component - [component-id] - (ptk/reify ::go-to-component - IDeref - (-deref [_] {:layout :assets}) - - ptk/WatchEvent - (watch [_ state _] - (let [components-v2 (features/active-feature? state "components/v2")] - (if components-v2 - (rx/of (go-to-main-instance nil component-id)) - (let [file-id (:current-file-id state) - project-id (:current-project-id state) - page-id (:current-page-id state) - - pparams {:file-id file-id :project-id project-id} - qparams {:page-id page-id :layout :assets}] - (rx/of (rt/nav :workspace pparams qparams) - (set-assets-section-open file-id :library true) - (set-assets-section-open file-id :components true) - (select-single-asset file-id component-id :components)))))) - - ptk/EffectEvent - (effect [_ state _] - (let [components-v2 (features/active-feature? state "components/v2") - wrapper-id (str "component-shape-id-" component-id)] - (when-not components-v2 - (tm/schedule-on-idle #(dom/scroll-into-view-if-needed! (dom/get-element wrapper-id)))))))) - (defn show-component-in-assets [component-id] + (ptk/reify ::show-component-in-assets ptk/WatchEvent (watch [_ state _] - (let [project-id (:current-project-id state) - file-id (:current-file-id state) - page-id (:current-page-id state) - pparams {:file-id file-id :project-id project-id} - qparams {:page-id page-id :layout :assets} - component-path (cfh/split-path (get-in state [:workspace-data :components component-id :path])) - paths (map (fn [i] (cfh/join-path (take (inc i) component-path))) (range (count component-path)))] + (let [component-path (cfh/split-path (get-in state [:workspace-data :components component-id :path])) + paths (map (fn [i] (cfh/join-path (take (inc i) component-path))) (range (count component-path))) + file-id (:current-file-id state)] (rx/concat (rx/from (map #(set-assets-group-open file-id :components % true) paths)) - (rx/of (rt/nav :workspace pparams qparams) + (rx/of (dcm/go-to-workspace :layout :assets) (set-assets-section-open file-id :library true) (set-assets-section-open file-id :components true) (select-single-asset file-id component-id :components))))) @@ -1356,55 +1140,6 @@ (let [wrapper-id (str "component-shape-id-" component-id)] (tm/schedule-on-idle #(dom/scroll-into-view-if-needed! (dom/get-element wrapper-id))))))) -(def go-to-file - (ptk/reify ::go-to-file - ptk/WatchEvent - (watch [_ state _] - (let [{:keys [id project-id data] :as file} (:workspace-file state) - page-id (get-in data [:pages 0]) - pparams {:project-id project-id :file-id id} - qparams {:page-id page-id}] - (rx/of (rt/nav :workspace pparams qparams)))))) - -(defn go-to-viewer - ([] (go-to-viewer {})) - ([{:keys [file-id page-id section frame-id]}] - (ptk/reify ::go-to-viewer - ptk/WatchEvent - (watch [_ state _] - (let [{:keys [current-file-id current-page-id]} state - pparams {:file-id (or file-id current-file-id)} - qparams (cond-> {:page-id (or page-id current-page-id)} - (some? section) - (assoc :section section) - (some? frame-id) - (assoc :frame-id frame-id))] - (rx/of ::dps/force-persist - (rt/nav-new-window* {:rname :viewer - :path-params pparams - :query-params qparams - :name (str "viewer-" (:file-id pparams))}))))))) - -(defn go-to-dashboard - ([] (go-to-dashboard nil)) - ([{:keys [team-id]}] - (ptk/reify ::go-to-dashboard - ptk/WatchEvent - (watch [_ state _] - (when-let [team-id (or team-id (:current-team-id state))] - (rx/of ::dps/force-persist - (rt/nav :dashboard-projects {:team-id team-id}))))))) - -(defn go-to-dashboard-fonts - [] - (ptk/reify ::go-to-dashboard-fonts - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (rx/of ::dps/force-persist - (rt/nav :dashboard-fonts {:team-id team-id})))))) - - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Context Menu ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1640,8 +1375,8 @@ (rx/catch on-copy-error) (rx/ignore))) - ;; FIXME: this is to support Firefox versions below 116 that don't support `ClipboardItem` - ;; after the version 116 is less common we could remove this. + ;; FIXME: this is to support Firefox versions below 116 that don't support + ;; `ClipboardItem` after the version 116 is less common we could remove this. ;; https://caniuse.com/?search=ClipboardItem (->> (rx/from shapes) (rx/merge-map (partial prepare-object objects frame-id)) @@ -1924,7 +1659,8 @@ ;; the pasted object doesn't fit we try to: ;; ;; - Align it to the limits on the x and y axis - ;; - Respect the distance of the object to the right and bottom in the original frame + ;; - Respect the distance of the object to the right + ;; and bottom in the original frame (gpt/point paste-x paste-y))] [frame-id delta (dec (count (:shapes selected-frame-obj)))])) diff --git a/frontend/src/app/main/data/workspace/comments.cljs b/frontend/src/app/main/data/workspace/comments.cljs index 6664f69cc..d1b250cc1 100644 --- a/frontend/src/app/main/data/workspace/comments.cljs +++ b/frontend/src/app/main/data/workspace/comments.cljs @@ -13,7 +13,8 @@ [app.common.schema :as sm] [app.common.types.shape-tree :as ctst] [app.main.data.changes :as dch] - [app.main.data.comments :as dcm] + [app.main.data.comments :as dcmt] + [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.data.workspace :as dw] [app.main.data.workspace.common :as dwco] @@ -23,7 +24,6 @@ [app.main.repo :as rp] [app.main.streams :as ms] [app.util.mouse :as mse] - [app.util.router :as rt] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -38,7 +38,7 @@ (watch [_ _ stream] (let [stopper (rx/filter #(= ::finalize %) stream)] (rx/merge - (rx/of (dcm/retrieve-comment-threads file-id)) + (rx/of (dcmt/retrieve-comment-threads file-id)) (->> stream (rx/filter mse/mouse-event?) (rx/filter mse/mouse-click-event?) @@ -60,8 +60,8 @@ (watch [_ state _] (let [local (:comments-local state)] (cond - (:draft local) (rx/of (dcm/close-thread)) - (:open local) (rx/of (dcm/close-thread)) + (:draft local) (rx/of (dcmt/close-thread)) + (:open local) (rx/of (dcmt/close-thread)) :else (rx/of (dw/clear-edition-mode) @@ -78,19 +78,19 @@ (watch [_ state _] (let [local (:comments-local state)] (if (some? (:open local)) - (rx/of (dcm/close-thread)) + (rx/of (dcmt/close-thread)) (let [page-id (:current-page-id state) file-id (:current-file-id state) params {:position position :page-id page-id :file-id file-id}] - (rx/of (dcm/create-draft params)))))))) + (rx/of (dcmt/create-draft params)))))))) (defn center-to-comment-thread [{:keys [position] :as thread}] (dm/assert! "expected valid comment thread" - (dcm/check-comment-thread! thread)) + (dcmt/check-comment-thread! thread)) (ptk/reify ::center-to-comment-thread ptk/UpdateEvent @@ -109,22 +109,21 @@ [thread] (dm/assert! "expected valid comment thread" - (dcm/check-comment-thread! thread)) + (dcmt/check-comment-thread! thread)) (ptk/reify ::open-comment-thread ptk/WatchEvent (watch [_ _ stream] - (let [pparams {:project-id (:project-id thread) - :file-id (:file-id thread)} - qparams {:page-id (:page-id thread)}] - (rx/merge - (rx/of (rt/nav :workspace pparams qparams)) - (->> stream - (rx/filter (ptk/type? ::dwv/initialize-viewport)) - (rx/take 1) - (rx/mapcat #(rx/of (center-to-comment-thread thread) - (dwd/select-for-drawing :comments) - (with-meta (dcm/open-thread thread) - {::ev/origin "workspace"}))))))))) + (rx/merge + (rx/of (dcm/go-to-workspace :file-id (:file-id thread) + :page-id (:page-id thread))) + + (->> stream + (rx/filter (ptk/type? ::dwv/initialize-viewport)) + (rx/take 1) + (rx/mapcat #(rx/of (center-to-comment-thread thread) + (dwd/select-for-drawing :comments) + (with-meta (dcmt/open-thread thread) + {::ev/origin "workspace"})))))))) (defn update-comment-thread-position ([thread [new-x new-y]] @@ -133,7 +132,7 @@ ([thread [new-x new-y] frame-id] (dm/assert! "expected valid comment thread" - (dcm/check-comment-thread! thread)) + (dcmt/check-comment-thread! thread)) (ptk/reify ::update-comment-thread-position ptk/WatchEvent (watch [it state _] diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index f5ee8fe4d..7fd97095d 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -27,6 +27,7 @@ [app.config :as cf] [app.main.data.changes :as dch] [app.main.data.comments :as dc] + [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] @@ -40,14 +41,15 @@ [app.main.data.workspace.thumbnails :as dwt] [app.main.data.workspace.transforms :as dwtr] [app.main.data.workspace.undo :as dwu] + [app.main.data.workspace.zoom :as dwz] [app.main.features :as features] [app.main.features.pointer-map :as fpmap] [app.main.refs :as refs] [app.main.repo :as rp] + [app.main.router :as rt] [app.main.store :as st] [app.util.color :as uc] [app.util.i18n :refer [tr]] - [app.util.router :as rt] [app.util.storage :as storage] [app.util.time :as dt] [beicon.v2.core :as rx] @@ -684,21 +686,49 @@ (rx/of (when can-detach? (dch/commit-changes changes))))))) -(defn nav-to-component-file +(defn go-to-component-file [file-id component] (dm/assert! (uuid? file-id)) (dm/assert! (some? component)) (ptk/reify ::nav-to-component-file ptk/WatchEvent (watch [_ state _] - (let [project-id (get-in state [:workspace-libraries file-id :project-id]) - path-params {:project-id project-id - :file-id file-id} - query-params {:page-id (:main-instance-page component) - :component-id (:id component)}] - (rx/of (rt/nav-new-window* {:rname :workspace - :path-params path-params - :query-params query-params})))))) + (let [params (-> (rt/get-params state) + (assoc :file-id file-id) + (assoc :page-id (:main-instance-page component)) + (assoc :component-id (:id component)))] + (rx/of (rt/nav :workspace params :new-window? true)))))) + + +(defn go-to-local-component + [& {:keys [id] :as options}] + (ptk/reify ::go-to-local-component + ptk/WatchEvent + (watch [_ state stream] + (let [current-page-id (:current-page-id state) + + select-and-zoom + (fn [shape-id] + (rx/of (dws/select-shapes (d/ordered-set shape-id)) + dwz/zoom-to-selected-shape)) + + redirect-to-page + (fn [page-id shape-id] + (rx/merge + (rx/of (dcm/go-to-workspace :page-id page-id)) + (->> stream + (rx/filter (ptk/type? ::initialize-page)) + (rx/take 1) + (rx/observe-on :async) + (rx/mapcat (fn [_] (select-and-zoom shape-id))))))] + + (when-let [component (dm/get-in state [:workspace-data :components id])] + (let [page-id (:main-instance-page component) + shape-id (:main-instance-id component)] + (when (some? page-id) + (if (= page-id current-page-id) + (select-and-zoom shape-id) + (redirect-to-page page-id shape-id))))))))) (defn library-thumbnails-fetched [thumbnails] @@ -1117,9 +1147,11 @@ ptk/WatchEvent (watch [_ state _] - (rp/cmd! :ignore-file-library-sync-status - {:file-id (get-in state [:workspace-file :id]) - :date (dt/now)})))) + (let [file-id (:current-file-id state)] + (->> (rp/cmd! :ignore-file-library-sync-status + {:file-id file-id + :date (dt/now)}) + (rx/ignore)))))) (defn assets-need-sync "Get a lazy sequence of all the assets of each type in the library that have @@ -1309,23 +1341,6 @@ (->> (rp/cmd! :set-file-shared params) (rx/ignore)))))) -(defn- shared-files-fetched - [files] - (ptk/reify ::shared-files-fetched - ptk/UpdateEvent - (update [_ state] - (let [state (dissoc state :files)] - (assoc state :workspace-shared-files files))))) - -(defn fetch-shared-files - [{:keys [team-id] :as params}] - (dm/assert! (uuid? team-id)) - (ptk/reify ::fetch-shared-files - ptk/WatchEvent - (watch [_ _ _] - (->> (rp/cmd! :get-team-shared-files {:team-id team-id}) - (rx/map shared-files-fetched))))) - ;; --- Link and unlink Files (defn link-file-to-library diff --git a/frontend/src/app/main/data/workspace/notifications.cljs b/frontend/src/app/main/data/workspace/notifications.cljs index d4b6b7ffe..55e6baba7 100644 --- a/frontend/src/app/main/data/workspace/notifications.cljs +++ b/frontend/src/app/main/data/workspace/notifications.cljs @@ -16,6 +16,7 @@ [app.main.data.modal :as modal] [app.main.data.plugins :as dpl] [app.main.data.websocket :as dws] + [app.main.data.workspace :as-alias dw] [app.main.data.workspace.common :as dwc] [app.main.data.workspace.edition :as dwe] [app.main.data.workspace.layout :as dwly] @@ -30,9 +31,6 @@ [clojure.set :as set] [potok.v2.core :as ptk])) -;; From app.main.data.workspace we can use directly because it causes a circular dependency -(def reload-file nil) - ;; FIXME: this ns should be renamed to something different (declare process-message) @@ -292,7 +290,7 @@ curr-vern (dm/get-in state [:workspace-file :vern]) reload? (and (= file-id curr-file-id) (not= vern curr-vern))] (when reload? - (rx/of (reload-file))))))) + (rx/of (ptk/event ::dw/reload-current-file))))))) (def ^:private schema:handle-library-change [:map {:title "handle-library-change"} diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index 5ca5ba95c..7a2db7bcc 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -110,13 +110,15 @@ :undo-group undo-group}))) (rx/empty)))))) -(defn initialize +(defn initialize-shape-layout [] - (ptk/reify ::initialize + (ptk/reify ::initialize-shape-layout ptk/WatchEvent (watch [_ _ stream] - (let [stopper (rx/filter (ptk/type? ::finalize) stream)] + (let [stopper (rx/filter (ptk/type? ::finalize-shape-layout) stream)] (->> stream + ;; FIXME: we don't need use types for simple signaling, + ;; we can just use a keyword for it (rx/filter (ptk/type? :layout/update)) (rx/map deref) ;; We buffer the updates to the layout so if there are many changes at the same time @@ -129,9 +131,9 @@ (update-layout-positions {:ids ids})))) (rx/take-until stopper)))))) -(defn finalize +(defn finalize-shape-layout [] - (ptk/reify ::finalize)) + (ptk/data-event ::finalize-shape-layout)) (defn create-layout-from-id [id type & {:keys [from-frame? calculate-params?] :or {from-frame? false calculate-params? true}}] diff --git a/frontend/src/app/main/data/workspace/shortcuts.cljs b/frontend/src/app/main/data/workspace/shortcuts.cljs index 64f987d98..40879477e 100644 --- a/frontend/src/app/main/data/workspace/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/shortcuts.cljs @@ -7,6 +7,7 @@ (ns app.main.data.workspace.shortcuts (:require [app.common.data.macros :as dm] + [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.data.exports.assets :as de] [app.main.data.modal :as modal] @@ -440,17 +441,18 @@ :toggle-layers {:tooltip (ds/alt "L") :command (ds/a-mod "l") :subsections [:panels] - :fn #(st/emit! (dw/go-to-layout :layers))} + :fn #(st/emit! (dcm/go-to-workspace :layout :layers))} :toggle-assets {:tooltip (ds/alt "I") :command (ds/a-mod "i") :subsections [:panels] - :fn #(st/emit! (dw/go-to-layout :assets))} + :fn #(st/emit! (dcm/go-to-workspace :layout :assets))} :toggle-history {:tooltip (ds/alt "H") :command (ds/a-mod "h") :subsections [:panels] - :fn #(emit-when-no-readonly (dw/go-to-layout :document-history))} + :fn #(emit-when-no-readonly + (dcm/go-to-workspace :layout :document-history))} :toggle-colorpalette {:tooltip (ds/alt "P") :command (ds/a-mod "p") @@ -516,22 +518,22 @@ :open-viewer {:tooltip "G V" :command "g v" :subsections [:navigation-workspace] - :fn #(st/emit! (dw/go-to-viewer))} + :fn #(st/emit! (dcm/go-to-viewer))} :open-inspect {:tooltip "G I" :command "g i" :subsections [:navigation-workspace] - :fn #(st/emit! (dw/go-to-viewer {:section :inspect}))} + :fn #(st/emit! (dcm/go-to-viewer :section :inspect))} :open-comments {:tooltip "G C" :command "g c" :subsections [:navigation-workspace] - :fn #(st/emit! (dw/go-to-viewer {:section :comments}))} + :fn #(st/emit! (dcm/go-to-viewer :section :comments))} :open-dashboard {:tooltip "G D" :command "g d" :subsections [:navigation-workspace] - :fn #(st/emit! (dw/go-to-dashboard))} + :fn #(st/emit! (dcm/go-to-dashboard-recent))} :select-prev {:tooltip (ds/shift "tab") :command "shift+tab" diff --git a/frontend/src/app/main/data/workspace/state_helpers.cljs b/frontend/src/app/main/data/workspace/state_helpers.cljs index 6c55e9da8..4d33f2838 100644 --- a/frontend/src/app/main/data/workspace/state_helpers.cljs +++ b/frontend/src/app/main/data/workspace/state_helpers.cljs @@ -27,10 +27,6 @@ (-> (lookup-page state page-id) (get :objects)))) -(defn lookup-viewer-objects - ([state page-id] - (dm/get-in state [:viewer :pages page-id :objects]))) - (defn lookup-library-objects [state file-id page-id] (dm/get-in state [:workspace-libraries file-id :data :pages-index page-id :objects])) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index 4d7f5cf39..4128d1761 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -27,7 +27,7 @@ [app.main.data.workspace.undo :as dwu] [app.main.features :as features] [app.main.fonts :as fonts] - [app.util.router :as rt] + [app.main.router :as rt] [app.util.text-editor :as ted] [app.util.text.content.styles :as styles] [app.util.timers :as ts] diff --git a/frontend/src/app/main/data/workspace/undo.cljs b/frontend/src/app/main/data/workspace/undo.cljs index 529965fbb..2df61c5e5 100644 --- a/frontend/src/app/main/data/workspace/undo.cljs +++ b/frontend/src/app/main/data/workspace/undo.cljs @@ -13,8 +13,8 @@ [app.common.schema :as sm] [app.common.types.shape.layout :as ctl] [app.main.data.changes :as dch] + [app.main.data.common :as dcm] [app.main.data.workspace.state-helpers :as wsh] - [app.util.router :as rt] [app.util.time :as dt] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -290,14 +290,8 @@ (ptk/reify ::assure-valid-current-page ptk/WatchEvent (watch [_ state _] - (let [current_page (:current-page-id state) - pages (get-in state [:workspace-data :pages]) - exists? (some #(= current_page %) pages) - - project-id (:current-project-id state) - file-id (:current-file-id state) - pparams {:file-id file-id :project-id project-id} - qparams {:page-id (first pages)}] - (if exists? + (let [page-id (:current-page-id state) + pages (dm/get-in state [:workspace-data :pages])] + (if (contains? pages page-id) (rx/empty) - (rx/of (rt/nav :workspace pparams qparams))))))) + (rx/of (dcm/go-to-workspace :page-id (first pages)))))))) diff --git a/frontend/src/app/main/data/workspace/versions.cljs b/frontend/src/app/main/data/workspace/versions.cljs index a5dfdc2da..994565a0a 100644 --- a/frontend/src/app/main/data/workspace/versions.cljs +++ b/frontend/src/app/main/data/workspace/versions.cljs @@ -8,6 +8,7 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.schema :as sm] [app.main.data.event :as ev] [app.main.data.persistence :as dwp] [app.main.data.workspace :as dw] @@ -25,7 +26,7 @@ (declare fetch-versions) (defn init-version-state - [file-id] + [] (ptk/reify ::init-version-state ptk/UpdateEvent (update [_ state] @@ -33,7 +34,7 @@ ptk/WatchEvent (watch [_ _ _] - (rx/of (fetch-versions file-id))))) + (rx/of (fetch-versions))))) (defn update-version-state [version-state] @@ -43,123 +44,90 @@ (update state :workspace-versions merge version-state)))) (defn fetch-versions - [file-id] - (dm/assert! (uuid? file-id)) + [] (ptk/reify ::fetch-versions ptk/WatchEvent - (watch [_ _ _] - (->> (rp/cmd! :get-file-snapshots {:file-id file-id}) - (rx/map #(update-version-state {:status :loaded :data %})))))) + (watch [_ state _] + (let [file-id (:current-file-id state)] + (->> (rp/cmd! :get-file-snapshots {:file-id file-id}) + (rx/map #(update-version-state {:status :loaded :data %}))))))) (defn create-version - [file-id] - (dm/assert! (uuid? file-id)) + [] (ptk/reify ::create-version ptk/WatchEvent - (watch [_ _ _] - (let [label (dt/format (dt/now) :date-full)] + (watch [_ state _] + (let [label (dt/format (dt/now) :date-full) + file-id (:current-file-id state)] + ;; Force persist before creating snapshot, otherwise we could loss changes (rx/concat - (rx/of ::dwp/force-persist) + (rx/of ::dwp/force-persist + (ptk/event ::ev/event {::ev/name "create-version"})) + (->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) (rx/filter #(or (nil? %) (= :saved %))) (rx/take 1) (rx/mapcat #(rp/cmd! :create-file-snapshot {:file-id file-id :label label})) (rx/mapcat (fn [{:keys [id]}] - (rx/of - (update-version-state {:editing id}) - (fetch-versions file-id))))) - (rx/of (ptk/event ::ev/event {::ev/name "create-version"}))))))) - -(defn create-version-from-plugins - [file-id label resolve reject] - (dm/assert! (uuid? file-id)) - (ptk/reify ::create-version-plugins - ptk/WatchEvent - (watch [_ _ _] - ;; Force persist before creating snapshot, otherwise we could loss changes - (->> (rx/concat - (rx/of ::dwp/force-persist) - (->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) - (rx/filter #(or (nil? %) (= :saved %))) - (rx/take 1) - (rx/mapcat #(rp/cmd! :create-file-snapshot {:file-id file-id :label label})) - - (rx/mapcat - (fn [{:keys [id]}] - (->> (rp/cmd! :get-file-snapshots {:file-id file-id}) - (rx/take 1) - (rx/map (fn [versions] (d/seek #(= id (:id %)) versions)))))) - (rx/tap resolve) - (rx/ignore)) - (rx/of (ptk/event ::ev/event {::ev/origin "plugins" - ::ev/name "create-version"}))) - - ;; On error reject the promise and empty the stream - (rx/catch (fn [error] - (reject error) - (rx/empty))))))) + (rx/of (update-version-state {:editing id}) + (fetch-versions)))))))))) (defn rename-version - [file-id id label] - (dm/assert! (uuid? file-id)) - (dm/assert! (uuid? id)) - (dm/assert! (and (string? label) (d/not-empty? label))) + [id label] + (assert (uuid? id) "expected valid uuid for `id`") + (assert (sm/valid-text? label) "expected not empty string for `label`") (ptk/reify ::rename-version ptk/WatchEvent - (watch [_ _ _] - (rx/merge - (rx/of (update-version-state {:editing false})) - (->> (rp/cmd! :update-file-snapshot {:id id :label label}) - (rx/map #(fetch-versions file-id))) - (rx/of (ptk/event ::ev/event {::ev/name "rename-version"})))))) + (watch [_ state _] + (let [file-id (:current-file-id state)] + (rx/merge + (rx/of (update-version-state {:editing false}) + (ptk/event ::ev/event {::ev/name "rename-version" + :file-id file-id})) + (->> (rp/cmd! :update-file-snapshot {:id id :label label}) + (rx/map fetch-versions))))))) (defn restore-version - [project-id file-id id origin] - (dm/assert! (uuid? project-id)) - (dm/assert! (uuid? file-id)) - (dm/assert! (uuid? id)) + [id origin] + (assert (uuid? id) "expected valid uuid for `id`") (ptk/reify ::restore-version ptk/WatchEvent - (watch [_ _ _] - (rx/concat - (rx/of ::dwp/force-persist) - (->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) - (rx/filter #(or (nil? %) (= :saved %))) - (rx/take 1) - (rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id})) - (rx/map #(dw/initialize-file project-id file-id))) - (case origin - :version - (rx/of (ptk/event ::ev/event {::ev/name "restore-pin-version"})) + (watch [_ state _] + (let [file-id (:current-file-id state)] + (rx/concat + (rx/of ::dwp/force-persist) - :snapshot - (rx/of (ptk/event ::ev/event {::ev/name "restore-autosave"})) + ;; FIXME: we should abstract this + (->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) + (rx/filter #(or (nil? %) (= :saved %))) + (rx/take 1) + (rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id})) + (rx/map #(dw/initialize-workspace file-id))) - :plugin - (rx/of (ptk/event ::ev/event {::ev/name "restore-version-plugin"})) - - (rx/empty)))))) + (when-let [name (case origin + :version "restore-pin-version" + :snapshot "restore-autosave" + :plugin "restore-version-plugin" + nil)] + (rx/of (ptk/event ::ev/event {::ev/name name})))))))) (defn delete-version - [file-id id] - (dm/assert! (uuid? file-id)) - (dm/assert! (uuid? id)) + [id] + (assert (uuid? id) "expected valid uuid for `id`") (ptk/reify ::delete-version ptk/WatchEvent (watch [_ _ _] (->> (rp/cmd! :delete-file-snapshot {:id id}) - (rx/map #(fetch-versions file-id)))))) + (rx/map fetch-versions))))) (defn pin-version - [file-id id] - (dm/assert! (uuid? file-id)) - (dm/assert! (uuid? id)) - + [id] + (assert (uuid? id) "expected valid uuid for `id`") (ptk/reify ::pin-version ptk/WatchEvent (watch [_ state _] @@ -168,8 +136,82 @@ params {:id id :label (dt/format (:created-at version) :date-full)}] - (rx/concat - (->> (rp/cmd! :update-file-snapshot params) - (rx/mapcat #(rx/of (update-version-state {:editing id}) - (fetch-versions file-id)))) - (rx/of (ptk/event ::ev/event {::ev/name "pin-version"}))))))) + (->> (rp/cmd! :update-file-snapshot params) + (rx/mapcat (fn [_] + (rx/of (update-version-state {:editing id}) + (fetch-versions) + (ptk/event ::ev/event {::ev/name "pin-version"}))))))))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; PLUGINS SPECIFIC EVENTS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- wait-persisted-status + [] + (->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) + (rx/filter #(or (nil? %) (= :saved %))) + (rx/take 1))) + +(defn create-version-from-plugins + [file-id label resolve reject] + + (assert (uuid? file-id) "expected valid uuid for `file-id`") + (assert (sm/valid-text? label) "expected not empty string for `label`") + + (ptk/reify ::create-version-from-plugins + ptk/WatchEvent + (watch [_ state _] + (let [current-file-id (:current-file-id state)] + ;; Force persist before creating snapshot, otherwise we could loss changes + (->> (rx/concat + (rx/of (ptk/event ::ev/event {::ev/origin "plugins" + ::ev/name "create-version"})) + + (when (= file-id current-file-id) + (rx/of ::dwp/force-persist)) + + (->> (if (= file-id current-file-id) + (wait-persisted-status) + (rx/of :nothing)) + (rx/mapcat + (fn [_] + (rp/cmd! :create-file-snapshot {:file-id file-id :label label}))) + (rx/mapcat + (fn [{:keys [id]}] + (->> (rp/cmd! :get-file-snapshots {:file-id file-id}) + (rx/take 1) + (rx/map (fn [versions] (d/seek #(= id (:id %)) versions)))))) + (rx/tap resolve) + (rx/ignore))) + + ;; On error reject the promise and empty the stream + (rx/catch (fn [error] + (reject error) + (rx/empty)))))))) + +(defn restore-version-from-plugin + [file-id id resolve _reject] + (assert (uuid? id) "expected valid uuid for `id`") + + (ptk/reify ::restore-version-from-plugins + ptk/WatchEvent + (watch [_ _ _] + (rx/concat + (rx/of (ptk/event ::ev/event {::ev/name "restore-version-plugin"}) + ::dwp/force-persist) + + ;; FIXME: we should abstract this + (->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) + (rx/filter #(or (nil? %) (= :saved %))) + (rx/take 1) + (rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id})) + (rx/map #(dw/initialize-workspace file-id))) + + (->> (rx/of 1) + (rx/tap resolve) + (rx/ignore)))))) + + + + diff --git a/frontend/src/app/main/errors.cljs b/frontend/src/app/main/errors.cljs index 2a59336ed..5ad3c84a2 100644 --- a/frontend/src/app/main/errors.cljs +++ b/frontend/src/app/main/errors.cljs @@ -13,10 +13,11 @@ [app.main.data.auth :as da] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] + [app.main.data.workspace :as-alias dw] + [app.main.router :as rt] [app.main.store :as st] [app.util.globals :as glob] [app.util.i18n :refer [tr]] - [app.util.router :as rt] [app.util.timers :as ts] [cuerdas.core :as str] [potok.v2.core :as ptk])) @@ -141,7 +142,7 @@ :timeout 3000}))) (= code :vern-conflict) - (st/emit! (reload-file)) + (st/emit! (ptk/event ::dw/reload-current-file)) :else (st/async-emit! (rt/assign-exception error)))) @@ -212,7 +213,6 @@ (ts/schedule #(st/emit! (rt/assign-exception error)))) - (defn- redirect-to-dashboard [] (let [team-id (:current-team-id @st/state) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index cc7e193a8..25c538324 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -22,13 +22,13 @@ ;; ---- Global refs (def route - (l/derived :route st/state)) + (l/derived (l/key :route) st/state)) (def router - (l/derived :router st/state)) + (l/derived (l/key :router) st/state)) (def profile - (l/derived :profile st/state)) + (l/derived (l/key :profile) st/state)) (def team (l/derived (fn [state] @@ -37,11 +37,18 @@ (get teams team-id))) st/state)) +(def project + (l/derived (fn [state] + (let [project-id (:current-project-id state) + projects (:projects state)] + (get projects project-id))) + st/state)) + (def permissions - (l/derived :permissions team)) + (l/derived (l/key :permissions) team)) (def teams - (l/derived :teams st/state)) + (l/derived (l/key :teams) st/state)) (def exception (l/derived :exception st/state)) @@ -65,8 +72,13 @@ (l/derived :files st/state)) (def shared-files + "A derived state that points to the current list of shared + files (without the content, only summary)" (l/derived :shared-files st/state)) +(def libraries + (l/derived :libraries st/state)) + (defn extract-selected-files [files selected] (let [get-file #(get files %) @@ -86,7 +98,6 @@ (def selected-project (l/derived :selected-project st/state)) - (def dashboard-local (l/derived :dashboard-local st/state)) @@ -243,12 +254,6 @@ (def workspace-file-typography (l/derived :typographies workspace-data)) -(def workspace-project - (l/derived :workspace-project st/state)) - -(def workspace-shared-files - (l/derived :workspace-shared-files st/state)) - (def workspace-local-library (l/derived (fn [state] (select-keys (:workspace-data state) @@ -505,12 +510,16 @@ ;; ---- Viewer refs +(defn get-viewer-objects + [state page-id] + (dm/get-in state [:viewer :pages page-id :objects])) + (defn lookup-viewer-objects-by-id [page-id] - (l/derived #(wsh/lookup-viewer-objects % page-id) st/state =)) + (l/derived #(get-viewer-objects % page-id) st/state =)) (def viewer-data - (l/derived :viewer st/state)) + (l/derived (l/key :viewer) st/state)) (def viewer-file (l/derived :file viewer-data)) @@ -536,14 +545,8 @@ (def comments-local (l/derived :comments-local st/state)) -(def users - (l/derived :users st/state)) - -(def current-file-comments-users - (l/derived :current-file-comments-users st/state)) - -(def current-team-comments-users - (l/derived :current-team-comments-users st/state)) +(def profiles + (l/derived :profiles st/state)) (def viewer-fullscreen? (l/derived (fn [state] @@ -555,14 +558,11 @@ (dm/get-in state [:viewer-local :zoom-type])) st/state)) -(def workspace-thumbnails - (l/derived :workspace-thumbnails st/state)) - (defn workspace-thumbnail-by-id [object-id] (l/derived (fn [state] - (some-> (dm/get-in state [:workspace-thumbnails object-id]) + (some-> (dm/get-in state [:thumbnails object-id]) (cf/resolve-media))) st/state)) @@ -608,35 +608,9 @@ (every? (partial ctl/grid-layout-immediate-child? objects)))) workspace-page-objects =)) -;; FIXME: move to viewer.inspect.code -(defn get-flex-child-viewer - [ids page-id] - (l/derived - (fn [state] - (let [objects (wsh/lookup-viewer-objects state page-id)] - (into [] - (comp (map (d/getf objects)) - (filter (partial ctl/flex-layout-immediate-child? objects))) - ids))) - st/state =)) - -;; FIXME: move to viewer.inspect.code -(defn get-viewer-objects - ([] - (let [route (deref route) - page-id (:page-id (:query-params route))] - (get-viewer-objects page-id))) - ([page-id] - (l/derived - (fn [state] - (let [objects (wsh/lookup-viewer-objects state page-id)] - objects)) - st/state =))) - (def colorpicker (l/derived :colorpicker st/state)) - (def workspace-grid-edition (l/derived :workspace-grid-edition st/state)) @@ -644,6 +618,7 @@ [id] (l/derived #(get % id) workspace-grid-edition)) +;; FIXME: remove (def current-file-id (l/derived :current-file-id st/state)) diff --git a/frontend/src/app/util/router.cljs b/frontend/src/app/main/router.cljs similarity index 76% rename from frontend/src/app/util/router.cljs rename to frontend/src/app/main/router.cljs index 880d61997..2639834ee 100644 --- a/frontend/src/app/util/router.cljs +++ b/frontend/src/app/main/router.cljs @@ -4,7 +4,7 @@ ;; ;; Copyright (c) KALEIDOS INC -(ns app.util.router +(ns app.main.router (:refer-clojure :exclude [resolve]) (:require [app.common.data.macros :as dm] @@ -28,11 +28,10 @@ (r/map->Match data)) (defn resolve - ([router id] (resolve router id {} {})) - ([router id path-params] (resolve router id path-params {})) - ([router id path-params query-params] - (when-let [match (r/match-by-name router id path-params)] - (r/match->path match query-params)))) + ([router id] (resolve router id {})) + ([router id params] + (when-let [match (r/match-by-name router id)] + (r/match->path match params)))) (defn create [routes] @@ -63,6 +62,9 @@ (defn navigated [match] (ptk/reify ::navigated + IDeref + (-deref [_] match) + ev/Event (-data [_] (let [route (dm/get-in match [:data :name]) @@ -77,25 +79,29 @@ (assoc :route match) (dissoc :exception))))) -(defn navigate* - [id path-params query-params replace] +(defn navigate + [id params & {:keys [::replace ::new-window] :as options}] (ptk/reify ::navigate IDeref (-deref [_] {:id id - :path-params path-params - :query-params query-params - :replace replace}) + :params params + :options options}) ptk/EffectEvent (effect [_ state _] (let [router (:router state) history (:history state) - path (resolve router id path-params query-params)] - (ts/asap - #(if ^boolean replace - (bhistory/replace-token! history path) - (bhistory/set-token! history path))))))) + path (resolve router id params)] + + (if ^boolean new-window + (let [name (or (::window-name options) "_blank") + uri (assoc cf/public-uri :fragment path)] + (dom/open-new-window uri name nil)) + (ts/asap + #(if ^boolean replace + (bhistory/replace-token! history path) + (bhistory/set-token! history path)))))))) (defn assign-exception [error] @@ -107,27 +113,14 @@ (assoc state :exception error))))) (defn nav - ([id] (nav id nil nil)) - ([id path-params] (nav id path-params nil)) - ([id path-params query-params] (navigate* id path-params query-params false))) + ([id] (navigate id nil)) + ([id params] (navigate id params)) + ([id params & {:as options}] + (navigate id params options))) -(defn nav' - ([id] (nav id nil nil)) - ([id path-params] (nav id path-params nil)) - ([id path-params query-params] (navigate* id path-params query-params true))) - -(def navigate nav) - -(defn nav-new-window* - [{:keys [rname path-params query-params name]}] - (ptk/reify ::nav-new-window - ptk/EffectEvent - (effect [_ state _] - (let [router (:router state) - path (resolve router rname path-params query-params) - name (or name "_blank") - uri (assoc cf/public-uri :fragment path)] - (dom/open-new-window uri name nil))))) +(defn get-params + [state] + (dm/get-in state [:route :params :query])) (defn nav-back [] diff --git a/frontend/src/app/main/store.cljs b/frontend/src/app/main/store.cljs index 703b3952d..949e3b77e 100644 --- a/frontend/src/app/main/store.cljs +++ b/frontend/src/app/main/store.cljs @@ -60,7 +60,7 @@ :app.main.data.workspace.persistence/update-persistence-status :app.main.data.websocket/send-message :app.main.data.workspace.notifications/handle-pointer-send - :app.util.router/assign-exception}] + :app.main.router/assign-exception}] (->> (rx/merge (->> stream (rx/filter (ptk/type? :app.main.data.changes/commit)) diff --git a/frontend/src/app/main/ui.cljs b/frontend/src/app/main/ui.cljs index 90605632d..d1f197e18 100644 --- a/frontend/src/app/main/ui.cljs +++ b/frontend/src/app/main/ui.cljs @@ -33,7 +33,7 @@ (mf/lazy-component app.main.ui.auth.verify-token/verify-token)) (def viewer-page - (mf/lazy-component app.main.ui.viewer/viewer)) + (mf/lazy-component app.main.ui.viewer/viewer*)) (def dashboard-page (mf/lazy-component app.main.ui.dashboard/dashboard*)) @@ -42,7 +42,7 @@ (mf/lazy-component app.main.ui.settings/settings)) (def workspace-page - (mf/lazy-component app.main.ui.workspace/workspace)) + (mf/lazy-component app.main.ui.workspace/workspace*)) (mf/defc team-container* {::mf/props :obj @@ -62,13 +62,13 @@ ;; all dom tree instead of simple rerender. [:* {:key (str team-id)} children]]]))) -(mf/defc page-container* +(mf/defc page* {::mf/props :obj ::mf/private true} [{:keys [route profile]}] (let [{:keys [data params]} route - props (get profile :props) - route-name (get data :name) + props (get profile :props) + section (get data :name) show-question-modal? @@ -95,7 +95,7 @@ (not= "0.0" (:main cf/version)))] [:& (mf/provider ctx/current-route) {:value route} - (case route-name + (case section (:auth-login :auth-register :auth-register-validate @@ -119,20 +119,20 @@ [:& icons-preview]) (:dashboard-search - :dashboard-projects + :dashboard-recent :dashboard-files :dashboard-libraries :dashboard-fonts :dashboard-font-providers - :dashboard-team-members - :dashboard-team-invitations - :dashboard-team-webhooks - :dashboard-team-settings) - (let [team-id (some-> params :path :team-id uuid) - project-id (some-> params :path :project-id uuid) - search-term (some-> params :query :search-term) - plugin-url (some-> params :query :plugin)] - + :dashboard-members + :dashboard-invitations + :dashboard-webhooks + :dashboard-settings) + (let [params (get params :query) + team-id (some-> params :team-id uuid) + project-id (some-> params :project-id uuid) + search-term (some-> params :search-term) + plugin-url (some-> params :plugin)] [:? #_[:& app.main.ui.releases/release-notes-modal {:version "2.3"}] #_[:& app.main.ui.onboarding/onboarding-templates-modal] @@ -154,13 +154,105 @@ [:> team-container* {:team-id team-id} [:> dashboard-page {:profile profile - :route-name route-name + :section section + :team-id team-id + :search-term search-term + :plugin-url plugin-url + :project-id project-id}]]]) + + (:dashboard-legacy-search + :dashboard-legacy-projects + :dashboard-legacy-files + :dashboard-legacy-libraries + :dashboard-legacy-fonts + :dashboard-legacy-font-providers + :dashboard-legacy-team-members + :dashboard-legacy-team-invitations + :dashboard-legacy-team-webhooks + :dashboard-legacy-team-settings) + (let [team-id (some-> params :path :team-id uuid) + project-id (some-> params :path :project-id uuid) + search-term (some-> params :query :search-term) + plugin-url (some-> params :query :plugin) + section (case section + :dashboard-legacy-search + :dashboard-search + :dashboard-legacy-projects + :dashboard-recent + :dashboard-legacy-files + :dashboard-files + :dashboard-legacy-libraries + :dashboard-libraries + :dashboard-legacy-fonts + :dashboard-fonts + :dashboard-legacy-font-providers + :dashboard-font-providers + :dashboard-legacy-team-members + :dashboard-members + :dashboard-legacy-team-invitations + :dashboard-invitations + :dashboard-legacy-team-webhooks + :dashboard-webhooks + :dashboard-legacy-team-settings + :dashboard-settings)] + [:? + #_[:& app.main.ui.releases/release-notes-modal {:version "2.3"}] + #_[:& app.main.ui.onboarding/onboarding-templates-modal] + #_[:& app.main.ui.onboarding/onboarding-modal] + #_[:& app.main.ui.onboarding.team-choice/onboarding-team-modal] + + (cond + show-question-modal? + [:& questions-modal] + + show-newsletter-modal? + [:& onboarding-newsletter] + + show-team-modal? + [:& onboarding-team-modal {:go-to-team? true}] + + show-release-modal? + [:& release-notes-modal {:version (:main cf/version)}]) + + [:> team-container* {:team-id team-id} + [:> dashboard-page {:profile profile + :section section :team-id team-id :search-term search-term :plugin-url plugin-url :project-id project-id}]]]) :workspace + (let [params (get params :query) + team-id (some-> params :team-id uuid) + project-id (some-> params :project-id uuid) + file-id (some-> params :file-id uuid) + page-id (some-> params :page-id uuid) + layout (some-> params :layout keyword)] + [:? {} + (when (cf/external-feature-flag "onboarding-03" "test") + (cond + show-question-modal? + [:& questions-modal] + + show-newsletter-modal? + [:& onboarding-newsletter] + + show-team-modal? + [:& onboarding-team-modal {:go-to-team? false}] + + show-release-modal? + [:& release-notes-modal {:version (:main cf/version)}])) + + [:> team-container* {:team-id team-id} + [:> workspace-page {:project-id project-id + :team-id team-id + :file-id file-id + :page-id page-id + :layout-name layout + :key file-id}]]]) + + :workspace-legacy (let [project-id (some-> params :path :project-id uuid) file-id (some-> params :path :file-id uuid) page-id (some-> params :query :page-id uuid) @@ -181,14 +273,38 @@ [:& release-notes-modal {:version (:main cf/version)}])) [:* - [:& workspace-page {:project-id project-id + [:> workspace-page {:project-id project-id :file-id file-id :page-id page-id :layout-name layout :key file-id}]]]) - :viewer + (let [params (get params :query) + index (some-> (:index params) parse-long) + share-id (some-> (:share-id params) parse-uuid) + section (or (some-> (:section params) keyword) + :interactions) + + file-id (some-> (:file-id params) parse-uuid) + page-id (some-> (:page-id params) parse-uuid) + imode (or (some-> (:interactions-mode params) keyword) + :show-on-click) + frame-id (some-> (:frame-id params) parse-uuid) + share (:share params)] + + [:? {} + [:> viewer-page + {:page-id page-id + :file-id file-id + :frame-id frame-id + :section section + :index index + :share-id share-id + :interactions-mode imode + :share share}]]) + + :viewer-legacy (let [{:keys [query-params path-params]} route {:keys [index share-id section page-id interactions-mode frame-id share] :or {section :interactions interactions-mode :show-on-click}} query-params @@ -200,17 +316,17 @@ [:div.main-message (tr "viewer.breaking-change.message")] [:div.desc-message (tr "viewer.breaking-change.description")]] - [:& viewer-page + [:> viewer-page {:page-id page-id :file-id file-id :section section :index index :share-id share-id :interactions-mode (keyword interactions-mode) - :interactions-show? (case (keyword interactions-mode) - :hide false - :show true - :show-on-click false) + :show-interactions (case (keyword interactions-mode) + :hide false + :show true + :show-on-click false) :frame-id frame-id :share share}])]) @@ -237,4 +353,4 @@ [:> error-boundary* {:fallback static/internal-error*} [:& notifications/current-notification] (when route - [:> page-container* {:route route :profile profile}])])]])) + [:> page* {:route route :profile profile}])])]])) diff --git a/frontend/src/app/main/ui/auth/login.cljs b/frontend/src/app/main/ui/auth/login.cljs index 13513d46a..e30b25957 100644 --- a/frontend/src/app/main/ui/auth/login.cljs +++ b/frontend/src/app/main/ui/auth/login.cljs @@ -13,6 +13,7 @@ [app.main.data.auth :as da] [app.main.data.notifications :as ntf] [app.main.repo :as rp] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.components.button-link :as bl] [app.main.ui.components.forms :as fm] @@ -22,7 +23,6 @@ [app.util.dom :as dom] [app.util.i18n :refer [tr]] [app.util.keyboard :as k] - [app.util.router :as rt] [app.util.storage :as s] [beicon.v2.core :as rx] [rumext.v2 :as mf])) @@ -124,7 +124,7 @@ (mf/use-fn (fn [data] (when-let [token (:invitation-token data)] - (st/emit! (rt/nav :auth-verify-token {} {:token token}))))) + (st/emit! (rt/nav :auth-verify-token {:token token}))))) on-success (fn [data] @@ -283,7 +283,7 @@ [{:keys [params] :as props}] (let [go-register (mf/use-fn - #(st/emit! (rt/nav :auth-register {} params)))] + #(st/emit! (rt/nav :auth-register params)))] [:div {:class (stl/css :auth-form-wrapper)} [:h1 {:class (stl/css :auth-title) diff --git a/frontend/src/app/main/ui/auth/recovery.cljs b/frontend/src/app/main/ui/auth/recovery.cljs index 85c2af513..009dcd1f1 100644 --- a/frontend/src/app/main/ui/auth/recovery.cljs +++ b/frontend/src/app/main/ui/auth/recovery.cljs @@ -10,10 +10,10 @@ [app.common.schema :as sm] [app.main.data.notifications :as ntf] [app.main.data.profile :as du] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.components.forms :as fm] [app.util.i18n :as i18n :refer [tr]] - [app.util.router :as rt] [rumext.v2 :as mf])) (def ^:private schema:recovery-form diff --git a/frontend/src/app/main/ui/auth/recovery_request.cljs b/frontend/src/app/main/ui/auth/recovery_request.cljs index a76c76e9f..74f19116b 100644 --- a/frontend/src/app/main/ui/auth/recovery_request.cljs +++ b/frontend/src/app/main/ui/auth/recovery_request.cljs @@ -10,11 +10,11 @@ [app.common.schema :as sm] [app.main.data.notifications :as ntf] [app.main.data.profile :as du] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.components.forms :as fm] [app.main.ui.components.link :as lk] [app.util.i18n :as i18n :refer [tr]] - [app.util.router :as rt] [beicon.v2.core :as rx] [rumext.v2 :as mf])) diff --git a/frontend/src/app/main/ui/auth/register.cljs b/frontend/src/app/main/ui/auth/register.cljs index 7f2907182..7b6bc2f99 100644 --- a/frontend/src/app/main/ui/auth/register.cljs +++ b/frontend/src/app/main/ui/auth/register.cljs @@ -13,13 +13,13 @@ [app.main.data.auth :as da] [app.main.data.notifications :as ntf] [app.main.repo :as rp] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.auth.login :as login] [app.main.ui.components.forms :as fm] [app.main.ui.components.link :as lk] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] - [app.util.router :as rt] [app.util.storage :as storage] [beicon.v2.core :as rx] [rumext.v2 :as mf])) @@ -74,7 +74,7 @@ on-success (fn [data] (if (fn? on-success-callback) (on-success-callback data) - (st/emit! (rt/nav :auth-register-validate {} data))))] + (st/emit! (rt/nav :auth-register-validate data))))] (->> (rp/cmd! :prepare-register-profile cdata) (rx/map #(merge % params)) @@ -131,7 +131,7 @@ [:div {:class (stl/css :links)} [:div {:class (stl/css :account)} [:span {:class (stl/css :account-text)} (tr "auth.already-have-account") " "] - [:& lk/link {:action #(st/emit! (rt/nav :auth-login {} params)) + [:& lk/link {:action #(st/emit! (rt/nav :auth-login params)) :class (stl/css :account-link) :data-testid "login-here-link"} (tr "auth.login-here")]] @@ -191,7 +191,7 @@ (cond (some? (:invitation-token params)) (let [token (:invitation-token params)] - (st/emit! (rt/nav :auth-verify-token {} {:token token}))) + (st/emit! (rt/nav :auth-verify-token {:token token}))) (:is-active params) (st/emit! (da/login-from-register)) @@ -257,7 +257,7 @@ [:div {:class (stl/css :links)} [:div {:class (stl/css :go-back)} - [:& lk/link {:action #(st/emit! (rt/nav :auth-register {} {})) + [:& lk/link {:action #(st/emit! (rt/nav :auth-register {})) :class (stl/css :go-back-link)} (tr "labels.go-back")]]]]) diff --git a/frontend/src/app/main/ui/auth/verify_token.cljs b/frontend/src/app/main/ui/auth/verify_token.cljs index 57483ed31..01196186c 100644 --- a/frontend/src/app/main/ui/auth/verify_token.cljs +++ b/frontend/src/app/main/ui/auth/verify_token.cljs @@ -7,15 +7,16 @@ (ns app.main.ui.auth.verify-token (:require [app.main.data.auth :as da] + [app.main.data.common :as dcm] [app.main.data.notifications :as ntf] [app.main.data.profile :as du] [app.main.repo :as rp] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.ds.product.loader :refer [loader*]] [app.main.ui.static :as static] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] - [app.util.router :as rt] [app.util.timers :as ts] [beicon.v2.core :as rx] [rumext.v2 :as mf])) @@ -43,15 +44,16 @@ [tdata] (case (:state tdata) :created - (st/emit! - (ntf/success (tr "auth.notifications.team-invitation-accepted")) - (du/fetch-profile) - (rt/nav :dashboard-projects {:team-id (:team-id tdata)})) + (let [team-id (:team-id tdata)] + (st/emit! + (ntf/success (tr "auth.notifications.team-invitation-accepted")) + (du/fetch-profile) + (dcm/go-to-dashboard-recent :team-id team-id))) :pending (let [token (:invitation-token tdata) route-id (:redirect-to tdata :auth-register)] - (st/emit! (rt/nav route-id {} {:invitation-token token}))))) + (st/emit! (rt/nav route-id {:invitation-token token}))))) (defmethod handle-token :default [_tdata] diff --git a/frontend/src/app/main/ui/comments.cljs b/frontend/src/app/main/ui/comments.cljs index 61a348058..643c35d23 100644 --- a/frontend/src/app/main/ui/comments.cljs +++ b/frontend/src/app/main/ui/comments.cljs @@ -253,8 +253,8 @@ :disabled disabled?}]]])) (mf/defc comment-item - [{:keys [comment thread users origin] :as props}] - (let [owner (get users (:owner-id comment)) + [{:keys [comment thread profiles origin] :as props}] + (let [owner (get profiles (:owner-id comment)) profile (mf/deref refs/profile) options (mf/deref comments-local-options) edition? (mf/use-state false) @@ -384,7 +384,7 @@ (mf/defc thread-comments {::mf/wrap [mf/memo]} - [{:keys [thread zoom users origin position-modifier viewport]}] + [{:keys [thread zoom profiles origin position-modifier viewport]}] (let [ref (mf/use-ref) thread-id (:id thread) thread-pos (:position thread) @@ -435,13 +435,13 @@ [:div {:class (stl/css :comments)} [:& comment-item {:comment comment - :users users + :profiles profiles :thread thread :origin origin}] (for [item (rest comments)] [:* {:key (dm/str (:id item))} [:& comment-item {:comment item - :users users + :profiles profiles :origin origin}]])] [:& reply-form {:thread thread}] [:div {:ref ref}]]))) @@ -573,8 +573,8 @@ [:span (:seqn thread)]])) (mf/defc comment-thread - [{:keys [item users on-click]}] - (let [owner (get users (:owner-id item)) + [{:keys [item profiles on-click]}] + (let [owner (get profiles (:owner-id item)) on-click* (mf/use-fn (mf/deps item) @@ -613,7 +613,7 @@ [:span {:class (stl/css :new-replies)} (str unread " new replies")]))])]])) (mf/defc comment-thread-group - [{:keys [group users on-thread-click]}] + [{:keys [group profiles on-thread-click]}] [:div {:class (stl/css :thread-group)} (if (:file-name group) [:div {:class (stl/css :section-title) @@ -631,5 +631,5 @@ [:& comment-thread {:item item :on-click on-thread-click - :users users + :profiles profiles :key (:id item)}])]]) diff --git a/frontend/src/app/main/ui/dashboard.cljs b/frontend/src/app/main/ui/dashboard.cljs index 470b6553d..23bc172c0 100644 --- a/frontend/src/app/main/ui/dashboard.cljs +++ b/frontend/src/app/main/ui/dashboard.cljs @@ -16,6 +16,7 @@ [app.main.data.notifications :as notif] [app.main.data.plugins :as dp] [app.main.refs :as refs] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.context :as ctx] [app.main.ui.dashboard.files :refer [files-section*]] @@ -33,7 +34,6 @@ [app.util.dom :as dom] [app.util.keyboard :as kbd] [app.util.object :as obj] - [app.util.router :as rt] [beicon.v2.core :as rx] [goog.events :as events] [okulary.core :as l] @@ -84,7 +84,7 @@ :on-click clear-selected-fn :ref container} (case section - :dashboard-projects + :dashboard-recent [:* [:> projects-section* {:team team @@ -126,16 +126,16 @@ [:> libraries-page* {:team team :default-project default-project}] - :dashboard-team-members + :dashboard-members [:> team-members-page* {:team team :profile profile}] - :dashboard-team-invitations + :dashboard-invitations [:> team-invitations-page* {:team team}] - :dashboard-team-webhooks + :dashboard-webhooks [:> webhooks-page* {:team team}] - :dashboard-team-settings + :dashboard-settings [:> team-settings-page* {:team team :profile profile}] nil)])) @@ -151,8 +151,9 @@ (st/emit! (dp/delay-open-plugin plugin) (rt/nav :workspace - {:project-id project-id :file-id id} - {:page-id (dm/get-in data [:pages 0])}))) + {:page-id (dm/get-in data [:pages 0]) + :project-id project-id + :file-id id}))) create-file! (fn [plugin] @@ -182,11 +183,11 @@ :on-accept #(do (preg/install-plugin! plugin) (st/emit! (modal/hide) - (rt/nav :dashboard-projects {:team-id team-id}) + (rt/nav :dashboard-recent {:team-id team-id}) (open-try-out-dialog plugin))) :on-close #(st/emit! (modal/hide) - (rt/nav :dashboard-projects {:team-id team-id}))}))] + (rt/nav :dashboard-recent {:team-id team-id}))}))] (mf/with-layout-effect [plugin-url team-id project-id] @@ -204,7 +205,7 @@ (mf/defc dashboard* {::mf/props :obj} - [{:keys [profile project-id team-id search-term plugin-url route-name]}] + [{:keys [profile project-id team-id search-term plugin-url section]}] (let [team (mf/deref refs/team) projects (mf/deref refs/projects) @@ -253,7 +254,7 @@ :project project :default-project default-project :profile profile - :section route-name + :section section :search-term search-term}] (when (seq projects) [:> dashboard-content* @@ -261,6 +262,6 @@ :profile profile :project project :default-project default-project - :section route-name + :section section :search-term search-term :team team}])]])) diff --git a/frontend/src/app/main/ui/dashboard/comments.cljs b/frontend/src/app/main/ui/dashboard/comments.cljs index 75ebd8e29..c2b77134a 100644 --- a/frontend/src/app/main/ui/dashboard/comments.cljs +++ b/frontend/src/app/main/ui/dashboard/comments.cljs @@ -63,7 +63,7 @@ (mf/defc comments-section [{:keys [profile team show? on-hide-comments]}] (let [threads-map (mf/deref refs/comment-threads) - users (mf/deref refs/current-team-comments-users) + profiles (mf/deref refs/profiles) team-id (:id team) tgroups (->> (vals threads-map) @@ -114,13 +114,13 @@ {:group (first tgroups) :on-thread-click on-navigate :show-file-name true - :users users}] + :profiles profiles}] (for [tgroup (rest tgroups)] [:& cmt/comment-thread-group {:group tgroup :on-thread-click on-navigate :show-file-name true - :users users + :profiles profiles :key (:page-id tgroup)}])] [:div {:class (stl/css :thread-groups-placeholder)} diff --git a/frontend/src/app/main/ui/dashboard/file_menu.cljs b/frontend/src/app/main/ui/dashboard/file_menu.cljs index c210efe08..de4a7518c 100644 --- a/frontend/src/app/main/ui/dashboard/file_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/file_menu.cljs @@ -15,12 +15,12 @@ [app.main.data.notifications :as ntf] [app.main.refs :as refs] [app.main.repo :as rp] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.components.context-menu-a11y :refer [context-menu*]] [app.main.ui.context :as ctx] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] - [app.util.router :as rt] [beicon.v2.core :as rx] [rumext.v2 :as mf])) @@ -85,10 +85,9 @@ on-new-tab (fn [_] - (let [path-params {:project-id (:project-id file) - :file-id (:id file)}] - (st/emit! (rt/nav-new-window* {:rname :workspace - :path-params path-params})))) + (st/emit! (dcm/go-to-workspace + {:file-id (:id file) + ::rt/new-window true}))) on-duplicate (fn [_] @@ -134,7 +133,9 @@ (st/emit! (ntf/success (tr "dashboard.success-move-files"))) (st/emit! (ntf/success (tr "dashboard.success-move-file")))) (if (or navigate (not= team-id current-team-id)) - (st/emit! (dd/go-to-files project-id team-id)) + (st/emit! (dcm/go-to-dashboard-files + {:project-id project-id + :team-id team-id})) (st/emit! (dd/fetch-recent-files) (dd/clear-selected-files)))) diff --git a/frontend/src/app/main/ui/dashboard/files.cljs b/frontend/src/app/main/ui/dashboard/files.cljs index a1d6bedf5..8572cf103 100644 --- a/frontend/src/app/main/ui/dashboard/files.cljs +++ b/frontend/src/app/main/ui/dashboard/files.cljs @@ -7,8 +7,10 @@ (ns app.main.ui.dashboard.files (:require-macros [app.main.style :as stl]) (:require + [app.main.data.common :as dcm] [app.main.data.dashboard :as dd] [app.main.data.event :as ev] + [app.main.data.project :as dpj] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.dashboard.grid :refer [grid]] @@ -21,7 +23,6 @@ [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.router :as rt] [cuerdas.core :as str] [rumext.v2 :as mf])) @@ -32,9 +33,12 @@ {::mf/props :obj ::mf/private true} [{:keys [project create-fn can-edit]}] - (let [local (mf/use-state - {:menu-open false - :edition false}) + (let [project-id (:id project) + + local + (mf/use-state + {:menu-open false + :edition false}) on-create-click (mf/use-fn @@ -63,9 +67,9 @@ on-import (mf/use-fn - (mf/deps (:id project)) + (mf/deps project-id) (fn [] - (st/emit! (dd/fetch-files {:project-id (:id project)}) + (st/emit! (dpj/fetch-files project-id) (dd/clear-selected-files))))] @@ -153,11 +157,10 @@ on-file-created (mf/use-fn - (fn [data] - (let [pparams {:project-id (:project-id data) - :file-id (:id data)} - qparams {:page-id (get-in data [:data :pages 0])}] - (st/emit! (rt/nav :workspace pparams qparams))))) + (fn [file-data] + (let [file-id (:id file-data) + page-id (get-in file-data [:pages 0])] + (st/emit! (dcm/go-to-workspace :file-id file-id :page-id page-id))))) create-file (mf/use-fn @@ -176,7 +179,7 @@ (dom/set-html-title (tr "title.dashboard.files" pname))))) (mf/with-effect [project-id] - (st/emit! (dd/fetch-files {:project-id project-id}) + (st/emit! (dpj/fetch-files project-id) (dd/clear-selected-files))) [:* diff --git a/frontend/src/app/main/ui/dashboard/grid.cljs b/frontend/src/app/main/ui/dashboard/grid.cljs index f73bebd11..13427d601 100644 --- a/frontend/src/app/main/ui/dashboard/grid.cljs +++ b/frontend/src/app/main/ui/dashboard/grid.cljs @@ -12,8 +12,11 @@ [app.common.geom.point :as gpt] [app.common.logging :as log] [app.config :as cf] + [app.main.data.common :as dcm] [app.main.data.dashboard :as dd] [app.main.data.notifications :as ntf] + [app.main.data.project :as dpj] + [app.main.data.team :as dtm] [app.main.features :as features] [app.main.fonts :as fonts] [app.main.rasterizer :as thr] @@ -79,8 +82,7 @@ revn (get file :revn) thumbnail-id (get file :thumbnail-id) - ;; FIXME: revisit maybe bug - bg-color (dm/get-in file [:data :options :background]) + bg-color (dm/get-in file [:data :background]) container (mf/use-ref) visible? (h/use-visible container :once? true)] @@ -270,12 +272,12 @@ on-navigate (mf/use-fn - (mf/deps file) + (mf/deps file-id) (fn [event] (let [menu-icon (mf/ref-val menu-ref) target (dom/get-target event)] (when-not (dom/child? target menu-icon) - (st/emit! (dd/go-to-workspace file)))))) + (st/emit! (dcm/go-to-workspace :file-id file-id)))))) on-drag-start (mf/use-fn @@ -427,10 +429,12 @@ on-finish-import (mf/use-fn (fn [] - (st/emit! (dd/fetch-files {:project-id project-id}) - (dd/fetch-shared-files) + (st/emit! (dpj/fetch-files project-id) + (dtm/fetch-shared-files) (dd/clear-selected-files)))) + + import-files (use-import-file project-id on-finish-import) on-drag-enter diff --git a/frontend/src/app/main/ui/dashboard/libraries.cljs b/frontend/src/app/main/ui/dashboard/libraries.cljs index f1d428e93..1ba3e856f 100644 --- a/frontend/src/app/main/ui/dashboard/libraries.cljs +++ b/frontend/src/app/main/ui/dashboard/libraries.cljs @@ -8,6 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.main.data.dashboard :as dd] + [app.main.data.team :as dtm] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.dashboard.grid :refer [grid]] @@ -41,7 +42,7 @@ (dom/set-html-title (tr "title.dashboard.shared-libraries" tname)))) (mf/with-effect [team] - (st/emit! (dd/fetch-shared-files) + (st/emit! (dtm/fetch-shared-files) (dd/clear-selected-files))) [:* diff --git a/frontend/src/app/main/ui/dashboard/project_menu.cljs b/frontend/src/app/main/ui/dashboard/project_menu.cljs index f99da5d3d..367fca95e 100644 --- a/frontend/src/app/main/ui/dashboard/project_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/project_menu.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.dashboard.project-menu (:require + [app.main.data.common :as dcm] [app.main.data.dashboard :as dd] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] @@ -16,7 +17,6 @@ [app.main.ui.dashboard.import :as udi] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] - [app.util.router :as rt] [rumext.v2 :as mf])) (mf/defc project-menu* @@ -32,9 +32,9 @@ on-duplicate-success (fn [new-project] (st/emit! (ntf/success (tr "dashboard.success-duplicate-project")) - (rt/nav :dashboard-files - {:team-id (:team-id new-project) - :project-id (:id new-project)}))) + (dcm/go-to-dashboard-files + :team-id (:team-id new-project) + :project-id (:id new-project)))) on-duplicate (fn [] @@ -46,7 +46,7 @@ on-move-success (fn [team-id] - (st/emit! (dd/go-to-projects team-id))) + (st/emit! (dcm/go-to-dashboard-recent :team-id team-id))) on-move (fn [team-id] @@ -57,9 +57,10 @@ delete-fn (fn [_] - (st/emit! (ntf/success (tr "dashboard.success-delete-project")) - (dd/delete-project project) - (dd/go-to-projects (:team-id project)))) + (let [team-id (:team-id project)] + (st/emit! (ntf/success (tr "dashboard.success-delete-project")) + (dd/delete-project project) + (dcm/go-to-dashboard-recent :team-id team-id)))) on-delete #(st/emit! diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs index 082bfd7d1..4f76b121f 100644 --- a/frontend/src/app/main/ui/dashboard/projects.cljs +++ b/frontend/src/app/main/ui/dashboard/projects.cljs @@ -8,9 +8,11 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.geom.point :as gpt] + [app.main.data.common :as dcm] [app.main.data.dashboard :as dd] [app.main.data.event :as ev] [app.main.data.modal :as modal] + [app.main.data.project :as dpj] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.dashboard.grid :refer [line-grid]] @@ -23,7 +25,6 @@ [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.router :as rt] [app.util.storage :as storage] [app.util.time :as dt] [cuerdas.core :as str] @@ -62,7 +63,7 @@ {::mf/wrap [mf/memo] ::mf/props :obj} [{:keys [team on-close]}] - (let [on-nav-members-click (mf/use-fn #(st/emit! (dd/go-to-team-members))) + (let [on-nav-members-click (mf/use-fn #(st/emit! (dcm/go-to-dashboard-members))) on-invite (mf/use-fn @@ -105,7 +106,6 @@ (let [locale (mf/deref i18n/locale) project-id (:id project) - team-id (:id team) file-count (or (:count project) 0) is-draft? (:is-default project) @@ -124,11 +124,10 @@ on-nav (mf/use-fn - (mf/deps project-id team-id) + (mf/deps project-id) (fn [] - (st/emit! (rt/nav :dashboard-files - {:team-id team-id - :project-id project-id})))) + (st/emit! (dcm/go-to-dashboard-files :project-id project-id)))) + toggle-pin (mf/use-fn (mf/deps project) @@ -171,11 +170,9 @@ on-file-created (mf/use-fn - (fn [data] - (let [pparams {:project-id (:project-id data) - :file-id (:id data)} - qparams {:page-id (get-in data [:data :pages 0])}] - (st/emit! (rt/nav :workspace pparams qparams))))) + (fn [{:keys [id data]}] + (let [page-id (get-in data [:pages 0])] + (st/emit! (dcm/go-to-workspace :file-id id :page-id page-id))))) create-file (mf/use-fn @@ -194,9 +191,9 @@ on-import (mf/use-fn - (mf/deps project-id (:id team)) + (mf/deps project-id) (fn [] - (st/emit! (dd/fetch-files {:project-id project-id}) + (st/emit! (dpj/fetch-files project-id) (dd/fetch-recent-files) (dd/fetch-projects) (dd/clear-selected-files)))) diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs index 1c341fdc2..990019067 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs @@ -11,12 +11,14 @@ [app.common.spec :as us] [app.config :as cf] [app.main.data.auth :as da] + [app.main.data.common :as dcm] [app.main.data.dashboard :as dd] [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] [app.main.data.team :as dtm] [app.main.refs :as refs] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]] [app.main.ui.components.link :refer [link]] @@ -29,7 +31,6 @@ [app.util.dom.dnd :as dnd] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.router :as rt] [app.util.timers :as ts] [beicon.v2.core :as rx] [cljs.spec.alpha :as s] @@ -73,32 +74,34 @@ edit-id (:project-for-edit dstate) local* (mf/use-state - {:menu-open false - :menu-pos nil - :edition? (= (:id item) edit-id) - :dragging? false}) + #(do {:menu-open false + :menu-pos nil + :edition? (= (:id item) edit-id) + :dragging? false})) - local @local* + local (deref local*) + + project-id (get item :id) on-click (mf/use-fn - (mf/deps item) + (mf/deps project-id) (fn [] - (st/emit! (dd/go-to-files (:id item))))) + (st/emit! (dcm/go-to-dashboard-files :project-id project-id)))) on-key-down (mf/use-fn - (mf/deps item) + (mf/deps project-id) (fn [event] (when (kbd/enter? event) - (st/emit! (dd/go-to-files (:id item)) - (ts/schedule-on-idle - (fn [] - (let [project-title (dom/get-element (str (:id item)))] - (when project-title - (dom/set-attribute! project-title "tabindex" "0") - (dom/focus! project-title) - (dom/set-attribute! project-title "tabindex" "-1"))))))))) + (st/emit! + (dcm/go-to-dashboard-files :project-id project-id) + (ts/schedule-on-idle + (fn [] + (when-let [title (dom/get-element (str project-id))] + (dom/set-attribute! title "tabindex" "0") + (dom/focus! title) + (dom/set-attribute! title "tabindex" "-1")))))))) on-menu-click (mf/use-fn @@ -148,9 +151,10 @@ on-drop-success (mf/use-fn - (mf/deps (:id item)) - #(st/emit! (ntf/success (tr "dashboard.success-move-file")) - (dd/go-to-files (:id item)))) + (mf/deps project-id) + (fn [_] + (st/emit! (dcm/go-to-dashboard-files :project-id project-id) + (ntf/success (tr "dashboard.success-move-file"))))) on-drop (mf/use-fn @@ -201,19 +205,18 @@ on-search-change (mf/use-fn - (mf/deps team-id) (fn [event] (let [value (dom/get-target-val event)] - (emit! (dd/go-to-search value))))) + (emit! (dcm/go-to-dashboard-search :term value))))) on-clear-click (mf/use-fn (mf/deps team-id) (fn [e] + (emit! (dcm/go-to-dashboard-search)) (let [search-input (dom/get-element "search-input")] (dom/clean-value! search-input) (dom/focus! search-input) - (emit! (dd/go-to-search)) (dom/prevent-default e) (dom/stop-propagation e)))) @@ -278,7 +281,8 @@ (fn [event] (let [team-id (-> (dom/get-current-target event) (dom/get-data "value"))] - (st/emit! (dd/go-to-projects team-id))))) + + (st/emit! (dcm/go-to-dashboard-recent :team-id team-id))))) handle-select-default (mf/use-fn @@ -343,10 +347,10 @@ (mf/defc team-options-dropdown [{:keys [team profile] :as props}] - (let [go-members #(st/emit! (dd/go-to-team-members)) - go-invitations #(st/emit! (dd/go-to-team-invitations)) - go-webhooks #(st/emit! (dd/go-to-team-webhooks)) - go-settings #(st/emit! (dd/go-to-team-settings)) + (let [go-members #(st/emit! (dcm/go-to-dashboard-members)) + go-invitations #(st/emit! (dcm/go-to-dashboard-invitations)) + go-webhooks #(st/emit! (dcm/go-to-dashboard-webhooks)) + go-settings #(st/emit! (dcm/go-to-dashboard-settings)) members (get team :members) permissions (get team :permissions) @@ -356,9 +360,9 @@ on-success (fn [] ;; FIXME: this should be handled in the event, not here - (st/emit! (dd/go-to-projects (:default-team-id profile)) - (modal/hide) - (dtm/fetch-teams))) + (let [team-id (:default-team-id profile)] + (rx/of (dcm/go-to-dashboard-recent :team-id team-id) + (modal/hide)))) on-error (fn [{:keys [code] :as error}] @@ -692,38 +696,38 @@ (let [default-project-id (get default-project :id) - projects? (= section :dashboard-projects) + team-id (get team :id) + + projects? (= section :dashboard-recent) fonts? (= section :dashboard-fonts) libs? (= section :dashboard-libraries) drafts? (and (= section :dashboard-files) (= (:id project) default-project-id)) go-projects - (mf/use-fn - (mf/deps team) - #(st/emit! (rt/nav :dashboard-projects {:team-id (:id team)}))) + (mf/use-fn #(st/emit! (dcm/go-to-dashboard-recent))) go-projects-with-key (mf/use-fn - (mf/deps team) - #(st/emit! (rt/nav :dashboard-projects {:team-id (:id team)}) - (ts/schedule-on-idle - (fn [] - (let [projects-title (dom/get-element "dashboard-projects-title")] - (when projects-title - (dom/set-attribute! projects-title "tabindex" "0") - (dom/focus! projects-title) - (dom/set-attribute! projects-title "tabindex" "-1"))))))) + (mf/deps team-id) + (fn [] + (st/emit! (dcm/go-to-dashboard-recent :team-id team-id) + (ts/schedule-on-idle + (fn [] + (when-let [projects-title (dom/get-element "dashboard-projects-title")] + (dom/set-attribute! projects-title "tabindex" "0") + (dom/focus! projects-title) + (dom/set-attribute! projects-title "tabindex" "-1"))))))) go-fonts (mf/use-fn - (mf/deps team) - #(st/emit! (rt/nav :dashboard-fonts {:team-id (:id team)}))) + (mf/deps team-id) + #(st/emit! (dcm/go-to-dashboard-fonts :team-id team-id))) go-fonts-with-key (mf/use-fn (mf/deps team) - #(st/emit! (rt/nav :dashboard-fonts {:team-id (:id team)}) + #(st/emit! (dcm/go-to-dashboard-fonts :team-id team-id) (ts/schedule-on-idle (fn [] (let [font-title (dom/get-element "dashboard-fonts-title")] @@ -733,34 +737,31 @@ (dom/set-attribute! font-title "tabindex" "-1"))))))) go-drafts (mf/use-fn - (mf/deps team default-project-id) + (mf/deps team-id default-project-id) (fn [] - (st/emit! (rt/nav :dashboard-files - {:team-id (:id team) - :project-id default-project-id})))) + (st/emit! (dcm/go-to-dashboard-files :team-id team-id :project-id default-project-id)))) go-drafts-with-key (mf/use-fn - (mf/deps team default-project-id) - #(st/emit! (rt/nav :dashboard-files {:team-id (:id team) - :project-id default-project-id}) - (ts/schedule-on-idle - (fn [] - (let [drafts-title (dom/get-element "dashboard-drafts-title")] - (when drafts-title - (dom/set-attribute! drafts-title "tabindex" "0") - (dom/focus! drafts-title) - (dom/set-attribute! drafts-title "tabindex" "-1"))))))) + (mf/deps team-id default-project-id) + (fn [] + (st/emit! (dcm/go-to-dashboard-files :team-id team-id :project-id default-project-id)) + (ts/schedule-on-idle + (fn [] + (when-let [title (dom/get-element "dashboard-drafts-title")] + (dom/set-attribute! title "tabindex" "0") + (dom/focus! title) + (dom/set-attribute! title "tabindex" "-1")))))) go-libs (mf/use-fn - (mf/deps team) - #(st/emit! (rt/nav :dashboard-libraries {:team-id (:id team)}))) + (mf/deps team-id) + #(st/emit! (dcm/go-to-dashboard-libraries :team-id team-id))) go-libs-with-key (mf/use-fn - (mf/deps team) - #(st/emit! (rt/nav :dashboard-libraries {:team-id (:id team)}) + (mf/deps team-id) + #(st/emit! (dcm/go-to-dashboard-libraries :team-id team-id) (ts/schedule-on-idle (fn [] (let [libs-title (dom/get-element "dashboard-libraries-title")] diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index dd63d2441..1c2c2e4ea 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -11,7 +11,7 @@ [app.common.data.macros :as dm] [app.common.schema :as sm] [app.config :as cfg] - [app.main.data.dashboard :as dd] + [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] @@ -62,10 +62,10 @@ {::mf/wrap [mf/memo] ::mf/props :obj} [{:keys [section team]}] - (let [on-nav-members (mf/use-fn #(st/emit! (dd/go-to-team-members))) - on-nav-settings (mf/use-fn #(st/emit! (dd/go-to-team-settings))) - on-nav-invitations (mf/use-fn #(st/emit! (dd/go-to-team-invitations))) - on-nav-webhooks (mf/use-fn #(st/emit! (dd/go-to-team-webhooks))) + (let [on-nav-members (mf/use-fn #(st/emit! (dcm/go-to-dashboard-members))) + on-nav-settings (mf/use-fn #(st/emit! (dcm/go-to-dashboard-settings))) + on-nav-invitations (mf/use-fn #(st/emit! (dcm/go-to-dashboard-invitations))) + on-nav-webhooks (mf/use-fn #(st/emit! (dcm/go-to-dashboard-webhooks))) route (mf/deref refs/route) invite-email (-> route :query-params :invite-email) @@ -375,7 +375,7 @@ (st/emit! (modal/show params))))) on-success - (mf/use-fn (fn [] (rx/of (dd/go-to-default-team)))) + (mf/use-fn #(rx/of (dcm/go-to-dashboard-recent :team-id :default))) on-error (mf/use-fn diff --git a/frontend/src/app/main/ui/dashboard/team_form.cljs b/frontend/src/app/main/ui/dashboard/team_form.cljs index 04b3deb68..a83ce0d87 100644 --- a/frontend/src/app/main/ui/dashboard/team_form.cljs +++ b/frontend/src/app/main/ui/dashboard/team_form.cljs @@ -8,6 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.schema :as sm] + [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] @@ -18,7 +19,6 @@ [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.router :as rt] [beicon.v2.core :as rx] [rumext.v2 :as mf])) @@ -28,15 +28,15 @@ (defn- on-create-success [_form response] - (let [msg "Team created successfully"] - (st/emit! (ntf/success msg) - (modal/hide) - (rt/nav :dashboard-projects {:team-id (:id response)})))) + (let [message "Team created successfully" + team-id (:id response)] + (st/emit! (ntf/success message) + (dcm/go-to-dashboard-recent :team-id team-id)))) (defn- on-update-success [_form _response] - (let [msg "Team created successfully"] - (st/emit! (ntf/success msg) + (let [message "Team created successfully"] + (st/emit! (ntf/success message) (modal/hide)))) (defn- on-error diff --git a/frontend/src/app/main/ui/dashboard/templates.cljs b/frontend/src/app/main/ui/dashboard/templates.cljs index 81e27a432..b33694236 100644 --- a/frontend/src/app/main/ui/dashboard/templates.cljs +++ b/frontend/src/app/main/ui/dashboard/templates.cljs @@ -9,6 +9,7 @@ (:require [app.common.data.macros :as dm] [app.config :as cf] + [app.main.data.common :as dcm] [app.main.data.dashboard :as dd] [app.main.data.event :as ev] [app.main.data.modal :as modal] @@ -18,7 +19,6 @@ [app.util.dom :as dom] [app.util.i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.router :as rt] [app.util.storage :as storage] [okulary.core :as l] [potok.v2.core :as ptk] @@ -43,9 +43,9 @@ :section section}) (when-not (some? project-id) - (rt/nav :dashboard-files - {:team-id team-id - :project-id default-project-id}))))] + (dcm/go-to-dashboard-recent + :team-id team-id + :project-id default-project-id))))] (st/emit! (ptk/event ::ev/event {::ev/name "import-template-launch" diff --git a/frontend/src/app/main/ui/onboarding/team_choice.cljs b/frontend/src/app/main/ui/onboarding/team_choice.cljs index b799dd646..1bec75b40 100644 --- a/frontend/src/app/main/ui/onboarding/team_choice.cljs +++ b/frontend/src/app/main/ui/onboarding/team_choice.cljs @@ -9,6 +9,7 @@ (:require [app.common.data.macros :as dm] [app.common.schema :as sm] + [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.data.profile :as du] [app.main.data.team :as dtm] @@ -17,7 +18,6 @@ [app.main.ui.icons :as i] [app.main.ui.notifications.context-notification :refer [context-notification]] [app.util.i18n :as i18n :refer [tr]] - [app.util.router :as rt] [potok.v2.core :as ptk] [rumext.v2 :as mf])) @@ -84,7 +84,7 @@ (st/emit! (du/update-profile-props {:onboarding-team-id team-id :onboarding-viewed true}) (when go-to-team? - (rt/nav :dashboard-projects {:team-id team-id})))))) + (dcm/go-to-dashboard-recent :team-id team-id)))))) on-error (mf/use-fn diff --git a/frontend/src/app/main/ui/routes.cljs b/frontend/src/app/main/ui/routes.cljs index 74f10126f..a19905697 100644 --- a/frontend/src/app/main/ui/routes.cljs +++ b/frontend/src/app/main/ui/routes.cljs @@ -7,34 +7,17 @@ (ns app.main.ui.routes (:require [app.common.data.macros :as dm] - [app.common.spec :as us] [app.common.uri :as u] [app.common.uuid :as uuid] [app.config :as cf] [app.main.data.team :as dtm] [app.main.repo :as rp] + [app.main.router :as rt] [app.main.store :as st] - [app.util.router :as rt] [beicon.v2.core :as rx] - [cljs.spec.alpha :as s] [cuerdas.core :as str] [potok.v2.core :as ptk])) -(s/def ::page-id ::us/uuid) -(s/def ::file-id ::us/uuid) -(s/def ::section ::us/keyword) -(s/def ::index ::us/integer) -(s/def ::token (s/nilable ::us/not-empty-string)) -(s/def ::share-id ::us/uuid) - -(s/def ::viewer-path-params - (s/keys :req-un [::file-id])) - -(s/def ::viewer-query-params - (s/keys :opt-un [::index ::share-id ::section ::page-id])) - -(s/def ::any any?) - (def routes [["/auth" ["/login" :auth-login] @@ -53,11 +36,10 @@ ["/access-tokens" :settings-access-tokens]] ["/frame-preview" :frame-preview] - ["/view/:file-id" - {:name :viewer - :conform - {:path-params ::viewer-path-params - :query-params ::viewer-query-params}}] + + ["/view" :viewer] + + ["/view/:file-id" :viewer-legacy] (when *assert* ["/debug/icons-preview" :debug-icons-preview]) @@ -65,33 +47,32 @@ ;; Used for export ["/render-sprite/:file-id" :render-sprite] - ["/dashboard/team/:team-id" - ["/members" :dashboard-team-members] - ["/invitations" :dashboard-team-invitations] - ["/webhooks" :dashboard-team-webhooks] - ["/settings" :dashboard-team-settings] - ["/projects" :dashboard-projects] + ["/dashboard" + ["/members" :dashboard-members] + ["/invitations" :dashboard-invitations] + ["/webhooks" :dashboard-webhooks] + ["/settings" :dashboard-settings] + ["/recent" :dashboard-recent] ["/search" :dashboard-search] ["/fonts" :dashboard-fonts] ["/fonts/providers" :dashboard-font-providers] ["/libraries" :dashboard-libraries] - ["/projects/:project-id" :dashboard-files]] + ["/files" :dashboard-files]] - ["/workspace/:project-id/:file-id" :workspace]]) + ["/dashboard/team/:team-id" + ["/members" :dashboard-legacy-team-members] + ["/invitations" :dashboard-legacy-team-invitations] + ["/webhooks" :dashboard-legacy-team-webhooks] + ["/settings" :dashboard-legacy-team-settings] + ["/projects" :dashboard-legacy-projects] + ["/search" :dashboard-legacy-search] + ["/fonts" :dashboard-legacy-fonts] + ["/fonts/providers" :dashboard-legacy-font-providers] + ["/libraries" :dashboard-legacy-libraries] + ["/projects/:project-id" :dashboard-legacy-files]] -(defn- match-path - [router path] - (when-let [match (rt/match router path)] - (if-let [conform (get-in match [:data :conform])] - (let [spath (get conform :path-params ::any) - squery (get conform :query-params ::any)] - (try - (-> (dissoc match :params) - (assoc :path-params (us/conform spath (get match :path-params)) - :query-params (us/conform squery (get match :query-params)))) - (catch :default _ - nil))) - match))) + ["/workspace" :workspace] + ["/workspace/:project-id/:file-id" :workspace-legacy]]) (defn on-navigate [router path] @@ -99,8 +80,9 @@ [base-path qs] (str/split path "?") location-path (dm/str (.-origin location) (.-pathname location)) valid-location? (= location-path (dm/str cf/public-uri)) - match (match-path router path) + match (rt/match router path) empty-path? (or (= base-path "") (= base-path "/"))] + (cond (not valid-location?) (st/emit! (rt/assign-exception {:type :not-found})) @@ -121,9 +103,9 @@ empty-path? (let [team-id (or (dtm/get-last-team-id) (:default-team-id profile))] - (st/emit! (rt/nav :dashboard-projects - {:team-id team-id} - (u/query-string->map qs)))) + (st/emit! (rt/nav :dashboard-recent + (-> (u/query-string->map qs) + (assoc :team-id team-id))))) :else (st/emit! (rt/assign-exception {:type :not-found}))))))))) diff --git a/frontend/src/app/main/ui/settings.cljs b/frontend/src/app/main/ui/settings.cljs index ce6099c88..30f7d15ef 100644 --- a/frontend/src/app/main/ui/settings.cljs +++ b/frontend/src/app/main/ui/settings.cljs @@ -9,6 +9,7 @@ (:require [app.main.data.dashboard.shortcuts :as sc] [app.main.refs :as refs] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.hooks :as hooks] [app.main.ui.settings.access-tokens :refer [access-tokens-page]] @@ -20,7 +21,6 @@ [app.main.ui.settings.profile :refer [profile-page]] [app.main.ui.settings.sidebar :refer [sidebar]] [app.util.i18n :as i18n :refer [tr]] - [app.util.router :as rt] [rumext.v2 :as mf])) (mf/defc header diff --git a/frontend/src/app/main/ui/settings/sidebar.cljs b/frontend/src/app/main/ui/settings/sidebar.cljs index 4f5d4dcac..8da8bb6a3 100644 --- a/frontend/src/app/main/ui/settings/sidebar.cljs +++ b/frontend/src/app/main/ui/settings/sidebar.cljs @@ -8,15 +8,16 @@ (:require-macros [app.main.style :as stl]) (:require [app.config :as cf] + [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.team :as dtm] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.dashboard.sidebar :refer [profile-section*]] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.router :as rt] [potok.v2.core :as ptk] [rumext.v2 :as mf])) @@ -26,6 +27,7 @@ (def ^:private feedback-icon (i/icon-xref :feedback (stl/css :feedback-icon))) +;; FIXME: move to common (def ^:private go-settings-profile #(st/emit! (rt/nav :settings-profile))) @@ -64,7 +66,7 @@ go-dashboard (mf/use-fn (mf/deps team-id) - #(st/emit! (rt/nav :dashboard-projects {:team-id team-id})))] + #(st/emit! (dcm/go-to-dashboard-recent :team-id team-id)))] [:div {:class (stl/css :sidebar-content)} [:div {:class (stl/css :sidebar-content-section)} diff --git a/frontend/src/app/main/ui/static.cljs b/frontend/src/app/main/ui/static.cljs index 565894f7b..ccf892622 100644 --- a/frontend/src/app/main/ui/static.cljs +++ b/frontend/src/app/main/ui/static.cljs @@ -11,10 +11,11 @@ [app.common.data :as d] [app.common.pprint :as pp] [app.common.uri :as u] - [app.main.data.common :as dc] + [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.refs :as refs] [app.main.repo :as rp] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.auth.login :refer [login-methods]] [app.main.ui.auth.recovery-request :refer [recovery-request-page recovery-sent-page]] @@ -26,7 +27,6 @@ [app.main.ui.viewer.header :as viewer.header] [app.util.dom :as dom] [app.util.i18n :refer [tr]] - [app.util.router :as rt] [app.util.webapi :as wapi] [beicon.v2.core :as rx] [cuerdas.core :as str] @@ -213,7 +213,8 @@ (mf/use-fn (mf/deps profile) (fn [] - (st/emit! (rt/nav :dashboard-projects {:team-id (:default-team-id profile)})))) + (let [team-id (:default-team-id profile)] + (st/emit! (dcm/go-to-dashboard-recent :team-id team-id))))) on-success (mf/use-fn @@ -233,7 +234,7 @@ {:team-id team-id}) mdata {:on-success on-success :on-error on-error}] - (st/emit! (dc/create-team-access-request + (st/emit! (dcm/create-team-access-request (with-meta params mdata))))))] [:* diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index 0410bd1df..1578a4911 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -25,7 +25,7 @@ [app.main.ui.ds.product.loader :refer [loader*]] [app.main.ui.hooks :as hooks] [app.main.ui.icons :as i] - [app.main.ui.viewer.comments :refer [comments-layer comments-sidebar]] + [app.main.ui.viewer.comments :refer [comments-layer comments-sidebar*]] [app.main.ui.viewer.header :as header] [app.main.ui.viewer.inspect :as inspect] [app.main.ui.viewer.interactions :as interactions] @@ -129,8 +129,8 @@ :comment-sidebar show-sidebar?}] (when show-sidebar? - [:& comments-sidebar - {:users users + [:> comments-sidebar* + {:profiles users :frame frame :page page}])])) @@ -274,9 +274,9 @@ :page page :zoom zoom}])]]) -(mf/defc viewer-content - {::mf/wrap-props false} - [{:keys [data page-id share-id section index interactions-mode share] :as props}] +(mf/defc viewer-content* + {::mf/props :obj} + [{:keys [data page-id share-id section index interactions-mode share]}] (let [{:keys [file users project permissions]} data allowed (or (= section :interactions) @@ -620,8 +620,8 @@ ;; --- Component: Viewer -(mf/defc viewer - {::mf/wrap-props false} +(mf/defc viewer* + {::mf/props :obj} [{:keys [file-id share-id page-id] :as props}] (mf/with-effect [file-id page-id share-id] (let [params {:file-id file-id @@ -630,9 +630,10 @@ (st/emit! (dv/initialize params)) (fn [] (st/emit! (dv/finalize params))))) + (if-let [data (mf/deref refs/viewer-data)] (let [props (obj/merge props #js {:data data :key (dm/str file-id)})] - [:> viewer-content props]) + [:> viewer-content* props]) [:> loader* {:title (tr "labels.loading") :overlay true}])) diff --git a/frontend/src/app/main/ui/viewer/comments.cljs b/frontend/src/app/main/ui/viewer/comments.cljs index 1d3c7e000..2ab261bb3 100644 --- a/frontend/src/app/main/ui/viewer/comments.cljs +++ b/frontend/src/app/main/ui/viewer/comments.cljs @@ -220,7 +220,7 @@ {:thread thread :position-modifier modifier1 :viewport {:offset-x 0 :offset-y 0 :width (:width vsize) :height (:height vsize)} - :users users + :profiles users :zoom zoom}]) (when-let [draft (:draft local)] @@ -231,10 +231,11 @@ :on-submit on-draft-submit :zoom zoom}])]]])) -(mf/defc comments-sidebar - [{:keys [users frame page]}] +(mf/defc comments-sidebar* + {::mf/props :obj} + [{:keys [profiles frame page]}] (let [profile (mf/deref refs/profile) - local (mf/deref refs/comments-local) + local (mf/deref refs/comments-local) threads-map (mf/deref refs/comment-threads) threads (->> (vals threads-map) (dcm/apply-filters local profile) @@ -242,4 +243,8 @@ (gsh/has-point? frame position))))] [:aside {:class (stl/css :comments-sidebar)} [:div {:class (stl/css :settings-bar-inside)} - [:& wc/comments-sidebar {:from-viewer true :users users :threads threads :page-id (:id page)}]]])) + [:> wc/comments-sidebar* + {:from-viewer true + :profiles profiles + :threads threads + :page-id (:id page)}]]])) diff --git a/frontend/src/app/main/ui/viewer/header.cljs b/frontend/src/app/main/ui/viewer/header.cljs index 6db8a69b9..86eb9eb04 100644 --- a/frontend/src/app/main/ui/viewer/header.cljs +++ b/frontend/src/app/main/ui/viewer/header.cljs @@ -18,7 +18,7 @@ [app.main.ui.formats :as fmt] [app.main.ui.icons :as i] [app.main.ui.viewer.comments :refer [comments-menu]] - [app.main.ui.viewer.interactions :refer [flows-menu* interactions-menu]] + [app.main.ui.viewer.interactions :refer [flows-menu* interactions-menu*]] [app.util.dom :as dom] [app.util.i18n :refer [tr]] [okulary.core :as l] @@ -173,7 +173,8 @@ :interactions [:* (when index [:> flows-menu* {:page page :index index}]) - [:& interactions-menu {:interactions-mode interactions-mode}]] + [:> interactions-menu* + {:interactions-mode interactions-mode}]] :comments [:& comments-menu] [:div {:class (stl/css :view-options)}]) diff --git a/frontend/src/app/main/ui/viewer/inspect/code.cljs b/frontend/src/app/main/ui/viewer/inspect/code.cljs index 2a1870070..ee15b210a 100644 --- a/frontend/src/app/main/ui/viewer/inspect/code.cljs +++ b/frontend/src/app/main/ui/viewer/inspect/code.cljs @@ -30,6 +30,7 @@ [app.util.webapi :as wapi] [beicon.v2.core :as rx] [cuerdas.core :as str] + [okulary.core :as l] [potok.v2.core :as ptk] [rumext.v2 :as mf])) @@ -49,13 +50,26 @@ ") +;; FIXME: this code need to be refactored +(defn get-viewer-objects + ([] + (let [route (deref refs/route) + page-id (:page-id (:query-params route))] + (get-viewer-objects page-id))) + ([page-id] + (l/derived + (fn [state] + (let [objects (refs/get-viewer-objects state page-id)] + objects)) + st/state =))) + (defn- use-objects [from] (let [page-objects-ref (mf/with-memo [from] (if (= from :workspace) ;; FIXME: fix naming consistency issues refs/workspace-page-objects - (refs/get-viewer-objects)))] + (get-viewer-objects)))] (mf/deref page-objects-ref))) (defn- shapes->images diff --git a/frontend/src/app/main/ui/viewer/interactions.cljs b/frontend/src/app/main/ui/viewer/interactions.cljs index ca5965656..c96535b0e 100644 --- a/frontend/src/app/main/ui/viewer/interactions.cljs +++ b/frontend/src/app/main/ui/viewer/interactions.cljs @@ -267,7 +267,8 @@ (when (= flow-id (:id current-flow)) [:span {:class (stl/css :icon)} i/tick])])]]]))) -(mf/defc interactions-menu +(mf/defc interactions-menu* + {::mf/props :obj} [{:keys [interactions-mode]}] (let [show-dropdown? (mf/use-state false) toggle-dropdown (mf/use-fn #(swap! show-dropdown? not)) @@ -281,6 +282,7 @@ (keyword))] (dom/stop-propagation event) (st/emit! (dv/set-interactions-mode mode)))))] + [:div {:on-click toggle-dropdown :class (stl/css :view-options)} [:span {:class (stl/css :dropdown-title)} (tr "viewer.header.interactions")] diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index 0f9aec6bf..cb7aba7fd 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -13,6 +13,7 @@ [app.common.types.shape.interactions :as ctsi] [app.main.data.viewer :as dv] [app.main.refs :as refs] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.shapes.bool :as bool] [app.main.ui.shapes.circle :as circle] @@ -26,7 +27,6 @@ [app.main.ui.shapes.text :as text] [app.util.dom :as dom] [app.util.object :as obj] - [app.util.router :as rt] [app.util.timers :as tm] [okulary.core :as l] [rumext.v2 :as mf])) @@ -34,8 +34,8 @@ (def base-frame-ctx (mf/create-context nil)) (def frame-offset-ctx (mf/create-context nil)) -(def viewer-interactions-show? - (l/derived :interactions-show? refs/viewer-local)) +(def ^:private ref:viewer-show-interactions + (l/derived :show-interactions refs/viewer-local)) (defn- find-relative-to-base-frame [shape objects overlays-ids base-frame] @@ -280,7 +280,7 @@ sems)))) (mf/defc interaction - [{:keys [shape interactions interactions-show?]}] + [{:keys [shape interactions show-interactions]}] (let [{:keys [x y width height]} (:selrect shape)] (when-not (empty? interactions) [:rect {:x (- x 1) @@ -289,8 +289,8 @@ :height (+ height 2) :fill "var(--color-accent-tertiary)" :stroke "var(--color-accent-tertiary)" - :stroke-width (if interactions-show? 1 0) - :fill-opacity (if interactions-show? 0.2 0) + :stroke-width (if show-interactions 1 0) + :fill-opacity (if show-interactions 0.2 0) :transform (gsh/transform-str shape)}]))) @@ -309,7 +309,7 @@ all-objects (or (unchecked-get props "all-objects") objects) base-frame (mf/use-ctx base-frame-ctx) frame-offset (mf/use-ctx frame-offset-ctx) - interactions-show? (mf/deref viewer-interactions-show?) + show-interactions (mf/deref ref:viewer-show-interactions) overlays (mf/deref refs/viewer-overlays) interactions (:interactions shape) svg-element? (and (= :svg-raw (:type shape)) @@ -355,7 +355,7 @@ [:& interaction {:shape shape :interactions interactions - :interactions-show? interactions-show?}]] + :show-interactions show-interactions}]] ;; Don't wrap svg elements inside a otherwise some can break [:& component {:shape shape diff --git a/frontend/src/app/main/ui/viewer/share_link.cljs b/frontend/src/app/main/ui/viewer/share_link.cljs index 0bac1bac4..ddda62a0f 100644 --- a/frontend/src/app/main/ui/viewer/share_link.cljs +++ b/frontend/src/app/main/ui/viewer/share_link.cljs @@ -16,12 +16,12 @@ [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] [app.main.refs :as refs] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.components.select :refer [select]] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] - [app.util.router :as rt] [app.util.webapi :as wapi] [potok.v2.core :as ptk] [rumext.v2 :as mf])) @@ -69,17 +69,16 @@ (= (:pages %) pages)) slinks)] (when slink - (let [pparams (:path-params route) - page-id (d/seek #(contains? (:pages slink) %) page-ids) - qparams (-> (:query-params route) + (let [page-id (d/seek #(contains? (:pages slink) %) page-ids) + params (-> (:query-params route) (assoc :share-id (:id slink)) (assoc :page-id page-id) (assoc :index "0")) - qparams (if (nil? zoom-type) - (dissoc qparams :zoom) - (assoc qparams :zoom zoom-type)) + params (if (nil? zoom-type) + (dissoc params :zoom) + (assoc params :zoom zoom-type)) - href (rt/resolve router :viewer pparams qparams)] + href (rt/resolve router :viewer params)] (dm/str (assoc cf/public-uri :fragment href)))))) on-close diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs index d5104699c..88cbdc113 100644 --- a/frontend/src/app/main/ui/workspace.cljs +++ b/frontend/src/app/main/ui/workspace.cljs @@ -8,12 +8,10 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] - [app.main.data.modal :as modal] - [app.main.data.notifications :as ntf] + [app.main.data.common :as dcm] [app.main.data.persistence :as dps] [app.main.data.plugins :as dpl] [app.main.data.workspace :as dw] - [app.main.data.workspace.colors :as dc] [app.main.features :as features] [app.main.refs :as refs] [app.main.store :as st] @@ -46,7 +44,7 @@ [file-id] (l/derived (fn [state] (let [data (:workspace-data state)] - (and (:workspace-ready? state) + (and (:workspace-ready state) (= file-id (:current-file-id state)) (= file-id (:id data))))) st/state)) @@ -125,14 +123,17 @@ :file file :page-id page-id}]])])) -(mf/defc workspace-loader +(mf/defc workspace-loader* + {::mf/props :obj + ::mf/private true} [] [:> loader* {:title (tr "labels.loading") :class (stl/css :workspace-loader) :overlay true}]) -(mf/defc workspace-page - {::mf/wrap-props false} +(mf/defc workspace-page* + {::mf/props :obj + ::mf/private true} [{:keys [page-id file layout wglobal]}] (let [page-id (hooks/use-equal-memo page-id) page-ready* (mf/with-memo [page-id] @@ -147,18 +148,20 @@ (mf/with-effect [page-id] (if (some? page-id) (st/emit! (dw/initialize-page page-id)) - (st/emit! (dw/go-to-page))) + (st/emit! (dcm/go-to-workspace))) (fn [] (when (some? page-id) (st/emit! (dw/finalize-page page-id))))) + (if ^boolean page-ready? [:& workspace-content {:page-id page-id :file file :wglobal wglobal :layout layout}] - [:& workspace-loader]))) + [:& workspace-loader*]))) -(mf/defc workspace + +(mf/defc workspace* {::mf/wrap-props false ::mf/wrap [mf/memo]} [{:keys [project-id file-id page-id layout-name]}] @@ -168,9 +171,7 @@ team (mf/deref refs/team) file (mf/deref refs/workspace-file) - project (mf/deref refs/workspace-project) - team-id (:team-id project) file-name (:name file) permissions (:permissions team) @@ -192,36 +193,32 @@ ;; Setting the layout preset by its name (mf/with-effect [layout-name] - (st/emit! (dw/initialize-layout layout-name))) + (st/emit! (dw/initialize-workspace-layout layout-name))) (mf/with-effect [file-name] (when file-name (dom/set-html-title (tr "title.workspace" file-name)))) - (mf/with-effect [project-id file-id] - (st/emit! (dw/initialize-file project-id file-id)) + (mf/with-effect [file-id] + (st/emit! (dw/initialize-workspace file-id)) (fn [] (st/emit! ::dps/force-persist - (dc/stop-picker) - (modal/hide) - (ntf/hide) - (dw/finalize-file project-id file-id)))) + (dw/finalize-workspace file-id)))) - [:& (mf/provider ctx/current-file-id) {:value file-id} - [:& (mf/provider ctx/current-project-id) {:value project-id} - [:& (mf/provider ctx/current-team-id) {:value team-id} - [:& (mf/provider ctx/current-page-id) {:value page-id} - [:& (mf/provider ctx/components-v2) {:value components-v2?} - [:& (mf/provider ctx/design-tokens) {:value design-tokens?} - [:& (mf/provider ctx/workspace-read-only?) {:value read-only?} - [:& (mf/provider ctx/permissions) {:value permissions} - [:section {:class (stl/css :workspace) - :style {:background-color background-color - :touch-action "none"}} - [:& context-menu] - (if ^boolean file-ready? - [:& workspace-page {:page-id page-id - :file file - :wglobal wglobal - :layout layout}] - [:& workspace-loader])]]]]]]]]])) + [:& (mf/provider ctx/current-project-id) {:value project-id} + [:& (mf/provider ctx/current-file-id) {:value file-id} + [:& (mf/provider ctx/current-page-id) {:value page-id} + [:& (mf/provider ctx/components-v2) {:value components-v2?} + [:& (mf/provider ctx/design-tokens) {:value design-tokens?} + [:& (mf/provider ctx/workspace-read-only?) {:value read-only?} + [:section {:class (stl/css :workspace) + :style {:background-color background-color + :touch-action "none"}} + [:& context-menu] + + (if ^boolean file-ready? + [:> workspace-page* {:page-id page-id + :file file + :wglobal wglobal + :layout layout}] + [:> workspace-loader* {}])]]]]]]])) diff --git a/frontend/src/app/main/ui/workspace/comments.cljs b/frontend/src/app/main/ui/workspace/comments.cljs index c34f44ad0..7fa35a9af 100644 --- a/frontend/src/app/main/ui/workspace/comments.cljs +++ b/frontend/src/app/main/ui/workspace/comments.cljs @@ -7,11 +7,13 @@ (ns app.main.ui.workspace.comments (:require-macros [app.main.style :as stl]) (:require - [app.main.data.comments :as dcm] + [app.main.data.comments :as dcmt] + [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.data.workspace :as dw] [app.main.data.workspace.comments :as dwcm] [app.main.refs :as refs] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.comments :as cmt] [app.main.ui.components.dropdown :refer [dropdown]] @@ -37,14 +39,14 @@ (let [mode (-> (dom/get-current-target event) (dom/get-data "value") (keyword))] - (st/emit! (dcm/update-filters {:mode mode}))))) + (st/emit! (dcmt/update-filters {:mode mode}))))) update-show (mf/use-fn (mf/deps cshow) (fn [] (let [mode (if (= :pending cshow) :all :pending)] - (st/emit! (dcm/update-filters {:show mode})))))] + (st/emit! (dcmt/update-filters {:show mode})))))] [:ul {:class (stl/css-case :comment-mode-dropdown true :viewer-dropdown from-viewer)} @@ -68,13 +70,13 @@ [:span {:class (stl/css :label)} (tr "labels.hide-resolved-comments")] [:span {:class (stl/css :icon)} i/tick]]])) -(mf/defc comments-sidebar +(mf/defc comments-sidebar* {::mf/props :obj} - [{:keys [users threads page-id from-viewer]}] + [{:keys [profiles threads page-id from-viewer]}] (let [threads-map (mf/deref refs/threads-ref) profile (mf/deref refs/profile) - users-refs (mf/deref refs/current-file-comments-users) - users (or users users-refs) + profiles' (mf/deref refs/profiles) + profiles (or profiles profiles') local (mf/deref refs/comments-local) state* (mf/use-state false) @@ -84,7 +86,7 @@ (->> (vals threads-map) (sort-by :modified-at) (reverse) - (dcm/apply-filters local profile)) + (dcmt/apply-filters local profile)) threads) close-section @@ -92,12 +94,12 @@ (mf/deps from-viewer) (fn [] (if from-viewer - (st/emit! (dcm/update-options {:show-sidebar? false})) + (st/emit! (dcmt/update-options {:show-sidebar? false})) (st/emit! (dw/clear-edition-mode) (dw/deselect-all true))))) tgroups (->> threads - (dcm/group-threads-by-page)) + (dcmt/group-threads-by-page)) page-id (or page-id (mf/use-ctx ctx/current-page-id)) @@ -112,14 +114,16 @@ (mf/deps page-id) (fn [thread] (when (not= page-id (:page-id thread)) - (st/emit! (dw/go-to-page (:page-id thread)))) + (st/emit! (dcm/go-to-workspace :page-id (:page-id thread) + ::rt/new-window true))) (tm/schedule (fn [] (st/emit! (when (not= page-id (:page-id thread)) (dw/select-for-drawing :comments)) (dwcm/center-to-comment-thread thread) - (-> (dcm/open-thread thread) + (-> (dcmt/open-thread thread) (with-meta {::ev/origin "workspace"})))))))] + [:div {:class (stl/css-case :comments-section true :from-viewer from-viewer)} [:div {:class (stl/css-case :comments-section-title true @@ -149,12 +153,12 @@ [:& cmt/comment-thread-group {:group (first tgroups) :on-thread-click on-thread-click - :users users}] + :profiles profiles}] (for [tgroup (rest tgroups)] [:& cmt/comment-thread-group {:group tgroup :on-thread-click on-thread-click - :users users + :profiles profiles :key (:page-id tgroup)}])] [:div {:class (stl/css :thread-group-placeholder)} diff --git a/frontend/src/app/main/ui/workspace/left_header.cljs b/frontend/src/app/main/ui/workspace/left_header.cljs index 1055426db..3694fad7a 100644 --- a/frontend/src/app/main/ui/workspace/left_header.cljs +++ b/frontend/src/app/main/ui/workspace/left_header.cljs @@ -8,10 +8,12 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] + [app.main.data.common :as dcm] [app.main.data.modal :as modal] [app.main.data.workspace :as dw] [app.main.data.workspace.colors :as dc] [app.main.refs :as refs] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.context :as ctx] [app.main.ui.icons :as i] @@ -19,21 +21,20 @@ [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.router :as rt] [cuerdas.core :as str] [rumext.v2 :as mf])) ;; --- Header Component (mf/defc left-header - {::mf/wrap-props false} + {::mf/props :obj} [{:keys [file layout project page-id class]}] (let [profile (mf/deref refs/profile) file-id (:id file) file-name (:name file) - project-id (:id project) team-id (:team-id project) shared? (:is-shared file) + read-only? (mf/use-ctx ctx/workspace-read-only?) editing* (mf/use-state false) @@ -69,22 +70,20 @@ go-back (mf/use-fn - (mf/deps project) (fn [] (close-modals) + ;; FIXME: move set-mode to uri? (st/emit! (dw/set-options-mode :design) - (dw/go-to-dashboard project)))) + (dcm/go-to-dashboard-recent)))) nav-to-project (mf/use-fn - (mf/deps team-id project-id) - #(st/emit! (rt/nav-new-window* {:rname :dashboard-files - :path-params {:team-id team-id - :project-id project-id}})))] + #(st/emit! (dcm/go-to-dashboard-files ::rt/new-window true)))] (mf/with-effect [editing?] (when ^boolean editing? (dom/select-text! (mf/ref-val input-ref)))) + [:header {:class (dm/str class " " (stl/css :workspace-header-left))} [:a {:on-click go-back :class (stl/css :main-icon)} i/logo-icon] diff --git a/frontend/src/app/main/ui/workspace/libraries.cljs b/frontend/src/app/main/ui/workspace/libraries.cljs index ead9db735..741e4fb6b 100644 --- a/frontend/src/app/main/ui/workspace/libraries.cljs +++ b/frontend/src/app/main/ui/workspace/libraries.cljs @@ -17,6 +17,7 @@ [app.config :as cf] [app.main.data.modal :as modal] [app.main.data.profile :as du] + [app.main.data.team :as dtm] [app.main.data.workspace.colors :as mdc] [app.main.data.workspace.libraries :as dwl] [app.main.refs :as refs] @@ -103,10 +104,10 @@ [:li {:class (stl/css :element-count)} (tr "workspace.libraries.typography" typography-count)])]) - -(mf/defc libraries-tab - {::mf/wrap-props false} - [{:keys [file-id shared? linked-libraries shared-libraries]}] +(mf/defc libraries-tab* + {::mf/props :obj + ::mf/private true} + [{:keys [file-id is-shared linked-libraries shared-libraries]}] (let [search-term* (mf/use-state "") search-term (deref search-term*) library-ref (mf/with-memo [file-id] @@ -127,7 +128,7 @@ shared-libraries (mf/with-memo [shared-libraries linked-libraries file-id search-term] (when shared-libraries - (->> shared-libraries + (->> (vals shared-libraries) (remove #(= (:id %) file-id)) (remove #(contains? linked-libraries (:id %))) (filter #(matches-search (:name %) search-term)) @@ -219,7 +220,7 @@ :graphics-count (count media) :colors-count (count colors) :typography-count (count typographies)}]]] - (if ^boolean shared? + (if ^boolean is-shared [:input {:class (stl/css :item-unpublish) :type "button" :value (tr "common.unpublish") @@ -348,8 +349,9 @@ :colors colors :typographies typographies}])) -(mf/defc updates-tab - {::mf/wrap-props false} +(mf/defc updates-tab* + {::mf/props :obj + ::mf/private true} [{:keys [file-id file-data libraries]}] (let [summary?* (mf/use-state true) summary? (deref summary?*) @@ -487,11 +489,9 @@ {::mf/register modal/components ::mf/register-as :libraries-dialog} [{:keys [starting-tab] :as props :or {starting-tab :libraries}}] - (let [project (mf/deref refs/workspace-project) - file-data (mf/deref refs/workspace-data) + (let [file-data (mf/deref refs/workspace-data) file (mf/deref ref:workspace-file) - team-id (:team-id project) file-id (:id file) shared? (:is-shared file) @@ -499,37 +499,44 @@ libraries (mf/with-memo [libraries] (d/removem (fn [[_ val]] (:is-indirect val)) libraries)) - ;; NOTE: we really don't need react on shared files shared-libraries - (mf/deref refs/workspace-shared-files) + (mf/deref refs/shared-files) close-dialog-outside - (mf/use-fn (fn [event] - (when (= (dom/get-target event) (dom/get-current-target event)) - (modal/hide!)))) + (mf/use-fn + (fn [event] + (when (= (dom/get-target event) (dom/get-current-target event)) + (modal/hide!)))) close-dialog - (mf/use-fn (fn [_] - (modal/hide!) - (modal/disallow-click-outside!))) + (mf/use-fn + (fn [_] + (modal/hide!) + (modal/disallow-click-outside!))) + + libraries-tab + (mf/html [:> libraries-tab* + {:file-id file-id + :is-shared shared? + :linked-libraries libraries + :shared-libraries shared-libraries}]) + + updates-tab + (mf/html [:> updates-tab* + {:file-id file-id + :file-data file-data + :libraries libraries}]) tabs #js [#js {:label (tr "workspace.libraries.libraries") :id "libraries" - :content (mf/html [:& libraries-tab {:file-id file-id - :shared? shared? - :linked-libraries libraries - :shared-libraries shared-libraries}])} - + :content libraries-tab} #js {:label (tr "workspace.libraries.updates") :id "updates" - :content (mf/html [:& updates-tab {:file-id file-id - :file-data file-data - :libraries libraries}])}]] + :content updates-tab}]] - (mf/with-effect [team-id] - (when team-id - (st/emit! (dwl/fetch-shared-files {:team-id team-id})))) + (mf/with-effect [] + (st/emit! (dtm/fetch-shared-files))) [:div {:class (stl/css :modal-overlay) :on-click close-dialog-outside :data-testid "libraries-modal"} [:div {:class (stl/css :modal-dialog)} diff --git a/frontend/src/app/main/ui/workspace/main_menu.cljs b/frontend/src/app/main/ui/workspace/main_menu.cljs index f30710419..18722fc68 100644 --- a/frontend/src/app/main/ui/workspace/main_menu.cljs +++ b/frontend/src/app/main/ui/workspace/main_menu.cljs @@ -37,7 +37,6 @@ [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.router :as rt] [beicon.v2.core :as rx] [potok.v2.core :as ptk] [rumext.v2 :as mf])) @@ -68,7 +67,7 @@ (mf/use-fn #(dom/open-new-window "https://penpot.app/terms")) nav-to-feedback - (mf/use-fn #(st/emit! (rt/nav-new-window* {:rname :settings-feedback}))) + (mf/use-fn #(st/emit! (dcm/go-to-feedback))) plugins? (features/active-feature? @st/state "plugins/runtime") @@ -540,9 +539,8 @@ on-pin-version (mf/use-fn - (mf/deps file-id) (fn [_] - (st/emit! (dwv/create-version file-id)))) + (st/emit! (dwv/create-version)))) on-pin-version-key-down (mf/use-fn diff --git a/frontend/src/app/main/ui/workspace/presence.cljs b/frontend/src/app/main/ui/workspace/presence.cljs index 6a071fbda..7e22276be 100644 --- a/frontend/src/app/main/ui/workspace/presence.cljs +++ b/frontend/src/app/main/ui/workspace/presence.cljs @@ -32,14 +32,14 @@ (mf/defc active-sessions {::mf/memo true} [] - (let [users (mf/deref refs/users) - presence (mf/deref refs/workspace-presence) + (let [profiles (mf/deref refs/profiles) + presence (mf/deref refs/workspace-presence) - sessions (vals presence) - num-sessions (count sessions) + sessions (vals presence) + num-sessions (count sessions) - open* (mf/use-state false) - open? (and ^boolean (deref open*) (> num-sessions 2)) + open* (mf/use-state false) + open? (and ^boolean (deref open*) (> num-sessions 2)) on-open (mf/use-fn (fn [] @@ -61,7 +61,7 @@ [:& session-widget {:color (:color session) :index 0 - :profile (get users (:profile-id session)) + :profile (get profiles (:profile-id session)) :key (dm/str (:id session))}])]]) [:button {:class (stl/css-case :active-users true) @@ -74,5 +74,5 @@ [:& session-widget {:color (:color session) :index index - :profile (get users (:profile-id session)) + :profile (get profiles (:profile-id session)) :key (dm/str (:id session))}])]]])) diff --git a/frontend/src/app/main/ui/workspace/right_header.cljs b/frontend/src/app/main/ui/workspace/right_header.cljs index d308b4d13..cb1d2d930 100644 --- a/frontend/src/app/main/ui/workspace/right_header.cljs +++ b/frontend/src/app/main/ui/workspace/right_header.cljs @@ -8,6 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.config :as cf] + [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.shortcuts :as scd] @@ -169,7 +170,7 @@ (let [params {:page-id page-id :file-id file-id :section "interactions"}] - (st/emit! (dw/go-to-viewer params))))) + (st/emit! (dcm/go-to-viewer params))))) active-comments (mf/use-fn diff --git a/frontend/src/app/main/ui/workspace/sidebar.cljs b/frontend/src/app/main/ui/workspace/sidebar.cljs index cc9a9c550..80f8a0379 100644 --- a/frontend/src/app/main/ui/workspace/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar.cljs @@ -8,14 +8,16 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] + [app.main.data.common :as dcm] [app.main.data.workspace :as dw] + [app.main.features :as features] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.context :as muc] [app.main.ui.ds.foundations.assets.icon :refer [icon*]] [app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]] [app.main.ui.hooks.resize :refer [use-resize-hook]] - [app.main.ui.workspace.comments :refer [comments-sidebar]] + [app.main.ui.workspace.comments :refer [comments-sidebar*]] [app.main.ui.workspace.left-header :refer [left-header]] [app.main.ui.workspace.right-header :refer [right-header]] [app.main.ui.workspace.sidebar.assets :refer [assets-toolbox]] @@ -47,36 +49,44 @@ (mf/defc left-sidebar {::mf/wrap [mf/memo] - ::mf/wrap-props false} + ::mf/props :obj} [{:keys [layout file page-id] :as props}] (let [options-mode (mf/deref refs/options-mode-global) + project (mf/deref refs/project) + + design-tokens? (features/use-feature "design-tokens/v1") mode-inspect? (= options-mode :inspect) - project (mf/deref refs/workspace-project) - - design-tokens? (mf/use-ctx muc/design-tokens) - - section (cond (or mode-inspect? (contains? layout :layers)) :layers - (contains? layout :assets) :assets - (contains? layout :tokens) :tokens) - shortcuts? (contains? layout :shortcuts) show-debug? (contains? layout :debug-panel) - {on-pointer-down :on-pointer-down on-lost-pointer-capture :on-lost-pointer-capture on-pointer-move :on-pointer-move parent-ref :parent-ref size :size} + section (cond + (or mode-inspect? (contains? layout :layers)) :layers + (contains? layout :assets) :assets + (contains? layout :tokens) :tokens) + + {on-pointer-down :on-pointer-down + on-lost-pointer-capture :on-lost-pointer-capture + on-pointer-move :on-pointer-move + parent-ref :parent-ref + size :size} (use-resize-hook :left-sidebar 275 275 500 :x false :left) - {on-pointer-down-pages :on-pointer-down on-lost-pointer-capture-pages :on-lost-pointer-capture on-pointer-move-pages :on-pointer-move size-pages-opened :size} + {on-pointer-down-pages :on-pointer-down + on-lost-pointer-capture-pages :on-lost-pointer-capture + on-pointer-move-pages :on-pointer-move + size-pages-opened :size} (use-resize-hook :sitemap 200 38 400 :y false nil) show-pages? (mf/use-state true) - toggle-pages (mf/use-callback #(reset! show-pages? not)) - size-pages (mf/use-memo (mf/deps show-pages? size-pages-opened) (fn [] (if @show-pages? size-pages-opened 32))) + toggle-pages (mf/use-fn #(reset! show-pages? not)) + size-pages (mf/with-memo [show-pages? size-pages-opened] + (if @show-pages? size-pages-opened 32)) handle-collapse (mf/use-fn #(st/emit! (dw/toggle-layout-flag :collapse-left-sidebar))) on-tab-change - (mf/use-fn #(st/emit! (dw/go-to-layout (keyword %)))) + (mf/use-fn #(st/emit! (dcm/go-to-workspace :layout (keyword %)))) layers-tab (mf/html @@ -138,8 +148,12 @@ :global/four-row (> size 400)) :style #js {"--width" (dm/str size "px")}} - [:& left-header {:file file :layout layout :project project :page-id page-id - :class (stl/css :left-header)}] + [:& left-header + {:file file + :layout layout + :project project + :page-id page-id + :class (stl/css :left-header)}] [:div {:on-pointer-down on-pointer-down :on-lost-pointer-capture on-lost-pointer-capture @@ -234,7 +248,7 @@ [:& debug-shape-info] (true? is-comments?) - [:& comments-sidebar] + [:> comments-sidebar* {}] (true? is-history?) [:> tab-switcher* diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index 6c4d03a07..57dfc2e09 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -25,9 +25,10 @@ [cuerdas.core :as str] [rumext.v2 :as mf])) -(mf/defc assets-libraries +(mf/defc assets-libraries* {::mf/wrap [mf/memo] - ::mf/wrap-props false} + ::mf/props :obj + ::mf/private true} [{:keys [filters]}] (let [libraries (mf/deref refs/workspace-libraries) libraries (mf/with-memo [libraries] @@ -193,4 +194,4 @@ [:& (mf/provider cmm/assets-toggle-list-style) {:value toggle-list-style} [:* [:& assets-local-library {:filters filters}] - [:& assets-libraries {:filters filters}]]]]]])) + [:> assets-libraries* {:filters filters}]]]]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs index e64313bbf..6af530691 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs @@ -391,20 +391,18 @@ (do-update-remote-component)) do-show-in-assets - #(st/emit! (if components-v2 - (dw/show-component-in-assets component-id) - (dw/go-to-component component-id))) + #(st/emit! (dw/show-component-in-assets component-id)) do-create-annotation #(st/emit! (dw/set-annotations-id-for-create id)) do-show-local-component - #(st/emit! (dw/go-to-component component-id)) + #(st/emit! (dwl/go-to-local-component component-id)) + ;; When the show-remote is after a restore, the component may still be deleted do-show-remote-component - #(let [comp (find-component shape true)] ;; When the show-remote is after a restore, the component may still be deleted - (when comp - (st/emit! (dwl/nav-to-component-file library-id comp)))) + #(when-let [comp (find-component shape true)] + (st/emit! (dwl/go-to-component-file library-id comp))) do-show-component (fn [] diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs index 65e1e7d23..0b3a5d1c1 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs @@ -98,8 +98,8 @@ (fn [event] (dom/stop-propagation event) (if local - (st/emit! (dw/go-to-component component-id)) - (st/emit! (dwl/nav-to-component-file file-id component))))) + (st/emit! (dwl/go-to-local-component component-id)) + (st/emit! (dwl/go-to-component-file file-id component))))) on-drop (mf/use-fn @@ -491,9 +491,9 @@ (fn [event] (dom/stop-propagation event) (if local? - (st/emit! (dw/go-to-component current-component-id)) + (st/emit! (dwl/go-to-local-component :id current-component-id)) (let [component (d/seek #(= (:id %) current-component-id) components)] - (st/emit! (dwl/nav-to-component-file file-id component)))))) + (st/emit! (dwl/go-to-component-file file-id component)))))) on-asset-click (mf/use-fn (mf/deps groups on-asset-click) (partial on-asset-click groups))] diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs index 678c1a714..d069ae416 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs @@ -14,6 +14,7 @@ [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.undo :as dwu] [app.main.refs :as refs] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.components.title-bar :refer [title-bar]] [app.main.ui.context :as ctx] @@ -27,54 +28,53 @@ [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.router :as rt] [cuerdas.core :as str] [okulary.core :as l] [potok.v2.core :as ptk] [rumext.v2 :as mf])) -(def lens:open-status +(def ^:private ref:open-status (l/derived (l/in [:workspace-assets :open-status]) st/state)) -(def lens:selected +(def ^:private ref:selected (-> (l/in [:workspace-assets :selected]) (l/derived st/state))) -(mf/defc file-library-title - {::mf/wrap-props false} - [{:keys [open? local? project-id file-id page-id file-name]}] +(mf/defc file-library-title* + {::mf/props :obj} + [{:keys [is-open is-local file-id page-id file-name]}] (let [router (mf/deref refs/router) + team-id (mf/use-ctx ctx/current-team-id) url (rt/resolve router :workspace - {:project-id project-id - :file-id file-id} - {:page-id page-id}) + {:team-id team-id + :file-id file-id + :page-id page-id}) toggle-open (mf/use-fn - (mf/deps file-id open?) + (mf/deps file-id is-open) (fn [] - (st/emit! (dw/set-assets-section-open file-id :library (not open?))))) + (st/emit! (dw/set-assets-section-open file-id :library (not is-open))))) on-click (mf/use-fn (fn [ev] (dom/stop-propagation ev) - (st/emit! - (ptk/event ::ev/event {::ev/name "navigate-to-library-file"}))))] + (st/emit! (ptk/data-event ::ev/event {::ev/name "navigate-to-library-file"}))))] [:div {:class (stl/css-case :library-title true - :open open?)} + :open is-open)} [:& title-bar {:collapsable true - :collapsed (not open?) + :collapsed (not is-open) :all-clickable true :on-collapsed toggle-open - :title (if local? + :title (if is-local (mf/html [:div {:class (stl/css :special-title)} (tr "workspace.assets.local-library")]) ;; Do we need to add shared info here? (mf/html [:div {:class (stl/css :special-title)} file-name]))} - (when-not local? + (when-not is-local [:span {:title (tr "workspace.assets.open-library")} [:a {:class (stl/css :file-link) :href (str "#" url) @@ -138,7 +138,7 @@ selected-lens (mf/with-memo [file-id] (-> (l/key file-id) - (l/derived lens:selected))) + (l/derived ref:selected))) selected (mf/deref selected-lens) @@ -329,17 +329,15 @@ (some #(> 60 (count %)) [filtered-components filtered-colors filtered-media filtered-typographies])))) (mf/defc file-library - {::mf/wrap-props false} + {::mf/props :obj} [{:keys [file local? default-open? filters]}] (let [file-id (:id file) file-name (:name file) - shared? (:is-shared file) - project-id (:project-id file) page-id (dm/get-in file [:data :pages 0]) open-status-ref (mf/with-memo [file-id] (-> (l/key file-id) - (l/derived lens:open-status))) + (l/derived ref:open-status))) open-status (mf/deref open-status-ref) force-open-lib? (force-lib-open? file-id filters) @@ -356,14 +354,12 @@ [:div {:class (stl/css :tool-window) :on-context-menu dom/prevent-default :on-click unselect-all} - [:& file-library-title - {:project-id project-id - :file-id file-id + [:> file-library-title* + {:file-id file-id :page-id page-id :file-name file-name - :open? open? - :local? local? - :shared? shared?}] + :is-open open? + :is-local local?}] (when ^boolean open? [:& file-library-content {:file file diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs index f088060e1..926f03e0f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs @@ -277,7 +277,6 @@ (assoc file :data data))))] (l/derived get-libraries st/state))) - (defn- find-common-path ([components] (let [paths (map (comp cfh/split-path :path) components)] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs index ca14bbab4..1a4213a26 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs @@ -12,9 +12,9 @@ [app.common.data.macros :as dm] [app.common.exceptions :as ex] [app.common.text :as txt] + [app.main.data.common :as dcm] [app.main.data.fonts :as fts] [app.main.data.shortcuts :as dsc] - [app.main.data.workspace :as dw] [app.main.features :as features] [app.main.fonts :as fonts] [app.main.refs :as refs] @@ -564,7 +564,7 @@ (mf/deps file-id) (fn [] (when file-id - (st/emit! (dw/navigate-to-library file-id))))) + (st/emit! (dcm/go-to-workspace :file-id file-id))))) on-key-down (mf/use-fn diff --git a/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs b/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs index f30af0735..0486d9249 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs @@ -9,6 +9,7 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] + [app.main.data.common :as dcm] [app.main.data.modal :as modal] [app.main.data.workspace :as dw] [app.main.refs :as refs] @@ -34,7 +35,7 @@ (let [input-ref (mf/use-ref) id (:id page) delete-fn (mf/use-fn (mf/deps id) #(st/emit! (dw/delete-page id))) - navigate-fn (mf/use-fn (mf/deps id) #(st/emit! :interrupt (dw/go-to-page id))) + navigate-fn (mf/use-fn (mf/deps id) #(st/emit! :interrupt (dcm/go-to-workspace :page-id id))) read-only? (mf/use-ctx ctx/workspace-read-only?) on-delete diff --git a/frontend/src/app/main/ui/workspace/sidebar/versions.cljs b/frontend/src/app/main/ui/workspace/sidebar/versions.cljs index b96e99924..2e39285a0 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/versions.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/versions.cljs @@ -226,11 +226,11 @@ (mf/defc versions-toolbox [] - (let [users (mf/deref refs/users) - profile (mf/deref refs/profile) - project-id (mf/deref refs/current-project-id) - file-id (mf/deref refs/current-file-id) - expanded (mf/use-state #{}) + (let [profiles (mf/deref refs/profiles) + profile (mf/deref refs/profile) + + expanded (mf/use-state #{}) + {:keys [status data editing]} (mf/deref versions) @@ -242,7 +242,6 @@ (fn [] (into #{} (keep (fn [{:keys [created-by profile-id]}] (when (= "user" created-by) profile-id))) data))) - data (mf/use-memo (mf/deps @versions) @@ -257,7 +256,7 @@ handle-create-version (mf/use-fn (fn [] - (st/emit! (dwv/create-version file-id)))) + (st/emit! (dwv/create-version)))) handle-toggle-expand (mf/use-fn @@ -271,14 +270,12 @@ handle-rename-version (mf/use-fn - (mf/deps file-id) (fn [id label] - (st/emit! (dwv/rename-version file-id id label)))) + (st/emit! (dwv/rename-version id label)))) handle-restore-version (mf/use-fn - (mf/deps project-id file-id) (fn [origin id] (st/emit! (ntf/dialog @@ -289,7 +286,7 @@ :callback #(st/emit! (ntf/hide))} {:label (tr "labels.restore") :type :primary - :callback #(st/emit! (dwv/restore-version project-id file-id id origin))}] + :callback #(st/emit! (dwv/restore-version id origin))}] :tag :restore-dialog)))) handle-restore-version-pinned @@ -306,15 +303,13 @@ handle-delete-version (mf/use-fn - (mf/deps file-id) (fn [id] - (st/emit! (dwv/delete-version file-id id)))) + (st/emit! (dwv/delete-version id)))) handle-pin-version (mf/use-fn - (mf/deps file-id) (fn [id] - (st/emit! (dwv/pin-version file-id id)))) + (st/emit! (dwv/pin-version id)))) handle-change-filter (mf/use-fn @@ -329,10 +324,8 @@ :else (st/emit! (dwv/update-version-state {:filter filter})))))] - (mf/with-effect - [file-id] - (when file-id - (st/emit! (dwv/init-version-state file-id)))) + (mf/with-effect [] + (st/emit! (dwv/init-version-state))) [:div {:class (stl/css :version-toolbox)} [:& select @@ -343,7 +336,7 @@ (->> data-users (keep (fn [id] - (let [{:keys [fullname]} (get users id)] + (let [{:keys [fullname]} (get profiles id)] (when (not= id (:id profile)) {:value id :label (tr "workspace.versions.filter.user" fullname)})))))) :on-change handle-change-filter}] @@ -374,7 +367,7 @@ [:& version-entry {:key idx-entry :entry entry :editing? (= (:id entry) editing) - :profile (get users (:profile-id entry)) + :profile (get profiles (:profile-id entry)) :on-rename-version handle-rename-version :on-restore-version handle-restore-version-pinned :on-delete-version handle-delete-version}] diff --git a/frontend/src/app/main/ui/workspace/viewport/comments.cljs b/frontend/src/app/main/ui/workspace/viewport/comments.cljs index 0e35d681a..1b226dcae 100644 --- a/frontend/src/app/main/ui/workspace/viewport/comments.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/comments.cljs @@ -36,7 +36,7 @@ pos-y (* (- vbox-y) zoom) profile (mf/deref refs/profile) - users (mf/deref refs/current-file-comments-users) + profiles (mf/deref refs/profiles) local (mf/deref refs/comments-local) positions-ref @@ -84,7 +84,7 @@ (when-let [thread (get threads-map id)] (when (seq (dcm/apply-filters local profile [thread])) [:& cmt/thread-comments {:thread (update-position positions thread) - :users users + :profiles profiles :viewport {:offset-x pos-x :offset-y pos-y :width (:width vport) :height (:height vport)} :zoom zoom}]))) diff --git a/frontend/src/app/main/ui/workspace/viewport/presence.cljs b/frontend/src/app/main/ui/workspace/viewport/presence.cljs index f2909b239..68e508ee6 100644 --- a/frontend/src/app/main/ui/workspace/viewport/presence.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/presence.cljs @@ -56,7 +56,7 @@ {::mf/props :obj} [{:keys [page-id]}] (let [counter (mf/use-state 0) - users (mf/deref refs/users) + profiles (mf/deref refs/profiles) sessions (mf/deref refs/workspace-presence) zoom (mf/deref refs/selected-zoom) @@ -73,5 +73,5 @@ [:& session-cursor {:session session :zoom zoom - :profile (get users (:profile-id session)) + :profile (get profiles (:profile-id session)) :key (dm/str (:id session))}]))) diff --git a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs index e7471f897..270a9f4a4 100644 --- a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs @@ -15,6 +15,7 @@ [app.common.types.shape-tree :as ctt] [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid] + [app.main.data.common :as dcm] [app.main.data.workspace :as dw] [app.main.data.workspace.interactions :as dwi] [app.main.refs :as refs] @@ -291,7 +292,7 @@ (when (dom/left-mouse? event) (dom/prevent-default event) (dom/stop-propagation event) - (st/emit! (dw/go-to-viewer params)))))) + (st/emit! (dcm/go-to-viewer params)))))) on-double-click (mf/use-fn diff --git a/frontend/src/app/plugins/api.cljs b/frontend/src/app/plugins/api.cljs index 7bc0fd1fc..2c7157f4c 100644 --- a/frontend/src/app/plugins/api.cljs +++ b/frontend/src/app/plugins/api.cljs @@ -18,12 +18,14 @@ [app.common.types.shape :as cts] [app.common.uuid :as uuid] [app.main.data.changes :as ch] + [app.main.data.common :as dcm] [app.main.data.workspace :as dw] [app.main.data.workspace.bool :as dwb] [app.main.data.workspace.colors :as dwc] [app.main.data.workspace.groups :as dwg] [app.main.data.workspace.media :as dwm] [app.main.data.workspace.selection :as dws] + [app.main.router :as rt] [app.main.store :as st] [app.plugins.events :as events] [app.plugins.file :as file] @@ -409,7 +411,7 @@ (let [params {:page-id (:current-page-id @st/state) :file-id (:current-file-id @st/state) :section "interactions"}] - (st/emit! (dw/go-to-viewer params)))) + (st/emit! (dcm/go-to-viewer params)))) :createPage (fn [] @@ -420,7 +422,7 @@ :openPage (fn [page] (let [id (obj/get page "$id")] - (st/emit! (dw/go-to-page id)))) + (st/emit! (dcm/go-to-workspace :page-id id ::rt/new-window true)))) :alignHorizontal (fn [shapes direction] diff --git a/frontend/src/app/plugins/file.cljs b/frontend/src/app/plugins/file.cljs index 22f4049cc..decedbf43 100644 --- a/frontend/src/app/plugins/file.cljs +++ b/frontend/src/app/plugins/file.cljs @@ -70,14 +70,15 @@ :restore (fn [] - (cond - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :restore "Plugin doesn't have 'content:write' permission") + (js/Promise. + (fn [resolve reject] + (cond + (not (r/check-permission plugin-id "content:write")) + (u/reject-not-valid reject :restore "Plugin doesn't have 'content:write' permission") - :else - (let [project-id (:current-project-id @st/state) - version-id (get @data :id)] - (st/emit! (dwv/restore-version project-id file-id version-id :plugin))))) + :else + (let [version-id (get @data :id)] + (st/emit! (dwv/restore-version-from-plugin file-id version-id resolve reject))))))) :remove (fn [] @@ -90,7 +91,8 @@ :else (let [version-id (:id @data)] (->> (rp/cmd! :delete-file-snapshot {:id version-id}) - (rx/subs! #(resolve) reject))))))) + (rx/map (constantly nil)) + (rx/subs! resolve reject))))))) :pin (fn [] diff --git a/frontend/src/app/plugins/page.cljs b/frontend/src/app/plugins/page.cljs index 65cbe093e..e73cd5683 100644 --- a/frontend/src/app/plugins/page.cljs +++ b/frontend/src/app/plugins/page.cljs @@ -14,10 +14,12 @@ [app.common.spec :as us] [app.common.uuid :as uuid] [app.main.data.comments :as dc] + [app.main.data.common :as dcm] [app.main.data.workspace :as dw] [app.main.data.workspace.guides :as dwgu] [app.main.data.workspace.interactions :as dwi] [app.main.repo :as rp] + [app.main.router :as-alias rt] [app.main.store :as st] [app.plugins.comments :as pc] [app.plugins.format :as format] @@ -266,7 +268,7 @@ (u/display-not-valid :openPage "Plugin doesn't have 'content:read' permission") :else - (st/emit! (dw/go-to-page id)))) + (st/emit! (dcm/go-to-workspace :page-id id ::rt/new-window true)))) :createFlow (fn [name frame] diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index 797946bae..f13553a26 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -17,6 +17,7 @@ [app.util.webapi :as wapi] [cuerdas.core :as str] [goog.dom :as dom] + [potok.v2.core :as ptk] [promesa.core :as p]) (:import goog.events.BrowserEvent)) @@ -853,3 +854,10 @@ measures (.measureText context-2d text)] {:descent (.-actualBoundingBoxDescent measures) :ascent (.-actualBoundingBoxAscent measures)})) + +(defmethod ptk/resolve ::focus-element + [_ {:keys [name]}] + (ptk/reify ::focus-element + ptk/EffectEvent + (effect [_ _ _] + (focus! (get-element name))))) diff --git a/frontend/src/debug.cljs b/frontend/src/debug.cljs index 98364b27e..8290e09e9 100644 --- a/frontend/src/debug.cljs +++ b/frontend/src/debug.cljs @@ -16,6 +16,7 @@ [app.common.types.file :as ctf] [app.common.uuid :as uuid] [app.main.data.changes :as dwc] + [app.main.data.common :as dcm] [app.main.data.dashboard.shortcuts] [app.main.data.preview :as dp] [app.main.data.viewer.shortcuts] @@ -232,7 +233,7 @@ (defn ^:export select-by-object-id [object-id] (let [[_ page-id shape-id _] (str/split object-id #"/")] - (st/emit! (dw/go-to-page (uuid/uuid page-id))) + (st/emit! (dcm/go-to-workspace :page-id (uuid/uuid page-id))) (st/emit! (dws/select-shape (uuid/uuid shape-id))))) (defn ^:export select-by-id From 80b763dd98d747d01eef27468372863a6ae18880 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 3 Dec 2024 21:31:39 +0100 Subject: [PATCH 9/9] :recycle: Add backward compatibility for old urls --- frontend/src/app/main/ui.cljs | 237 ++++++++++++++++++---------------- 1 file changed, 124 insertions(+), 113 deletions(-) diff --git a/frontend/src/app/main/ui.cljs b/frontend/src/app/main/ui.cljs index d1f197e18..ab1c753f5 100644 --- a/frontend/src/app/main/ui.cljs +++ b/frontend/src/app/main/ui.cljs @@ -6,16 +6,20 @@ (ns app.main.ui (:require + [app.common.data :as d] [app.config :as cf] + [app.main.data.common :as dcm] [app.main.data.team :as dtm] [app.main.refs :as refs] + [app.main.repo :as rp] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.context :as ctx] [app.main.ui.debug.icons-preview :refer [icons-preview]] + [app.main.ui.ds.product.loader :refer [loader*]] [app.main.ui.error-boundary :refer [error-boundary*]] [app.main.ui.exports.files] [app.main.ui.frame-preview :as frame-preview] - [app.main.ui.icons :as i] [app.main.ui.notifications :as notifications] [app.main.ui.onboarding.newsletter :refer [onboarding-newsletter]] [app.main.ui.onboarding.questions :refer [questions-modal]] @@ -24,6 +28,8 @@ [app.main.ui.static :as static] [app.util.dom :as dom] [app.util.i18n :refer [tr]] + [beicon.v2.core :as rx] + [potok.v2.core :as ptk] [rumext.v2 :as mf])) (def auth-page @@ -44,6 +50,79 @@ (def workspace-page (mf/lazy-component app.main.ui.workspace/workspace*)) +(mf/defc workspace-legacy-redirect* + {::mf/props :obj + ::mf/private true} + [{:keys [project-id file-id page-id layout]}] + (mf/with-effect [] + (->> (rp/cmd! :get-project {:id project-id}) + (rx/subs! (fn [{:keys [team-id]}] + (st/emit! (dcm/go-to-workspace :team-id team-id + :file-id file-id + :page-id page-id + :layout layout))) + ptk/handle-error))) + + [:> loader* + {:title (tr "labels.loading") + :overlay true}]) + +(mf/defc dashboard-legacy-redirect* + {::mf/props :obj + ::mf/private true} + [{:keys [section team-id project-id search-term plugin-url]}] + (let [section (case section + :dashboard-legacy-search + :dashboard-search + :dashboard-legacy-projects + :dashboard-recent + :dashboard-legacy-files + :dashboard-files + :dashboard-legacy-libraries + :dashboard-libraries + :dashboard-legacy-fonts + :dashboard-fonts + :dashboard-legacy-font-providers + :dashboard-font-providers + :dashboard-legacy-team-members + :dashboard-members + :dashboard-legacy-team-invitations + :dashboard-invitations + :dashboard-legacy-team-webhooks + :dashboard-webhooks + :dashboard-legacy-team-settings + :dashboard-settings)] + + (mf/with-effect [] + (let [params {:team-id team-id + :project-id project-id + :search-term search-term + :plugin plugin-url}] + (st/emit! (rt/nav section (d/without-nils params))))) + + [:> loader* + {:title (tr "labels.loading") + :overlay true}])) + +(mf/defc viewer-legacy-redirect* + {::mf/props :obj + ::mf/private true} + [{:keys [page-id file-id section index share-id interactions-mode frame-id share]}] + (mf/with-effect [] + (let [params {:page-id page-id + :file-id file-id + :section section + :index index + :share-id share-id + :interactions-mode interactions-mode + :frame-id frame-id + :share share}] + (st/emit! (rt/nav :viewer (d/without-nils params))))) + + [:> loader* + {:title (tr "labels.loading") + :overlay true}]) + (mf/defc team-container* {::mf/props :obj ::mf/private true} @@ -160,72 +239,9 @@ :plugin-url plugin-url :project-id project-id}]]]) - (:dashboard-legacy-search - :dashboard-legacy-projects - :dashboard-legacy-files - :dashboard-legacy-libraries - :dashboard-legacy-fonts - :dashboard-legacy-font-providers - :dashboard-legacy-team-members - :dashboard-legacy-team-invitations - :dashboard-legacy-team-webhooks - :dashboard-legacy-team-settings) - (let [team-id (some-> params :path :team-id uuid) - project-id (some-> params :path :project-id uuid) - search-term (some-> params :query :search-term) - plugin-url (some-> params :query :plugin) - section (case section - :dashboard-legacy-search - :dashboard-search - :dashboard-legacy-projects - :dashboard-recent - :dashboard-legacy-files - :dashboard-files - :dashboard-legacy-libraries - :dashboard-libraries - :dashboard-legacy-fonts - :dashboard-fonts - :dashboard-legacy-font-providers - :dashboard-font-providers - :dashboard-legacy-team-members - :dashboard-members - :dashboard-legacy-team-invitations - :dashboard-invitations - :dashboard-legacy-team-webhooks - :dashboard-webhooks - :dashboard-legacy-team-settings - :dashboard-settings)] - [:? - #_[:& app.main.ui.releases/release-notes-modal {:version "2.3"}] - #_[:& app.main.ui.onboarding/onboarding-templates-modal] - #_[:& app.main.ui.onboarding/onboarding-modal] - #_[:& app.main.ui.onboarding.team-choice/onboarding-team-modal] - - (cond - show-question-modal? - [:& questions-modal] - - show-newsletter-modal? - [:& onboarding-newsletter] - - show-team-modal? - [:& onboarding-team-modal {:go-to-team? true}] - - show-release-modal? - [:& release-notes-modal {:version (:main cf/version)}]) - - [:> team-container* {:team-id team-id} - [:> dashboard-page {:profile profile - :section section - :team-id team-id - :search-term search-term - :plugin-url plugin-url - :project-id project-id}]]]) - :workspace (let [params (get params :query) team-id (some-> params :team-id uuid) - project-id (some-> params :project-id uuid) file-id (some-> params :file-id uuid) page-id (some-> params :page-id uuid) layout (some-> params :layout keyword)] @@ -245,35 +261,7 @@ [:& release-notes-modal {:version (:main cf/version)}])) [:> team-container* {:team-id team-id} - [:> workspace-page {:project-id project-id - :team-id team-id - :file-id file-id - :page-id page-id - :layout-name layout - :key file-id}]]]) - - :workspace-legacy - (let [project-id (some-> params :path :project-id uuid) - file-id (some-> params :path :file-id uuid) - page-id (some-> params :query :page-id uuid) - layout (some-> params :query :layout keyword)] - [:? {} - (when (cf/external-feature-flag "onboarding-03" "test") - (cond - show-question-modal? - [:& questions-modal] - - show-newsletter-modal? - [:& onboarding-newsletter] - - show-team-modal? - [:& onboarding-team-modal {:go-to-team? false}] - - show-release-modal? - [:& release-notes-modal {:version (:main cf/version)}])) - - [:* - [:> workspace-page {:project-id project-id + [:> workspace-page {:team-id team-id :file-id file-id :page-id page-id :layout-name layout @@ -304,38 +292,61 @@ :interactions-mode imode :share share}]]) + + :workspace-legacy + (let [project-id (some-> params :path :project-id uuid) + file-id (some-> params :path :file-id uuid) + page-id (some-> params :query :page-id uuid) + layout (some-> params :query :layout keyword)] + + [:> workspace-legacy-redirect* + {:project-id project-id + :file-id file-id + :page-id page-id + :layout layout}]) + + (:dashboard-legacy-search + :dashboard-legacy-projects + :dashboard-legacy-files + :dashboard-legacy-libraries + :dashboard-legacy-fonts + :dashboard-legacy-font-providers + :dashboard-legacy-team-members + :dashboard-legacy-team-invitations + :dashboard-legacy-team-webhooks + :dashboard-legacy-team-settings) + (let [team-id (some-> params :path :team-id uuid) + project-id (some-> params :path :project-id uuid) + search-term (some-> params :query :search-term) + plugin-url (some-> params :query :plugin)] + [:> dashboard-legacy-redirect* + {:team-id team-id + :section section + :project-id project-id + :search-term search-term + :plugin-url plugin-url}]) + :viewer-legacy (let [{:keys [query-params path-params]} route {:keys [index share-id section page-id interactions-mode frame-id share] :or {section :interactions interactions-mode :show-on-click}} query-params {:keys [file-id]} path-params] - [:? {} - (if (:token query-params) - [:> static/error-container* {} - [:div.image i/detach] - [:div.main-message (tr "viewer.breaking-change.message")] - [:div.desc-message (tr "viewer.breaking-change.description")]] - [:> viewer-page - {:page-id page-id - :file-id file-id - :section section - :index index - :share-id share-id - :interactions-mode (keyword interactions-mode) - :show-interactions (case (keyword interactions-mode) - :hide false - :show true - :show-on-click false) - :frame-id frame-id - :share share}])]) + [:> viewer-legacy-redirect* + {:page-id page-id + :file-id file-id + :section section + :index index + :share-id share-id + :interactions-mode (keyword interactions-mode) + :frame-id frame-id + :share share}]) :frame-preview [:& frame-preview/frame-preview] nil)])) - (mf/defc app [] (let [route (mf/deref refs/route)