♻️ 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 :share-links links
:libraries libs :libraries libs
:file file :file file
:team team :team (assoc team :permissions perms)
:permissions perms})) :permissions perms}))
(def schema:get-view-only-bundle (def schema:get-view-only-bundle

View file

@ -1010,6 +1010,9 @@
(def valid-safe-number? (def valid-safe-number?
(lazy-validator ::safe-number)) (lazy-validator ::safe-number))
(def valid-text?
(validator ::text))
(def check-safe-int! (def check-safe-int!
(check-fn ::safe-int)) (check-fn ::safe-int))

View file

@ -412,7 +412,6 @@
(recur (when continue? (rest styles)) taking? to result)) (recur (when continue? (rest styles)) taking? to result))
result)))) result))))
(defn content->text (defn content->text
"Given a root node of a text content extracts the texts with its associated styles" "Given a root node of a text content extracts the texts with its associated styles"
[content] [content]

View file

@ -18,11 +18,18 @@
java.nio.ByteBuffer))) java.nio.ByteBuffer)))
(defn uuid (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] [s]
#?(:clj (UUID/fromString s) #?(:clj (UUID/fromString s)
:cljs (c/uuid 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 (defn next
[] []
#?(:clj (UUIDv8/create) #?(: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() { async goToDashboard() {
await this.page.goto( await this.page.goto(
`#/dashboard/team/${DashboardPage.anyTeamId}/projects`, `#/dashboard/recent?team-id=${DashboardPage.anyTeamId}`,
); );
await expect(this.mainHeading).toBeVisible(); await expect(this.mainHeading).toBeVisible();
} }
async goToSecondTeamDashboard() { async goToSecondTeamDashboard() {
await this.page.goto( await this.page.goto(
`#/dashboard/team/${DashboardPage.secondTeamId}/projects`, `#/dashboard/recent?team-id=${DashboardPage.secondTeamId}`,
); );
} }
async goToSecondTeamMembersSection() { async goToSecondTeamMembersSection() {
await this.page.goto( await this.page.goto(
`#/dashboard/team/${DashboardPage.secondTeamId}/members`, `#/dashboard/members?team-id=${DashboardPage.secondTeamId}`,
); );
} }
async goToSecondTeamInvitationsSection() { async goToSecondTeamInvitationsSection() {
await this.page.goto( await this.page.goto(
`#/dashboard/team/${DashboardPage.secondTeamId}/invitations`, `#/dashboard/invitations?team-id=${DashboardPage.secondTeamId}`,
); );
} }
async goToSecondTeamWebhooksSection() { async goToSecondTeamWebhooksSection() {
await this.page.goto( await this.page.goto(
`#/dashboard/team/${DashboardPage.secondTeamId}/webhooks`, `#/dashboard/webhooks?team-id=${DashboardPage.secondTeamId}`,
); );
} }
async goToSecondTeamWebhooksSection() { async goToSecondTeamWebhooksSection() {
await this.page.goto( await this.page.goto(
`#/dashboard/team/${DashboardPage.secondTeamId}/webhooks`, `#/dashboard/webhooks?team-id=${DashboardPage.secondTeamId}`,
); );
} }
async goToSecondTeamSettingsSection() { async goToSecondTeamSettingsSection() {
await this.page.goto( await this.page.goto(
`#/dashboard/team/${DashboardPage.secondTeamId}/settings`, `#/dashboard/settings?team-id=${DashboardPage.secondTeamId}`,
); );
} }
async goToSearch() { async goToSearch() {
await this.page.goto(`#/dashboard/team/${DashboardPage.anyTeamId}/search`); await this.page.goto(
`#/dashboard/search?team-id=${DashboardPage.anyTeamId}`,
);
} }
async goToDrafts() { async goToDrafts() {
await this.page.goto( 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"); await expect(this.mainHeading).toHaveText("Drafts");
} }
async goToFonts() { 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"); await expect(this.mainHeading).toHaveText("Fonts");
} }

View file

@ -85,7 +85,7 @@ export class ViewerPage extends BaseWebSocketPage {
pageId = ViewerPage.anyPageId, pageId = ViewerPage.anyPageId,
} = {}) { } = {}) {
await this.page.goto( 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(); this.#ws = await this.waitForNotificationsWebSocket();

View file

@ -36,6 +36,14 @@ export class WorkspacePage extends BaseWebSocketPage {
"get-team?id=*", "get-team?id=*",
"workspace/get-team-default.json", "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( await BaseWebSocketPage.mockRPC(
page, page,
"get-profiles-for-file-comments?file-id=*", "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 anyProjectId = "c7ce0794-0992-8105-8004-38e630f7920b";
static anyFileId = "c7ce0794-0992-8105-8004-38f280443849"; static anyFileId = "c7ce0794-0992-8105-8004-38f280443849";
static anyPageId = "c7ce0794-0992-8105-8004-38f28044384a"; static anyPageId = "c7ce0794-0992-8105-8004-38f28044384a";
@ -83,7 +92,7 @@ export class WorkspacePage extends BaseWebSocketPage {
pageId = WorkspacePage.anyPageId, pageId = WorkspacePage.anyPageId,
} = {}) { } = {}) {
await this.page.goto( 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(); this.#ws = await this.waitForNotificationsWebSocket();

View file

@ -7,11 +7,7 @@ test.beforeEach(async ({ page }) => {
const workspacePage = new WorkspacePage(page); const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile(page); await workspacePage.setupEmptyFile(page);
await WorkspacePage.mockRPC( await WorkspacePage.mockRPC(page, "get-teams", "get-teams-role-viewer.json");
page,
"get-team?id=*",
"workspace/get-team-role-viewer.json",
);
await workspacePage.goToWorkspace(); await workspacePage.goToWorkspace();
}); });

View file

@ -12,14 +12,15 @@
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.common :as dcm]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.data.profile :as dp] [app.main.data.profile :as dp]
[app.main.data.team :as dtm] [app.main.data.team :as dtm]
[app.main.data.websocket :as ws] [app.main.data.websocket :as ws]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.router :as rt]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[app.util.storage :as storage] [app.util.storage :as storage]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[potok.v2.core :as ptk])) [potok.v2.core :as ptk]))
@ -45,8 +46,9 @@
(rx/of (rt/reload true)) (rx/of (rt/reload true))
(rx/of (rt/nav-raw :href redirect-href)))) (rx/of (rt/nav-raw :href redirect-href))))
(if-let [file-id (get props :welcome-file-id)] (if-let [file-id (get props :welcome-file-id)]
(rx/of (rt/nav' :workspace {:project-id (:default-project-id profile) (rx/of (dcm/go-to-workspace
:file-id file-id}) :file-id file-id
:team-id (:default-team-id profile))
(dp/update-profile-props {:welcome-file-id nil})) (dp/update-profile-props {:welcome-file-id nil}))
(let [teams (into #{} (map :id) teams) (let [teams (into #{} (map :id) teams)
@ -54,7 +56,7 @@
team-id (if (and team-id (contains? teams team-id)) team-id (if (and team-id (contains? teams team-id))
team-id team-id
(:default-team-id profile))] (: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 (ptk/reify ::logged-in
ev/Event ev/Event

View file

@ -603,3 +603,18 @@
(filter (fn [comment] (some #(= % (:frame-id comment)) frame-ids?))) (filter (fn [comment] (some #(= % (:frame-id comment)) frame-ids?)))
(map update-comment-thread-frame) (map update-comment-thread-frame)
(rx/from)))))) (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.common.types.team :as ctt]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.data.persistence :as-alias dps]
[app.main.features :as features] [app.main.features :as features]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.util.dom :as-alias dom]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
[app.util.router :as rt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[potok.v2.core :as ptk])) [potok.v2.core :as ptk]))
(declare go-to-dashboard-recent)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SHARE LINK ;; SHARE LINK
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -233,12 +237,165 @@
(watch [_ state _] (watch [_ state _]
(when (= :removed change) (when (= :removed change)
(let [message (tr "dashboard.removed-from-team" team-name) (let [message (tr "dashboard.removed-from-team" team-name)
profile (:profile state)] team-id (-> state :profile :default-team-id)]
(rx/concat (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)) (->> (rx/of (ntf/info message))
;; Delay so the navigation can finish ;; Delay so the navigation can finish
(rx/delay 250)))))))) (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.logging :as log]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.uuid :as uuid] [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.event :as ev]
[app.main.data.fonts :as df] [app.main.data.fonts :as df]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.websocket :as dws] [app.main.data.websocket :as dws]
[app.main.features :as features] [app.main.features :as features]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[app.util.sse :as sse] [app.util.sse :as sse]
[app.util.storage :as storage]
[app.util.time :as dt] [app.util.time :as dt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[clojure.set :as set] [clojure.set :as set]
@ -115,55 +112,6 @@
(rx/map (fn [result] (rx/map (fn [result]
#(assoc % :search-result 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 ;; --- EVENT: recent-files
(defn recent-files-fetched (defn recent-files-fetched
@ -630,133 +578,6 @@
(rx/tap on-success) (rx/tap on-success)
(rx/catch on-error)))))) (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 (defn create-element
[] []
(ptk/reify ::create-element (ptk/reify ::create-element
@ -793,8 +614,7 @@
(watch [_ state _] (watch [_ state _]
(let [[file-id :as files] (get state :selected-files)] (let [[file-id :as files] (get state :selected-files)]
(if (= 1 (count files)) (if (= 1 (count files))
(let [file (dm/get-in state [files file-id])] (rx/of (dcm/go-to-workspace :file-id file-id))
(rx/of (go-to-workspace file)))
(rx/empty)))))) (rx/empty))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -806,13 +626,13 @@
(ptk/reify ::handle-change-team-role (ptk/reify ::handle-change-team-role
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ _ _]
(rx/of (dc/change-team-role params) (rx/of (dcm/change-team-role params)
(modal/hide))))) (modal/hide)))))
(defn- process-message (defn- process-message
[{:keys [type] :as msg}] [{:keys [type] :as msg}]
(case type (case type
:notification (dc/handle-notification msg) :notification (dcm/handle-notification msg)
:team-role-change (handle-change-team-role 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)) nil))

View file

@ -6,6 +6,7 @@
(ns app.main.data.dashboard.shortcuts (ns app.main.data.dashboard.shortcuts
(:require (:require
[app.main.data.common :as dcm]
[app.main.data.dashboard :as dd] [app.main.data.dashboard :as dd]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.profile :as du] [app.main.data.profile :as du]
@ -16,17 +17,17 @@
{:go-to-search {:tooltip (ds/meta "F") {:go-to-search {:tooltip (ds/meta "F")
:command (ds/c-mod "f") :command (ds/c-mod "f")
:subsections [:navigation-dashboard] :subsections [:navigation-dashboard]
:fn #(st/emit! (dd/go-to-search))} :fn #(st/emit! (dcm/go-to-dashboard-search))}
:go-to-drafts {:tooltip "G D" :go-to-drafts {:tooltip "G D"
:command "g d" :command "g d"
:subsections [:navigation-dashboard] :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" :go-to-libs {:tooltip "G L"
:command "g l" :command "g l"
:subsections [:navigation-dashboard] :subsections [:navigation-dashboard]
:fn #(st/emit! (dd/go-to-libs))} :fn #(st/emit! (dcm/go-to-dashboard-libraries))}
:create-new-project {:tooltip "+" :create-new-project {:tooltip "+"
:command "+" :command "+"

View file

@ -64,7 +64,7 @@
(rx/merge (rx/merge
(let [stopper (rx/filter (ptk/type? ::hide) stream)] (let [stopper (rx/filter (ptk/type? ::hide) stream)]
(->> stream (->> stream
(rx/filter (ptk/type? :app.util.router/navigate)) (rx/filter (ptk/type? :app.main.router/navigate))
(rx/map (fn [_] (hide))) (rx/map (fn [_] (hide)))
(rx/take-until stopper))) (rx/take-until stopper)))
(when (:timeout data) (when (:timeout data)

View file

@ -16,9 +16,9 @@
[app.main.data.media :as di] [app.main.data.media :as di]
[app.main.data.team :as-alias dtm] [app.main.data.team :as-alias dtm]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.router :as rt]
[app.plugins.register :as plugins.register] [app.plugins.register :as plugins.register]
[app.util.i18n :as i18n] [app.util.i18n :as i18n]
[app.util.router :as rt]
[app.util.storage :as storage] [app.util.storage :as storage]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[potok.v2.core :as ptk])) [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 (ns app.main.data.team
(:require (:require
[app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.logging :as log] [app.common.logging :as log]
[app.common.schema :as sm] [app.common.schema :as sm]
@ -16,7 +17,7 @@
[app.main.data.media :as di] [app.main.data.media :as di]
[app.main.features :as features] [app.main.features :as features]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.util.router :as rt] [app.main.router :as rt]
[app.util.storage :as storage] [app.util.storage :as storage]
[app.util.webapi :as wapi] [app.util.webapi :as wapi]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
@ -57,7 +58,9 @@
(ptk/reify ::members-fetched (ptk/reify ::members-fetched
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (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 (defn fetch-members
[] []
@ -145,6 +148,7 @@
(if (= team-id' team-id) (if (= team-id' team-id)
(-> state (-> state
(dissoc :current-team-id) (dissoc :current-team-id)
(dissoc :shared-files)
(dissoc :fonts)) (dissoc :fonts))
state))))) state)))))
@ -220,6 +224,26 @@
(->> (rp/cmd! :get-webhooks {:team-id team-id}) (->> (rp/cmd! :get-webhooks {:team-id team-id})
(rx/map (partial webhooks-fetched 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 ;; Data Modification
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -334,7 +358,7 @@
(when reassign-to (when reassign-to
(assert (uuid? reassign-to) "expect a valid uuid for `reassign-to`")) (assert (uuid? reassign-to) "expect a valid uuid for `reassign-to`"))
(ptk/reify ::leave-team (ptk/reify ::leave-current-team
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [team-id (get state :current-team-id) (let [team-id (get state :current-team-id)
@ -405,7 +429,7 @@
(->> (rp/cmd! :get-team-invitation-token params) (->> (rp/cmd! :get-team-invitation-token params)
(rx/map (fn [params] (rx/map (fn [params]
(rt/resolve router :auth-verify-token {} params))) (rt/resolve router :auth-verify-token params)))
(rx/map (fn [fragment] (rx/map (fn [fragment]
(assoc cf/public-uri :fragment fragment))) (assoc cf/public-uri :fragment fragment)))
(rx/tap (fn [uri] (rx/tap (fn [uri]
@ -532,3 +556,4 @@

View file

@ -15,13 +15,15 @@
[app.common.transit :as t] [app.common.transit :as t]
[app.common.types.shape-tree :as ctt] [app.common.types.shape-tree :as ctt]
[app.common.types.shape.interactions :as ctsi] [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.event :as ev]
[app.main.data.fonts :as df] [app.main.data.fonts :as df]
[app.main.features :as features] [app.main.features :as features]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.router :as rt]
[app.util.globals :as ug] [app.util.globals :as ug]
[app.util.router :as rt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[potok.v2.core :as ptk])) [potok.v2.core :as ptk]))
@ -32,7 +34,7 @@
{:zoom 1 {:zoom 1
:fullscreen? false :fullscreen? false
:interactions-mode :show-on-click :interactions-mode :show-on-click
:interactions-show? false :show-interactions false
:comments-mode :all :comments-mode :all
:comments-show :unresolved :comments-show :unresolved
:selected #{} :selected #{}
@ -55,7 +57,7 @@
[:page-id {:optional true} ::sm/uuid]]) [:page-id {:optional true} ::sm/uuid]])
(defn initialize (defn initialize
[{:keys [file-id share-id interactions-show?] :as params}] [{:keys [file-id share-id] :as params}]
(dm/assert! (dm/assert!
"expected valid params" "expected valid params"
(sm/check schema:initialize params)) (sm/check schema:initialize params))
@ -65,13 +67,13 @@
(update [_ state] (update [_ state]
(-> state (-> state
(assoc :current-file-id file-id) (assoc :current-file-id file-id)
(assoc :current-share-id share-id)
(update :viewer-local (update :viewer-local
(fn [lstate] (fn [lstate]
(if (nil? lstate) (if (nil? lstate)
default-local-state default-local-state
lstate))) lstate)))
(assoc-in [:viewer-local :share-id] share-id) (assoc-in [:viewer-local :share-id] share-id)))
(assoc-in [:viewer-local :interactions-show?] interactions-show?)))
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
@ -256,12 +258,9 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [zoom-type (get-in state [:viewer-local :zoom-type]) (let [zoom-type (get-in state [:viewer-local :zoom-type])
route (:route state) params (rt/get-params state)]
screen (-> route :data :name keyword)
qparams (:query-params route)
pparams (:path-params route)]
(rx/of (rt/nav screen pparams (assoc qparams :zoom zoom-type))))))) (rx/of (rt/nav :viewer (assoc params :zoom zoom-type)))))))
(def increase-zoom (def increase-zoom
(ptk/reify ::increase-zoom (ptk/reify ::increase-zoom
@ -293,13 +292,16 @@
(ptk/reify ::zoom-to-fit (ptk/reify ::zoom-to-fit
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [srect (as-> (get-in state [:route :query-params :page-id]) % (let [params (rt/get-params state)
(get-in state [:viewer :pages % :frames]) page-id (some-> (:page-id params) uuid/parse)
(nth % (get-in state [:route :query-params :index])) index (some-> (:index params) parse-long)
(get % :selrect))
orig-size (get-in state [:viewer-local :viewport-size]) frames (dm/get-in state [:viewer :pages page-id :frames])
wdiff (/ (:width orig-size) (:width srect)) srect (-> (nth frames index)
hdiff (/ (:height orig-size) (:height srect)) (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)] minzoom (min wdiff hdiff)]
(-> state (-> state
(assoc-in [:viewer-local :zoom] minzoom) (assoc-in [:viewer-local :zoom] minzoom)
@ -312,17 +314,25 @@
(ptk/reify ::zoom-to-fill (ptk/reify ::zoom-to-fill
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [srect (as-> (get-in state [:route :query-params :page-id]) %
(get-in state [:viewer :pages % :frames]) (let [params (rt/get-params state)
(nth % (get-in state [:route :query-params :index])) page-id (some-> (:page-id params) uuid/parse)
(get % :selrect)) index (some-> (:index params) parse-long)
orig-size (get-in state [:viewer-local :viewport-size])
wdiff (/ (:width orig-size) (:width srect)) frames (dm/get-in state [:viewer :pages page-id :frames])
hdiff (/ (:height orig-size) (:height srect)) 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)] maxzoom (max wdiff hdiff)]
(-> state (-> state
(assoc-in [:viewer-local :zoom] maxzoom) (assoc-in [:viewer-local :zoom] maxzoom)
(assoc-in [:viewer-local :zoom-type] :fill)))) (assoc-in [:viewer-local :zoom-type] :fill))))
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (rx/of update-zoom-querystring)))) (watch [_ _ _] (rx/of update-zoom-querystring))))
@ -376,16 +386,15 @@
(-> state (-> state
(dissoc :viewer-animations) (dissoc :viewer-animations)
(assoc :viewer-overlays []))) (assoc :viewer-overlays [])))
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [route (:route state) (let [params (rt/get-params state)
qparams (:query-params route) index (some-> params :index parse-long)]
pparams (:path-params route)
index (:index qparams)]
(when (pos? index) (when (pos? index)
(rx/of (rx/of
(dcm/close-thread) (dcmt/close-thread)
(rt/nav :viewer pparams (assoc qparams :index (dec index))))))))) (rt/nav :viewer (assoc params :index (dec index)))))))))
(def select-next-frame (def select-next-frame
(ptk/reify ::select-next-frame (ptk/reify ::select-next-frame
@ -396,30 +405,25 @@
(assoc :viewer-overlays []))) (assoc :viewer-overlays [])))
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [route (:route state) (let [params (rt/get-params state)
pparams (:path-params route) index (some-> params :index parse-long)
qparams (:query-params route) page-id (some-> params :page-id parse-uuid)
page-id (:page-id qparams)
index (:index qparams)
total (count (get-in state [:viewer :pages page-id :frames]))] total (count (get-in state [:viewer :pages page-id :frames]))]
(when (< index (dec total)) (when (< index (dec total))
(rx/of (rx/of
(dcm/close-thread) (dcmt/close-thread)
(rt/nav :viewer pparams (assoc qparams :index (inc index))))))))) (rt/nav :viewer params (assoc params :index (inc index)))))))))
(def select-first-frame (def select-first-frame
(ptk/reify ::select-first-frame (ptk/reify ::select-first-frame
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [route (:route state) (let [params (rt/get-params state)]
qparams (:query-params route)
pparams (:path-params route)]
(rx/of (rx/of
(dcm/close-thread) (dcmt/close-thread)
(rt/nav :viewer pparams (assoc qparams :index 0))))))) (rt/nav :viewer (assoc params :index 0)))))))
(def valid-interaction-modes (def valid-interaction-modes
#{:hide :show :show-on-click}) #{:hide :show :show-on-click})
@ -434,17 +438,14 @@
(update [_ state] (update [_ state]
(-> state (-> state
(assoc-in [:viewer-local :interactions-mode] mode) (assoc-in [:viewer-local :interactions-mode] mode)
(assoc-in [:viewer-local :interactions-show?] (case mode (assoc-in [:viewer-local :show-interactions] (case mode
:hide false :hide false
:show true :show true
:show-on-click false)))) :show-on-click false))))
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [route (:route state) (let [params (rt/get-params state)]
screen (-> route :data :name keyword) (rx/of (rt/nav :viewer (assoc params :interactions-mode mode)))))))
qparams (:query-params route)
pparams (:path-params route)]
(rx/of (rt/nav screen pparams (assoc qparams :interactions-mode mode)))))))
(declare flash-done) (declare flash-done)
@ -453,7 +454,7 @@
(ptk/reify ::flash-interactions (ptk/reify ::flash-interactions
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(assoc-in state [:viewer-local :interactions-show?] true)) (assoc-in state [:viewer-local :show-interactions] true))
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ stream] (watch [_ _ stream]
@ -466,7 +467,7 @@
(ptk/reify ::flash-done (ptk/reify ::flash-done
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(assoc-in state [:viewer-local :interactions-show?] false)))) (assoc-in state [:viewer-local :show-interactions] false))))
(defn set-nav-scroll (defn set-nav-scroll
[scroll] [scroll]
@ -500,11 +501,8 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [route (:route state) (let [params (rt/get-params state)]
screen (-> route :data :name keyword) (rx/of (rt/nav :viewer (assoc params :index index)))))))
qparams (:query-params route)
pparams (:path-params route)]
(rx/of (rt/nav screen pparams (assoc qparams :index index)))))))
(defn go-to-frame (defn go-to-frame
([frame-id] ([frame-id]
@ -573,10 +571,8 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [route (:route state) (let [params (rt/get-params state)]
pparams (:path-params route) (rx/of (rt/nav :viewer (assoc params :section section)))))))
qparams (:query-params route)]
(rx/of (rt/nav :viewer pparams (assoc qparams :section section)))))))
;; --- Overlays ;; --- Overlays
@ -771,9 +767,8 @@
(ptk/reify ::go-to-dashboard (ptk/reify ::go-to-dashboard
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [team-id (get-in state [:viewer :project :team-id]) (let [team-id (get-in state [:viewer :project :team-id])]
params {:team-id team-id}] (rx/of (dcm/go-to-dashboard-recent :team-id team-id))))))
(rx/of (rt/nav :dashboard-projects params))))))
(defn go-to-page (defn go-to-page
[page-id] [page-id]
@ -784,13 +779,10 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [route (:route state) (let [params (-> (rt/get-params state)
pparams (:path-params route)
qparams (-> (:query-params route)
(assoc :index 0) (assoc :index 0)
(assoc :page-id page-id)) (assoc :page-id page-id))]
rname (get-in route [:data :name])] (rx/of (rt/nav :viewer params))))))
(rx/of (rt/nav rname pparams qparams))))))
(defn go-to-workspace (defn go-to-workspace
([] (go-to-workspace nil)) ([] (go-to-workspace nil))
@ -798,14 +790,16 @@
(ptk/reify ::go-to-workspace (ptk/reify ::go-to-workspace
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [route (:route state) (let [params (rt/get-params state)
project-id (get-in state [:viewer :project :id])
file-id (get-in state [:viewer :file :id]) file-id (get-in state [:viewer :file :id])
saved-page-id (get-in route [:query-params :page-id]) team-id (get-in state [:viewer :project :team-id])
pparams {:project-id project-id :file-id file-id} page-id (or page-id
qparams {:page-id (or page-id saved-page-id)}] (some-> (:page-id params) uuid/parse))
(rx/of (rt/nav-new-window* params {:page-id page-id
{:rname :workspace :file-id file-id
:path-params pparams :team-id team-id}
:query-params qparams name (dm/str "workspace-" file-id)]
:name (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 (ns app.main.data.viewer.shortcuts
(:require (:require
[app.main.data.common :as dcm]
[app.main.data.shortcuts :as ds] [app.main.data.shortcuts :as ds]
[app.main.data.viewer :as dv] [app.main.data.viewer :as dv]
[app.main.store :as st])) [app.main.store :as st]))
@ -69,7 +70,7 @@
:open-workspace {:tooltip "G W" :open-workspace {:tooltip "G W"
:command "g w" :command "g w"
:subsections [:navigation-viewer] :subsections [:navigation-viewer]
:fn #(st/emit! (dv/go-to-workspace))}}) :fn #(st/emit! (dcm/go-to-workspace))}})
(defn get-tooltip [shortcut] (defn get-tooltip [shortcut]
(assert (contains? shortcuts shortcut) (str shortcut)) (assert (contains? shortcuts shortcut) (str shortcut))

View file

@ -36,17 +36,18 @@
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
[app.main.data.changes :as dch] [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.event :as ev]
[app.main.data.fonts :as df] [app.main.data.fonts :as df]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.data.persistence :as dps]
[app.main.data.plugins :as dp] [app.main.data.plugins :as dp]
[app.main.data.profile :as du] [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.bool :as dwb]
[app.main.data.workspace.collapse :as dwco] [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.drawing :as dwd]
[app.main.data.workspace.edition :as dwe] [app.main.data.workspace.edition :as dwe]
[app.main.data.workspace.fix-broken-shapes :as fbs] [app.main.data.workspace.fix-broken-shapes :as fbs]
@ -75,6 +76,7 @@
[app.main.features :as features] [app.main.features :as features]
[app.main.features.pointer-map :as fpmap] [app.main.features.pointer-map :as fpmap]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.router :as rt]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.main.worker :as uw] [app.main.worker :as uw]
[app.render-wasm :as wasm] [app.render-wasm :as wasm]
@ -82,7 +84,6 @@
[app.util.globals :as ug] [app.util.globals :as ug]
[app.util.http :as http] [app.util.http :as http]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[app.util.storage :as storage] [app.util.storage :as storage]
[app.util.timers :as tm] [app.util.timers :as tm]
[app.util.webapi :as wapi] [app.util.webapi :as wapi]
@ -101,12 +102,15 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare ^:private workspace-initialized) (declare ^:private workspace-initialized)
(declare ^:private fetch-libraries)
(declare ^:private libraries-fetched) (declare ^:private libraries-fetched)
(declare go-to-layout) (declare ^:private preload-data-uris)
;; (declare go-to-layout)
;; --- Initialize Workspace ;; --- Initialize Workspace
(defn initialize-layout (defn initialize-workspace-layout
[lname] [lname]
(ptk/reify ::initialize-layout (ptk/reify ::initialize-layout
ptk/UpdateEvent ptk/UpdateEvent
@ -121,73 +125,35 @@
(rx/of (layout/ensure-layout lname)) (rx/of (layout/ensure-layout lname))
(rx/of (layout/ensure-layout :layers)))))) (rx/of (layout/ensure-layout :layers))))))
(defn- workspace-initialized (defn- datauri->blob-uri
[] [uri]
(ptk/reify ::workspace-initialized (->> (http/send! {:uri uri
ptk/UpdateEvent :response-type :blob
(update [_ state] :method :get})
(-> state (rx/map :body)
(assoc :workspace-undo {}) (rx/map (fn [blob] (wapi/create-uri blob)))))
(assoc :workspace-ready? true)))
ptk/WatchEvent (defn- get-file-object-thumbnails
(watch [_ state _] [file-id]
(rx/of (->> (rp/cmd! :get-file-object-thumbnails {:file-id file-id})
(when (and (not (boolean (-> state :profile :props :v2-info-shown))) (rx/mapcat (fn [thumbnails]
(features/active-feature? state "components/v2")) (->> (rx/from thumbnails)
(modal/show :v2-info {})) (rx/mapcat (fn [[k v]]
(dp/check-open-plugin) ;; we only need to fetch the thumbnail if
(fdf/fix-deleted-fonts) ;; it is a data:uri, otherwise we can just
(fbs/fix-broken-shapes))))) ;; 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 (defn- resolve-file
[data] [file]
(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) (->> (fpmap/resolve-file file)
(rx/map :data) (rx/map :data)
(rx/mapcat (fn [{:keys [pages-index] :as data}] (rx/mapcat
(fn [{:keys [pages-index] :as data}]
(->> (rx/from (seq pages-index)) (->> (rx/from (seq pages-index))
(rx/mapcat (rx/mapcat
(fn [[id page]] (fn [[id page]]
@ -196,30 +162,8 @@
(rx/map (fn [_] [id page])))))) (rx/map (fn [_] [id page]))))))
(rx/reduce conj {}) (rx/reduce conj {})
(rx/map (fn [pages-index] (rx/map (fn [pages-index]
(assoc data :pages-index pages-index)))))) (let [data (assoc data :pages-index pages-index)]
(rx/map workspace-data-loaded)) (assoc file :data (d/removem (comp t/pointer? val) data))))))))))
;; 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- libraries-fetched (defn- libraries-fetched
[libraries] [libraries]
@ -240,151 +184,153 @@
(rx/concat (rx/timer 1000) (rx/concat (rx/timer 1000)
(rx/of (dwl/notify-sync-file file-id)))))))) (rx/of (dwl/notify-sync-file file-id))))))))
(defn- datauri->blob-uri (defn- fetch-libraries
[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
[file-id] [file-id]
(->> (rp/cmd! :get-file-object-thumbnails {:file-id file-id}) (ptk/reify ::fetch-libries
(rx/mapcat (fn [thumbnails] ptk/WatchEvent
(->> (rx/from thumbnails) (watch [_ state _]
(rx/mapcat (fn [[k v]] (let [features (features/get-team-enabled-features state)]
;; we only need to fetch the thumbnail if (->> (rp/cmd! :get-file-libraries {:file-id file-id})
;; it is a data:uri, otherwise we can just (rx/mapcat
;; use the value as is. (fn [libraries]
(if (str/starts-with? v "data:") (rx/merge
(->> (datauri->blob-uri v) (->> (rx/from libraries)
(rx/map (fn [uri] [k uri]))) (rx/merge-map
(rx/of [k v]))))))) (fn [{:keys [id synced-at]}]
(rx/reduce conj {}))) (->> (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))))))))))
(defn- fetch-bundle-stage-1 (defn- workspace-initialized
[project-id file-id] []
(ptk/reify ::fetch-bundle-stage-1 (ptk/reify ::workspace-initialized
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc :workspace-undo {})
(assoc :workspace-ready true)))
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- bundle-fetched
[{:keys [features file thumbnails]}]
(ptk/reify ::bundle-fetched
IDeref
(-deref [_]
{:features features
:file file
:thumbnails thumbnails})
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"
[file-id]
(ptk/reify ::fetch-bundle
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [render-wasm? (features/active-feature? state "render-wasm/v1")] (let [features (features/get-team-enabled-features state)
(->> (rp/cmd! :get-project {:id project-id}) render-wasm? (contains? features "render-wasm/v1")
(rx/mapcat (fn [project] stopper-s (rx/filter (ptk/type? ::finalize-workspace) stream)]
(rx/concat
;; Wait the wasm module to be loaded or failed to
;; load. We need to wait the promise to be resolved
;; before continue with the next workspace loading
;; steps
(->> (rx/concat
;; Firstly load wasm module if it is enabled and fonts
(rx/merge
(if ^boolean render-wasm? (if ^boolean render-wasm?
(->> (rx/from @wasm/module) (->> (rx/from @wasm/module)
(rx/ignore)) (rx/ignore))
(rx/empty)) (rx/empty))
(->> (rp/cmd! :get-team {:id (:team-id project)}) (->> stream
(rx/mapcat (fn [team] (rx/filter (ptk/type? ::df/fonts-loaded))
(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
ptk/UpdateEvent
(update [_ state]
(-> state
(update :projects assoc project-id project)))
ptk/WatchEvent
(watch [_ state stream]
(let [features (features/get-team-enabled-features state)
;; 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/take 1)
(rx/map (fn [[file thumbnails team-users comments-users]] (rx/ignore))
(let [bundle (-> bundle (rx/of (df/fetch-fonts)))
(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)))))))
(declare go-to-component) ;; Then fetch file and thumbnails
(->> (rx/zip (rp/cmd! :get-file {:id file-id :features features})
(defn- fetch-bundle (get-file-object-thumbnails file-id))
"Multi-stage file bundle fetch coordinator"
[project-id file-id]
(ptk/reify ::fetch-bundle
ptk/WatchEvent
(watch [_ state stream]
(->> (rx/merge
(rx/of (fetch-bundle-stage-1 project-id file-id))
(->> stream
(rx/filter (ptk/type? ::bundle-stage-1))
(rx/observe-on :async)
(rx/map deref)
(rx/map fetch-bundle-stage-2))
(->> stream
(rx/filter (ptk/type? ::bundle-stage-2))
(rx/observe-on :async)
(rx/map deref)
(rx/map bundle-fetched))
(when-let [component-id (get-in state [:route :query-params :component-id])]
(->> stream
(rx/filter (ptk/type? ::workspace-initialized))
(rx/observe-on :async)
(rx/take 1) (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/take-until stopper-s))))))
(rx/filter (ptk/type? ::fetch-bundle) stream))))))
(defn initialize-file (defn initialize-workspace
[project-id file-id] [file-id]
(dm/assert! (uuid? project-id)) (assert (uuid? file-id) "expected valud uuid for `file-id`")
(dm/assert! (uuid? file-id))
(ptk/reify ::initialize-file (ptk/reify ::initialize-workspace
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(assoc state (assoc state
:recent-colors (:recent-colors storage/user) :recent-colors (:recent-colors storage/user)
:workspace-ready? false :workspace-ready false
:current-file-id file-id :current-file-id file-id
:current-project-id project-id
:workspace-presence {})) :workspace-presence {}))
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ stream] (watch [_ state stream]
(log/debug :hint "initialize-file" :file-id file-id) (log/debug :hint "initialize-workspace" :file-id file-id)
(let [stoper-s (rx/filter (ptk/type? ::finalize-file) stream)] (let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream)
(rx/merge rparams (rt/get-params state)]
(->> (rx/merge
(rx/of (ntf/hide) (rx/of (ntf/hide)
;; We initialize the features without knowning the (dcmt/retrieve-comment-threads file-id)
;; team specific features in this step. (dcmt/fetch-profiles)
(features/initialize) (fetch-bundle file-id))
(dcm/retrieve-comment-threads file-id)
(fetch-bundle project-id file-id)) (->> 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 (->> stream
(rx/filter dch/commit?) (rx/filter dch/commit?)
@ -396,37 +342,22 @@
:undo-group undo-group :undo-group undo-group
:tags tags}] :tags tags}]
(rx/of (dwu/append-undo entry stack-undo?))) (rx/of (dwu/append-undo entry stack-undo?)))
(rx/empty)))) (rx/empty))))))
(rx/take-until stoper-s))))
(rx/take-until stoper-s)))))
ptk/EffectEvent ptk/EffectEvent
(effect [_ _ _] (effect [_ _ _]
(let [name (dm/str "workspace-" file-id)] (let [name (dm/str "workspace-" file-id)]
(unchecked-set ug/global "name" name))))) (unchecked-set ug/global "name" name)))))
(defn reload-file (defn finalize-workspace
[] [file-id]
(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]
(ptk/reify ::finalize-file (ptk/reify ::finalize-file
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(-> state (-> state
(dissoc (dissoc
:current-file-id :current-file-id
:current-project-id
:workspace-data :workspace-data
:workspace-editor-state :workspace-editor-state
:workspace-file :workspace-file
@ -434,22 +365,37 @@
:workspace-media-objects :workspace-media-objects
:workspace-persistence :workspace-persistence
:workspace-presence :workspace-presence
:workspace-ready? :workspace-ready
:workspace-undo) :workspace-undo)
(update :workspace-global dissoc :read-only?) (update :workspace-global dissoc :read-only?)
(assoc-in [:workspace-global :options-mode] :design))) (assoc-in [:workspace-global :options-mode] :design)))
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ state _]
(rx/of (dwn/finalize file-id) (let [project-id (:current-project-id state)]
(dwsl/finalize)))))
(declare go-to-page) (rx/of (dwn/finalize file-id)
(declare ^:private preload-data-uris) (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 (defn initialize-page
[page-id] [page-id]
(dm/assert! (uuid? page-id)) (assert (uuid? page-id) "expected valid uuid for `page-id`")
(ptk/reify ::initialize-page (ptk/reify ::initialize-page
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
@ -464,30 +410,20 @@
;; FIXME: this should be done on `initialize-layout` (?) ;; FIXME: this should be done on `initialize-layout` (?)
(update :workspace-layout layout/load-layout-flags) (update :workspace-layout layout/load-layout-flags)
(update :workspace-global layout/load-layout-state) (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))))
state)) state))
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (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)] (let [file-id (:current-file-id state)]
(rx/of (preload-data-uris page-id) (rx/of (preload-data-uris page-id)
(dwth/watch-state-changes file-id page-id) (dwth/watch-state-changes file-id page-id)
(dwl/watch-component-changes))) (dwl/watch-component-changes))))))
(let [page-id (dm/get-in state [:workspace-data :pages 0])]
(rx/of (go-to-page page-id))))))))
(defn finalize-page (defn finalize-page
[page-id] [page-id]
(dm/assert! (uuid? page-id)) (assert (uuid? page-id) "expected valid uuid for `page-id`")
(ptk/reify ::finalize-page (ptk/reify ::finalize-page
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
@ -683,7 +619,7 @@
(rx/of (dch/commit-changes changes) (rx/of (dch/commit-changes changes)
(when (= id (:current-page-id state)) (when (= id (:current-page-id state))
go-to-file)))))) (go-to-file)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; WORKSPACE File Actions ;; WORKSPACE File Actions
@ -847,7 +783,7 @@
(let [selected (wsh/lookup-selected state) (let [selected (wsh/lookup-selected state)
id (first selected)] id (first selected)]
(when (= (count selected) 1) (when (= (count selected) 1)
(rx/of (go-to-layout :layers) (rx/of (dcm/go-to-workspace :layout :layers)
(start-rename-shape id))))))) (start-rename-shape id)))))))
;; --- Shape Vertical Ordering ;; --- Shape Vertical Ordering
@ -1111,76 +1047,17 @@
(rx/of (dwsh/update-shapes selected #(assoc % :proportion-lock true))) (rx/of (dwsh/update-shapes selected #(assoc % :proportion-lock true)))
(rx/of (dwsh/update-shapes selected #(update % :proportion-lock not)))))))) (rx/of (dwsh/update-shapes selected #(update % :proportion-lock not))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Navigation
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn workspace-focus-lost (defn workspace-focus-lost
[] []
(ptk/reify ::workspace-focus-lost (ptk/reify ::workspace-focus-lost
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
;; FIXME: remove the `?` from show-distances?
(assoc-in state [:workspace-global :show-distances?] false)))) (assoc-in state [:workspace-global :show-distances?] false))))
(defn navigate-to-project ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[project-id] ;; Navigation
(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)))))))
(defn set-assets-section-open (defn set-assets-section-open
[file-id section open?] [file-id section open?]
@ -1242,111 +1119,18 @@
(update-in state [:workspace-assets :selected] dissoc file-id) (update-in state [:workspace-assets :selected] dissoc file-id)
(update state :workspace-assets dissoc :selected)))))) (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 (defn show-component-in-assets
[component-id] [component-id]
(ptk/reify ::show-component-in-assets (ptk/reify ::show-component-in-assets
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [project-id (:current-project-id state) (let [component-path (cfh/split-path (get-in state [:workspace-data :components component-id :path]))
file-id (:current-file-id state) paths (map (fn [i] (cfh/join-path (take (inc i) component-path))) (range (count component-path)))
page-id (:current-page-id state) file-id (:current-file-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)))]
(rx/concat (rx/concat
(rx/from (map #(set-assets-group-open file-id :components % true) paths)) (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 :library true)
(set-assets-section-open file-id :components true) (set-assets-section-open file-id :components true)
(select-single-asset file-id component-id :components))))) (select-single-asset file-id component-id :components)))))
@ -1356,55 +1140,6 @@
(let [wrapper-id (str "component-shape-id-" component-id)] (let [wrapper-id (str "component-shape-id-" component-id)]
(tm/schedule-on-idle #(dom/scroll-into-view-if-needed! (dom/get-element wrapper-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 ;; Context Menu
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -1640,8 +1375,8 @@
(rx/catch on-copy-error) (rx/catch on-copy-error)
(rx/ignore))) (rx/ignore)))
;; FIXME: this is to support Firefox versions below 116 that don't support `ClipboardItem` ;; FIXME: this is to support Firefox versions below 116 that don't support
;; after the version 116 is less common we could remove this. ;; `ClipboardItem` after the version 116 is less common we could remove this.
;; https://caniuse.com/?search=ClipboardItem ;; https://caniuse.com/?search=ClipboardItem
(->> (rx/from shapes) (->> (rx/from shapes)
(rx/merge-map (partial prepare-object objects frame-id)) (rx/merge-map (partial prepare-object objects frame-id))
@ -1924,7 +1659,8 @@
;; the pasted object doesn't fit we try to: ;; the pasted object doesn't fit we try to:
;; ;;
;; - Align it to the limits on the x and y axis ;; - 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))] (gpt/point paste-x paste-y))]
[frame-id delta (dec (count (:shapes selected-frame-obj)))])) [frame-id delta (dec (count (:shapes selected-frame-obj)))]))

