From 3e090b126ef190e745e0ccdd0f9d4d104384ef7a Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 3 Dec 2024 18:23:41 +0100 Subject: [PATCH] :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