0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-18 02:32:13 -05:00

🐛 Fix incorrect redirect handling on request-access go-home button

This commit is contained in:
Andrey Antukh 2024-09-16 18:21:25 +02:00
parent 179d534237
commit 86c5ca4213
8 changed files with 182 additions and 109 deletions

View file

@ -143,4 +143,3 @@
(reinit))))
(set! (.-stackTraceLimit js/Error) 50)

View file

@ -171,6 +171,7 @@
(let [team-id (get-current-team-id profile)
welcome-file-id (dm/get-in profile [:props :welcome-file-id])
redirect-href (:login-redirect @s/session)]
(cond
(some? redirect-href)
(binding [s/*sync* true]

View file

@ -42,7 +42,8 @@
(mf/lazy-component app.main.ui.workspace/workspace))
(mf/defc main-page
{::mf/props :obj}
{::mf/props :obj
::mf/private true}
[{:keys [route profile]}]
(let [{:keys [data params]} route
props (get profile :props)
@ -68,6 +69,7 @@
(:onboarding-viewed props)
(not= (:release-notes-viewed props) (:main cf/version))
(not= "0.0" (:main cf/version)))]
[:& (mf/provider ctx/current-route) {:value route}
(case (:name data)
(:auth-login

View file

@ -42,9 +42,7 @@
(let [search-term (get-in route [:params :query :search-term])
team-id (get-in route [:params :path :team-id])
project-id (get-in route [:params :path :project-id])]
(cond->
{:search-term search-term}
(cond-> {:search-term search-term}
(uuid-str? team-id)
(assoc :team-id (uuid team-id))
@ -84,10 +82,10 @@
(mf/use-effect on-resize)
[:div {:class (stl/css :dashboard-content)
:style {:pointer-events (when file-menu-open? "none")}
:on-click clear-selected-fn :ref container}
:on-click clear-selected-fn
:ref container}
(case section
:dashboard-projects
[:*
@ -146,7 +144,8 @@
(l/derived :current-team-id st/state))
(mf/defc dashboard
[{:keys [route profile] :as props}]
{::mf/props :obj}
[{:keys [route profile]}]
(let [section (get-in route [:data :name])
params (parse-params route)
@ -181,13 +180,13 @@
[:& (mf/provider ctx/current-team-id) {:value team-id}
[:& (mf/provider ctx/current-project-id) {:value project-id}
;; NOTE: dashboard events and other related functions assumes
;; that the team is a implicit context variable that is
;; available using react context or accessing
;; the :current-team-id on the state. We set the key to the
;; team-id because we want to completely refresh all the
;; components on team change. Many components assumes that the
;; team is already set so don't put the team into mf/deps.
;; NOTE: dashboard events and other related functions assumes
;; that the team is a implicit context variable that is
;; available using react context or accessing
;; the :current-team-id on the state. We set the key to the
;; team-id because we want to completely refresh all the
;; components on team change. Many components assumes that the
;; team is already set so don't put the team into mf/deps.
(when (and team initialized?)
[:main {:class (stl/css :dashboard)
:key (:id team)}

View file

@ -106,9 +106,9 @@
(st/emit! (rt/navigated match))
:else
;; We just recheck with an additional profile request; this avoids
;; some race conditions that causes unexpected redirects on
;; invitations workflows (and probably other cases).
;; We just recheck with an additional profile request; this
;; avoids some race conditions that causes unexpected redirects
;; on invitations workflows (and probably other cases).
(->> (rp/cmd! :get-profile)
(rx/subs! (fn [{:keys [id] :as profile}]
(cond

View file

@ -7,19 +7,21 @@
(ns app.main.ui.static
(:require-macros [app.main.style :as stl])
(:require
["rxjs" :as rxjs]
[app.common.data :as d]
[app.common.pprint :as pp]
[app.common.uri :as u]
[app.main.data.common :as dc]
[app.main.data.events :as ev]
[app.main.refs :as refs]
[app.main.repo :as rp]
[app.main.store :as st]
[app.main.ui.auth.login :refer [login-methods]]
[app.main.ui.auth.recovery-request :refer [recovery-request-page recovery-sent-page]]
[app.main.ui.auth.register :refer [register-methods register-validate-form register-success-page terms-register]]
[app.main.ui.auth.register :as register]
[app.main.ui.dashboard.sidebar :refer [sidebar]]
[app.main.ui.icons :as i]
[app.main.ui.viewer.header :as header]
[app.main.ui.viewer.header :as viewer.header]
[app.util.dom :as dom]
[app.util.i18n :refer [tr]]
[app.util.router :as rt]
@ -29,10 +31,14 @@
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
;; FIXME: this is a workaround until we export this class on beicon library
(def TimeoutError rxjs/TimeoutError)
(mf/defc error-container*
{::mf/props :obj}
[{:keys [children]}]
(let [profile-id (:profile-id @st/state)]
(let [profile-id (:profile-id @st/state)
on-nav-root (mf/use-fn #(st/emit! (rt/nav-root)))]
[:section {:class (stl/css :exception-layout)}
[:button
{:class (stl/css :exception-header)
@ -44,7 +50,7 @@
[:div {:class (stl/css :deco-before)} i/logo-error-screen]
(when-not profile-id
[:button {:class (stl/css :login-header)
:on-click rt/nav-root}
:on-click on-nav-root}
(tr "labels.login")])
[:div {:class (stl/css :exception-content)}
@ -65,8 +71,8 @@
{::mf/props :obj}
[{:keys [show-dialog]}]
(let [current-section (mf/use-state :login)
user-email (mf/use-state "")
register-token (mf/use-state "")
user-email (mf/use-state "")
register-token (mf/use-state "")
set-section
(mf/use-fn
@ -85,29 +91,37 @@
#(reset! current-section :login))
success-login
(fn []
(reset! show-dialog false)
(.reload js/window.location true))
(mf/use-fn
(fn []
(reset! show-dialog false)
(st/emit! (rt/reload true))))
success-register
(fn [data]
(reset! register-token (:token data))
(reset! current-section :register-validate))
(mf/use-fn
(fn [data]
(reset! register-token (:token data))
(reset! current-section :register-validate)))
register-email-sent
(fn [email]
(reset! user-email email)
(reset! current-section :register-email-sent))
(mf/use-fn
(fn [email]
(reset! user-email email)
(reset! current-section :register-email-sent)))
recovery-email-sent
(fn [email]
(reset! user-email email)
(reset! current-section :recovery-email-sent))]
(mf/use-fn
(fn [email]
(reset! user-email email)
(reset! current-section :recovery-email-sent)))
on-nav-root
(mf/use-fn #(st/emit! (rt/nav-root)))]
[:div {:class (stl/css :overlay)}
[:div {:class (stl/css :dialog-login)}
[:div {:class (stl/css :modal-close)}
[:button {:class (stl/css :modal-close-button) :on-click rt/nav-root}
[:button {:class (stl/css :modal-close-button)
:on-click on-nav-root}
i/close]]
[:div {:class (stl/css :login)}
[:div {:class (stl/css :logo)} i/logo]
@ -125,13 +139,14 @@
(tr "auth.register")
" "
[:a {:data-section "register"
:on-click set-section} (tr "auth.register-submit")]]]
:on-click set-section}
(tr "auth.register-submit")]]]
:register
[:*
[:div {:class (stl/css :logo-title)} (tr "not-found.login.signup-free")]
[:div {:class (stl/css :logo-subtitle)} (tr "not-found.login.start-using")]
[:& register-methods {:on-success-callback success-register :hide-separator true}]
[:& register/register-methods {:on-success-callback success-register :hide-separator true}]
#_[:hr {:class (stl/css :separator)}]
[:div {:class (stl/css :separator)}]
[:div {:class (stl/css :change-section)}
@ -141,12 +156,13 @@
:on-click set-section} (tr "auth.login-here")]]
[:div {:class (stl/css :links)}
[:hr {:class (stl/css :separator)}]
[:& terms-register]]]
[:& register/terms-register]]]
:register-validate
[:div {:class (stl/css :form-container)}
[:& register-validate-form {:params {:token @register-token}
:on-success-callback register-email-sent}]
[:& register/register-validate-form
{:params {:token @register-token}
:on-success-callback register-email-sent}]
[:div {:class (stl/css :links)}
[:div {:class (stl/css :register)}
[:a {:data-section "register"
@ -155,7 +171,7 @@
:register-email-sent
[:div {:class (stl/css :form-container)}
[:& register-success-page {:params {:email @user-email :hide-logo true}}]]
[:& register/register-success-page {:params {:email @user-email :hide-logo true}}]]
:recovery-request
[:& recovery-request-page {:go-back-callback set-section-login
@ -175,40 +191,49 @@
[:button {:class (stl/css :modal-close-button) :on-click on-close}
i/close]]
[:div {:class (stl/css :dialog-title)} title]
(for [txt content]
[:div txt])
(for [[index content] (d/enumerate content)]
[:div {:key index} content])
[:div {:class (stl/css :sign-info)}
(when cancel-text
[:button {:class (stl/css :cancel-button) :on-click on-close} cancel-text])
[:button {:class (stl/css :cancel-button)
:on-click on-close}
cancel-text])
[:button {:on-click on-click} button-text]]]]))
(mf/defc request-access
{::mf/props :obj}
[{:keys [file-id team-id is-default workspace?]}]
(let [profile (:profile @st/state)
(let [profile (mf/deref refs/profile)
requested* (mf/use-state {:sent false :already-requested false})
requested (deref requested*)
show-dialog (mf/use-state true)
on-close
(mf/use-fn
(mf/deps profile)
(fn []
(st/emit! (rt/nav :dashboard-projects {:team-id (:default-team-id profile)}))))
on-success
(mf/use-fn
#(reset! requested* {:sent true :already-requested false}))
on-error
(mf/use-fn
#(reset! requested* {:sent true :already-requested true}))
on-request-access
(mf/use-fn
(mf/deps file-id team-id workspace?)
(fn []
(let [params (if (some? file-id) {:file-id file-id :is-viewer (not workspace?)} {:team-id team-id})
mdata {:on-success on-success :on-error on-error}]
(st/emit! (dc/create-team-access-request (with-meta params mdata))))))]
(let [params (if (some? file-id)
{:file-id file-id
:is-viewer (not workspace?)}
{:team-id team-id})
mdata {:on-success on-success
:on-error on-error}]
(st/emit! (dc/create-team-access-request
(with-meta params mdata))))))]
[:*
(if (some? file-id)
@ -220,17 +245,24 @@
[:div {:class (stl/css :project-name)} (tr "not-found.no-permission.project-name")]
[:div {:class (stl/css :file-name)} (tr "not-found.no-permission.penpot-file")]]]
[:div {:class (stl/css :workspace-right)}]]
[:div {:class (stl/css :viewer)}
[:& header/header {:project {:name (tr "not-found.no-permission.project-name")}
:index 0
:file {:name (tr "not-found.no-permission.penpot-file")}
:page nil
:frame nil
:permissions {:is-logged true}
:zoom 1
:section :interactions
:shown-thumbnails false
:interactions-mode nil}]])
;; FIXME: the viewer header was never designed to be reused
;; from other parts of the application, and this code looks
;; like a fast workaround reusing it as-is without a proper
;; component adaptation for be able to use it easily it on
;; viewer context or static error page context
[:& viewer.header/header {:project
{:name (tr "not-found.no-permission.project-name")}
:index 0
:file {:name (tr "not-found.no-permission.penpot-file")}
:page nil
:frame nil
:permissions {:is-logged true}
:zoom 1
:section :interactions
:shown-thumbnails false
:interactions-mode nil}]])
[:div {:class (stl/css :dashboard)}
[:div {:class (stl/css :dashboard-sidebar)}
@ -392,64 +424,92 @@
[:div {:class (stl/css :sign-info)}
[:button {:on-click on-reset} (tr "labels.retry")]]]))
(defn- load-info
"Load exception page info"
[path-params]
(let [default {:loaded true}
stream (cond
(:file-id path-params)
(->> (rp/cmd! :get-file-info {:id (:file-id path-params)})
(rx/map (fn [info]
{:loaded true
:file-id (:id info)})))
(:team-id path-params)
(->> (rp/cmd! :get-team-info {:id (:team-id path-params)})
(rx/map (fn [info]
{:loaded true
:team-id (:id info)
:team-default (:is-default info)})))
:else
(rx/of default))]
(->> stream
(rx/timeout 3000)
(rx/catch (fn [cause]
(if (instance? TimeoutError cause)
(rx/of default)
(rx/throw cause)))))))
(mf/defc exception-page*
{::mf/props :obj}
[{:keys [data route] :as props}]
(let [file-info (mf/use-state {:pending true})
team-info (mf/use-state {:pending true})
type (:type data)
path (:path route)
(let [type (:type data)
path (:path route)
workspace? (str/includes? path "workspace")
dashboard? (str/includes? path "dashboard")
view? (str/includes? path "view")
query-params (:query-params route)
path-params (:path-params route)
request-access? (and
(or workspace? dashboard? view?)
(or (not (str/empty? (:file-id @file-info))) (not (str/empty? (:team-id @team-info)))))
workspace? (str/includes? path "workspace")
dashboard? (str/includes? path "dashboard")
view? (str/includes? path "view")
query-params (u/map->query-string (:query-params route))
pparams (:path-params route)
on-file-info (mf/use-fn
(fn [info]
(reset! file-info {:file-id (:id info)})))
on-team-info (mf/use-fn
(fn [info]
(reset! team-info {:team-id (:id info) :is-default (:is-default info)})))]
;; We stora the request access info int this state
info* (mf/use-state nil)
info (deref info*)
(mf/with-effect [type path query-params pparams @file-info @team-info]
(st/emit! (ptk/event ::ev/event {::ev/name "exception-page" :type type :path path :query-params query-params}))
loaded? (get info :loaded false)
(when (and (:file-id pparams) (:pending @file-info))
(->> (rp/cmd! :get-file-info {:id (:file-id pparams)})
(rx/subs! on-file-info)))
request-access?
(and
(or workspace? dashboard? view?)
(or (:file-id info)
(:team-id info)))]
(when (and (:team-id pparams) (:pending @team-info))
(->> (rp/cmd! :get-team-info {:id (:team-id pparams)})
(rx/subs! on-team-info))))
(mf/with-effect [type path query-params path-params]
(let [query-params (u/map->query-string query-params)
event-params {::ev/name "exception-page"
:type type
:path path
:query-params query-params}]
(st/emit! (ptk/event ::ev/event event-params))))
(case (:type data)
:not-found
(mf/with-effect [path-params info]
(when-not (:loaded info)
(->> (load-info path-params)
(rx/subs! (partial reset! info*)))))
(when loaded?
(if request-access?
[:& request-access {:file-id (:file-id @file-info)
:team-id (:team-id @team-info)
:is-default (:is-default @team-info)
[:& request-access {:file-id (:file-id info)
:team-id (:team-id info)
:is-default (:team-default info)
:workspace? workspace?}]
[:> not-found* {}])
:authentication
(if request-access?
[:& request-access {:file-id (:file-id @file-info)
:team-id (:team-id @team-info)
:is-default (:is-default @team-info)
:workspace? workspace?}]
[:> not-found* {}])
(case (:type data)
:not-found
[:> not-found* {}]
:bad-gateway
[:> bad-gateway* props]
:authentication
[:> not-found* {}]
:service-unavailable
[:& service-unavailable]
:bad-gateway
[:> bad-gateway* props]
[:> internal-error* props])))
:service-unavailable
[:& service-unavailable]
[:> internal-error* props])))))

View file

@ -760,8 +760,10 @@
(.back (.-history js/window)))
(defn reload-current-window
[]
(.reload (.-location js/window)))
([]
(.reload globals/location))
([force?]
(.reload globals/location force?)))
(defn scroll-by!
([element x y]

View file

@ -148,7 +148,17 @@
(defn nav-root
"Navigate to the root page."
[]
(set! (.-href globals/location) "/"))
(ptk/reify ::nav-root
ptk/EffectEvent
(effect [_ _ _]
(set! (.-href globals/location) "/"))))
(defn reload
[force?]
(ptk/reify ::reload
ptk/EffectEvent
(effect [_ _ _]
(ts/asap (partial dom/reload-current-window force?)))))
(defn nav-raw
[& {:keys [href uri]}]