View file

@ -13,7 +13,8 @@
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[app.main.data.changes :as dch] [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.event :as ev]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.common :as dwco] [app.main.data.workspace.common :as dwco]
@ -23,7 +24,6 @@
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.util.mouse :as mse] [app.util.mouse :as mse]
[app.util.router :as rt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[potok.v2.core :as ptk])) [potok.v2.core :as ptk]))
@ -38,7 +38,7 @@
(watch [_ _ stream] (watch [_ _ stream]
(let [stopper (rx/filter #(= ::finalize %) stream)] (let [stopper (rx/filter #(= ::finalize %) stream)]
(rx/merge (rx/merge
(rx/of (dcm/retrieve-comment-threads file-id)) (rx/of (dcmt/retrieve-comment-threads file-id))
(->> stream (->> stream
(rx/filter mse/mouse-event?) (rx/filter mse/mouse-event?)
(rx/filter mse/mouse-click-event?) (rx/filter mse/mouse-click-event?)
@ -60,8 +60,8 @@
(watch [_ state _] (watch [_ state _]
(let [local (:comments-local state)] (let [local (:comments-local state)]
(cond (cond
(:draft local) (rx/of (dcm/close-thread)) (:draft local) (rx/of (dcmt/close-thread))
(:open local) (rx/of (dcm/close-thread)) (:open local) (rx/of (dcmt/close-thread))
:else :else
(rx/of (dw/clear-edition-mode) (rx/of (dw/clear-edition-mode)
@ -78,19 +78,19 @@
(watch [_ state _] (watch [_ state _]
(let [local (:comments-local state)] (let [local (:comments-local state)]
(if (some? (:open local)) (if (some? (:open local))
(rx/of (dcm/close-thread)) (rx/of (dcmt/close-thread))
(let [page-id (:current-page-id state) (let [page-id (:current-page-id state)
file-id (:current-file-id state) file-id (:current-file-id state)
params {:position position params {:position position
:page-id page-id :page-id page-id
:file-id file-id}] :file-id file-id}]
(rx/of (dcm/create-draft params)))))))) (rx/of (dcmt/create-draft params))))))))
(defn center-to-comment-thread (defn center-to-comment-thread
[{:keys [position] :as thread}] [{:keys [position] :as thread}]
(dm/assert! (dm/assert!
"expected valid comment thread" "expected valid comment thread"
(dcm/check-comment-thread! thread)) (dcmt/check-comment-thread! thread))
(ptk/reify ::center-to-comment-thread (ptk/reify ::center-to-comment-thread
ptk/UpdateEvent ptk/UpdateEvent
@ -109,22 +109,21 @@
[thread] [thread]
(dm/assert! (dm/assert!
"expected valid comment thread" "expected valid comment thread"
(dcm/check-comment-thread! thread)) (dcmt/check-comment-thread! thread))
(ptk/reify ::open-comment-thread (ptk/reify ::open-comment-thread
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ stream] (watch [_ _ stream]
(let [pparams {:project-id (:project-id thread)
:file-id (:file-id thread)}
qparams {:page-id (:page-id thread)}]
(rx/merge (rx/merge
(rx/of (rt/nav :workspace pparams qparams)) (rx/of (dcm/go-to-workspace :file-id (:file-id thread)
:page-id (:page-id thread)))
(->> stream (->> stream
(rx/filter (ptk/type? ::dwv/initialize-viewport)) (rx/filter (ptk/type? ::dwv/initialize-viewport))
(rx/take 1) (rx/take 1)
(rx/mapcat #(rx/of (center-to-comment-thread thread) (rx/mapcat #(rx/of (center-to-comment-thread thread)
(dwd/select-for-drawing :comments) (dwd/select-for-drawing :comments)
(with-meta (dcm/open-thread thread) (with-meta (dcmt/open-thread thread)
{::ev/origin "workspace"}))))))))) {::ev/origin "workspace"}))))))))
(defn update-comment-thread-position (defn update-comment-thread-position
([thread [new-x new-y]] ([thread [new-x new-y]]
@ -133,7 +132,7 @@
([thread [new-x new-y] frame-id] ([thread [new-x new-y] frame-id]
(dm/assert! (dm/assert!
"expected valid comment thread" "expected valid comment thread"
(dcm/check-comment-thread! thread)) (dcmt/check-comment-thread! thread))
(ptk/reify ::update-comment-thread-position (ptk/reify ::update-comment-thread-position
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]

View file

@ -27,6 +27,7 @@
[app.config :as cf] [app.config :as cf]
[app.main.data.changes :as dch] [app.main.data.changes :as dch]
[app.main.data.comments :as dc] [app.main.data.comments :as dc]
[app.main.data.common :as dcm]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
@ -40,14 +41,15 @@
[app.main.data.workspace.thumbnails :as dwt] [app.main.data.workspace.thumbnails :as dwt]
[app.main.data.workspace.transforms :as dwtr] [app.main.data.workspace.transforms :as dwtr]
[app.main.data.workspace.undo :as dwu] [app.main.data.workspace.undo :as dwu]
[app.main.data.workspace.zoom :as dwz]
[app.main.features :as features] [app.main.features :as features]
[app.main.features.pointer-map :as fpmap] [app.main.features.pointer-map :as fpmap]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.util.color :as uc] [app.util.color :as uc]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
[app.util.router :as rt]
[app.util.storage :as storage] [app.util.storage :as storage]
[app.util.time :as dt] [app.util.time :as dt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
@ -684,21 +686,49 @@
(rx/of (when can-detach? (rx/of (when can-detach?
(dch/commit-changes changes))))))) (dch/commit-changes changes)))))))
(defn nav-to-component-file (defn go-to-component-file
[file-id component] [file-id component]
(dm/assert! (uuid? file-id)) (dm/assert! (uuid? file-id))
(dm/assert! (some? component)) (dm/assert! (some? component))
(ptk/reify ::nav-to-component-file (ptk/reify ::nav-to-component-file
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [project-id (get-in state [:workspace-libraries file-id :project-id]) (let [params (-> (rt/get-params state)
path-params {:project-id project-id (assoc :file-id file-id)
:file-id file-id} (assoc :page-id (:main-instance-page component))
query-params {:page-id (:main-instance-page component) (assoc :component-id (:id component)))]
:component-id (:id component)}] (rx/of (rt/nav :workspace params :new-window? true))))))
(rx/of (rt/nav-new-window* {:rname :workspace
:path-params path-params
:query-params query-params})))))) (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 (defn library-thumbnails-fetched
[thumbnails] [thumbnails]
@ -1117,9 +1147,11 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(rp/cmd! :ignore-file-library-sync-status (let [file-id (:current-file-id state)]
{:file-id (get-in state [:workspace-file :id]) (->> (rp/cmd! :ignore-file-library-sync-status
:date (dt/now)})))) {:file-id file-id
:date (dt/now)})
(rx/ignore))))))
(defn assets-need-sync (defn assets-need-sync
"Get a lazy sequence of all the assets of each type in the library that have "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) (->> (rp/cmd! :set-file-shared params)
(rx/ignore)))))) (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 ;; --- Link and unlink Files
(defn link-file-to-library (defn link-file-to-library

View file

@ -16,6 +16,7 @@
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.plugins :as dpl] [app.main.data.plugins :as dpl]
[app.main.data.websocket :as dws] [app.main.data.websocket :as dws]
[app.main.data.workspace :as-alias dw]
[app.main.data.workspace.common :as dwc] [app.main.data.workspace.common :as dwc]
[app.main.data.workspace.edition :as dwe] [app.main.data.workspace.edition :as dwe]
[app.main.data.workspace.layout :as dwly] [app.main.data.workspace.layout :as dwly]
@ -30,9 +31,6 @@
[clojure.set :as set] [clojure.set :as set]
[potok.v2.core :as ptk])) [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 ;; FIXME: this ns should be renamed to something different
(declare process-message) (declare process-message)
@ -292,7 +290,7 @@
curr-vern (dm/get-in state [:workspace-file :vern]) curr-vern (dm/get-in state [:workspace-file :vern])
reload? (and (= file-id curr-file-id) (not= vern curr-vern))] reload? (and (= file-id curr-file-id) (not= vern curr-vern))]
(when reload? (when reload?
(rx/of (reload-file))))))) (rx/of (ptk/event ::dw/reload-current-file)))))))
(def ^:private schema:handle-library-change (def ^:private schema:handle-library-change
[:map {:title "handle-library-change"} [:map {:title "handle-library-change"}

View file

@ -110,13 +110,15 @@
:undo-group undo-group}))) :undo-group undo-group})))
(rx/empty)))))) (rx/empty))))))
(defn initialize (defn initialize-shape-layout
[] []
(ptk/reify ::initialize (ptk/reify ::initialize-shape-layout
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ stream] (watch [_ _ stream]
(let [stopper (rx/filter (ptk/type? ::finalize) stream)] (let [stopper (rx/filter (ptk/type? ::finalize-shape-layout) stream)]
(->> 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/filter (ptk/type? :layout/update))
(rx/map deref) (rx/map deref)
;; We buffer the updates to the layout so if there are many changes at the same time ;; 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})))) (update-layout-positions {:ids ids}))))
(rx/take-until stopper)))))) (rx/take-until stopper))))))
(defn finalize (defn finalize-shape-layout
[] []
(ptk/reify ::finalize)) (ptk/data-event ::finalize-shape-layout))
(defn create-layout-from-id (defn create-layout-from-id
[id type & {:keys [from-frame? calculate-params?] :or {from-frame? false calculate-params? true}}] [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 (ns app.main.data.workspace.shortcuts
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.main.data.common :as dcm]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.exports.assets :as de] [app.main.data.exports.assets :as de]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
@ -440,17 +441,18 @@
:toggle-layers {:tooltip (ds/alt "L") :toggle-layers {:tooltip (ds/alt "L")
:command (ds/a-mod "l") :command (ds/a-mod "l")
:subsections [:panels] :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") :toggle-assets {:tooltip (ds/alt "I")
:command (ds/a-mod "i") :command (ds/a-mod "i")
:subsections [:panels] :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") :toggle-history {:tooltip (ds/alt "H")
:command (ds/a-mod "h") :command (ds/a-mod "h")
:subsections [:panels] :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") :toggle-colorpalette {:tooltip (ds/alt "P")
:command (ds/a-mod "p") :command (ds/a-mod "p")
@ -516,22 +518,22 @@
:open-viewer {:tooltip "G V" :open-viewer {:tooltip "G V"
:command "g v" :command "g v"
:subsections [:navigation-workspace] :subsections [:navigation-workspace]
:fn #(st/emit! (dw/go-to-viewer))} :fn #(st/emit! (dcm/go-to-viewer))}
:open-inspect {:tooltip "G I" :open-inspect {:tooltip "G I"
:command "g i" :command "g i"
:subsections [:navigation-workspace] :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" :open-comments {:tooltip "G C"
:command "g c" :command "g c"
:subsections [:navigation-workspace] :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" :open-dashboard {:tooltip "G D"
:command "g d" :command "g d"
:subsections [:navigation-workspace] :subsections [:navigation-workspace]
:fn #(st/emit! (dw/go-to-dashboard))} :fn #(st/emit! (dcm/go-to-dashboard-recent))}
:select-prev {:tooltip (ds/shift "tab") :select-prev {:tooltip (ds/shift "tab")
:command "shift+tab" :command "shift+tab"

View file

@ -27,10 +27,6 @@
(-> (lookup-page state page-id) (-> (lookup-page state page-id)
(get :objects)))) (get :objects))))
(defn lookup-viewer-objects
([state page-id]
(dm/get-in state [:viewer :pages page-id :objects])))
(defn lookup-library-objects (defn lookup-library-objects
[state file-id page-id] [state file-id page-id]
(dm/get-in state [:workspace-libraries file-id :data :pages-index page-id :objects])) (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.data.workspace.undo :as dwu]
[app.main.features :as features] [app.main.features :as features]
[app.main.fonts :as fonts] [app.main.fonts :as fonts]
[app.util.router :as rt] [app.main.router :as rt]
[app.util.text-editor :as ted] [app.util.text-editor :as ted]
[app.util.text.content.styles :as styles] [app.util.text.content.styles :as styles]
[app.util.timers :as ts] [app.util.timers :as ts]

View file

@ -13,8 +13,8 @@
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.main.data.changes :as dch] [app.main.data.changes :as dch]
[app.main.data.common :as dcm]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.util.router :as rt]
[app.util.time :as dt] [app.util.time :as dt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[potok.v2.core :as ptk])) [potok.v2.core :as ptk]))
@ -290,14 +290,8 @@
(ptk/reify ::assure-valid-current-page (ptk/reify ::assure-valid-current-page
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [current_page (:current-page-id state) (let [page-id (:current-page-id state)
pages (get-in state [:workspace-data :pages]) pages (dm/get-in state [:workspace-data :pages])]
exists? (some #(= current_page %) pages) (if (contains? pages page-id)
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?
(rx/empty) (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 (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.schema :as sm]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.persistence :as dwp] [app.main.data.persistence :as dwp]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
@ -25,7 +26,7 @@
(declare fetch-versions) (declare fetch-versions)
(defn init-version-state (defn init-version-state
[file-id] []
(ptk/reify ::init-version-state (ptk/reify ::init-version-state
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
@ -33,7 +34,7 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ _ _]
(rx/of (fetch-versions file-id))))) (rx/of (fetch-versions)))))
(defn update-version-state (defn update-version-state
[version-state] [version-state]
@ -43,123 +44,90 @@
(update state :workspace-versions merge version-state)))) (update state :workspace-versions merge version-state))))
(defn fetch-versions (defn fetch-versions
[file-id] []
(dm/assert! (uuid? file-id))
(ptk/reify ::fetch-versions (ptk/reify ::fetch-versions
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ state _]
(let [file-id (:current-file-id state)]
(->> (rp/cmd! :get-file-snapshots {:file-id file-id}) (->> (rp/cmd! :get-file-snapshots {:file-id file-id})
(rx/map #(update-version-state {:status :loaded :data %})))))) (rx/map #(update-version-state {:status :loaded :data %})))))))
(defn create-version (defn create-version
[file-id] []
(dm/assert! (uuid? file-id))
(ptk/reify ::create-version (ptk/reify ::create-version
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ state _]
(let [label (dt/format (dt/now) :date-full)] (let [label (dt/format (dt/now) :date-full)
file-id (:current-file-id state)]
;; Force persist before creating snapshot, otherwise we could loss changes ;; Force persist before creating snapshot, otherwise we could loss changes
(rx/concat (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/from-atom refs/persistence-state {:emit-current-value? true})
(rx/filter #(or (nil? %) (= :saved %))) (rx/filter #(or (nil? %) (= :saved %)))
(rx/take 1) (rx/take 1)
(rx/mapcat #(rp/cmd! :create-file-snapshot {:file-id file-id :label label})) (rx/mapcat #(rp/cmd! :create-file-snapshot {:file-id file-id :label label}))
(rx/mapcat (rx/mapcat
(fn [{:keys [id]}] (fn [{:keys [id]}]
(rx/of (rx/of (update-version-state {:editing id})
(update-version-state {:editing id}) (fetch-versions))))))))))
(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)))))))
(defn rename-version (defn rename-version
[file-id id label] [id label]
(dm/assert! (uuid? file-id)) (assert (uuid? id) "expected valid uuid for `id`")
(dm/assert! (uuid? id)) (assert (sm/valid-text? label) "expected not empty string for `label`")
(dm/assert! (and (string? label) (d/not-empty? label)))
(ptk/reify ::rename-version (ptk/reify ::rename-version
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ state _]
(let [file-id (:current-file-id state)]
(rx/merge (rx/merge
(rx/of (update-version-state {:editing false})) (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}) (->> (rp/cmd! :update-file-snapshot {:id id :label label})
(rx/map #(fetch-versions file-id))) (rx/map fetch-versions)))))))
(rx/of (ptk/event ::ev/event {::ev/name "rename-version"}))))))
(defn restore-version (defn restore-version
[project-id file-id id origin] [id origin]
(dm/assert! (uuid? project-id)) (assert (uuid? id) "expected valid uuid for `id`")
(dm/assert! (uuid? file-id))
(dm/assert! (uuid? id))
(ptk/reify ::restore-version (ptk/reify ::restore-version
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ state _]
(let [file-id (:current-file-id state)]
(rx/concat (rx/concat
(rx/of ::dwp/force-persist) (rx/of ::dwp/force-persist)
;; FIXME: we should abstract this
(->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) (->> (rx/from-atom refs/persistence-state {:emit-current-value? true})
(rx/filter #(or (nil? %) (= :saved %))) (rx/filter #(or (nil? %) (= :saved %)))
(rx/take 1) (rx/take 1)
(rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id})) (rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id}))
(rx/map #(dw/initialize-file project-id file-id))) (rx/map #(dw/initialize-workspace file-id)))
(case origin
:version
(rx/of (ptk/event ::ev/event {::ev/name "restore-pin-version"}))
:snapshot (when-let [name (case origin
(rx/of (ptk/event ::ev/event {::ev/name "restore-autosave"})) :version "restore-pin-version"
:snapshot "restore-autosave"
:plugin :plugin "restore-version-plugin"
(rx/of (ptk/event ::ev/event {::ev/name "restore-version-plugin"})) nil)]
(rx/of (ptk/event ::ev/event {::ev/name name}))))))))
(rx/empty))))))
(defn delete-version (defn delete-version
[file-id id] [id]
(dm/assert! (uuid? file-id)) (assert (uuid? id) "expected valid uuid for `id`")
(dm/assert! (uuid? id))
(ptk/reify ::delete-version (ptk/reify ::delete-version
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ _ _]
(->> (rp/cmd! :delete-file-snapshot {:id id}) (->> (rp/cmd! :delete-file-snapshot {:id id})
(rx/map #(fetch-versions file-id)))))) (rx/map fetch-versions)))))
(defn pin-version (defn pin-version
[file-id id] [id]
(dm/assert! (uuid? file-id)) (assert (uuid? id) "expected valid uuid for `id`")
(dm/assert! (uuid? id))
(ptk/reify ::pin-version (ptk/reify ::pin-version
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
@ -168,8 +136,82 @@
params {:id id params {:id id
:label (dt/format (:created-at version) :date-full)}] :label (dt/format (:created-at version) :date-full)}]
(rx/concat
(->> (rp/cmd! :update-file-snapshot params) (->> (rp/cmd! :update-file-snapshot params)
(rx/mapcat #(rx/of (update-version-state {:editing id}) (rx/mapcat (fn [_]
(fetch-versions file-id)))) (rx/of (update-version-state {:editing id})
(rx/of (ptk/event ::ev/event {::ev/name "pin-version"}))))))) (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.auth :as da]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.data.workspace :as-alias dw]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.util.globals :as glob] [app.util.globals :as glob]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
[app.util.router :as rt]
[app.util.timers :as ts] [app.util.timers :as ts]
[cuerdas.core :as str] [cuerdas.core :as str]
[potok.v2.core :as ptk])) [potok.v2.core :as ptk]))
@ -141,7 +142,7 @@
:timeout 3000}))) :timeout 3000})))
(= code :vern-conflict) (= code :vern-conflict)
(st/emit! (reload-file)) (st/emit! (ptk/event ::dw/reload-current-file))
:else :else
(st/async-emit! (rt/assign-exception error)))) (st/async-emit! (rt/assign-exception error))))
@ -212,7 +213,6 @@
(ts/schedule (ts/schedule
#(st/emit! (rt/assign-exception error)))) #(st/emit! (rt/assign-exception error))))
(defn- redirect-to-dashboard (defn- redirect-to-dashboard
[] []
(let [team-id (:current-team-id @st/state) (let [team-id (:current-team-id @st/state)

View file

@ -22,13 +22,13 @@
;; ---- Global refs ;; ---- Global refs
(def route (def route
(l/derived :route st/state)) (l/derived (l/key :route) st/state))
(def router (def router
(l/derived :router st/state)) (l/derived (l/key :router) st/state))
(def profile (def profile
(l/derived :profile st/state)) (l/derived (l/key :profile) st/state))
(def team (def team
(l/derived (fn [state] (l/derived (fn [state]
@ -37,11 +37,18 @@
(get teams team-id))) (get teams team-id)))
st/state)) 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 (def permissions
(l/derived :permissions team)) (l/derived (l/key :permissions) team))
(def teams (def teams
(l/derived :teams st/state)) (l/derived (l/key :teams) st/state))
(def exception (def exception
(l/derived :exception st/state)) (l/derived :exception st/state))
@ -65,8 +72,13 @@
(l/derived :files st/state)) (l/derived :files st/state))
(def shared-files (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)) (l/derived :shared-files st/state))
(def libraries
(l/derived :libraries st/state))
(defn extract-selected-files (defn extract-selected-files
[files selected] [files selected]
(let [get-file #(get files %) (let [get-file #(get files %)
@ -86,7 +98,6 @@
(def selected-project (def selected-project
(l/derived :selected-project st/state)) (l/derived :selected-project st/state))
(def dashboard-local (def dashboard-local
(l/derived :dashboard-local st/state)) (l/derived :dashboard-local st/state))
@ -243,12 +254,6 @@
(def workspace-file-typography (def workspace-file-typography
(l/derived :typographies workspace-data)) (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 (def workspace-local-library
(l/derived (fn [state] (l/derived (fn [state]
(select-keys (:workspace-data state) (select-keys (:workspace-data state)
@ -505,12 +510,16 @@
;; ---- Viewer refs ;; ---- Viewer refs
(defn get-viewer-objects
[state page-id]
(dm/get-in state [:viewer :pages page-id :objects]))
(defn lookup-viewer-objects-by-id (defn lookup-viewer-objects-by-id
[page-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 (def viewer-data
(l/derived :viewer st/state)) (l/derived (l/key :viewer) st/state))
(def viewer-file (def viewer-file
(l/derived :file viewer-data)) (l/derived :file viewer-data))
@ -536,14 +545,8 @@
(def comments-local (def comments-local
(l/derived :comments-local st/state)) (l/derived :comments-local st/state))
(def users (def profiles
(l/derived :users st/state)) (l/derived :profiles 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 viewer-fullscreen? (def viewer-fullscreen?
(l/derived (fn [state] (l/derived (fn [state]
@ -555,14 +558,11 @@
(dm/get-in state [:viewer-local :zoom-type])) (dm/get-in state [:viewer-local :zoom-type]))
st/state)) st/state))
(def workspace-thumbnails
(l/derived :workspace-thumbnails st/state))
(defn workspace-thumbnail-by-id (defn workspace-thumbnail-by-id
[object-id] [object-id]
(l/derived (l/derived
(fn [state] (fn [state]
(some-> (dm/get-in state [:workspace-thumbnails object-id]) (some-> (dm/get-in state [:thumbnails object-id])
(cf/resolve-media))) (cf/resolve-media)))
st/state)) st/state))
@ -608,35 +608,9 @@
(every? (partial ctl/grid-layout-immediate-child? objects)))) (every? (partial ctl/grid-layout-immediate-child? objects))))
workspace-page-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 (def colorpicker
(l/derived :colorpicker st/state)) (l/derived :colorpicker st/state))
(def workspace-grid-edition (def workspace-grid-edition
(l/derived :workspace-grid-edition st/state)) (l/derived :workspace-grid-edition st/state))
@ -644,6 +618,7 @@
[id] [id]
(l/derived #(get % id) workspace-grid-edition)) (l/derived #(get % id) workspace-grid-edition))
;; FIXME: remove
(def current-file-id (def current-file-id
(l/derived :current-file-id st/state)) (l/derived :current-file-id st/state))

View file

@ -4,7 +4,7 @@
;; ;;
;; Copyright (c) KALEIDOS INC ;; Copyright (c) KALEIDOS INC
(ns app.util.router (ns app.main.router
(:refer-clojure :exclude [resolve]) (:refer-clojure :exclude [resolve])
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
@ -28,11 +28,10 @@
(r/map->Match data)) (r/map->Match data))
(defn resolve (defn resolve
([router id] (resolve router id {} {})) ([router id] (resolve router id {}))
([router id path-params] (resolve router id path-params {})) ([router id params]
([router id path-params query-params] (when-let [match (r/match-by-name router id)]
(when-let [match (r/match-by-name router id path-params)] (r/match->path match params))))
(r/match->path match query-params))))
(defn create (defn create
[routes] [routes]
@ -63,6 +62,9 @@
(defn navigated (defn navigated
[match] [match]
(ptk/reify ::navigated (ptk/reify ::navigated
IDeref
(-deref [_] match)
ev/Event ev/Event
(-data [_] (-data [_]
(let [route (dm/get-in match [:data :name]) (let [route (dm/get-in match [:data :name])
@ -77,25 +79,29 @@
(assoc :route match) (assoc :route match)
(dissoc :exception))))) (dissoc :exception)))))
(defn navigate* (defn navigate
[id path-params query-params replace] [id params & {:keys [::replace ::new-window] :as options}]
(ptk/reify ::navigate (ptk/reify ::navigate
IDeref IDeref
(-deref [_] (-deref [_]
{:id id {:id id
:path-params path-params :params params
:query-params query-params :options options})
:replace replace})
ptk/EffectEvent ptk/EffectEvent
(effect [_ state _] (effect [_ state _]
(let [router (:router state) (let [router (:router state)
history (:history state) history (:history state)
path (resolve router id path-params query-params)] 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 (ts/asap
#(if ^boolean replace #(if ^boolean replace
(bhistory/replace-token! history path) (bhistory/replace-token! history path)
(bhistory/set-token! history path))))))) (bhistory/set-token! history path))))))))
(defn assign-exception (defn assign-exception
[error] [error]
@ -107,27 +113,14 @@
(assoc state :exception error))))) (assoc state :exception error)))))
(defn nav (defn nav
([id] (nav id nil nil)) ([id] (navigate id nil))
([id path-params] (nav id path-params nil)) ([id params] (navigate id params))
([id path-params query-params] (navigate* id path-params query-params false))) ([id params & {:as options}]
(navigate id params options)))
(defn nav' (defn get-params
([id] (nav id nil nil)) [state]
([id path-params] (nav id path-params nil)) (dm/get-in state [:route :params :query]))
([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 nav-back (defn nav-back
[] []

View file

@ -60,7 +60,7 @@
:app.main.data.workspace.persistence/update-persistence-status :app.main.data.workspace.persistence/update-persistence-status
:app.main.data.websocket/send-message :app.main.data.websocket/send-message
:app.main.data.workspace.notifications/handle-pointer-send :app.main.data.workspace.notifications/handle-pointer-send
:app.util.router/assign-exception}] :app.main.router/assign-exception}]
(->> (rx/merge (->> (rx/merge
(->> stream (->> stream
(rx/filter (ptk/type? :app.main.data.changes/commit)) (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)) (mf/lazy-component app.main.ui.auth.verify-token/verify-token))
(def viewer-page (def viewer-page
(mf/lazy-component app.main.ui.viewer/viewer)) (mf/lazy-component app.main.ui.viewer/viewer*))
(def dashboard-page (def dashboard-page
(mf/lazy-component app.main.ui.dashboard/dashboard*)) (mf/lazy-component app.main.ui.dashboard/dashboard*))
@ -42,7 +42,7 @@
(mf/lazy-component app.main.ui.settings/settings)) (mf/lazy-component app.main.ui.settings/settings))
(def workspace-page (def workspace-page
(mf/lazy-component app.main.ui.workspace/workspace)) (mf/lazy-component app.main.ui.workspace/workspace*))
(mf/defc team-container* (mf/defc team-container*
{::mf/props :obj {::mf/props :obj
@ -62,13 +62,13 @@
;; all dom tree instead of simple rerender. ;; all dom tree instead of simple rerender.
[:* {:key (str team-id)} children]]]))) [:* {:key (str team-id)} children]]])))
(mf/defc page-container* (mf/defc page*
{::mf/props :obj {::mf/props :obj
::mf/private true} ::mf/private true}
[{:keys [route profile]}] [{:keys [route profile]}]
(let [{:keys [data params]} route (let [{:keys [data params]} route
props (get profile :props) props (get profile :props)
route-name (get data :name) section (get data :name)
show-question-modal? show-question-modal?
@ -95,7 +95,7 @@
(not= "0.0" (:main cf/version)))] (not= "0.0" (:main cf/version)))]
[:& (mf/provider ctx/current-route) {:value route} [:& (mf/provider ctx/current-route) {:value route}
(case route-name (case section
(:auth-login (:auth-login
:auth-register :auth-register
:auth-register-validate :auth-register-validate
@ -119,20 +119,20 @@
[:& icons-preview]) [:& icons-preview])
(:dashboard-search (:dashboard-search
:dashboard-projects :dashboard-recent
:dashboard-files :dashboard-files
:dashboard-libraries :dashboard-libraries
:dashboard-fonts :dashboard-fonts
:dashboard-font-providers :dashboard-font-providers
:dashboard-team-members :dashboard-members
:dashboard-team-invitations :dashboard-invitations
:dashboard-team-webhooks :dashboard-webhooks
:dashboard-team-settings) :dashboard-settings)
(let [team-id (some-> params :path :team-id uuid) (let [params (get params :query)
project-id (some-> params :path :project-id uuid) team-id (some-> params :team-id uuid)
search-term (some-> params :query :search-term) project-id (some-> params :project-id uuid)
plugin-url (some-> params :query :plugin)] search-term (some-> params :search-term)
plugin-url (some-> params :plugin)]
[:? [:?
#_[:& app.main.ui.releases/release-notes-modal {:version "2.3"}] #_[:& app.main.ui.releases/release-notes-modal {:version "2.3"}]
#_[:& app.main.ui.onboarding/onboarding-templates-modal] #_[:& app.main.ui.onboarding/onboarding-templates-modal]
@ -154,13 +154,105 @@
[:> team-container* {:team-id team-id} [:> team-container* {:team-id team-id}
[:> dashboard-page {:profile profile [:> 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 :team-id team-id
:search-term search-term :search-term search-term
:plugin-url plugin-url :plugin-url plugin-url
:project-id project-id}]]]) :project-id project-id}]]])
:workspace :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) (let [project-id (some-> params :path :project-id uuid)
file-id (some-> params :path :file-id uuid) file-id (some-> params :path :file-id uuid)
page-id (some-> params :query :page-id uuid) page-id (some-> params :query :page-id uuid)
@ -181,14 +273,38 @@
[:& release-notes-modal {:version (:main cf/version)}])) [:& release-notes-modal {:version (:main cf/version)}]))
[:* [:*
[:& workspace-page {:project-id project-id [:> workspace-page {:project-id project-id
:file-id file-id :file-id file-id
:page-id page-id :page-id page-id
:layout-name layout :layout-name layout
:key file-id}]]]) :key file-id}]]])
:viewer :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 (let [{:keys [query-params path-params]} route
{:keys [index share-id section page-id interactions-mode frame-id share] {:keys [index share-id section page-id interactions-mode frame-id share]
:or {section :interactions interactions-mode :show-on-click}} query-params :or {section :interactions interactions-mode :show-on-click}} query-params
@ -200,14 +316,14 @@
[:div.main-message (tr "viewer.breaking-change.message")] [:div.main-message (tr "viewer.breaking-change.message")]
[:div.desc-message (tr "viewer.breaking-change.description")]] [:div.desc-message (tr "viewer.breaking-change.description")]]
[:& viewer-page [:> viewer-page
{:page-id page-id {:page-id page-id
:file-id file-id :file-id file-id
:section section :section section
:index index :index index
:share-id share-id :share-id share-id
:interactions-mode (keyword interactions-mode) :interactions-mode (keyword interactions-mode)
:interactions-show? (case (keyword interactions-mode) :show-interactions (case (keyword interactions-mode)
:hide false :hide false
:show true :show true
:show-on-click false) :show-on-click false)
@ -237,4 +353,4 @@
[:> error-boundary* {:fallback static/internal-error*} [:> error-boundary* {:fallback static/internal-error*}
[:& notifications/current-notification] [:& notifications/current-notification]
(when route (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.auth :as da]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.button-link :as bl] [app.main.ui.components.button-link :as bl]
[app.main.ui.components.forms :as fm] [app.main.ui.components.forms :as fm]
@ -22,7 +23,6 @@
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
[app.util.keyboard :as k] [app.util.keyboard :as k]
[app.util.router :as rt]
[app.util.storage :as s] [app.util.storage :as s]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -124,7 +124,7 @@
(mf/use-fn (mf/use-fn
(fn [data] (fn [data]
(when-let [token (:invitation-token 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 on-success
(fn [data] (fn [data]
@ -283,7 +283,7 @@
[{:keys [params] :as props}] [{:keys [params] :as props}]
(let [go-register (let [go-register
(mf/use-fn (mf/use-fn
#(st/emit! (rt/nav :auth-register {} params)))] #(st/emit! (rt/nav :auth-register params)))]
[:div {:class (stl/css :auth-form-wrapper)} [:div {:class (stl/css :auth-form-wrapper)}
[:h1 {:class (stl/css :auth-title) [:h1 {:class (stl/css :auth-title)

View file

@ -10,10 +10,10 @@
[app.common.schema :as sm] [app.common.schema :as sm]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.data.profile :as du] [app.main.data.profile :as du]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.forms :as fm] [app.main.ui.components.forms :as fm]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def ^:private schema:recovery-form (def ^:private schema:recovery-form

View file

@ -10,11 +10,11 @@
[app.common.schema :as sm] [app.common.schema :as sm]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.data.profile :as du] [app.main.data.profile :as du]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.forms :as fm] [app.main.ui.components.forms :as fm]
[app.main.ui.components.link :as lk] [app.main.ui.components.link :as lk]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))

View file

@ -13,13 +13,13 @@
[app.main.data.auth :as da] [app.main.data.auth :as da]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.auth.login :as login] [app.main.ui.auth.login :as login]
[app.main.ui.components.forms :as fm] [app.main.ui.components.forms :as fm]
[app.main.ui.components.link :as lk] [app.main.ui.components.link :as lk]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[app.util.storage :as storage] [app.util.storage :as storage]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -74,7 +74,7 @@
on-success (fn [data] on-success (fn [data]
(if (fn? on-success-callback) (if (fn? on-success-callback)
(on-success-callback data) (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) (->> (rp/cmd! :prepare-register-profile cdata)
(rx/map #(merge % params)) (rx/map #(merge % params))
@ -131,7 +131,7 @@
[:div {:class (stl/css :links)} [:div {:class (stl/css :links)}
[:div {:class (stl/css :account)} [:div {:class (stl/css :account)}
[:span {:class (stl/css :account-text)} (tr "auth.already-have-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) :class (stl/css :account-link)
:data-testid "login-here-link"} :data-testid "login-here-link"}
(tr "auth.login-here")]] (tr "auth.login-here")]]
@ -191,7 +191,7 @@
(cond (cond
(some? (:invitation-token params)) (some? (:invitation-token params))
(let [token (: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) (:is-active params)
(st/emit! (da/login-from-register)) (st/emit! (da/login-from-register))
@ -257,7 +257,7 @@
[:div {:class (stl/css :links)} [:div {:class (stl/css :links)}
[:div {:class (stl/css :go-back)} [: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)} :class (stl/css :go-back-link)}
(tr "labels.go-back")]]]]) (tr "labels.go-back")]]]])

View file

@ -7,15 +7,16 @@
(ns app.main.ui.auth.verify-token (ns app.main.ui.auth.verify-token
(:require (:require
[app.main.data.auth :as da] [app.main.data.auth :as da]
[app.main.data.common :as dcm]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.data.profile :as du] [app.main.data.profile :as du]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.ds.product.loader :refer [loader*]] [app.main.ui.ds.product.loader :refer [loader*]]
[app.main.ui.static :as static] [app.main.ui.static :as static]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[app.util.timers :as ts] [app.util.timers :as ts]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -43,15 +44,16 @@
[tdata] [tdata]
(case (:state tdata) (case (:state tdata)
:created :created
(let [team-id (:team-id tdata)]
(st/emit! (st/emit!
(ntf/success (tr "auth.notifications.team-invitation-accepted")) (ntf/success (tr "auth.notifications.team-invitation-accepted"))
(du/fetch-profile) (du/fetch-profile)
(rt/nav :dashboard-projects {:team-id (:team-id tdata)})) (dcm/go-to-dashboard-recent :team-id team-id)))
:pending :pending
(let [token (:invitation-token tdata) (let [token (:invitation-token tdata)
route-id (:redirect-to tdata :auth-register)] 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 (defmethod handle-token :default
[_tdata] [_tdata]

View file

@ -253,8 +253,8 @@
:disabled disabled?}]]])) :disabled disabled?}]]]))
(mf/defc comment-item (mf/defc comment-item
[{:keys [comment thread users origin] :as props}] [{:keys [comment thread profiles origin] :as props}]
(let [owner (get users (:owner-id comment)) (let [owner (get profiles (:owner-id comment))
profile (mf/deref refs/profile) profile (mf/deref refs/profile)
options (mf/deref comments-local-options) options (mf/deref comments-local-options)
edition? (mf/use-state false) edition? (mf/use-state false)
@ -384,7 +384,7 @@
(mf/defc thread-comments (mf/defc thread-comments
{::mf/wrap [mf/memo]} {::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) (let [ref (mf/use-ref)
thread-id (:id thread) thread-id (:id thread)
thread-pos (:position thread) thread-pos (:position thread)
@ -435,13 +435,13 @@
[:div {:class (stl/css :comments)} [:div {:class (stl/css :comments)}
[:& comment-item {:comment comment [:& comment-item {:comment comment
:users users :profiles profiles
:thread thread :thread thread
:origin origin}] :origin origin}]
(for [item (rest comments)] (for [item (rest comments)]
[:* {:key (dm/str (:id item))} [:* {:key (dm/str (:id item))}
[:& comment-item {:comment item [:& comment-item {:comment item
:users users :profiles profiles
:origin origin}]])] :origin origin}]])]
[:& reply-form {:thread thread}] [:& reply-form {:thread thread}]
[:div {:ref ref}]]))) [:div {:ref ref}]])))
@ -573,8 +573,8 @@
[:span (:seqn thread)]])) [:span (:seqn thread)]]))
(mf/defc comment-thread (mf/defc comment-thread
[{:keys [item users on-click]}] [{:keys [item profiles on-click]}]
(let [owner (get users (:owner-id item)) (let [owner (get profiles (:owner-id item))
on-click* on-click*
(mf/use-fn (mf/use-fn
(mf/deps item) (mf/deps item)
@ -613,7 +613,7 @@
[:span {:class (stl/css :new-replies)} (str unread " new replies")]))])]])) [:span {:class (stl/css :new-replies)} (str unread " new replies")]))])]]))
(mf/defc comment-thread-group (mf/defc comment-thread-group
[{:keys [group users on-thread-click]}] [{:keys [group profiles on-thread-click]}]
[:div {:class (stl/css :thread-group)} [:div {:class (stl/css :thread-group)}
(if (:file-name group) (if (:file-name group)
[:div {:class (stl/css :section-title) [:div {:class (stl/css :section-title)
@ -631,5 +631,5 @@
[:& comment-thread [:& comment-thread
{:item item {:item item
:on-click on-thread-click :on-click on-thread-click
:users users :profiles profiles
:key (:id item)}])]]) :key (:id item)}])]])

View file

@ -16,6 +16,7 @@
[app.main.data.notifications :as notif] [app.main.data.notifications :as notif]
[app.main.data.plugins :as dp] [app.main.data.plugins :as dp]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.context :as ctx] [app.main.ui.context :as ctx]
[app.main.ui.dashboard.files :refer [files-section*]] [app.main.ui.dashboard.files :refer [files-section*]]
@ -33,7 +34,6 @@
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[app.util.object :as obj] [app.util.object :as obj]
[app.util.router :as rt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[goog.events :as events] [goog.events :as events]
[okulary.core :as l] [okulary.core :as l]
@ -84,7 +84,7 @@
:on-click clear-selected-fn :on-click clear-selected-fn
:ref container} :ref container}
(case section (case section
:dashboard-projects :dashboard-recent
[:* [:*
[:> projects-section* [:> projects-section*
{:team team {:team team
@ -126,16 +126,16 @@
[:> libraries-page* {:team team [:> libraries-page* {:team team
:default-project default-project}] :default-project default-project}]
:dashboard-team-members :dashboard-members
[:> team-members-page* {:team team :profile profile}] [:> team-members-page* {:team team :profile profile}]
:dashboard-team-invitations :dashboard-invitations
[:> team-invitations-page* {:team team}] [:> team-invitations-page* {:team team}]
:dashboard-team-webhooks :dashboard-webhooks
[:> webhooks-page* {:team team}] [:> webhooks-page* {:team team}]
:dashboard-team-settings :dashboard-settings
[:> team-settings-page* {:team team :profile profile}] [:> team-settings-page* {:team team :profile profile}]
nil)])) nil)]))
@ -151,8 +151,9 @@
(st/emit! (st/emit!
(dp/delay-open-plugin plugin) (dp/delay-open-plugin plugin)
(rt/nav :workspace (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! create-file!
(fn [plugin] (fn [plugin]
@ -182,11 +183,11 @@
:on-accept :on-accept
#(do (preg/install-plugin! plugin) #(do (preg/install-plugin! plugin)
(st/emit! (modal/hide) (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))) (open-try-out-dialog plugin)))
:on-close :on-close
#(st/emit! (modal/hide) #(st/emit! (modal/hide)
(rt/nav :dashboard-projects {:team-id team-id}))}))] (rt/nav :dashboard-recent {:team-id team-id}))}))]
(mf/with-layout-effect (mf/with-layout-effect
[plugin-url team-id project-id] [plugin-url team-id project-id]
@ -204,7 +205,7 @@
(mf/defc dashboard* (mf/defc dashboard*
{::mf/props :obj} {::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) (let [team (mf/deref refs/team)
projects (mf/deref refs/projects) projects (mf/deref refs/projects)
@ -253,7 +254,7 @@
:project project :project project
:default-project default-project :default-project default-project
:profile profile :profile profile
:section route-name :section section
:search-term search-term}] :search-term search-term}]
(when (seq projects) (when (seq projects)
[:> dashboard-content* [:> dashboard-content*
@ -261,6 +262,6 @@
:profile profile :profile profile
:project project :project project
:default-project default-project :default-project default-project
:section route-name :section section
:search-term search-term :search-term search-term
:team team}])]])) :team team}])]]))

