0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-13 15:31:26 -05:00

♻️ 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.
This commit is contained in:
Andrey Antukh 2024-12-03 18:23:41 +01:00
parent b2f02de5c1
commit 3e090b126e
90 changed files with 1617 additions and 1548 deletions

View file

@ -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

View file

@ -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))

View file

@ -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]

View file

@ -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)

View file

@ -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
}]

View file

@ -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
}]

View file

@ -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");
}

View file

@ -85,7 +85,7 @@ export class ViewerPage extends BaseWebSocketPage {
pageId = ViewerPage.anyPageId,
} = {}) {
await this.page.goto(
`/#/view/${fileId}?page-id=${pageId}&section=interactions&index=0`,
`/#/view?file-id=${fileId}&page-id=${pageId}&section=interactions&index=0`,
);
this.#ws = await this.waitForNotificationsWebSocket();

View file

@ -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();

View file

@ -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();
});

View file

@ -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

View file

@ -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)))))))))

View file

@ -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))))))

View file

@ -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))

View file

@ -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 "+"

View file

@ -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)

View file

@ -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]))

View file

@ -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))))))

View file

@ -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 @@

View file

@ -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)))))))

View file

@ -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))

View file

@ -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)))]))

View file

@ -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 _]

View file

@ -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

View file

@ -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"}

View file

@ -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}}]

View file

@ -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"

View file

@ -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]))

View file

@ -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]

View file

@ -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))))))))

View file

@ -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))))))

View file

@ -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)

View file

@ -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))

View file

@ -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
[]

View file

@ -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))

View file

@ -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}])])]]))

View file

@ -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)

View file

@ -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

View file

@ -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]))

View file

@ -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")]]]])

View file

@ -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]

View file

@ -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)}])]])

View file

@ -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}])]]))

View file

@ -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)}

View file

@ -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))))

View file

@ -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)))
[:*

View file

@ -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

View file

@ -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)))
[:*

View file

@ -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!

View file

@ -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))))

View file

@ -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")]

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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})))))))))

View file

@ -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

View file

@ -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)}

View file

@ -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))))))]
[:*

View file

@ -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}]))

View file

@ -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)}]]]))

View file

@ -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)}])

View file

@ -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 @@
</body>
</html>")
;; 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

View file

@ -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")]

View file

@ -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 <g> otherwise some can break
[:& component {:shape shape

View file

@ -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

View file

@ -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* {}])]]]]]]]))

View file

@ -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)}

View file

@ -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]

View file

@ -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)}

View file

@ -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

View file

@ -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))}])]]]))

View file

@ -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

View file

@ -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*

View file

@ -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}]]]]]]))

View file

@ -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 []

View file

@ -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))]

View file

@ -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

View file

@ -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)]

View file

@ -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

View file

@ -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

View file

@ -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}]

View file

@ -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}])))

View file

@ -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))}])))

View file

@ -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

View file

@ -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]

View file

@ -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 []

View file

@ -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]

View file

@ -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)))))

View file

@ -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