diff --git a/backend/src/uxbox/http/handlers.clj b/backend/src/uxbox/http/handlers.clj index eb83d69df..b1403d8ab 100644 --- a/backend/src/uxbox/http/handlers.clj +++ b/backend/src/uxbox/http/handlers.clj @@ -17,26 +17,25 @@ [vertx.web :as vw] [vertx.eventbus :as ve])) -(def mutation-types-hierarchy - (-> (make-hierarchy) - (derive :login ::unauthenticated) - (derive :logout ::unauthenticated) - (derive :register-profile ::unauthenticated) - (derive :request-profile-recovery ::unauthenticated) - (derive :recover-profile ::unauthenticated) - (derive :create-demo-profile ::unauthenticated))) - -(def query-types-hierarchy - (make-hierarchy)) +(def unauthorized-services + #{:create-demo-profile + :logout + :profile + :recover-profile + :register-profile + :request-profile-recovery + :viewer-bundle + :login}) (defn query-handler [req] (let [type (keyword (get-in req [:path-params :type])) data (merge (:params req) - {::sq/type type - :profile-id (:profile-id req)})] + {::sq/type type}) + data (cond-> data + (:profile-id req) (assoc :profile-id (:profile-id req)))] (if (or (:profile-id req) - (isa? query-types-hierarchy type ::unauthenticated)) + (contains? unauthorized-services type)) (-> (sq/handle (with-meta data {:req req})) (p/then' (fn [result] {:status 200 @@ -51,10 +50,11 @@ data (merge (:params req) (:body-params req) (:uploads req) - {::sm/type type - :profile-id (:profile-id req)})] + {::sm/type type}) + data (cond-> data + (:profile-id req) (assoc :profile-id (:profile-id req)))] (if (or (:profile-id req) - (isa? mutation-types-hierarchy type ::unauthenticated)) + (contains? unauthorized-services type)) (-> (sm/handle (with-meta data {:req req})) (p/then' (fn [result] {:status 200 :body result}))) diff --git a/backend/src/uxbox/services/mutations/profile.clj b/backend/src/uxbox/services/mutations/profile.clj index 5e30d5108..c5b00e599 100644 --- a/backend/src/uxbox/services/mutations/profile.clj +++ b/backend/src/uxbox/services/mutations/profile.clj @@ -91,15 +91,6 @@ (db/query-one conn [sql:profile-by-email email])) -;; --- Mutation: Add additional email -;; TODO - -;; --- Mutation: Mark email as main email -;; TODO - -;; --- Mutation: Verify email (or maybe query?) -;; TODO - ;; --- Mutation: Update Profile (own) (def ^:private sql:update-profile @@ -158,6 +149,7 @@ (update-password conn params))) + ;; --- Mutation: Update Photo (declare upload-photo) diff --git a/backend/src/uxbox/services/queries/profile.clj b/backend/src/uxbox/services/queries/profile.clj index d15e19b44..c7a75aab4 100644 --- a/backend/src/uxbox/services/queries/profile.clj +++ b/backend/src/uxbox/services/queries/profile.clj @@ -15,6 +15,7 @@ [uxbox.images :as images] [uxbox.services.queries :as sq] [uxbox.services.util :as su] + [uxbox.util.uuid :as uuid] [uxbox.util.blob :as blob])) ;; --- Helpers & Specs @@ -32,11 +33,19 @@ ;; --- Query: Profile (own) -(defn retrieve-profile - [conn id] - (let [sql "select * from profile where id=$1 and deleted_at is null"] - (db/query-one db/pool [sql id]))) +(declare retrieve-profile) +(declare retrieve-additional-data) +(s/def ::profile + (s/keys :opt-un [::profile-id])) + +(sq/defquery ::profile + [{:keys [profile-id] :as params}] + (if profile-id + (db/with-atomic [conn db/pool] + (retrieve-profile conn profile-id)) + {:id uuid/zero + :fullname "Anonymous User"})) ;; NOTE: this query make the assumption that union all preserves the ;; order so the first id will always be the team id and the second the @@ -65,18 +74,19 @@ {:default-team-id (:id team) :default-project-id (:id project)})))) -(s/def ::profile - (s/keys :req-un [::profile-id])) +(defn retrieve-profile-data + [conn id] + (let [sql "select * from profile where id=$1 and deleted_at is null"] + (db/query-one conn [sql id]))) -(sq/defquery ::profile - [{:keys [profile-id] :as params}] - (db/with-atomic [conn db/pool] - (p/let [prof (-> (retrieve-profile conn profile-id) - (p/then' su/raise-not-found-if-nil) - (p/then' strip-private-attrs) - (p/then' #(images/resolve-media-uris % [:photo :photo-uri]))) - addt (retrieve-additional-data conn profile-id)] - (merge prof addt)))) +(defn retrieve-profile + [conn id] + (p/let [prof (-> (retrieve-profile-data conn id) + (p/then' su/raise-not-found-if-nil) + (p/then' strip-private-attrs) + (p/then' #(images/resolve-media-uris % [:photo :photo-uri]))) + addt (retrieve-additional-data conn id)] + (merge prof addt))) ;; --- Attrs Helpers diff --git a/frontend/resources/images/icons/full-screen-off.svg b/frontend/resources/images/icons/full-screen-off.svg new file mode 100644 index 000000000..47e7db42c --- /dev/null +++ b/frontend/resources/images/icons/full-screen-off.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/resources/images/icons/full-screen.svg b/frontend/resources/images/icons/full-screen.svg index 47e7db42c..252357a17 100644 --- a/frontend/resources/images/icons/full-screen.svg +++ b/frontend/resources/images/icons/full-screen.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/frontend/resources/styles/common/framework.scss b/frontend/resources/styles/common/framework.scss index 3cfb82e03..80db6b5fc 100644 --- a/frontend/resources/styles/common/framework.scss +++ b/frontend/resources/styles/common/framework.scss @@ -25,7 +25,7 @@ flex-shrink: 0; } &.btn-small { - font-size: $fs12; + font-size: $fs13; padding: .7rem 1rem; line-height: 1.15; } diff --git a/frontend/resources/styles/main.scss b/frontend/resources/styles/main.scss index 545e75779..6ea6dd0bf 100644 --- a/frontend/resources/styles/main.scss +++ b/frontend/resources/styles/main.scss @@ -45,7 +45,7 @@ @import 'main/partials/main-bar'; @import 'main/partials/workspace'; -@import 'main/partials/workspace-bar'; +@import 'main/partials/workspace-header'; @import 'main/partials/workspace-libraries'; @import 'main/partials/tool-bar'; @import 'main/partials/project-bar'; @@ -71,6 +71,7 @@ @import 'main/partials/debug-icons-preview'; @import 'main/partials/editable-label'; @import 'main/partials/tab-container'; +@import "main/partials/zoom-widget"; @import "main/partials/viewer-header"; @import "main/partials/viewer-thumbnails"; @import "main/partials/viewer"; diff --git a/frontend/resources/styles/main/partials/viewer-header.scss b/frontend/resources/styles/main/partials/viewer-header.scss index 130274e78..4372e4837 100644 --- a/frontend/resources/styles/main/partials/viewer-header.scss +++ b/frontend/resources/styles/main/partials/viewer-header.scss @@ -9,6 +9,10 @@ z-index: 12; justify-content: space-between; + a { + font-size: $fs13; + } + .main-icon { align-items: center; background-color: $color-gray-60; @@ -85,9 +89,13 @@ align-items: center; display: flex; width: 300px; - justify-content: space-between; + justify-content: flex-end; position: relative; + > * { + margin-left: $big; + } + .btn-share { display: flex; align-items: center; @@ -107,6 +115,10 @@ padding: 0.4rem 1rem; } + .btn-primary.btn-small { + height: 25px; + } + .btn-fullscreen { align-items: center; background-color: $color-gray-60; @@ -134,23 +146,25 @@ } .share-link-dropdown { - position: absolute; - left: -180px; - top: 45px; - - background-color: $color-gray-50; + background-color: $color-white; + border-radius: $br-small; + box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); display: flex; flex-direction: column; - + left: -180px; + position: absolute; padding: 1rem; + top: 45px; width: 400px; .share-link-title { - font-size: 18px; + color: $color-black; + font-size: $fs15; padding-bottom: 1rem; } .share-link-subtitle { + color: $color-gray-40; padding-bottom: 1rem; } @@ -168,111 +182,50 @@ } .share-link-input { - display: flex; - justify-content: space-between; - padding: $small; - align-items: center; - border: 1px solid $color-gray-30; + border: 1px solid $color-gray-20; border-radius: 3px; + display: flex; + height: 40px; + justify-content: space-between; margin-bottom: 1rem; + padding: 9px $small; + overflow: hidden; .link { + color: $color-gray-50; + line-height: 1.5; user-select: all; + overflow: hidden; } - svg { - width: 20px; - height: 20px; - fill: $color-gray-20; - stroke: $color-gray-20; + .link-button { + color: $color-primary-dark; + cursor: pointer; + flex-shrink: 0; + font-size: $fs15; + + &:hover { + color: $color-black; + } } } + &:before { + background-color: $color-white; + content: ""; + height: 16px; + left: 53%; + position: absolute; + transform: rotate(45deg); + top: -5px; + width: 16px; + } + } - .zoom-widget { - cursor: pointer; - - align-items: center; - display: flex; - position: relative; - - .input-container { - display: flex; - } - - span { - color: $color-gray-10; - font-size: $fs15; - margin-left: $x-small; - } - - .dropdown-button svg { - fill: $color-gray-10; - height: 10px; - width: 10px; - } - - .zoom-dropdown { - position: absolute; - right: -25px; - top: 45px; - z-index: 12; - width: 150px; - - background-color: $color-white; - border-radius: $br-small; - box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); - - li { - color: $color-gray-60; - cursor: pointer; - font-size: $fs12; - display: flex; - padding: $small; - - span { - color: $color-gray-40; - font-size: $fs12; - margin-left: auto; - } - - &:hover { - background-color: $color-primary-lighter; - } - - } - } - - .add-zoom, - .remove-zoom { - align-items: center; - background-color: $color-gray-60; - border-radius: $br-small; - cursor: pointer; - color: $color-gray-20; - display: flex; - opacity: 0; - flex-shrink: 0; - font-size: $fs20; - font-weight: bold; - height: 20px; - justify-content: center; - width: 20px; - - &:hover { - color: $color-primary; - } - - } - - &:hover { - .add-zoom, - .remove-zoom { - opacity: 100%; - } - } - + .zoom-dropdown { + left : 150px; + top: 45px; } .users-zone { diff --git a/frontend/resources/styles/main/partials/workspace-bar.scss b/frontend/resources/styles/main/partials/workspace-bar.scss deleted file mode 100644 index 52c129cd5..000000000 --- a/frontend/resources/styles/main/partials/workspace-bar.scss +++ /dev/null @@ -1,386 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2015-2016 Andrey Antukh -// Copyright (c) 2015-2016 Juan de la Cruz - -.workspace-bar { - align-items: center; - background-color: $color-gray-50; - border-bottom: 1px solid $color-gray-60; - display: flex; - height: 40px; - padding: $x-small $medium $x-small 55px; - position: relative; - z-index: 12; - - .preview { - align-items: center; - background-color: $color-gray-60; - border-radius: $br-small; - cursor: pointer; - display: flex; - height: 25px; - justify-content: center; - width: 25px; - - svg { - fill: $color-gray-20; - width: 15px; - height: 15px; - } - - &:hover { - background-color: $color-primary; - - svg { - fill: $color-gray-60; - } - } - } - - .workspace-menu { - position: absolute; - top: 40px; - left: 40px; - width: 230px; - z-index: 12; - @include animation(0,.2s,fadeInDown); - - background-color: $color-white; - border-radius: $br-small; - box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); - - li { - cursor: pointer; - font-size: $fs12; - padding: $small $x-small; - - svg { - fill: $color-gray-60; - height: 12px; - width: 12px; - } - - span { - color: $color-gray-60; - margin: 0 $x-small; - } - - &:hover { - background-color: $color-primary-lighter; - } - } - } - - - .main-icon { - align-items: center; - background-color: $color-gray-60; - cursor: pointer; - display: flex; - height: 100%; - justify-content: center; - left: 0; - position: absolute; - top: 0; - width: 40px; - - a { - height: 30px; - - svg { - fill: $color-gray-30; - height: 30px; - width: 28px; - - } - - &:hover { - - svg { - fill: $color-primary; - } - - } - - } - - } - - .menu-btn { - align-items: center; - background-color: $color-gray-60; - cursor: pointer; - border-radius: $br-small; - display: flex; - margin-right: $x-small; - padding: $x-small; - - svg { - height: 15px; - fill: $color-gray-20; - width: 15px; - } - - &:hover { - background-color: $color-primary; - - svg { - fill: $color-gray-60; - } - } - } - - .project-tree-btn { - align-items: center; - cursor: pointer; - display: flex; - padding: $x-small; - - svg { - fill: $color-gray-20; - height: 20px; - margin-right: $small; - width: 20px; - } - - span { - color: $color-white; - font-size: $fs14; - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - &.project-name { - color: $color-gray-20; - margin-right: $x-small; - } - } - - } - - .workspace-options { - display: flex; - margin: auto; - } - -} - -.zoom-input { - align-items: center; - display: flex; - position: relative; - - span { - color: $color-gray-10; - font-size: $fs15; - margin-left: $x-small; - } - - .dropdown-button { - svg { - fill: $color-gray-10; - height: 10px; - width: 10px; - } - } - - .zoom-dropdown { - position: absolute; - right: 0; - z-index: 12; - width: 150px; - @include animation(0,.2s,fadeInDown); - - background-color: $color-white; - border-radius: $br-small; - box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); - - li { - color: $color-gray-60; - cursor: pointer; - font-size: $fs12; - display: flex; - padding: $small; - - span { - color: $color-gray-40; - font-size: $fs12; - margin-left: auto; - } - - &:hover { - background-color: $color-primary-lighter; - } - - } - } - - .add-zoom, - .remove-zoom { - align-items: center; - background-color: $color-gray-60; - border-radius: $br-small; - cursor: pointer; - color: $color-gray-20; - display: flex; - opacity: 0; - flex-shrink: 0; - font-size: $fs20; - font-weight: bold; - height: 20px; - justify-content: center; - width: 20px; - - &:hover { - color: $color-primary; - } - - } - - &:hover { - .add-zoom, - .remove-zoom { - opacity: 100%; - } - } - -} - -.options-btn { - align-items: center; - border-right: 4px double $color-gray-60; - display: flex; - margin: 0; - - &:last-child { - border: none; - } - - li { - align-items: center; - background-color: $color-gray-60; - border: 1px solid transparent; - border-radius: $br-small; - cursor: pointer; - display: flex; - flex-shrink: 0; - height: 30px; - justify-content: center; - margin: 0 $small; - position: relative; - width: 30px; - - a { - padding-top: 6px; - } - - svg { - fill: $color-gray-20; - height: 18px; - width: 18px; - } - - &:hover { - background-color: $color-gray-10; - border-color: $color-gray-60; - } - - &.selected { - background-color: $color-primary; - - svg { - fill: $color-white; - } - - } - - } - -} - -.secondary-options { - align-items: center; - display: flex; - - .view-mode { - background-color: $color-gray-20; - align-items: center; - border-radius: $br-small; - cursor: pointer; - display: flex; - flex-shrink: 0; - height: 30px; - justify-content: center; - margin: 0 $small; - position: relative; - width: 30px; - - a { - padding-top: 6px; - } - - svg { - fill: $color-gray-60; - height: 18px; - width: 18px; - } - - &:hover { - background-color: $color-primary; - - svg { - fill: $color-gray-60; - } - - } - - } - -} - -.user-multi { - align-items: center; - cursor: pointer; - display: flex; - margin: 0; - - li { - margin-left: $small; - position: relative; - - img { - border: 3px solid #f3dd14; - border-radius: 50%; - flex-shrink: 0; - height: 25px; - width: 25px; - } - } -} - -.multiuser-cursor { - align-items: center; - display: flex; - left: 0; - position: absolute; - top: 0; - z-index: 10000; - - svg { - height: 15px; - fill: #f3dd14; - width: 15px; - } - - span { - background-color: #f3dd14; - border-radius: $br-small; - color: $color-black; - font-size: $fs12; - margin-left: $small; - padding: $x-small; - } -} diff --git a/frontend/resources/styles/main/partials/workspace-header.scss b/frontend/resources/styles/main/partials/workspace-header.scss new file mode 100644 index 000000000..638d0849f --- /dev/null +++ b/frontend/resources/styles/main/partials/workspace-header.scss @@ -0,0 +1,209 @@ +// 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) 2015-2016 Andrey Antukh +// Copyright (c) 2015-2016 Juan de la Cruz + +.workspace-header { + align-items: center; + background-color: $color-gray-50; + border-bottom: 1px solid $color-gray-60; + display: flex; + height: 40px; + padding: $x-small $medium $x-small 55px; + position: relative; + z-index: 12; + justify-content: space-between; + + .main-icon { + align-items: center; + background-color: $color-gray-60; + cursor: pointer; + display: flex; + height: 100%; + justify-content: center; + left: 0; + position: absolute; + top: 0; + width: 40px; + + a { + height: 30px; + + svg { + fill: $color-gray-30; + height: 30px; + width: 28px; + + } + + &:hover { + + svg { + fill: $color-primary; + } + + } + + } + } + + .menu-section { + display: flex; + justify-content: flex-start; + align-items: center; + } + + .users-section { + display: flex; + } + + .options-section { + display: flex; + justify-content: flex-end; + align-items: center; + position: relative; + + > * { + margin-left: $big; + } + + .zoom-dropdown { + top: 45px; + left: -30px; + } + } + + .menu-button { + align-items: center; + background-color: $color-gray-60; + cursor: pointer; + border-radius: $br-small; + display: flex; + margin-right: $x-small; + padding: $x-small; + width: 25px; + height: 25px; + + svg { + height: 15px; + fill: $color-gray-20; + width: 15px; + } + + &:hover { + background-color: $color-primary; + + svg { + fill: $color-gray-60; + } + } + } + + .project-tree { + align-items: center; + cursor: pointer; + display: flex; + padding: $x-small; + + svg { + fill: $color-gray-20; + height: 20px; + margin-right: $small; + width: 20px; + } + + span { + color: $color-white; + font-size: $fs14; + overflow-x: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + &.project-name { + color: $color-gray-20; + margin-right: $x-small; + } + } + } + + .preview-button { + align-items: center; + background-color: $color-gray-60; + border-radius: $br-small; + cursor: pointer; + display: flex; + height: 25px; + justify-content: center; + width: 25px; + + svg { + fill: $color-gray-20; + width: 15px; + height: 15px; + } + + &:hover { + background-color: $color-primary; + + svg { + fill: $color-gray-60; + } + } + } + + .menu { + position: absolute; + top: 40px; + left: 40px; + width: 230px; + z-index: 12; + @include animation(0,.2s,fadeInDown); + + background-color: $color-white; + border-radius: $br-small; + box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); + + li { + cursor: pointer; + font-size: $fs12; + padding: $small $x-small; + + svg { + fill: $color-gray-60; + height: 12px; + width: 12px; + } + + span { + color: $color-gray-60; + margin: 0 $x-small; + } + + &:hover { + background-color: $color-primary-lighter; + } + } + } + + .active-users { + align-items: center; + cursor: pointer; + display: flex; + margin: 0; + + li { + margin-left: $small; + position: relative; + + img { + border: 3px solid #f3dd14; + border-radius: 50%; + flex-shrink: 0; + height: 25px; + width: 25px; + } + } + } +} diff --git a/frontend/resources/styles/main/partials/workspace.scss b/frontend/resources/styles/main/partials/workspace.scss index ff623ca27..b7ab8f403 100644 --- a/frontend/resources/styles/main/partials/workspace.scss +++ b/frontend/resources/styles/main/partials/workspace.scss @@ -183,3 +183,28 @@ fill: $color-primary-dark; } + + +.multiuser-cursor { + align-items: center; + display: flex; + left: 0; + position: absolute; + top: 0; + z-index: 10000; + + svg { + height: 15px; + fill: #f3dd14; + width: 15px; + } + + span { + background-color: #f3dd14; + border-radius: $br-small; + color: $color-black; + font-size: $fs12; + margin-left: $small; + padding: $x-small; + } +} diff --git a/frontend/resources/styles/main/partials/zoom-widget.scss b/frontend/resources/styles/main/partials/zoom-widget.scss new file mode 100644 index 000000000..95bd8788b --- /dev/null +++ b/frontend/resources/styles/main/partials/zoom-widget.scss @@ -0,0 +1,45 @@ +.zoom-widget { + cursor: pointer; + display: flex; + + span { + color: $color-gray-10; + font-size: $fs14; + margin-left: $x-small; + } + + .dropdown-button svg { + fill: $color-gray-10; + height: 10px; + width: 10px; + } + + .zoom-dropdown { + position: absolute; + z-index: 12; + width: 150px; + + background-color: $color-white; + border-radius: $br-small; + box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); + + li { + color: $color-gray-60; + cursor: pointer; + font-size: $fs12; + display: flex; + padding: $small; + + span { + color: $color-gray-40; + font-size: $fs12; + margin-left: auto; + } + + &:hover { + background-color: $color-primary-lighter; + } + + } + } +} diff --git a/frontend/src/uxbox/builtins/icons.cljs b/frontend/src/uxbox/builtins/icons.cljs index 069f01f84..5636956f1 100644 --- a/frontend/src/uxbox/builtins/icons.cljs +++ b/frontend/src/uxbox/builtins/icons.cljs @@ -39,6 +39,7 @@ (def folder (icon-xref :folder)) (def folder-zip (icon-xref :folder-zip)) (def full-screen (icon-xref :full-screen)) +(def full-screen-off (icon-xref :full-screen-off)) (def grid (icon-xref :grid)) (def grid-snap (icon-xref :grid-snap)) (def icon-set (icon-xref :icon-set)) @@ -103,7 +104,7 @@ {::mf/wrap-props false} [props] [:section.debug-icons-preview - (for [[key val] (ns-publics 'uxbox.builtins.icons)] + (for [[key val] (sort-by first (ns-publics 'uxbox.builtins.icons))] (when (not= key 'debug-icons-preview) [:div.icon-item {:key key} (deref val) diff --git a/frontend/src/uxbox/main/data/users.cljs b/frontend/src/uxbox/main/data/users.cljs index f12dd1656..0a6e4158e 100644 --- a/frontend/src/uxbox/main/data/users.cljs +++ b/frontend/src/uxbox/main/data/users.cljs @@ -18,23 +18,25 @@ ;; --- Common Specs -(s/def ::id uuid?) -(s/def ::fullname string?) +(s/def ::id ::us/uuid) +(s/def ::fullname ::us/string) (s/def ::email ::us/email) -(s/def ::password string?) -(s/def ::language string?) -(s/def ::photo string?) -(s/def ::created-at inst?) -(s/def ::password-1 string?) -(s/def ::password-2 string?) -(s/def ::password-old string?) +(s/def ::password ::us/string) +(s/def ::language ::us/string) +(s/def ::photo ::us/string) +(s/def ::created-at ::us/inst) +(s/def ::password-1 ::us/string) +(s/def ::password-2 ::us/string) +(s/def ::password-old ::us/string) +(s/def ::lang (s/nilable ::us/string)) (s/def ::profile - (s/keys :req-un [::id + (s/keys :req-un [::id] + :opt-un [::created-at ::fullname + ::photo ::email - ::created-at - ::photo])) + ::lang])) ;; --- Profile Fetched diff --git a/frontend/src/uxbox/main/data/viewer.cljs b/frontend/src/uxbox/main/data/viewer.cljs index 26fe30479..d3ad06487 100644 --- a/frontend/src/uxbox/main/data/viewer.cljs +++ b/frontend/src/uxbox/main/data/viewer.cljs @@ -41,7 +41,7 @@ (declare bundle-fetched) (defn initialize - [page-id] + [page-id share-token] (ptk/reify ::initialize ptk/UpdateEvent (update [_ state] @@ -49,18 +49,21 @@ ptk/WatchEvent (watch [_ state stream] - (rx/of (fetch-bundle page-id))))) + (rx/of (fetch-bundle page-id share-token))))) ;; --- Data Fetching (defn fetch-bundle - [page-id] + [page-id share-token] (ptk/reify ::fetch-file ptk/WatchEvent (watch [_ state stream] - (->> (rp/query :viewer-bundle {:page-id page-id}) - (rx/map bundle-fetched))))) - + (let [params (cond-> {:page-id page-id} + (string? share-token) (assoc :share-token share-token))] + (->> (rp/query :viewer-bundle params) + (rx/map bundle-fetched) + (rx/catch (fn [error-data] + (rx/of (rt/nav :not-found))))))))) (defn- extract-frames [page] diff --git a/frontend/src/uxbox/main/refs.cljs b/frontend/src/uxbox/main/refs.cljs index 19da8d924..2a8fab50c 100644 --- a/frontend/src/uxbox/main/refs.cljs +++ b/frontend/src/uxbox/main/refs.cljs @@ -5,7 +5,7 @@ ;; This Source Code Form is "Incompatible With Secondary Licenses", as ;; defined by the Mozilla Public License, v. 2.0. ;; -;; Copyright (c) 2017-2019 Andrey Antukh +;; Copyright (c) 2020 UXBOX Labs SL (ns uxbox.main.refs "A collection of derived refs." diff --git a/frontend/src/uxbox/main/ui.cljs b/frontend/src/uxbox/main/ui.cljs index 8eb0cb911..7a0cf1602 100644 --- a/frontend/src/uxbox/main/ui.cljs +++ b/frontend/src/uxbox/main/ui.cljs @@ -101,12 +101,6 @@ :profile-recovery [:& profile-recovery-page] - :viewer - (let [index (d/parse-integer (get-in route [:params :query :index])) - page-id (uuid (get-in route [:params :path :page-id]))] - [:& viewer-page {:page-id page-id - :index index}]) - (:settings-profile :settings-password) [:& settings/settings {:route route}] @@ -126,6 +120,14 @@ :dashboard-library-palettes-index) [:& dashboard {:route route}] + :viewer + (let [index (d/parse-integer (get-in route [:params :query :index])) + token (get-in route [:params :query :token]) + page-id (uuid (get-in route [:params :path :page-id]))] + [:& viewer-page {:page-id page-id + :index index + :token token}]) + :workspace (let [project-id (uuid (get-in route [:params :path :project-id])) file-id (uuid (get-in route [:params :path :file-id])) diff --git a/frontend/src/uxbox/main/ui/viewer.cljs b/frontend/src/uxbox/main/ui/viewer.cljs index 87ac9f540..f9a71c21b 100644 --- a/frontend/src/uxbox/main/ui/viewer.cljs +++ b/frontend/src/uxbox/main/ui/viewer.cljs @@ -96,8 +96,11 @@ (l/derive st/state))) (mf/defc viewer-page - [{:keys [page-id index] :as props}] - (mf/use-effect (mf/deps page-id) #(st/emit! (dv/initialize page-id))) + [{:keys [page-id index token] :as props}] + (mf/use-effect + (mf/deps page-id token) + #(st/emit! (dv/initialize page-id token))) + (let [data (mf/deref viewer-data-ref) local (mf/deref viewer-local-ref)] (when data diff --git a/frontend/src/uxbox/main/ui/viewer/header.cljs b/frontend/src/uxbox/main/ui/viewer/header.cljs index 4964ed707..f4377ab1f 100644 --- a/frontend/src/uxbox/main/ui/viewer/header.cljs +++ b/frontend/src/uxbox/main/ui/viewer/header.cljs @@ -17,43 +17,18 @@ [uxbox.builtins.icons :as i] [uxbox.main.store :as st] [uxbox.main.ui.components.dropdown :refer [dropdown]] + [uxbox.main.ui.workspace.header :refer [zoom-widget]] [uxbox.main.data.viewer :as dv] + [uxbox.main.refs :as refs] [uxbox.util.data :refer [classnames]] [uxbox.util.dom :as dom] + [uxbox.util.uuid :as uuid] [uxbox.util.i18n :as i18n :refer [t tr]] [uxbox.util.math :as mth] [uxbox.util.router :as rt]) (:import goog.events.EventType goog.events.KeyCodes)) -(mf/defc zoom-widget - {:wrap [mf/memo]} - [{:keys [zoom] :as props}] - (let [show-dropdown? (mf/use-state false) - increase #(st/emit! dv/increase-zoom) - decrease #(st/emit! dv/decrease-zoom) - zoom-to-50 #(st/emit! dv/zoom-to-50) - zoom-to-100 #(st/emit! dv/reset-zoom) - zoom-to-200 #(st/emit! dv/zoom-to-200)] - [:div.zoom-widget - [:span.add-zoom {:on-click decrease} "-"] - [:div.input-container {:on-click #(reset! show-dropdown? true)} - [:span {} (str (mth/round (* 100 zoom)) "%")] - [:span.dropdown-button i/arrow-down] - [:& dropdown {:show @show-dropdown? - :on-close #(reset! show-dropdown? false)} - [:ul.zoom-dropdown - [:li {:on-click increase} - "Zoom in" [:span "+"]] - [:li {:on-click decrease} - "Zoom out" [:span "-"]] - [:li {:on-click zoom-to-50} - "Zoom to 50%"] - [:li {:on-click zoom-to-100} - "Zoom to 100%" [:span "Shift + 0"]] - [:li {:on-click zoom-to-200} - "Zoom to 200%"]]]] - [:span.remove-zoom {:on-click increase} "+"]])) (mf/defc share-link [{:keys [page] :as props}] @@ -64,12 +39,11 @@ create #(st/emit! dv/create-share-link) delete #(st/emit! dv/delete-share-link) href (.-href js/location)] - [:* - [:span.btn-share.tooltip.tooltip-bottom + [:span.btn-primary.btn-small {:alt "Share link" :on-click #(swap! show-dropdown? not)} - i/exit] + "Share link"] [:& dropdown {:show @show-dropdown? :on-close #(swap! show-dropdown? not) @@ -78,28 +52,35 @@ [:span.share-link-title "Share link"] [:div.share-link-input (if (string? token) - [:span.link (str href "&" token)] - [:span "Share link will apear here"]) - i/chain] + [:span.link (str href "&token=" token)] + [:span.link-placeholder "Share link will apear here"]) + [:span.link-button "Copy link"]] [:span.share-link-subtitle "Anyone with the link will have access"] [:div.share-link-buttons (if (string? token) [:button.btn-delete {:on-click delete} "Remove link"] [:button.btn-primary {:on-click create} "Create link"])]]]])) - (mf/defc header [{:keys [data index local fullscreen? toggle-fullscreen] :as props}] (let [{:keys [project file page frames]} data total (count frames) on-click #(st/emit! dv/toggle-thumbnails-panel) + + profile (mf/deref refs/profile) + anonymous? (= uuid/zero (:id profile)) + + project-id (get-in data [:project :id]) + file-id (get-in data [:file :id]) + page-id (get-in data [:page :id]) + on-edit #(st/emit! (rt/nav :workspace - {:project-id (get-in data [:project :id]) - :file-id (get-in data [:file :id])} - {:page-id (get-in data [:page :id])}))] + {:project-id project-id + :file-id file-id} + {:page-id page-id}))] [:header.viewer-header [:div.main-icon - [:a i/logo-icon]] + [:a {:on-click on-edit} i/logo-icon]] [:div.sitemap-zone {:alt (tr "header.sitemap") :on-click on-click} @@ -112,13 +93,25 @@ [:span.counters (str (inc index) " / " total)]] [:div.options-zone - [:& share-link {:page (:page data)}] - [:span.btn-primary {:on-click on-edit} "Edit page"] - [:& zoom-widget {:zoom (:zoom local)}] + (when-not anonymous? + [:& share-link {:page (:page data)}]) + (when-not anonymous? + [:a {:on-click on-edit} "Edit page"]) + + [:& zoom-widget + {:zoom (:zoom local) + :on-increase #(st/emit! dv/increase-zoom) + :on-decrease #(st/emit! dv/decrease-zoom) + :on-zoom-to-50 #(st/emit! dv/zoom-to-50) + :on-zoom-to-100 #(st/emit! dv/reset-zoom) + :on-zoom-to-200 #(st/emit! dv/zoom-to-200)}] + [:span.btn-fullscreen.tooltip.tooltip-bottom - {:alt "Full screen" + {:alt "Full Screen" :on-click toggle-fullscreen} - i/full-screen] + (if fullscreen? + i/full-screen-off + i/full-screen)] ]])) diff --git a/frontend/src/uxbox/main/ui/workspace/header.cljs b/frontend/src/uxbox/main/ui/workspace/header.cljs index 10bd1dc65..425fcec7b 100644 --- a/frontend/src/uxbox/main/ui/workspace/header.cljs +++ b/frontend/src/uxbox/main/ui/workspace/header.cljs @@ -30,33 +30,38 @@ (mf/defc zoom-widget {:wrap [mf/memo]} - [props] - (let [zoom (mf/deref refs/selected-zoom) - show-dropdown? (mf/use-state false) - increase #(st/emit! dw/increase-zoom) - decrease #(st/emit! dw/decrease-zoom) - zoom-to-50 #(st/emit! dw/zoom-to-50) - zoom-to-100 #(st/emit! dw/reset-zoom) - zoom-to-200 #(st/emit! dw/zoom-to-200)] - [:div.zoom-input - [:span.add-zoom {:on-click decrease} "-"] - [:div {:on-click #(reset! show-dropdown? true)} - [:span {} (str (mth/round (* 100 zoom)) "%")] - [:span.dropdown-button i/arrow-down] - [:& dropdown {:show @show-dropdown? - :on-close #(reset! show-dropdown? false)} - [:ul.zoom-dropdown - [:li {:on-click increase} - "Zoom in" [:span "+"]] - [:li {:on-click decrease} - "Zoom out" [:span "-"]] - [:li {:on-click zoom-to-50} - "Zoom to 50%" [:span "Shift + 0"]] - [:li {:on-click zoom-to-100} - "Zoom to 100%" [:span "Shift + 1"]] - [:li {:on-click zoom-to-200} - "Zoom to 200%" [:span "Shift + 2"]]]]] - [:span.remove-zoom {:on-click increase} "+"]])) + [{:keys [zoom + on-increase + on-decrease + on-zoom-to-50 + on-zoom-to-100 + on-zoom-to-200] + :as props}] + (let [show-dropdown? (mf/use-state false) + ;; increase #(st/emit! dv/increase-zoom) + ;; decrease #(st/emit! dv/decrease-zoom) + ;; zoom-to-50 #(st/emit! dv/zoom-to-50) + ;; zoom-to-100 #(st/emit! dv/reset-zoom) + ;; zoom-to-200 #(st/emit! dv/zoom-to-200) + ] + [:div.zoom-widget {:on-click #(reset! show-dropdown? true)} + [:span {} (str (mth/round (* 100 zoom)) "%")] + [:span.dropdown-button i/arrow-down] + [:& dropdown {:show @show-dropdown? + :on-close #(reset! show-dropdown? false)} + [:ul.zoom-dropdown + [:li {:on-click on-increase} + "Zoom in" [:span "+"]] + [:li {:on-click on-decrease} + "Zoom out" [:span "-"]] + [:li {:on-click on-zoom-to-50} + "Zoom to 50%"] + [:li {:on-click on-zoom-to-100} + "Zoom to 100%" [:span "Shift + 0"]] + [:li {:on-click on-zoom-to-200} + "Zoom to 200%"]]]])) + + ;; --- Header Users @@ -77,7 +82,7 @@ [props] (let [profile (mf/deref refs/profile) users (mf/deref refs/workspace-users)] - [:ul.user-multi + [:ul.active-users [:& user-widget {:user profile :self? true}] (for [id (->> (:active users) (remove #(= % (:id profile))))] @@ -85,16 +90,22 @@ :key id}])])) (mf/defc menu - [{:keys [layout] :as props}] + [{:keys [layout project file] :as props}] (let [show-menu? (mf/use-state false) + toggle-sitemap #(st/emit! (dw/toggle-layout-flag :sitemap)) locale (i18n/use-locale)] - [:* - [:div.menu-btn {:on-click #(reset! show-menu? true)} i/actions] + [:div.menu-section + [:div.menu-button {:on-click #(reset! show-menu? true)} i/actions] + [:div.project-tree {:alt (t locale "header.sitemap") + :class (classnames :selected (contains? layout :sitemap)) + :on-click toggle-sitemap} + [:span.project-name (:name project) " /"] + [:span (:name file)]] [:& dropdown {:show @show-menu? :on-close #(reset! show-menu? false)} - [:ul.workspace-menu + [:ul.menu [:li {:on-click #(st/emit! (dw/toggle-layout-flag :rules))} [:span i/ruler] [:span @@ -139,28 +150,33 @@ (mf/defc header [{:keys [page file layout project] :as props}] - (let [go-to-dashboard #(st/emit! (rt/nav :dashboard-team {:team-id "self"})) - toggle-sitemap #(st/emit! (dw/toggle-layout-flag :sitemap)) + (let [locale (i18n/use-locale) + go-to-dashboard #(st/emit! (rt/nav :dashboard-team {:team-id "self"})) + zoom (mf/deref refs/selected-zoom) locale (i18n/use-locale) router (mf/deref router-ref) view-url (rt/resolve router :viewer {:page-id (:id page)} {:index 0})] - [:header.workspace-bar + [:header.workspace-header [:div.main-icon [:a {:on-click go-to-dashboard} i/logo-icon]] - [:& menu {:layout layout}] + [:& menu {:layout layout + :project project + :file file}] - [:div.project-tree-btn {:alt (tr "header.sitemap") - :class (classnames :selected (contains? layout :sitemap)) - :on-click toggle-sitemap} - [:span.project-name (:name project) " /"] - [:span (:name file)]] - - [:div.workspace-options + [:div.users-section [:& active-users]] - [:& zoom-widget] + [:div.options-section + [:& zoom-widget + {:zoom zoom + :on-increase #(st/emit! dw/increase-zoom) + :on-decrease #(st/emit! dw/decrease-zoom) + :on-zoom-to-50 #(st/emit! dw/zoom-to-50) + :on-zoom-to-100 #(st/emit! dw/reset-zoom) + :on-zoom-to-200 #(st/emit! dw/zoom-to-200)}] + + [:a.preview-button + {;; :target "__blank" + :href (str "#" view-url)} i/play]]])) - [:a.preview { - ;; :target "__blank" - :href (str "#" view-url)} i/play]]))