View file

@ -63,7 +63,7 @@
(mf/defc comments-section (mf/defc comments-section
[{:keys [profile team show? on-hide-comments]}] [{:keys [profile team show? on-hide-comments]}]
(let [threads-map (mf/deref refs/comment-threads) (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) team-id (:id team)
tgroups (->> (vals threads-map) tgroups (->> (vals threads-map)
@ -114,13 +114,13 @@
{:group (first tgroups) {:group (first tgroups)
:on-thread-click on-navigate :on-thread-click on-navigate
:show-file-name true :show-file-name true
:users users}] :profiles profiles}]
(for [tgroup (rest tgroups)] (for [tgroup (rest tgroups)]
[:& cmt/comment-thread-group [:& cmt/comment-thread-group
{:group tgroup {:group tgroup
:on-thread-click on-navigate :on-thread-click on-navigate
:show-file-name true :show-file-name true
:users users :profiles profiles
:key (:page-id tgroup)}])] :key (:page-id tgroup)}])]
[:div {:class (stl/css :thread-groups-placeholder)} [:div {:class (stl/css :thread-groups-placeholder)}

View file

@ -15,12 +15,12 @@
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.context-menu-a11y :refer [context-menu*]] [app.main.ui.components.context-menu-a11y :refer [context-menu*]]
[app.main.ui.context :as ctx] [app.main.ui.context :as ctx]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -85,10 +85,9 @@
on-new-tab on-new-tab
(fn [_] (fn [_]
(let [path-params {:project-id (:project-id file) (st/emit! (dcm/go-to-workspace
:file-id (:id file)}] {:file-id (:id file)
(st/emit! (rt/nav-new-window* {:rname :workspace ::rt/new-window true})))
:path-params path-params}))))
on-duplicate on-duplicate
(fn [_] (fn [_]
@ -134,7 +133,9 @@
(st/emit! (ntf/success (tr "dashboard.success-move-files"))) (st/emit! (ntf/success (tr "dashboard.success-move-files")))
(st/emit! (ntf/success (tr "dashboard.success-move-file")))) (st/emit! (ntf/success (tr "dashboard.success-move-file"))))
(if (or navigate (not= team-id current-team-id)) (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) (st/emit! (dd/fetch-recent-files)
(dd/clear-selected-files)))) (dd/clear-selected-files))))

View file

@ -7,8 +7,10 @@
(ns app.main.ui.dashboard.files (ns app.main.ui.dashboard.files
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.main.data.common :as dcm]
[app.main.data.dashboard :as dd] [app.main.data.dashboard :as dd]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.project :as dpj]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.dashboard.grid :refer [grid]] [app.main.ui.dashboard.grid :refer [grid]]
@ -21,7 +23,6 @@
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[app.util.router :as rt]
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -32,7 +33,10 @@
{::mf/props :obj {::mf/props :obj
::mf/private true} ::mf/private true}
[{:keys [project create-fn can-edit]}] [{:keys [project create-fn can-edit]}]
(let [local (mf/use-state (let [project-id (:id project)
local
(mf/use-state
{:menu-open false {:menu-open false
:edition false}) :edition false})
@ -63,9 +67,9 @@
on-import on-import
(mf/use-fn (mf/use-fn
(mf/deps (:id project)) (mf/deps project-id)
(fn [] (fn []
(st/emit! (dd/fetch-files {:project-id (:id project)}) (st/emit! (dpj/fetch-files project-id)
(dd/clear-selected-files))))] (dd/clear-selected-files))))]
@ -153,11 +157,10 @@
on-file-created on-file-created
(mf/use-fn (mf/use-fn
(fn [data] (fn [file-data]
(let [pparams {:project-id (:project-id data) (let [file-id (:id file-data)
:file-id (:id data)} page-id (get-in file-data [:pages 0])]
qparams {:page-id (get-in data [:data :pages 0])}] (st/emit! (dcm/go-to-workspace :file-id file-id :page-id page-id)))))
(st/emit! (rt/nav :workspace pparams qparams)))))
create-file create-file
(mf/use-fn (mf/use-fn
@ -176,7 +179,7 @@
(dom/set-html-title (tr "title.dashboard.files" pname))))) (dom/set-html-title (tr "title.dashboard.files" pname)))))
(mf/with-effect [project-id] (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))) (dd/clear-selected-files)))
[:* [:*

View file

@ -12,8 +12,11 @@
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.logging :as log] [app.common.logging :as log]
[app.config :as cf] [app.config :as cf]
[app.main.data.common :as dcm]
[app.main.data.dashboard :as dd] [app.main.data.dashboard :as dd]
[app.main.data.notifications :as ntf] [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.features :as features]
[app.main.fonts :as fonts] [app.main.fonts :as fonts]
[app.main.rasterizer :as thr] [app.main.rasterizer :as thr]
@ -79,8 +82,7 @@
revn (get file :revn) revn (get file :revn)
thumbnail-id (get file :thumbnail-id) thumbnail-id (get file :thumbnail-id)
;; FIXME: revisit maybe bug bg-color (dm/get-in file [:data :background])
bg-color (dm/get-in file [:data :options :background])
container (mf/use-ref) container (mf/use-ref)
visible? (h/use-visible container :once? true)] visible? (h/use-visible container :once? true)]
@ -270,12 +272,12 @@
on-navigate on-navigate
(mf/use-fn (mf/use-fn
(mf/deps file) (mf/deps file-id)
(fn [event] (fn [event]
(let [menu-icon (mf/ref-val menu-ref) (let [menu-icon (mf/ref-val menu-ref)
target (dom/get-target event)] target (dom/get-target event)]
(when-not (dom/child? target menu-icon) (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 on-drag-start
(mf/use-fn (mf/use-fn
@ -427,10 +429,12 @@
on-finish-import on-finish-import
(mf/use-fn (mf/use-fn
(fn [] (fn []
(st/emit! (dd/fetch-files {:project-id project-id}) (st/emit! (dpj/fetch-files project-id)
(dd/fetch-shared-files) (dtm/fetch-shared-files)
(dd/clear-selected-files)))) (dd/clear-selected-files))))
import-files (use-import-file project-id on-finish-import) import-files (use-import-file project-id on-finish-import)
on-drag-enter on-drag-enter

View file

@ -8,6 +8,7 @@
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.main.data.dashboard :as dd] [app.main.data.dashboard :as dd]
[app.main.data.team :as dtm]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.dashboard.grid :refer [grid]] [app.main.ui.dashboard.grid :refer [grid]]
@ -41,7 +42,7 @@
(dom/set-html-title (tr "title.dashboard.shared-libraries" tname)))) (dom/set-html-title (tr "title.dashboard.shared-libraries" tname))))
(mf/with-effect [team] (mf/with-effect [team]
(st/emit! (dd/fetch-shared-files) (st/emit! (dtm/fetch-shared-files)
(dd/clear-selected-files))) (dd/clear-selected-files)))
[:* [:*

View file

@ -6,6 +6,7 @@
(ns app.main.ui.dashboard.project-menu (ns app.main.ui.dashboard.project-menu
(:require (:require
[app.main.data.common :as dcm]
[app.main.data.dashboard :as dd] [app.main.data.dashboard :as dd]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
@ -16,7 +17,6 @@
[app.main.ui.dashboard.import :as udi] [app.main.ui.dashboard.import :as udi]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(mf/defc project-menu* (mf/defc project-menu*
@ -32,9 +32,9 @@
on-duplicate-success on-duplicate-success
(fn [new-project] (fn [new-project]
(st/emit! (ntf/success (tr "dashboard.success-duplicate-project")) (st/emit! (ntf/success (tr "dashboard.success-duplicate-project"))
(rt/nav :dashboard-files (dcm/go-to-dashboard-files
{:team-id (:team-id new-project) :team-id (:team-id new-project)
:project-id (:id new-project)}))) :project-id (:id new-project))))
on-duplicate on-duplicate
(fn [] (fn []
@ -46,7 +46,7 @@
on-move-success on-move-success
(fn [team-id] (fn [team-id]
(st/emit! (dd/go-to-projects team-id))) (st/emit! (dcm/go-to-dashboard-recent :team-id team-id)))
on-move on-move
(fn [team-id] (fn [team-id]
@ -57,9 +57,10 @@
delete-fn delete-fn
(fn [_] (fn [_]
(let [team-id (:team-id project)]
(st/emit! (ntf/success (tr "dashboard.success-delete-project")) (st/emit! (ntf/success (tr "dashboard.success-delete-project"))
(dd/delete-project project) (dd/delete-project project)
(dd/go-to-projects (:team-id project)))) (dcm/go-to-dashboard-recent :team-id team-id))))
on-delete on-delete
#(st/emit! #(st/emit!

View file

@ -8,9 +8,11 @@
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.main.data.common :as dcm]
[app.main.data.dashboard :as dd] [app.main.data.dashboard :as dd]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.project :as dpj]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.dashboard.grid :refer [line-grid]] [app.main.ui.dashboard.grid :refer [line-grid]]
@ -23,7 +25,6 @@
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[app.util.router :as rt]
[app.util.storage :as storage] [app.util.storage :as storage]
[app.util.time :as dt] [app.util.time :as dt]
[cuerdas.core :as str] [cuerdas.core :as str]
@ -62,7 +63,7 @@
{::mf/wrap [mf/memo] {::mf/wrap [mf/memo]
::mf/props :obj} ::mf/props :obj}
[{:keys [team on-close]}] [{: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 on-invite
(mf/use-fn (mf/use-fn
@ -105,7 +106,6 @@
(let [locale (mf/deref i18n/locale) (let [locale (mf/deref i18n/locale)
project-id (:id project) project-id (:id project)
team-id (:id team)
file-count (or (:count project) 0) file-count (or (:count project) 0)
is-draft? (:is-default project) is-draft? (:is-default project)
@ -124,11 +124,10 @@
on-nav on-nav
(mf/use-fn (mf/use-fn
(mf/deps project-id team-id) (mf/deps project-id)
(fn [] (fn []
(st/emit! (rt/nav :dashboard-files (st/emit! (dcm/go-to-dashboard-files :project-id project-id))))
{:team-id team-id
:project-id project-id}))))
toggle-pin toggle-pin
(mf/use-fn (mf/use-fn
(mf/deps project) (mf/deps project)
@ -171,11 +170,9 @@
on-file-created on-file-created
(mf/use-fn (mf/use-fn
(fn [data] (fn [{:keys [id data]}]
(let [pparams {:project-id (:project-id data) (let [page-id (get-in data [:pages 0])]
:file-id (:id data)} (st/emit! (dcm/go-to-workspace :file-id id :page-id page-id)))))
qparams {:page-id (get-in data [:data :pages 0])}]
(st/emit! (rt/nav :workspace pparams qparams)))))
create-file create-file
(mf/use-fn (mf/use-fn
@ -194,9 +191,9 @@
on-import on-import
(mf/use-fn (mf/use-fn
(mf/deps project-id (:id team)) (mf/deps project-id)
(fn [] (fn []
(st/emit! (dd/fetch-files {:project-id project-id}) (st/emit! (dpj/fetch-files project-id)
(dd/fetch-recent-files) (dd/fetch-recent-files)
(dd/fetch-projects) (dd/fetch-projects)
(dd/clear-selected-files)))) (dd/clear-selected-files))))

View file

@ -11,12 +11,14 @@
[app.common.spec :as us] [app.common.spec :as us]
[app.config :as cf] [app.config :as cf]
[app.main.data.auth :as da] [app.main.data.auth :as da]
[app.main.data.common :as dcm]
[app.main.data.dashboard :as dd] [app.main.data.dashboard :as dd]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.data.team :as dtm] [app.main.data.team :as dtm]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]] [app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]]
[app.main.ui.components.link :refer [link]] [app.main.ui.components.link :refer [link]]
@ -29,7 +31,6 @@
[app.util.dom.dnd :as dnd] [app.util.dom.dnd :as dnd]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[app.util.router :as rt]
[app.util.timers :as ts] [app.util.timers :as ts]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
@ -73,32 +74,34 @@
edit-id (:project-for-edit dstate) edit-id (:project-for-edit dstate)
local* (mf/use-state local* (mf/use-state
{:menu-open false #(do {:menu-open false
:menu-pos nil :menu-pos nil
:edition? (= (:id item) edit-id) :edition? (= (:id item) edit-id)
:dragging? false}) :dragging? false}))
local @local* local (deref local*)
project-id (get item :id)
on-click on-click
(mf/use-fn (mf/use-fn
(mf/deps item) (mf/deps project-id)
(fn [] (fn []
(st/emit! (dd/go-to-files (:id item))))) (st/emit! (dcm/go-to-dashboard-files :project-id project-id))))
on-key-down on-key-down
(mf/use-fn (mf/use-fn
(mf/deps item) (mf/deps project-id)
(fn [event] (fn [event]
(when (kbd/enter? event) (when (kbd/enter? event)
(st/emit! (dd/go-to-files (:id item)) (st/emit!
(dcm/go-to-dashboard-files :project-id project-id)
(ts/schedule-on-idle (ts/schedule-on-idle
(fn [] (fn []
(let [project-title (dom/get-element (str (:id item)))] (when-let [title (dom/get-element (str project-id))]
(when project-title (dom/set-attribute! title "tabindex" "0")
(dom/set-attribute! project-title "tabindex" "0") (dom/focus! title)
(dom/focus! project-title) (dom/set-attribute! title "tabindex" "-1"))))))))
(dom/set-attribute! project-title "tabindex" "-1")))))))))
on-menu-click on-menu-click
(mf/use-fn (mf/use-fn
@ -148,9 +151,10 @@
on-drop-success on-drop-success
(mf/use-fn (mf/use-fn
(mf/deps (:id item)) (mf/deps project-id)
#(st/emit! (ntf/success (tr "dashboard.success-move-file")) (fn [_]
(dd/go-to-files (:id item)))) (st/emit! (dcm/go-to-dashboard-files :project-id project-id)
(ntf/success (tr "dashboard.success-move-file")))))
on-drop on-drop
(mf/use-fn (mf/use-fn
@ -201,19 +205,18 @@
on-search-change on-search-change
(mf/use-fn (mf/use-fn
(mf/deps team-id)
(fn [event] (fn [event]
(let [value (dom/get-target-val 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 on-clear-click
(mf/use-fn (mf/use-fn
(mf/deps team-id) (mf/deps team-id)
(fn [e] (fn [e]
(emit! (dcm/go-to-dashboard-search))
(let [search-input (dom/get-element "search-input")] (let [search-input (dom/get-element "search-input")]
(dom/clean-value! search-input) (dom/clean-value! search-input)
(dom/focus! search-input) (dom/focus! search-input)
(emit! (dd/go-to-search))
(dom/prevent-default e) (dom/prevent-default e)
(dom/stop-propagation e)))) (dom/stop-propagation e))))
@ -278,7 +281,8 @@
(fn [event] (fn [event]
(let [team-id (-> (dom/get-current-target event) (let [team-id (-> (dom/get-current-target event)
(dom/get-data "value"))] (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 handle-select-default
(mf/use-fn (mf/use-fn
@ -343,10 +347,10 @@
(mf/defc team-options-dropdown (mf/defc team-options-dropdown
[{:keys [team profile] :as props}] [{:keys [team profile] :as props}]
(let [go-members #(st/emit! (dd/go-to-team-members)) (let [go-members #(st/emit! (dcm/go-to-dashboard-members))
go-invitations #(st/emit! (dd/go-to-team-invitations)) go-invitations #(st/emit! (dcm/go-to-dashboard-invitations))
go-webhooks #(st/emit! (dd/go-to-team-webhooks)) go-webhooks #(st/emit! (dcm/go-to-dashboard-webhooks))
go-settings #(st/emit! (dd/go-to-team-settings)) go-settings #(st/emit! (dcm/go-to-dashboard-settings))
members (get team :members) members (get team :members)
permissions (get team :permissions) permissions (get team :permissions)
@ -356,9 +360,9 @@
on-success on-success
(fn [] (fn []
;; FIXME: this should be handled in the event, not here ;; FIXME: this should be handled in the event, not here
(st/emit! (dd/go-to-projects (:default-team-id profile)) (let [team-id (:default-team-id profile)]
(modal/hide) (rx/of (dcm/go-to-dashboard-recent :team-id team-id)
(dtm/fetch-teams))) (modal/hide))))
on-error on-error
(fn [{:keys [code] :as error}] (fn [{:keys [code] :as error}]
@ -692,38 +696,38 @@
(let [default-project-id (let [default-project-id
(get default-project :id) (get default-project :id)
projects? (= section :dashboard-projects) team-id (get team :id)
projects? (= section :dashboard-recent)
fonts? (= section :dashboard-fonts) fonts? (= section :dashboard-fonts)
libs? (= section :dashboard-libraries) libs? (= section :dashboard-libraries)
drafts? (and (= section :dashboard-files) drafts? (and (= section :dashboard-files)
(= (:id project) default-project-id)) (= (:id project) default-project-id))
go-projects go-projects
(mf/use-fn (mf/use-fn #(st/emit! (dcm/go-to-dashboard-recent)))
(mf/deps team)
#(st/emit! (rt/nav :dashboard-projects {:team-id (:id team)})))
go-projects-with-key go-projects-with-key
(mf/use-fn (mf/use-fn
(mf/deps team) (mf/deps team-id)
#(st/emit! (rt/nav :dashboard-projects {:team-id (:id team)}) (fn []
(st/emit! (dcm/go-to-dashboard-recent :team-id team-id)
(ts/schedule-on-idle (ts/schedule-on-idle
(fn [] (fn []
(let [projects-title (dom/get-element "dashboard-projects-title")] (when-let [projects-title (dom/get-element "dashboard-projects-title")]
(when projects-title
(dom/set-attribute! projects-title "tabindex" "0") (dom/set-attribute! projects-title "tabindex" "0")
(dom/focus! projects-title) (dom/focus! projects-title)
(dom/set-attribute! projects-title "tabindex" "-1"))))))) (dom/set-attribute! projects-title "tabindex" "-1")))))))
go-fonts go-fonts
(mf/use-fn (mf/use-fn
(mf/deps team) (mf/deps team-id)
#(st/emit! (rt/nav :dashboard-fonts {:team-id (:id team)}))) #(st/emit! (dcm/go-to-dashboard-fonts :team-id team-id)))
go-fonts-with-key go-fonts-with-key
(mf/use-fn (mf/use-fn
(mf/deps team) (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 (ts/schedule-on-idle
(fn [] (fn []
(let [font-title (dom/get-element "dashboard-fonts-title")] (let [font-title (dom/get-element "dashboard-fonts-title")]
@ -733,34 +737,31 @@
(dom/set-attribute! font-title "tabindex" "-1"))))))) (dom/set-attribute! font-title "tabindex" "-1")))))))
go-drafts go-drafts
(mf/use-fn (mf/use-fn
(mf/deps team default-project-id) (mf/deps team-id default-project-id)
(fn [] (fn []
(st/emit! (rt/nav :dashboard-files (st/emit! (dcm/go-to-dashboard-files :team-id team-id :project-id default-project-id))))
{:team-id (:id team)
:project-id default-project-id}))))
go-drafts-with-key go-drafts-with-key
(mf/use-fn (mf/use-fn
(mf/deps team default-project-id) (mf/deps team-id default-project-id)
#(st/emit! (rt/nav :dashboard-files {:team-id (:id team) (fn []
:project-id default-project-id}) (st/emit! (dcm/go-to-dashboard-files :team-id team-id :project-id default-project-id))
(ts/schedule-on-idle (ts/schedule-on-idle
(fn [] (fn []
(let [drafts-title (dom/get-element "dashboard-drafts-title")] (when-let [title (dom/get-element "dashboard-drafts-title")]
(when drafts-title (dom/set-attribute! title "tabindex" "0")
(dom/set-attribute! drafts-title "tabindex" "0") (dom/focus! title)
(dom/focus! drafts-title) (dom/set-attribute! title "tabindex" "-1"))))))
(dom/set-attribute! drafts-title "tabindex" "-1")))))))
go-libs go-libs
(mf/use-fn (mf/use-fn
(mf/deps team) (mf/deps team-id)
#(st/emit! (rt/nav :dashboard-libraries {:team-id (:id team)}))) #(st/emit! (dcm/go-to-dashboard-libraries :team-id team-id)))
go-libs-with-key go-libs-with-key
(mf/use-fn (mf/use-fn
(mf/deps team) (mf/deps team-id)
#(st/emit! (rt/nav :dashboard-libraries {:team-id (:id team)}) #(st/emit! (dcm/go-to-dashboard-libraries :team-id team-id)
(ts/schedule-on-idle (ts/schedule-on-idle
(fn [] (fn []
(let [libs-title (dom/get-element "dashboard-libraries-title")] (let [libs-title (dom/get-element "dashboard-libraries-title")]

View file

@ -11,7 +11,7 @@
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.config :as cfg] [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.event :as ev]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
@ -62,10 +62,10 @@
{::mf/wrap [mf/memo] {::mf/wrap [mf/memo]
::mf/props :obj} ::mf/props :obj}
[{:keys [section team]}] [{:keys [section team]}]
(let [on-nav-members (mf/use-fn #(st/emit! (dd/go-to-team-members))) (let [on-nav-members (mf/use-fn #(st/emit! (dcm/go-to-dashboard-members)))
on-nav-settings (mf/use-fn #(st/emit! (dd/go-to-team-settings))) on-nav-settings (mf/use-fn #(st/emit! (dcm/go-to-dashboard-settings)))
on-nav-invitations (mf/use-fn #(st/emit! (dd/go-to-team-invitations))) on-nav-invitations (mf/use-fn #(st/emit! (dcm/go-to-dashboard-invitations)))
on-nav-webhooks (mf/use-fn #(st/emit! (dd/go-to-team-webhooks))) on-nav-webhooks (mf/use-fn #(st/emit! (dcm/go-to-dashboard-webhooks)))
route (mf/deref refs/route) route (mf/deref refs/route)
invite-email (-> route :query-params :invite-email) invite-email (-> route :query-params :invite-email)
@ -375,7 +375,7 @@
(st/emit! (modal/show params))))) (st/emit! (modal/show params)))))
on-success 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 on-error
(mf/use-fn (mf/use-fn

View file

@ -8,6 +8,7 @@
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.schema :as sm] [app.common.schema :as sm]
[app.main.data.common :as dcm]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
@ -18,7 +19,6 @@
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[app.util.router :as rt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -28,15 +28,15 @@
(defn- on-create-success (defn- on-create-success
[_form response] [_form response]
(let [msg "Team created successfully"] (let [message "Team created successfully"
(st/emit! (ntf/success msg) team-id (:id response)]
(modal/hide) (st/emit! (ntf/success message)
(rt/nav :dashboard-projects {:team-id (:id response)})))) (dcm/go-to-dashboard-recent :team-id team-id))))
(defn- on-update-success (defn- on-update-success
[_form _response] [_form _response]
(let [msg "Team created successfully"] (let [message "Team created successfully"]
(st/emit! (ntf/success msg) (st/emit! (ntf/success message)
(modal/hide)))) (modal/hide))))
(defn- on-error (defn- on-error

View file

@ -9,6 +9,7 @@
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.config :as cf] [app.config :as cf]
[app.main.data.common :as dcm]
[app.main.data.dashboard :as dd] [app.main.data.dashboard :as dd]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
@ -18,7 +19,6 @@
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[app.util.router :as rt]
[app.util.storage :as storage] [app.util.storage :as storage]
[okulary.core :as l] [okulary.core :as l]
[potok.v2.core :as ptk] [potok.v2.core :as ptk]
@ -43,9 +43,9 @@
:section section}) :section section})
(when-not (some? project-id) (when-not (some? project-id)
(rt/nav :dashboard-files (dcm/go-to-dashboard-recent
{:team-id team-id :team-id team-id
:project-id default-project-id}))))] :project-id default-project-id))))]
(st/emit! (st/emit!
(ptk/event ::ev/event {::ev/name "import-template-launch" (ptk/event ::ev/event {::ev/name "import-template-launch"

View file

@ -9,6 +9,7 @@
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.main.data.common :as dcm]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.profile :as du] [app.main.data.profile :as du]
[app.main.data.team :as dtm] [app.main.data.team :as dtm]
@ -17,7 +18,6 @@
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.main.ui.notifications.context-notification :refer [context-notification]] [app.main.ui.notifications.context-notification :refer [context-notification]]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[potok.v2.core :as ptk] [potok.v2.core :as ptk]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -84,7 +84,7 @@
(st/emit! (du/update-profile-props {:onboarding-team-id team-id (st/emit! (du/update-profile-props {:onboarding-team-id team-id
:onboarding-viewed true}) :onboarding-viewed true})
(when go-to-team? (when go-to-team?
(rt/nav :dashboard-projects {:team-id team-id})))))) (dcm/go-to-dashboard-recent :team-id team-id))))))
on-error on-error
(mf/use-fn (mf/use-fn

View file

@ -7,34 +7,17 @@
(ns app.main.ui.routes (ns app.main.ui.routes
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.spec :as us]
[app.common.uri :as u] [app.common.uri :as u]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
[app.main.data.team :as dtm] [app.main.data.team :as dtm]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.util.router :as rt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[cljs.spec.alpha :as s]
[cuerdas.core :as str] [cuerdas.core :as str]
[potok.v2.core :as ptk])) [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 (def routes
[["/auth" [["/auth"
["/login" :auth-login] ["/login" :auth-login]
@ -53,11 +36,10 @@
["/access-tokens" :settings-access-tokens]] ["/access-tokens" :settings-access-tokens]]
["/frame-preview" :frame-preview] ["/frame-preview" :frame-preview]
["/view/:file-id"
{:name :viewer ["/view" :viewer]
:conform
{:path-params ::viewer-path-params ["/view/:file-id" :viewer-legacy]
:query-params ::viewer-query-params}}]
(when *assert* (when *assert*
["/debug/icons-preview" :debug-icons-preview]) ["/debug/icons-preview" :debug-icons-preview])
@ -65,33 +47,32 @@
;; Used for export ;; Used for export
["/render-sprite/:file-id" :render-sprite] ["/render-sprite/:file-id" :render-sprite]
["/dashboard/team/:team-id" ["/dashboard"
["/members" :dashboard-team-members] ["/members" :dashboard-members]
["/invitations" :dashboard-team-invitations] ["/invitations" :dashboard-invitations]
["/webhooks" :dashboard-team-webhooks] ["/webhooks" :dashboard-webhooks]
["/settings" :dashboard-team-settings] ["/settings" :dashboard-settings]
["/projects" :dashboard-projects] ["/recent" :dashboard-recent]
["/search" :dashboard-search] ["/search" :dashboard-search]
["/fonts" :dashboard-fonts] ["/fonts" :dashboard-fonts]
["/fonts/providers" :dashboard-font-providers] ["/fonts/providers" :dashboard-font-providers]
["/libraries" :dashboard-libraries] ["/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 ["/workspace" :workspace]
[router path] ["/workspace/:project-id/:file-id" :workspace-legacy]])
(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)))
(defn on-navigate (defn on-navigate
[router path] [router path]
@ -99,8 +80,9 @@
[base-path qs] (str/split path "?") [base-path qs] (str/split path "?")
location-path (dm/str (.-origin location) (.-pathname location)) location-path (dm/str (.-origin location) (.-pathname location))
valid-location? (= location-path (dm/str cf/public-uri)) 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 "/"))] empty-path? (or (= base-path "") (= base-path "/"))]
(cond (cond
(not valid-location?) (not valid-location?)
(st/emit! (rt/assign-exception {:type :not-found})) (st/emit! (rt/assign-exception {:type :not-found}))
@ -121,9 +103,9 @@
empty-path? empty-path?
(let [team-id (or (dtm/get-last-team-id) (let [team-id (or (dtm/get-last-team-id)
(:default-team-id profile))] (:default-team-id profile))]
(st/emit! (rt/nav :dashboard-projects (st/emit! (rt/nav :dashboard-recent
{:team-id team-id} (-> (u/query-string->map qs)
(u/query-string->map qs)))) (assoc :team-id team-id)))))
:else :else
(st/emit! (rt/assign-exception {:type :not-found}))))))))) (st/emit! (rt/assign-exception {:type :not-found})))))))))

View file

@ -9,6 +9,7 @@
(:require (:require
[app.main.data.dashboard.shortcuts :as sc] [app.main.data.dashboard.shortcuts :as sc]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.hooks :as hooks] [app.main.ui.hooks :as hooks]
[app.main.ui.settings.access-tokens :refer [access-tokens-page]] [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.profile :refer [profile-page]]
[app.main.ui.settings.sidebar :refer [sidebar]] [app.main.ui.settings.sidebar :refer [sidebar]]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(mf/defc header (mf/defc header

View file

@ -8,15 +8,16 @@
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.config :as cf] [app.config :as cf]
[app.main.data.common :as dcm]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.team :as dtm] [app.main.data.team :as dtm]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.dashboard.sidebar :refer [profile-section*]] [app.main.ui.dashboard.sidebar :refer [profile-section*]]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[app.util.router :as rt]
[potok.v2.core :as ptk] [potok.v2.core :as ptk]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -26,6 +27,7 @@
(def ^:private feedback-icon (def ^:private feedback-icon
(i/icon-xref :feedback (stl/css :feedback-icon))) (i/icon-xref :feedback (stl/css :feedback-icon)))
;; FIXME: move to common
(def ^:private go-settings-profile (def ^:private go-settings-profile
#(st/emit! (rt/nav :settings-profile))) #(st/emit! (rt/nav :settings-profile)))
@ -64,7 +66,7 @@
go-dashboard go-dashboard
(mf/use-fn (mf/use-fn
(mf/deps team-id) (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)}
[:div {:class (stl/css :sidebar-content-section)} [:div {:class (stl/css :sidebar-content-section)}

View file

@ -11,10 +11,11 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.pprint :as pp] [app.common.pprint :as pp]
[app.common.uri :as u] [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.data.event :as ev]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.auth.login :refer [login-methods]] [app.main.ui.auth.login :refer [login-methods]]
[app.main.ui.auth.recovery-request :refer [recovery-request-page recovery-sent-page]] [app.main.ui.auth.recovery-request :refer [recovery-request-page recovery-sent-page]]
@ -26,7 +27,6 @@
[app.main.ui.viewer.header :as viewer.header] [app.main.ui.viewer.header :as viewer.header]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
[app.util.router :as rt]
[app.util.webapi :as wapi] [app.util.webapi :as wapi]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[cuerdas.core :as str] [cuerdas.core :as str]
@ -213,7 +213,8 @@
(mf/use-fn (mf/use-fn
(mf/deps profile) (mf/deps profile)
(fn [] (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 on-success
(mf/use-fn (mf/use-fn
@ -233,7 +234,7 @@
{:team-id team-id}) {:team-id team-id})
mdata {:on-success on-success mdata {:on-success on-success
:on-error on-error}] :on-error on-error}]
(st/emit! (dc/create-team-access-request (st/emit! (dcm/create-team-access-request
(with-meta params mdata))))))] (with-meta params mdata))))))]
[:* [:*

View file

@ -25,7 +25,7 @@
[app.main.ui.ds.product.loader :refer [loader*]] [app.main.ui.ds.product.loader :refer [loader*]]
[app.main.ui.hooks :as hooks] [app.main.ui.hooks :as hooks]
[app.main.ui.icons :as i] [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.header :as header]
[app.main.ui.viewer.inspect :as inspect] [app.main.ui.viewer.inspect :as inspect]
[app.main.ui.viewer.interactions :as interactions] [app.main.ui.viewer.interactions :as interactions]
@ -129,8 +129,8 @@
:comment-sidebar show-sidebar?}] :comment-sidebar show-sidebar?}]
(when show-sidebar? (when show-sidebar?
[:& comments-sidebar [:> comments-sidebar*
{:users users {:profiles users
:frame frame :frame frame
:page page}])])) :page page}])]))
@ -274,9 +274,9 @@
:page page :page page
:zoom zoom}])]]) :zoom zoom}])]])
(mf/defc viewer-content (mf/defc viewer-content*
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [data page-id share-id section index interactions-mode share] :as props}] [{:keys [data page-id share-id section index interactions-mode share]}]
(let [{:keys [file users project permissions]} data (let [{:keys [file users project permissions]} data
allowed (or allowed (or
(= section :interactions) (= section :interactions)
@ -620,8 +620,8 @@
;; --- Component: Viewer ;; --- Component: Viewer
(mf/defc viewer (mf/defc viewer*
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [file-id share-id page-id] :as props}] [{:keys [file-id share-id page-id] :as props}]
(mf/with-effect [file-id page-id share-id] (mf/with-effect [file-id page-id share-id]
(let [params {:file-id file-id (let [params {:file-id file-id
@ -630,9 +630,10 @@
(st/emit! (dv/initialize params)) (st/emit! (dv/initialize params))
(fn [] (fn []
(st/emit! (dv/finalize params))))) (st/emit! (dv/finalize params)))))
(if-let [data (mf/deref refs/viewer-data)] (if-let [data (mf/deref refs/viewer-data)]
(let [props (obj/merge props #js {:data data :key (dm/str file-id)})] (let [props (obj/merge props #js {:data data :key (dm/str file-id)})]
[:> viewer-content props]) [:> viewer-content* props])
[:> loader* {:title (tr "labels.loading") [:> loader* {:title (tr "labels.loading")
:overlay true}])) :overlay true}]))

View file

@ -220,7 +220,7 @@
{:thread thread {:thread thread
:position-modifier modifier1 :position-modifier modifier1
:viewport {:offset-x 0 :offset-y 0 :width (:width vsize) :height (:height vsize)} :viewport {:offset-x 0 :offset-y 0 :width (:width vsize) :height (:height vsize)}
:users users :profiles users
:zoom zoom}]) :zoom zoom}])
(when-let [draft (:draft local)] (when-let [draft (:draft local)]
@ -231,8 +231,9 @@
:on-submit on-draft-submit :on-submit on-draft-submit
:zoom zoom}])]]])) :zoom zoom}])]]]))
(mf/defc comments-sidebar (mf/defc comments-sidebar*
[{:keys [users frame page]}] {::mf/props :obj}
[{:keys [profiles frame page]}]
(let [profile (mf/deref refs/profile) (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-map (mf/deref refs/comment-threads)
@ -242,4 +243,8 @@
(gsh/has-point? frame position))))] (gsh/has-point? frame position))))]
[:aside {:class (stl/css :comments-sidebar)} [:aside {:class (stl/css :comments-sidebar)}
[:div {:class (stl/css :settings-bar-inside)} [: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.formats :as fmt]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.main.ui.viewer.comments :refer [comments-menu]] [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.dom :as dom]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
[okulary.core :as l] [okulary.core :as l]
@ -173,7 +173,8 @@
:interactions [:* :interactions [:*
(when index (when index
[:> flows-menu* {:page page :index index}]) [:> flows-menu* {:page page :index index}])
[:& interactions-menu {:interactions-mode interactions-mode}]] [:> interactions-menu*
{:interactions-mode interactions-mode}]]
:comments [:& comments-menu] :comments [:& comments-menu]
[:div {:class (stl/css :view-options)}]) [:div {:class (stl/css :view-options)}])

View file

@ -30,6 +30,7 @@
[app.util.webapi :as wapi] [app.util.webapi :as wapi]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[cuerdas.core :as str] [cuerdas.core :as str]
[okulary.core :as l]
[potok.v2.core :as ptk] [potok.v2.core :as ptk]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -49,13 +50,26 @@
</body> </body>
</html>") </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] (defn- use-objects [from]
(let [page-objects-ref (let [page-objects-ref
(mf/with-memo [from] (mf/with-memo [from]
(if (= from :workspace) (if (= from :workspace)
;; FIXME: fix naming consistency issues ;; FIXME: fix naming consistency issues
refs/workspace-page-objects refs/workspace-page-objects
(refs/get-viewer-objects)))] (get-viewer-objects)))]
(mf/deref page-objects-ref))) (mf/deref page-objects-ref)))
(defn- shapes->images (defn- shapes->images

View file

@ -267,7 +267,8 @@
(when (= flow-id (:id current-flow)) (when (= flow-id (:id current-flow))
[:span {:class (stl/css :icon)} i/tick])])]]]))) [:span {:class (stl/css :icon)} i/tick])])]]])))
(mf/defc interactions-menu (mf/defc interactions-menu*
{::mf/props :obj}
[{:keys [interactions-mode]}] [{:keys [interactions-mode]}]
(let [show-dropdown? (mf/use-state false) (let [show-dropdown? (mf/use-state false)
toggle-dropdown (mf/use-fn #(swap! show-dropdown? not)) toggle-dropdown (mf/use-fn #(swap! show-dropdown? not))
@ -281,6 +282,7 @@
(keyword))] (keyword))]
(dom/stop-propagation event) (dom/stop-propagation event)
(st/emit! (dv/set-interactions-mode mode)))))] (st/emit! (dv/set-interactions-mode mode)))))]
[:div {:on-click toggle-dropdown [:div {:on-click toggle-dropdown
:class (stl/css :view-options)} :class (stl/css :view-options)}
[:span {:class (stl/css :dropdown-title)} (tr "viewer.header.interactions")] [:span {:class (stl/css :dropdown-title)} (tr "viewer.header.interactions")]

View file

@ -13,6 +13,7 @@
[app.common.types.shape.interactions :as ctsi] [app.common.types.shape.interactions :as ctsi]
[app.main.data.viewer :as dv] [app.main.data.viewer :as dv]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.shapes.bool :as bool] [app.main.ui.shapes.bool :as bool]
[app.main.ui.shapes.circle :as circle] [app.main.ui.shapes.circle :as circle]
@ -26,7 +27,6 @@
[app.main.ui.shapes.text :as text] [app.main.ui.shapes.text :as text]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.object :as obj] [app.util.object :as obj]
[app.util.router :as rt]
[app.util.timers :as tm] [app.util.timers :as tm]
[okulary.core :as l] [okulary.core :as l]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -34,8 +34,8 @@
(def base-frame-ctx (mf/create-context nil)) (def base-frame-ctx (mf/create-context nil))
(def frame-offset-ctx (mf/create-context nil)) (def frame-offset-ctx (mf/create-context nil))
(def viewer-interactions-show? (def ^:private ref:viewer-show-interactions
(l/derived :interactions-show? refs/viewer-local)) (l/derived :show-interactions refs/viewer-local))
(defn- find-relative-to-base-frame (defn- find-relative-to-base-frame
[shape objects overlays-ids base-frame] [shape objects overlays-ids base-frame]
@ -280,7 +280,7 @@
sems)))) sems))))
(mf/defc interaction (mf/defc interaction
[{:keys [shape interactions interactions-show?]}] [{:keys [shape interactions show-interactions]}]
(let [{:keys [x y width height]} (:selrect shape)] (let [{:keys [x y width height]} (:selrect shape)]
(when-not (empty? interactions) (when-not (empty? interactions)
[:rect {:x (- x 1) [:rect {:x (- x 1)
@ -289,8 +289,8 @@
:height (+ height 2) :height (+ height 2)
:fill "var(--color-accent-tertiary)" :fill "var(--color-accent-tertiary)"
:stroke "var(--color-accent-tertiary)" :stroke "var(--color-accent-tertiary)"
:stroke-width (if interactions-show? 1 0) :stroke-width (if show-interactions 1 0)
:fill-opacity (if interactions-show? 0.2 0) :fill-opacity (if show-interactions 0.2 0)
:transform (gsh/transform-str shape)}]))) :transform (gsh/transform-str shape)}])))
@ -309,7 +309,7 @@
all-objects (or (unchecked-get props "all-objects") objects) all-objects (or (unchecked-get props "all-objects") objects)
base-frame (mf/use-ctx base-frame-ctx) base-frame (mf/use-ctx base-frame-ctx)
frame-offset (mf/use-ctx frame-offset-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) overlays (mf/deref refs/viewer-overlays)
interactions (:interactions shape) interactions (:interactions shape)
svg-element? (and (= :svg-raw (:type shape)) svg-element? (and (= :svg-raw (:type shape))
@ -355,7 +355,7 @@
[:& interaction {:shape shape [:& interaction {:shape shape
:interactions interactions :interactions interactions
:interactions-show? interactions-show?}]] :show-interactions show-interactions}]]
;; Don't wrap svg elements inside a <g> otherwise some can break ;; Don't wrap svg elements inside a <g> otherwise some can break
[:& component {:shape shape [:& component {:shape shape

View file

@ -16,12 +16,12 @@
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.select :refer [select]] [app.main.ui.components.select :refer [select]]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[app.util.webapi :as wapi] [app.util.webapi :as wapi]
[potok.v2.core :as ptk] [potok.v2.core :as ptk]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -69,17 +69,16 @@
(= (:pages %) pages)) (= (:pages %) pages))
slinks)] slinks)]
(when slink (when slink
(let [pparams (:path-params route) (let [page-id (d/seek #(contains? (:pages slink) %) page-ids)
page-id (d/seek #(contains? (:pages slink) %) page-ids) params (-> (:query-params route)
qparams (-> (:query-params route)
(assoc :share-id (:id slink)) (assoc :share-id (:id slink))
(assoc :page-id page-id) (assoc :page-id page-id)
(assoc :index "0")) (assoc :index "0"))
qparams (if (nil? zoom-type) params (if (nil? zoom-type)
(dissoc qparams :zoom) (dissoc params :zoom)
(assoc qparams :zoom zoom-type)) (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)))))) (dm/str (assoc cf/public-uri :fragment href))))))
on-close on-close

View file

@ -8,12 +8,10 @@
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.main.data.modal :as modal] [app.main.data.common :as dcm]
[app.main.data.notifications :as ntf]
[app.main.data.persistence :as dps] [app.main.data.persistence :as dps]
[app.main.data.plugins :as dpl] [app.main.data.plugins :as dpl]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.colors :as dc]
[app.main.features :as features] [app.main.features :as features]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
@ -46,7 +44,7 @@
[file-id] [file-id]
(l/derived (fn [state] (l/derived (fn [state]
(let [data (:workspace-data state)] (let [data (:workspace-data state)]
(and (:workspace-ready? state) (and (:workspace-ready state)
(= file-id (:current-file-id state)) (= file-id (:current-file-id state))
(= file-id (:id data))))) (= file-id (:id data)))))
st/state)) st/state))
@ -125,14 +123,17 @@
:file file :file file
:page-id page-id}]])])) :page-id page-id}]])]))
(mf/defc workspace-loader (mf/defc workspace-loader*
{::mf/props :obj
::mf/private true}
[] []
[:> loader* {:title (tr "labels.loading") [:> loader* {:title (tr "labels.loading")
:class (stl/css :workspace-loader) :class (stl/css :workspace-loader)
:overlay true}]) :overlay true}])
(mf/defc workspace-page (mf/defc workspace-page*
{::mf/wrap-props false} {::mf/props :obj
::mf/private true}
[{:keys [page-id file layout wglobal]}] [{:keys [page-id file layout wglobal]}]
(let [page-id (hooks/use-equal-memo page-id) (let [page-id (hooks/use-equal-memo page-id)
page-ready* (mf/with-memo [page-id] page-ready* (mf/with-memo [page-id]
@ -147,18 +148,20 @@
(mf/with-effect [page-id] (mf/with-effect [page-id]
(if (some? page-id) (if (some? page-id)
(st/emit! (dw/initialize-page page-id)) (st/emit! (dw/initialize-page page-id))
(st/emit! (dw/go-to-page))) (st/emit! (dcm/go-to-workspace)))
(fn [] (fn []
(when (some? page-id) (when (some? page-id)
(st/emit! (dw/finalize-page page-id))))) (st/emit! (dw/finalize-page page-id)))))
(if ^boolean page-ready? (if ^boolean page-ready?
[:& workspace-content {:page-id page-id [:& workspace-content {:page-id page-id
:file file :file file
:wglobal wglobal :wglobal wglobal
:layout layout}] :layout layout}]
[:& workspace-loader]))) [:& workspace-loader*])))
(mf/defc workspace
(mf/defc workspace*
{::mf/wrap-props false {::mf/wrap-props false
::mf/wrap [mf/memo]} ::mf/wrap [mf/memo]}
[{:keys [project-id file-id page-id layout-name]}] [{:keys [project-id file-id page-id layout-name]}]
@ -168,9 +171,7 @@
team (mf/deref refs/team) team (mf/deref refs/team)
file (mf/deref refs/workspace-file) file (mf/deref refs/workspace-file)
project (mf/deref refs/workspace-project)
team-id (:team-id project)
file-name (:name file) file-name (:name file)
permissions (:permissions team) permissions (:permissions team)
@ -192,36 +193,32 @@
;; Setting the layout preset by its name ;; Setting the layout preset by its name
(mf/with-effect [layout-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] (mf/with-effect [file-name]
(when file-name (when file-name
(dom/set-html-title (tr "title.workspace" file-name)))) (dom/set-html-title (tr "title.workspace" file-name))))
(mf/with-effect [project-id file-id] (mf/with-effect [file-id]
(st/emit! (dw/initialize-file project-id file-id)) (st/emit! (dw/initialize-workspace file-id))
(fn [] (fn []
(st/emit! ::dps/force-persist (st/emit! ::dps/force-persist
(dc/stop-picker) (dw/finalize-workspace file-id))))
(modal/hide)
(ntf/hide)
(dw/finalize-file project-id file-id))))
[:& (mf/provider ctx/current-file-id) {:value file-id}
[:& (mf/provider ctx/current-project-id) {:value project-id} [:& (mf/provider ctx/current-project-id) {:value project-id}
[:& (mf/provider ctx/current-team-id) {:value team-id} [:& (mf/provider ctx/current-file-id) {:value file-id}
[:& (mf/provider ctx/current-page-id) {:value page-id} [:& (mf/provider ctx/current-page-id) {:value page-id}
[:& (mf/provider ctx/components-v2) {:value components-v2?} [:& (mf/provider ctx/components-v2) {:value components-v2?}
[:& (mf/provider ctx/design-tokens) {:value design-tokens?} [:& (mf/provider ctx/design-tokens) {:value design-tokens?}
[:& (mf/provider ctx/workspace-read-only?) {:value read-only?} [:& (mf/provider ctx/workspace-read-only?) {:value read-only?}
[:& (mf/provider ctx/permissions) {:value permissions}
[:section {:class (stl/css :workspace) [:section {:class (stl/css :workspace)
:style {:background-color background-color :style {:background-color background-color
:touch-action "none"}} :touch-action "none"}}
[:& context-menu] [:& context-menu]
(if ^boolean file-ready? (if ^boolean file-ready?
[:& workspace-page {:page-id page-id [:> workspace-page* {:page-id page-id
:file file :file file
:wglobal wglobal :wglobal wglobal
:layout layout}] :layout layout}]
[:& workspace-loader])]]]]]]]]])) [:> workspace-loader* {}])]]]]]]]))

View file

@ -7,11 +7,13 @@
(ns app.main.ui.workspace.comments (ns app.main.ui.workspace.comments
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (: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.event :as ev]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.comments :as dwcm] [app.main.data.workspace.comments :as dwcm]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.comments :as cmt] [app.main.ui.comments :as cmt]
[app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.components.dropdown :refer [dropdown]]
@ -37,14 +39,14 @@
(let [mode (-> (dom/get-current-target event) (let [mode (-> (dom/get-current-target event)
(dom/get-data "value") (dom/get-data "value")
(keyword))] (keyword))]
(st/emit! (dcm/update-filters {:mode mode}))))) (st/emit! (dcmt/update-filters {:mode mode})))))
update-show update-show
(mf/use-fn (mf/use-fn
(mf/deps cshow) (mf/deps cshow)
(fn [] (fn []
(let [mode (if (= :pending cshow) :all :pending)] (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 [:ul {:class (stl/css-case :comment-mode-dropdown true
:viewer-dropdown from-viewer)} :viewer-dropdown from-viewer)}
@ -68,13 +70,13 @@
[:span {:class (stl/css :label)} (tr "labels.hide-resolved-comments")] [:span {:class (stl/css :label)} (tr "labels.hide-resolved-comments")]
[:span {:class (stl/css :icon)} i/tick]]])) [:span {:class (stl/css :icon)} i/tick]]]))
(mf/defc comments-sidebar (mf/defc comments-sidebar*
{::mf/props :obj} {::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) (let [threads-map (mf/deref refs/threads-ref)
profile (mf/deref refs/profile) profile (mf/deref refs/profile)
users-refs (mf/deref refs/current-file-comments-users) profiles' (mf/deref refs/profiles)
users (or users users-refs) profiles (or profiles profiles')
local (mf/deref refs/comments-local) local (mf/deref refs/comments-local)
state* (mf/use-state false) state* (mf/use-state false)
@ -84,7 +86,7 @@
(->> (vals threads-map) (->> (vals threads-map)
(sort-by :modified-at) (sort-by :modified-at)
(reverse) (reverse)
(dcm/apply-filters local profile)) (dcmt/apply-filters local profile))
threads) threads)
close-section close-section
@ -92,12 +94,12 @@
(mf/deps from-viewer) (mf/deps from-viewer)
(fn [] (fn []
(if from-viewer (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) (st/emit! (dw/clear-edition-mode)
(dw/deselect-all true))))) (dw/deselect-all true)))))
tgroups (->> threads tgroups (->> threads
(dcm/group-threads-by-page)) (dcmt/group-threads-by-page))
page-id (or page-id (mf/use-ctx ctx/current-page-id)) page-id (or page-id (mf/use-ctx ctx/current-page-id))
@ -112,14 +114,16 @@
(mf/deps page-id) (mf/deps page-id)
(fn [thread] (fn [thread]
(when (not= page-id (:page-id 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 (tm/schedule
(fn [] (fn []
(st/emit! (when (not= page-id (:page-id thread)) (st/emit! (when (not= page-id (:page-id thread))
(dw/select-for-drawing :comments)) (dw/select-for-drawing :comments))
(dwcm/center-to-comment-thread thread) (dwcm/center-to-comment-thread thread)
(-> (dcm/open-thread thread) (-> (dcmt/open-thread thread)
(with-meta {::ev/origin "workspace"})))))))] (with-meta {::ev/origin "workspace"})))))))]
[:div {:class (stl/css-case :comments-section true [:div {:class (stl/css-case :comments-section true
:from-viewer from-viewer)} :from-viewer from-viewer)}
[:div {:class (stl/css-case :comments-section-title true [:div {:class (stl/css-case :comments-section-title true
@ -149,12 +153,12 @@
[:& cmt/comment-thread-group [:& cmt/comment-thread-group
{:group (first tgroups) {:group (first tgroups)
:on-thread-click on-thread-click :on-thread-click on-thread-click
:users users}] :profiles profiles}]
(for [tgroup (rest tgroups)] (for [tgroup (rest tgroups)]
[:& cmt/comment-thread-group [:& cmt/comment-thread-group
{:group tgroup {:group tgroup
:on-thread-click on-thread-click :on-thread-click on-thread-click
:users users :profiles profiles
:key (:page-id tgroup)}])] :key (:page-id tgroup)}])]
[:div {:class (stl/css :thread-group-placeholder)} [:div {:class (stl/css :thread-group-placeholder)}

View file

@ -8,10 +8,12 @@
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.main.data.common :as dcm]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.colors :as dc] [app.main.data.workspace.colors :as dc]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.context :as ctx] [app.main.ui.context :as ctx]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
@ -19,21 +21,20 @@
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[app.util.router :as rt]
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
;; --- Header Component ;; --- Header Component
(mf/defc left-header (mf/defc left-header
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [file layout project page-id class]}] [{:keys [file layout project page-id class]}]
(let [profile (mf/deref refs/profile) (let [profile (mf/deref refs/profile)
file-id (:id file) file-id (:id file)
file-name (:name file) file-name (:name file)
project-id (:id project)
team-id (:team-id project) team-id (:team-id project)
shared? (:is-shared file) shared? (:is-shared file)
read-only? (mf/use-ctx ctx/workspace-read-only?) read-only? (mf/use-ctx ctx/workspace-read-only?)
editing* (mf/use-state false) editing* (mf/use-state false)
@ -69,22 +70,20 @@
go-back go-back
(mf/use-fn (mf/use-fn
(mf/deps project)
(fn [] (fn []
(close-modals) (close-modals)
;; FIXME: move set-mode to uri?
(st/emit! (dw/set-options-mode :design) (st/emit! (dw/set-options-mode :design)
(dw/go-to-dashboard project)))) (dcm/go-to-dashboard-recent))))
nav-to-project nav-to-project
(mf/use-fn (mf/use-fn
(mf/deps team-id project-id) #(st/emit! (dcm/go-to-dashboard-files ::rt/new-window true)))]
#(st/emit! (rt/nav-new-window* {:rname :dashboard-files
:path-params {:team-id team-id
:project-id project-id}})))]
(mf/with-effect [editing?] (mf/with-effect [editing?]
(when ^boolean editing? (when ^boolean editing?
(dom/select-text! (mf/ref-val input-ref)))) (dom/select-text! (mf/ref-val input-ref))))
[:header {:class (dm/str class " " (stl/css :workspace-header-left))} [:header {:class (dm/str class " " (stl/css :workspace-header-left))}
[:a {:on-click go-back [:a {:on-click go-back
:class (stl/css :main-icon)} i/logo-icon] :class (stl/css :main-icon)} i/logo-icon]

View file

@ -17,6 +17,7 @@
[app.config :as cf] [app.config :as cf]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.profile :as du] [app.main.data.profile :as du]
[app.main.data.team :as dtm]
[app.main.data.workspace.colors :as mdc] [app.main.data.workspace.colors :as mdc]
[app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.libraries :as dwl]
[app.main.refs :as refs] [app.main.refs :as refs]
@ -103,10 +104,10 @@
[:li {:class (stl/css :element-count)} [:li {:class (stl/css :element-count)}
(tr "workspace.libraries.typography" typography-count)])]) (tr "workspace.libraries.typography" typography-count)])])
(mf/defc libraries-tab*
(mf/defc libraries-tab {::mf/props :obj
{::mf/wrap-props false} ::mf/private true}
[{:keys [file-id shared? linked-libraries shared-libraries]}] [{:keys [file-id is-shared linked-libraries shared-libraries]}]
(let [search-term* (mf/use-state "") (let [search-term* (mf/use-state "")
search-term (deref search-term*) search-term (deref search-term*)
library-ref (mf/with-memo [file-id] library-ref (mf/with-memo [file-id]
@ -127,7 +128,7 @@
shared-libraries shared-libraries
(mf/with-memo [shared-libraries linked-libraries file-id search-term] (mf/with-memo [shared-libraries linked-libraries file-id search-term]
(when shared-libraries (when shared-libraries
(->> shared-libraries (->> (vals shared-libraries)
(remove #(= (:id %) file-id)) (remove #(= (:id %) file-id))
(remove #(contains? linked-libraries (:id %))) (remove #(contains? linked-libraries (:id %)))
(filter #(matches-search (:name %) search-term)) (filter #(matches-search (:name %) search-term))
@ -219,7 +220,7 @@
:graphics-count (count media) :graphics-count (count media)
:colors-count (count colors) :colors-count (count colors)
:typography-count (count typographies)}]]] :typography-count (count typographies)}]]]
(if ^boolean shared? (if ^boolean is-shared
[:input {:class (stl/css :item-unpublish) [:input {:class (stl/css :item-unpublish)
:type "button" :type "button"
:value (tr "common.unpublish") :value (tr "common.unpublish")
@ -348,8 +349,9 @@
:colors colors :colors colors
:typographies typographies}])) :typographies typographies}]))
(mf/defc updates-tab (mf/defc updates-tab*
{::mf/wrap-props false} {::mf/props :obj
::mf/private true}
[{:keys [file-id file-data libraries]}] [{:keys [file-id file-data libraries]}]
(let [summary?* (mf/use-state true) (let [summary?* (mf/use-state true)
summary? (deref summary?*) summary? (deref summary?*)
@ -487,11 +489,9 @@
{::mf/register modal/components {::mf/register modal/components
::mf/register-as :libraries-dialog} ::mf/register-as :libraries-dialog}
[{:keys [starting-tab] :as props :or {starting-tab :libraries}}] [{:keys [starting-tab] :as props :or {starting-tab :libraries}}]
(let [project (mf/deref refs/workspace-project) (let [file-data (mf/deref refs/workspace-data)
file-data (mf/deref refs/workspace-data)
file (mf/deref ref:workspace-file) file (mf/deref ref:workspace-file)
team-id (:team-id project)
file-id (:id file) file-id (:id file)
shared? (:is-shared file) shared? (:is-shared file)
@ -499,37 +499,44 @@
libraries (mf/with-memo [libraries] libraries (mf/with-memo [libraries]
(d/removem (fn [[_ val]] (:is-indirect val)) libraries)) (d/removem (fn [[_ val]] (:is-indirect val)) libraries))
;; NOTE: we really don't need react on shared files
shared-libraries shared-libraries
(mf/deref refs/workspace-shared-files) (mf/deref refs/shared-files)
close-dialog-outside close-dialog-outside
(mf/use-fn (fn [event] (mf/use-fn
(fn [event]
(when (= (dom/get-target event) (dom/get-current-target event)) (when (= (dom/get-target event) (dom/get-current-target event))
(modal/hide!)))) (modal/hide!))))
close-dialog close-dialog
(mf/use-fn (fn [_] (mf/use-fn
(fn [_]
(modal/hide!) (modal/hide!)
(modal/disallow-click-outside!))) (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 tabs
#js [#js {:label (tr "workspace.libraries.libraries") #js [#js {:label (tr "workspace.libraries.libraries")
:id "libraries" :id "libraries"
:content (mf/html [:& libraries-tab {:file-id file-id :content libraries-tab}
:shared? shared?
:linked-libraries libraries
:shared-libraries shared-libraries}])}
#js {:label (tr "workspace.libraries.updates") #js {:label (tr "workspace.libraries.updates")
:id "updates" :id "updates"
:content (mf/html [:& updates-tab {:file-id file-id :content updates-tab}]]
:file-data file-data
:libraries libraries}])}]]
(mf/with-effect [team-id] (mf/with-effect []
(when team-id (st/emit! (dtm/fetch-shared-files)))
(st/emit! (dwl/fetch-shared-files {:team-id team-id}))))
[:div {:class (stl/css :modal-overlay) :on-click close-dialog-outside :data-testid "libraries-modal"} [:div {:class (stl/css :modal-overlay) :on-click close-dialog-outside :data-testid "libraries-modal"}
[:div {:class (stl/css :modal-dialog)} [:div {:class (stl/css :modal-dialog)}

View file

@ -37,7 +37,6 @@
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[app.util.router :as rt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[potok.v2.core :as ptk] [potok.v2.core :as ptk]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -68,7 +67,7 @@
(mf/use-fn #(dom/open-new-window "https://penpot.app/terms")) (mf/use-fn #(dom/open-new-window "https://penpot.app/terms"))
nav-to-feedback 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? plugins?
(features/active-feature? @st/state "plugins/runtime") (features/active-feature? @st/state "plugins/runtime")
@ -540,9 +539,8 @@
on-pin-version on-pin-version
(mf/use-fn (mf/use-fn
(mf/deps file-id)
(fn [_] (fn [_]
(st/emit! (dwv/create-version file-id)))) (st/emit! (dwv/create-version))))
on-pin-version-key-down on-pin-version-key-down
(mf/use-fn (mf/use-fn

View file

@ -32,7 +32,7 @@
(mf/defc active-sessions (mf/defc active-sessions
{::mf/memo true} {::mf/memo true}
[] []
(let [users (mf/deref refs/users) (let [profiles (mf/deref refs/profiles)
presence (mf/deref refs/workspace-presence) presence (mf/deref refs/workspace-presence)
sessions (vals presence) sessions (vals presence)
@ -61,7 +61,7 @@
[:& session-widget [:& session-widget
{:color (:color session) {:color (:color session)
:index 0 :index 0
:profile (get users (:profile-id session)) :profile (get profiles (:profile-id session))
:key (dm/str (:id session))}])]]) :key (dm/str (:id session))}])]])
[:button {:class (stl/css-case :active-users true) [:button {:class (stl/css-case :active-users true)
@ -74,5 +74,5 @@
[:& session-widget [:& session-widget
{:color (:color session) {:color (:color session)
:index index :index index
:profile (get users (:profile-id session)) :profile (get profiles (:profile-id session))
:key (dm/str (:id session))}])]]])) :key (dm/str (:id session))}])]]]))

View file

@ -8,6 +8,7 @@
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.config :as cf] [app.config :as cf]
[app.main.data.common :as dcm]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.shortcuts :as scd] [app.main.data.shortcuts :as scd]
@ -169,7 +170,7 @@
(let [params {:page-id page-id (let [params {:page-id page-id
:file-id file-id :file-id file-id
:section "interactions"}] :section "interactions"}]
(st/emit! (dw/go-to-viewer params))))) (st/emit! (dcm/go-to-viewer params)))))
active-comments active-comments
(mf/use-fn (mf/use-fn

View file

@ -8,14 +8,16 @@
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.main.data.common :as dcm]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.features :as features]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.context :as muc] [app.main.ui.context :as muc]
[app.main.ui.ds.foundations.assets.icon :refer [icon*]] [app.main.ui.ds.foundations.assets.icon :refer [icon*]]
[app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]] [app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]]
[app.main.ui.hooks.resize :refer [use-resize-hook]] [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.left-header :refer [left-header]]
[app.main.ui.workspace.right-header :refer [right-header]] [app.main.ui.workspace.right-header :refer [right-header]]
[app.main.ui.workspace.sidebar.assets :refer [assets-toolbox]] [app.main.ui.workspace.sidebar.assets :refer [assets-toolbox]]
@ -47,36 +49,44 @@
(mf/defc left-sidebar (mf/defc left-sidebar
{::mf/wrap [mf/memo] {::mf/wrap [mf/memo]
::mf/wrap-props false} ::mf/props :obj}
[{:keys [layout file page-id] :as props}] [{:keys [layout file page-id] :as props}]
(let [options-mode (mf/deref refs/options-mode-global) (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) 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) shortcuts? (contains? layout :shortcuts)
show-debug? (contains? layout :debug-panel) 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) (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) (use-resize-hook :sitemap 200 38 400 :y false nil)
show-pages? (mf/use-state true) show-pages? (mf/use-state true)
toggle-pages (mf/use-callback #(reset! show-pages? not)) toggle-pages (mf/use-fn #(reset! show-pages? not))
size-pages (mf/use-memo (mf/deps show-pages? size-pages-opened) (fn [] (if @show-pages? size-pages-opened 32))) size-pages (mf/with-memo [show-pages? size-pages-opened]
(if @show-pages? size-pages-opened 32))
handle-collapse handle-collapse
(mf/use-fn #(st/emit! (dw/toggle-layout-flag :collapse-left-sidebar))) (mf/use-fn #(st/emit! (dw/toggle-layout-flag :collapse-left-sidebar)))
on-tab-change 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 layers-tab
(mf/html (mf/html
@ -138,7 +148,11 @@
:global/four-row (> size 400)) :global/four-row (> size 400))
:style #js {"--width" (dm/str size "px")}} :style #js {"--width" (dm/str size "px")}}
[:& left-header {:file file :layout layout :project project :page-id page-id [:& left-header
{:file file
:layout layout
:project project
:page-id page-id
:class (stl/css :left-header)}] :class (stl/css :left-header)}]
[:div {:on-pointer-down on-pointer-down [:div {:on-pointer-down on-pointer-down
@ -234,7 +248,7 @@
[:& debug-shape-info] [:& debug-shape-info]
(true? is-comments?) (true? is-comments?)
[:& comments-sidebar] [:> comments-sidebar* {}]
(true? is-history?) (true? is-history?)
[:> tab-switcher* [:> tab-switcher*

View file

@ -25,9 +25,10 @@
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(mf/defc assets-libraries (mf/defc assets-libraries*
{::mf/wrap [mf/memo] {::mf/wrap [mf/memo]
::mf/wrap-props false} ::mf/props :obj
::mf/private true}
[{:keys [filters]}] [{:keys [filters]}]
(let [libraries (mf/deref refs/workspace-libraries) (let [libraries (mf/deref refs/workspace-libraries)
libraries (mf/with-memo [libraries] libraries (mf/with-memo [libraries]
@ -193,4 +194,4 @@
[:& (mf/provider cmm/assets-toggle-list-style) {:value toggle-list-style} [:& (mf/provider cmm/assets-toggle-list-style) {:value toggle-list-style}
[:* [:*
[:& assets-local-library {:filters filters}] [:& assets-local-library {:filters filters}]
[:& assets-libraries {:filters filters}]]]]]])) [:> assets-libraries* {:filters filters}]]]]]]))

View file

@ -391,20 +391,18 @@
(do-update-remote-component)) (do-update-remote-component))
do-show-in-assets do-show-in-assets
#(st/emit! (if components-v2 #(st/emit! (dw/show-component-in-assets component-id))
(dw/show-component-in-assets component-id)
(dw/go-to-component component-id)))
do-create-annotation do-create-annotation
#(st/emit! (dw/set-annotations-id-for-create id)) #(st/emit! (dw/set-annotations-id-for-create id))
do-show-local-component 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 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-let [comp (find-component shape true)]
(when comp (st/emit! (dwl/go-to-component-file library-id comp)))
(st/emit! (dwl/nav-to-component-file library-id comp))))
do-show-component do-show-component
(fn [] (fn []

View file

@ -98,8 +98,8 @@
(fn [event] (fn [event]
(dom/stop-propagation event) (dom/stop-propagation event)
(if local (if local
(st/emit! (dw/go-to-component component-id)) (st/emit! (dwl/go-to-local-component component-id))
(st/emit! (dwl/nav-to-component-file file-id component))))) (st/emit! (dwl/go-to-component-file file-id component)))))
on-drop on-drop
(mf/use-fn (mf/use-fn
@ -491,9 +491,9 @@
(fn [event] (fn [event]
(dom/stop-propagation event) (dom/stop-propagation event)
(if local? (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)] (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 on-asset-click
(mf/use-fn (mf/deps groups on-asset-click) (partial on-asset-click groups))] (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.libraries :as dwl]
[app.main.data.workspace.undo :as dwu] [app.main.data.workspace.undo :as dwu]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.title-bar :refer [title-bar]] [app.main.ui.components.title-bar :refer [title-bar]]
[app.main.ui.context :as ctx] [app.main.ui.context :as ctx]
@ -27,54 +28,53 @@
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[app.util.router :as rt]
[cuerdas.core :as str] [cuerdas.core :as str]
[okulary.core :as l] [okulary.core :as l]
[potok.v2.core :as ptk] [potok.v2.core :as ptk]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def lens:open-status (def ^:private ref:open-status
(l/derived (l/in [:workspace-assets :open-status]) st/state)) (l/derived (l/in [:workspace-assets :open-status]) st/state))
(def lens:selected (def ^:private ref:selected
(-> (l/in [:workspace-assets :selected]) (-> (l/in [:workspace-assets :selected])
(l/derived st/state))) (l/derived st/state)))
(mf/defc file-library-title (mf/defc file-library-title*
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [open? local? project-id file-id page-id file-name]}] [{:keys [is-open is-local file-id page-id file-name]}]
(let [router (mf/deref refs/router) (let [router (mf/deref refs/router)
team-id (mf/use-ctx ctx/current-team-id)
url (rt/resolve router :workspace url (rt/resolve router :workspace
{:project-id project-id {:team-id team-id
:file-id file-id} :file-id file-id
{:page-id page-id}) :page-id page-id})
toggle-open toggle-open
(mf/use-fn (mf/use-fn
(mf/deps file-id open?) (mf/deps file-id is-open)
(fn [] (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 on-click
(mf/use-fn (mf/use-fn
(fn [ev] (fn [ev]
(dom/stop-propagation ev) (dom/stop-propagation ev)
(st/emit! (st/emit! (ptk/data-event ::ev/event {::ev/name "navigate-to-library-file"}))))]
(ptk/event ::ev/event {::ev/name "navigate-to-library-file"}))))]
[:div {:class (stl/css-case :library-title true [:div {:class (stl/css-case :library-title true
:open open?)} :open is-open)}
[:& title-bar {:collapsable true [:& title-bar {:collapsable true
:collapsed (not open?) :collapsed (not is-open)
:all-clickable true :all-clickable true
:on-collapsed toggle-open :on-collapsed toggle-open
:title (if local? :title (if is-local
(mf/html [:div {:class (stl/css :special-title)} (mf/html [:div {:class (stl/css :special-title)}
(tr "workspace.assets.local-library")]) (tr "workspace.assets.local-library")])
;; Do we need to add shared info here? ;; Do we need to add shared info here?
(mf/html [:div {:class (stl/css :special-title)} (mf/html [:div {:class (stl/css :special-title)}
file-name]))} file-name]))}
(when-not local? (when-not is-local
[:span {:title (tr "workspace.assets.open-library")} [:span {:title (tr "workspace.assets.open-library")}
[:a {:class (stl/css :file-link) [:a {:class (stl/css :file-link)
:href (str "#" url) :href (str "#" url)
@ -138,7 +138,7 @@
selected-lens (mf/with-memo [file-id] selected-lens (mf/with-memo [file-id]
(-> (l/key file-id) (-> (l/key file-id)
(l/derived lens:selected))) (l/derived ref:selected)))
selected (mf/deref selected-lens) selected (mf/deref selected-lens)
@ -329,17 +329,15 @@
(some #(> 60 (count %)) [filtered-components filtered-colors filtered-media filtered-typographies])))) (some #(> 60 (count %)) [filtered-components filtered-colors filtered-media filtered-typographies]))))
(mf/defc file-library (mf/defc file-library
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [file local? default-open? filters]}] [{:keys [file local? default-open? filters]}]
(let [file-id (:id file) (let [file-id (:id file)
file-name (:name file) file-name (:name file)
shared? (:is-shared file)
project-id (:project-id file)
page-id (dm/get-in file [:data :pages 0]) page-id (dm/get-in file [:data :pages 0])
open-status-ref (mf/with-memo [file-id] open-status-ref (mf/with-memo [file-id]
(-> (l/key file-id) (-> (l/key file-id)
(l/derived lens:open-status))) (l/derived ref:open-status)))
open-status (mf/deref open-status-ref) open-status (mf/deref open-status-ref)
force-open-lib? (force-lib-open? file-id filters) force-open-lib? (force-lib-open? file-id filters)
@ -356,14 +354,12 @@
[:div {:class (stl/css :tool-window) [:div {:class (stl/css :tool-window)
:on-context-menu dom/prevent-default :on-context-menu dom/prevent-default
:on-click unselect-all} :on-click unselect-all}
[:& file-library-title [:> file-library-title*
{:project-id project-id {:file-id file-id
:file-id file-id
:page-id page-id :page-id page-id
:file-name file-name :file-name file-name
:open? open? :is-open open?
:local? local? :is-local local?}]
:shared? shared?}]
(when ^boolean open? (when ^boolean open?
[:& file-library-content [:& file-library-content
{:file file {:file file

View file

@ -277,7 +277,6 @@
(assoc file :data data))))] (assoc file :data data))))]
(l/derived get-libraries st/state))) (l/derived get-libraries st/state)))
(defn- find-common-path (defn- find-common-path
([components] ([components]
(let [paths (map (comp cfh/split-path :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.data.macros :as dm]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.text :as txt] [app.common.text :as txt]
[app.main.data.common :as dcm]
[app.main.data.fonts :as fts] [app.main.data.fonts :as fts]
[app.main.data.shortcuts :as dsc] [app.main.data.shortcuts :as dsc]
[app.main.data.workspace :as dw]
[app.main.features :as features] [app.main.features :as features]
[app.main.fonts :as fonts] [app.main.fonts :as fonts]
[app.main.refs :as refs] [app.main.refs :as refs]
@ -564,7 +564,7 @@
(mf/deps file-id) (mf/deps file-id)
(fn [] (fn []
(when file-id (when file-id
(st/emit! (dw/navigate-to-library file-id))))) (st/emit! (dcm/go-to-workspace :file-id file-id)))))
on-key-down on-key-down
(mf/use-fn (mf/use-fn

View file

@ -9,6 +9,7 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.main.data.common :as dcm]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.refs :as refs] [app.main.refs :as refs]
@ -34,7 +35,7 @@
(let [input-ref (mf/use-ref) (let [input-ref (mf/use-ref)
id (:id page) id (:id page)
delete-fn (mf/use-fn (mf/deps id) #(st/emit! (dw/delete-page id))) 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?) read-only? (mf/use-ctx ctx/workspace-read-only?)
on-delete on-delete

View file

@ -226,12 +226,12 @@
(mf/defc versions-toolbox (mf/defc versions-toolbox
[] []
(let [users (mf/deref refs/users) (let [profiles (mf/deref refs/profiles)
profile (mf/deref refs/profile) 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 #{}) expanded (mf/use-state #{})
{:keys [status data editing]} {:keys [status data editing]}
(mf/deref versions) (mf/deref versions)
@ -242,7 +242,6 @@
(fn [] (fn []
(into #{} (keep (fn [{:keys [created-by profile-id]}] (into #{} (keep (fn [{:keys [created-by profile-id]}]
(when (= "user" created-by) profile-id))) data))) (when (= "user" created-by) profile-id))) data)))
data data
(mf/use-memo (mf/use-memo
(mf/deps @versions) (mf/deps @versions)
@ -257,7 +256,7 @@
handle-create-version handle-create-version
(mf/use-fn (mf/use-fn
(fn [] (fn []
(st/emit! (dwv/create-version file-id)))) (st/emit! (dwv/create-version))))
handle-toggle-expand handle-toggle-expand
(mf/use-fn (mf/use-fn
@ -271,14 +270,12 @@
handle-rename-version handle-rename-version
(mf/use-fn (mf/use-fn
(mf/deps file-id)
(fn [id label] (fn [id label]
(st/emit! (dwv/rename-version file-id id label)))) (st/emit! (dwv/rename-version id label))))
handle-restore-version handle-restore-version
(mf/use-fn (mf/use-fn
(mf/deps project-id file-id)
(fn [origin id] (fn [origin id]
(st/emit! (st/emit!
(ntf/dialog (ntf/dialog
@ -289,7 +286,7 @@
:callback #(st/emit! (ntf/hide))} :callback #(st/emit! (ntf/hide))}
{:label (tr "labels.restore") {:label (tr "labels.restore")
:type :primary :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)))) :tag :restore-dialog))))
handle-restore-version-pinned handle-restore-version-pinned
@ -306,15 +303,13 @@
handle-delete-version handle-delete-version
(mf/use-fn (mf/use-fn
(mf/deps file-id)
(fn [id] (fn [id]
(st/emit! (dwv/delete-version file-id id)))) (st/emit! (dwv/delete-version id))))
handle-pin-version handle-pin-version
(mf/use-fn (mf/use-fn
(mf/deps file-id)
(fn [id] (fn [id]
(st/emit! (dwv/pin-version file-id id)))) (st/emit! (dwv/pin-version id))))
handle-change-filter handle-change-filter
(mf/use-fn (mf/use-fn
@ -329,10 +324,8 @@
:else :else
(st/emit! (dwv/update-version-state {:filter filter})))))] (st/emit! (dwv/update-version-state {:filter filter})))))]
(mf/with-effect (mf/with-effect []
[file-id] (st/emit! (dwv/init-version-state)))
(when file-id
(st/emit! (dwv/init-version-state file-id))))
[:div {:class (stl/css :version-toolbox)} [:div {:class (stl/css :version-toolbox)}
[:& select [:& select
@ -343,7 +336,7 @@
(->> data-users (->> data-users
(keep (keep
(fn [id] (fn [id]
(let [{:keys [fullname]} (get users id)] (let [{:keys [fullname]} (get profiles id)]
(when (not= id (:id profile)) (when (not= id (:id profile))
{:value id :label (tr "workspace.versions.filter.user" fullname)})))))) {:value id :label (tr "workspace.versions.filter.user" fullname)}))))))
:on-change handle-change-filter}] :on-change handle-change-filter}]
@ -374,7 +367,7 @@
[:& version-entry {:key idx-entry [:& version-entry {:key idx-entry
:entry entry :entry entry
:editing? (= (:id entry) editing) :editing? (= (:id entry) editing)
:profile (get users (:profile-id entry)) :profile (get profiles (:profile-id entry))
:on-rename-version handle-rename-version :on-rename-version handle-rename-version
:on-restore-version handle-restore-version-pinned :on-restore-version handle-restore-version-pinned
:on-delete-version handle-delete-version}] :on-delete-version handle-delete-version}]

View file

@ -36,7 +36,7 @@
pos-y (* (- vbox-y) zoom) pos-y (* (- vbox-y) zoom)
profile (mf/deref refs/profile) profile (mf/deref refs/profile)
users (mf/deref refs/current-file-comments-users) profiles (mf/deref refs/profiles)
local (mf/deref refs/comments-local) local (mf/deref refs/comments-local)
positions-ref positions-ref
@ -84,7 +84,7 @@
(when-let [thread (get threads-map id)] (when-let [thread (get threads-map id)]
(when (seq (dcm/apply-filters local profile [thread])) (when (seq (dcm/apply-filters local profile [thread]))
[:& cmt/thread-comments {:thread (update-position positions 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)} :viewport {:offset-x pos-x :offset-y pos-y :width (:width vport) :height (:height vport)}
:zoom zoom}]))) :zoom zoom}])))

View file

@ -56,7 +56,7 @@
{::mf/props :obj} {::mf/props :obj}
[{:keys [page-id]}] [{:keys [page-id]}]
(let [counter (mf/use-state 0) (let [counter (mf/use-state 0)
users (mf/deref refs/users) profiles (mf/deref refs/profiles)
sessions (mf/deref refs/workspace-presence) sessions (mf/deref refs/workspace-presence)
zoom (mf/deref refs/selected-zoom) zoom (mf/deref refs/selected-zoom)
@ -73,5 +73,5 @@
[:& session-cursor [:& session-cursor
{:session session {:session session
:zoom zoom :zoom zoom
:profile (get users (:profile-id session)) :profile (get profiles (:profile-id session))
:key (dm/str (:id session))}]))) :key (dm/str (:id session))}])))

View file

@ -15,6 +15,7 @@
[app.common.types.shape-tree :as ctt] [app.common.types.shape-tree :as ctt]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.common :as dcm]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.interactions :as dwi] [app.main.data.workspace.interactions :as dwi]
[app.main.refs :as refs] [app.main.refs :as refs]
@ -291,7 +292,7 @@
(when (dom/left-mouse? event) (when (dom/left-mouse? event)
(dom/prevent-default event) (dom/prevent-default event)
(dom/stop-propagation event) (dom/stop-propagation event)
(st/emit! (dw/go-to-viewer params)))))) (st/emit! (dcm/go-to-viewer params))))))
on-double-click on-double-click
(mf/use-fn (mf/use-fn

View file

@ -18,12 +18,14 @@
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.changes :as ch] [app.main.data.changes :as ch]
[app.main.data.common :as dcm]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.bool :as dwb] [app.main.data.workspace.bool :as dwb]
[app.main.data.workspace.colors :as dwc] [app.main.data.workspace.colors :as dwc]
[app.main.data.workspace.groups :as dwg] [app.main.data.workspace.groups :as dwg]
[app.main.data.workspace.media :as dwm] [app.main.data.workspace.media :as dwm]
[app.main.data.workspace.selection :as dws] [app.main.data.workspace.selection :as dws]
[app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
[app.plugins.events :as events] [app.plugins.events :as events]
[app.plugins.file :as file] [app.plugins.file :as file]
@ -409,7 +411,7 @@
(let [params {:page-id (:current-page-id @st/state) (let [params {:page-id (:current-page-id @st/state)
:file-id (:current-file-id @st/state) :file-id (:current-file-id @st/state)
:section "interactions"}] :section "interactions"}]
(st/emit! (dw/go-to-viewer params)))) (st/emit! (dcm/go-to-viewer params))))
:createPage :createPage
(fn [] (fn []
@ -420,7 +422,7 @@
:openPage :openPage
(fn [page] (fn [page]
(let [id (obj/get page "$id")] (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 :alignHorizontal
(fn [shapes direction] (fn [shapes direction]

View file

@ -70,14 +70,15 @@
:restore :restore
(fn [] (fn []
(js/Promise.
(fn [resolve reject]
(cond (cond
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :restore "Plugin doesn't have 'content:write' permission") (u/reject-not-valid reject :restore "Plugin doesn't have 'content:write' permission")
:else :else
(let [project-id (:current-project-id @st/state) (let [version-id (get @data :id)]
version-id (get @data :id)] (st/emit! (dwv/restore-version-from-plugin file-id version-id resolve reject)))))))
(st/emit! (dwv/restore-version project-id file-id version-id :plugin)))))
:remove :remove
(fn [] (fn []
@ -90,7 +91,8 @@
:else :else
(let [version-id (:id @data)] (let [version-id (:id @data)]
(->> (rp/cmd! :delete-file-snapshot {:id version-id}) (->> (rp/cmd! :delete-file-snapshot {:id version-id})
(rx/subs! #(resolve) reject))))))) (rx/map (constantly nil))
(rx/subs! resolve reject)))))))
:pin :pin
(fn [] (fn []

View file

@ -14,10 +14,12 @@
[app.common.spec :as us] [app.common.spec :as us]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.comments :as dc] [app.main.data.comments :as dc]
[app.main.data.common :as dcm]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.guides :as dwgu] [app.main.data.workspace.guides :as dwgu]
[app.main.data.workspace.interactions :as dwi] [app.main.data.workspace.interactions :as dwi]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.router :as-alias rt]
[app.main.store :as st] [app.main.store :as st]
[app.plugins.comments :as pc] [app.plugins.comments :as pc]
[app.plugins.format :as format] [app.plugins.format :as format]
@ -266,7 +268,7 @@
(u/display-not-valid :openPage "Plugin doesn't have 'content:read' permission") (u/display-not-valid :openPage "Plugin doesn't have 'content:read' permission")
:else :else
(st/emit! (dw/go-to-page id)))) (st/emit! (dcm/go-to-workspace :page-id id ::rt/new-window true))))
:createFlow :createFlow
(fn [name frame] (fn [name frame]

View file

@ -17,6 +17,7 @@
[app.util.webapi :as wapi] [app.util.webapi :as wapi]
[cuerdas.core :as str] [cuerdas.core :as str]
[goog.dom :as dom] [goog.dom :as dom]
[potok.v2.core :as ptk]
[promesa.core :as p]) [promesa.core :as p])
(:import goog.events.BrowserEvent)) (:import goog.events.BrowserEvent))
@ -853,3 +854,10 @@
measures (.measureText context-2d text)] measures (.measureText context-2d text)]
{:descent (.-actualBoundingBoxDescent measures) {:descent (.-actualBoundingBoxDescent measures)
:ascent (.-actualBoundingBoxAscent 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.types.file :as ctf]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.changes :as dwc] [app.main.data.changes :as dwc]
[app.main.data.common :as dcm]
[app.main.data.dashboard.shortcuts] [app.main.data.dashboard.shortcuts]
[app.main.data.preview :as dp] [app.main.data.preview :as dp]
[app.main.data.viewer.shortcuts] [app.main.data.viewer.shortcuts]
@ -232,7 +233,7 @@
(defn ^:export select-by-object-id (defn ^:export select-by-object-id
[object-id] [object-id]
(let [[_ page-id shape-id _] (str/split 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))))) (st/emit! (dws/select-shape (uuid/uuid shape-id)))))
(defn ^:export select-by-id (defn ^:export select-by-id