0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-09 00:10:11 -05:00

Improve multi-input initial value handling

And removes the hard coupling of invite-email from it
This commit is contained in:
Andrey Antukh 2024-11-12 16:17:54 +01:00
parent 607e0c5c1d
commit 6eadea8485
4 changed files with 73 additions and 70 deletions

View file

@ -420,14 +420,25 @@
(into [] (distinct) (conj coll item))) (into [] (distinct) (conj coll item)))
(mf/defc multi-input (mf/defc multi-input
[{:keys [form label class name trim valid-item-fn caution-item-fn on-submit invite-email] :as props}] [{:keys [form label class name trim valid-item-fn caution-item-fn on-submit] :as props}]
(let [form (or form (mf/use-ctx form-ctx)) (let [form (or form (mf/use-ctx form-ctx))
input-name (get props :name) input-name (get props :name)
touched? (get-in @form [:touched input-name]) touched? (get-in @form [:touched input-name])
error (get-in @form [:errors input-name]) error (get-in @form [:errors input-name])
focus? (mf/use-state false) focus? (mf/use-state false)
items (mf/use-state []) items (mf/use-state
(fn []
(let [initial (get-in @form [:data input-name])]
(if (or (vector? initial)
(set? initial))
(mapv (fn [val]
{:text val
:valid (valid-item-fn val)
:caution (caution-item-fn val)})
initial)
[]))))
value (mf/use-state "") value (mf/use-state "")
result (hooks/use-equal-memo @items) result (hooks/use-equal-memo @items)
@ -527,13 +538,8 @@
(let [val (cond-> @value trim str/trim) (let [val (cond-> @value trim str/trim)
values (conj-dedup result {:text val :valid (valid-item-fn val)}) values (conj-dedup result {:text val :valid (valid-item-fn val)})
values (filterv #(:valid %) values)] values (filterv #(:valid %) values)]
(update-form! values)))
(mf/with-effect [] (update-form! values)))
(when invite-email
(swap! items conj-dedup {:text (str/trim invite-email)
:valid (valid-item-fn invite-email)
:caution (caution-item-fn invite-email)})))
[:div {:class klass} [:div {:class klass}
[:input {:id (name input-name) [:input {:id (name input-name)

View file

@ -60,7 +60,7 @@
(assoc :project-id (uuid project-id))))) (assoc :project-id (uuid project-id)))))
(mf/defc dashboard-content (mf/defc dashboard-content
[{:keys [team projects project section search-term profile invite-email] :as props}] [{:keys [team projects project section search-term profile] :as props}]
(let [container (mf/use-ref) (let [container (mf/use-ref)
content-width (mf/use-state 0) content-width (mf/use-state 0)
project-id (:id project) project-id (:id project)
@ -143,7 +143,7 @@
[:& libraries-page {:team team}] [:& libraries-page {:team team}]
:dashboard-team-members :dashboard-team-members
[:& team-members-page {:team team :profile profile :invite-email invite-email}] [:& team-members-page {:team team :profile profile}]
:dashboard-team-invitations :dashboard-team-invitations
[:& team-invitations-page {:team team}] [:& team-invitations-page {:team team}]
@ -231,10 +231,7 @@
plugin-url (-> route :query-params :plugin) plugin-url (-> route :query-params :plugin)
invite-email (-> route :query-params :invite-email)
team (mf/deref refs/team) team (mf/deref refs/team)
projects (mf/deref refs/dashboard-projects) projects (mf/deref refs/dashboard-projects)
project (get projects project-id) project (get projects project-id)
@ -287,5 +284,4 @@
:project project :project project
:section section :section section
:search-term search-term :search-term search-term
:team team :team team}])])]]]))
:invite-email invite-email}])])]]]))

View file

@ -59,13 +59,16 @@
(mf/defc header (mf/defc header
{::mf/wrap [mf/memo] {::mf/wrap [mf/memo]
::mf/wrap-props false} ::mf/props :obj}
[{:keys [section team invite-email]}] [{: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! (dd/go-to-team-members)))
on-nav-settings (mf/use-fn #(st/emit! (dd/go-to-team-settings))) on-nav-settings (mf/use-fn #(st/emit! (dd/go-to-team-settings)))
on-nav-invitations (mf/use-fn #(st/emit! (dd/go-to-team-invitations))) on-nav-invitations (mf/use-fn #(st/emit! (dd/go-to-team-invitations)))
on-nav-webhooks (mf/use-fn #(st/emit! (dd/go-to-team-webhooks))) on-nav-webhooks (mf/use-fn #(st/emit! (dd/go-to-team-webhooks)))
route (mf/deref refs/route)
invite-email (-> route :query-params :invite-email)
members-section? (= section :dashboard-team-members) members-section? (= section :dashboard-team-members)
settings-section? (= section :dashboard-team-settings) settings-section? (= section :dashboard-team-settings)
invitations-section? (= section :dashboard-team-invitations) invitations-section? (= section :dashboard-team-invitations)
@ -74,14 +77,14 @@
on-invite-member on-invite-member
(mf/use-fn (mf/use-fn
(mf/deps team) (mf/deps team invite-email)
(fn [] (fn []
(st/emit! (modal/show {:type :invite-members (st/emit! (modal/show {:type :invite-members
:team team :team team
:origin :team :origin :team
:invite-email invite-email}))))] :invite-email invite-email}))))]
(mf/with-effect [] (mf/with-effect [team invite-email]
(when invite-email (when invite-email
(on-invite-member))) (on-invite-member)))
@ -134,7 +137,7 @@
(mf/defc invite-members-modal (mf/defc invite-members-modal
{::mf/register modal/components {::mf/register modal/components
::mf/register-as :invite-members ::mf/register-as :invite-members
::mf/wrap-props false} ::mf/props :obj}
[{:keys [team origin invite-email]}] [{:keys [team origin invite-email]}]
(let [members-map (mf/deref refs/dashboard-team-members) (let [members-map (mf/deref refs/dashboard-team-members)
perms (:permissions team) perms (:permissions team)
@ -143,8 +146,10 @@
(get-available-roles perms)) (get-available-roles perms))
team-id (:id team) team-id (:id team)
initial (mf/with-memo [team-id] initial (mf/with-memo [team-id invite-email]
{:role "editor" :team-id team-id}) (if invite-email
{:role "editor" :team-id team-id :emails #{invite-email}}
{:role "editor" :team-id team-id}))
form (fm/use-form :schema schema:invite-member-form form (fm/use-form :schema schema:invite-member-form
:initial initial) :initial initial)
@ -230,8 +235,7 @@
:trim true :trim true
:valid-item-fn sm/parse-email :valid-item-fn sm/parse-email
:caution-item-fn current-members-emails :caution-item-fn current-members-emails
:label (tr "modals.invite-member.emails") :label (tr "modals.invite-member.emails")}]]
:invite-email invite-email}]]
[:div {:class (stl/css :action-buttons)} [:div {:class (stl/css :action-buttons)}
[:> fm/submit-button* [:> fm/submit-button*
@ -245,7 +249,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(mf/defc member-info (mf/defc member-info
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [member profile]}] [{:keys [member profile]}]
(let [is-you? (= (:id profile) (:id member))] (let [is-you? (= (:id profile) (:id member))]
[:* [:*
@ -258,7 +262,7 @@
[:div {:class (stl/css :member-email)} (:email member)]]])) [:div {:class (stl/css :member-email)} (:email member)]]]))
(mf/defc rol-info (mf/defc rol-info
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [member team on-set-admin on-set-editor on-set-owner on-set-viewer profile]}] [{:keys [member team on-set-admin on-set-editor on-set-owner on-set-viewer profile]}]
(let [member-is-owner (:is-owner member) (let [member-is-owner (:is-owner member)
member-is-admin (and (:is-admin member) (not member-is-owner)) member-is-admin (and (:is-admin member) (not member-is-owner))
@ -309,7 +313,7 @@
(tr "labels.owner")])]]])) (tr "labels.owner")])]]]))
(mf/defc member-actions (mf/defc member-actions
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [member team on-delete on-leave profile]}] [{:keys [member team on-delete on-leave profile]}]
(let [is-owner? (:is-owner member) (let [is-owner? (:is-owner member)
owner? (dm/get-in team [:permissions :is-owner]) owner? (dm/get-in team [:permissions :is-owner])
@ -345,7 +349,7 @@
(mf/defc team-member (mf/defc team-member
{::mf/wrap [mf/memo] {::mf/wrap [mf/memo]
::mf/wrap-props false} ::mf/props :obj}
[{:keys [team member members profile]}] [{:keys [team member members profile]}]
(let [member-id (:id member) (let [member-id (:id member)
@ -479,7 +483,7 @@
:on-leave on-leave'}]]])) :on-leave on-leave'}]]]))
(mf/defc team-members (mf/defc team-members
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [members-map team profile]}] [{:keys [members-map team profile]}]
(let [members (mf/with-memo [members-map] (let [members (mf/with-memo [members-map]
(->> (vals members-map) (->> (vals members-map)
@ -510,8 +514,8 @@
:members members-map}])]])) :members members-map}])]]))
(mf/defc team-members-page (mf/defc team-members-page
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [team profile invite-email]}] [{:keys [team profile]}]
(let [members-map (mf/deref refs/dashboard-team-members)] (let [members-map (mf/deref refs/dashboard-team-members)]
(mf/with-effect [team] (mf/with-effect [team]
@ -525,7 +529,7 @@
(st/emit! (dd/fetch-team-members (:id team)))) (st/emit! (dd/fetch-team-members (:id team))))
[:* [:*
[:& header {:section :dashboard-team-members :team team :invite-email invite-email}] [:& header {:section :dashboard-team-members :team team}]
[:section {:class (stl/css :dashboard-container :dashboard-team-members)} [:section {:class (stl/css :dashboard-container :dashboard-team-members)}
[:& team-members [:& team-members
{:profile profile {:profile profile
@ -536,9 +540,9 @@
;; INVITATIONS SECTION ;; INVITATIONS SECTION
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(mf/defc invitation-role-selector (mf/defc invitation-role-selector*
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [can-invite? role status on-change]}] [{:keys [can-invite role status on-change]}]
(let [show? (mf/use-state false) (let [show? (mf/use-state false)
label (cond label (cond
(= role :owner) (tr "labels.owner") (= role :owner) (tr "labels.owner")
@ -559,7 +563,7 @@
(on-change role event))))] (on-change role event))))]
[:* [:*
(if (and can-invite? (= status :pending)) (if (and can-invite (= status :pending))
[:div {:class (stl/css :rol-selector :has-priv) [:div {:class (stl/css :rol-selector :has-priv)
:on-click on-show} :on-click on-show}
[:span {:class (stl/css :rol-label)} label] [:span {:class (stl/css :rol-label)} label]
@ -583,7 +587,7 @@
(tr "labels.viewer")]]]])) (tr "labels.viewer")]]]]))
(mf/defc invitation-actions (mf/defc invitation-actions
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [invitation team-id]}] [{:keys [invitation team-id]}]
(let [show? (mf/use-state false) (let [show? (mf/use-state false)
@ -678,10 +682,11 @@
:class (stl/css :action-dropdown-item)} :class (stl/css :action-dropdown-item)}
(tr "labels.delete-invitation")]]]])) (tr "labels.delete-invitation")]]]]))
(mf/defc invitation-row (mf/defc invitation-row*
{::mf/wrap [mf/memo] {::mf/wrap [mf/memo]
::mf/wrap-props false} ::mf/private true
[{:keys [invitation can-invite? team-id] :as props}] ::mf/props :obj}
[{:keys [invitation can-invite team-id]}]
(let [expired? (:expired invitation) (let [expired? (:expired invitation)
email (:email invitation) email (:email invitation)
@ -704,8 +709,8 @@
[:div {:class (stl/css :table-field :field-email)} email] [:div {:class (stl/css :table-field :field-email)} email]
[:div {:class (stl/css :table-field :field-roles)} [:div {:class (stl/css :table-field :field-roles)}
[:& invitation-role-selector [:> invitation-role-selector*
{:can-invite? can-invite? {:can-invite can-invite
:role role :role role
:status status :status status
:on-change on-change-role}]] :on-change on-change-role}]]
@ -714,16 +719,16 @@
[:& badge-notification {:type type :content badge-content}]] [:& badge-notification {:type type :content badge-content}]]
[:div {:class (stl/css :table-field :field-actions)} [:div {:class (stl/css :table-field :field-actions)}
(when can-invite? (when can-invite
[:& invitation-actions [:& invitation-actions
{:invitation invitation {:invitation invitation
:team-id team-id}])]])) :team-id team-id}])]]))
(mf/defc empty-invitation-table (mf/defc empty-invitation-table
[{:keys [can-invite?] :as props}] [{:keys [can-invite] :as props}]
[:div {:class (stl/css :empty-invitations)} [:div {:class (stl/css :empty-invitations)}
[:span (tr "labels.no-invitations")] [:span (tr "labels.no-invitations")]
(when can-invite? (when can-invite
[:> i18n/tr-html* {:content (tr "labels.no-invitations-hint") [:> i18n/tr-html* {:content (tr "labels.no-invitations-hint")
:tag-name "span"}])]) :tag-name "span"}])])
@ -731,7 +736,7 @@
[{:keys [team invitations] :as props}] [{:keys [team invitations] :as props}]
(let [owner? (dm/get-in team [:permissions :is-owner]) (let [owner? (dm/get-in team [:permissions :is-owner])
admin? (dm/get-in team [:permissions :is-admin]) admin? (dm/get-in team [:permissions :is-admin])
can-invite? (or owner? admin?) can-invite (or owner? admin?)
team-id (:id team)] team-id (:id team)]
[:div {:class (stl/css :invitations)} [:div {:class (stl/css :invitations)}
@ -740,17 +745,17 @@
[:div {:class (stl/css :title-field-role)} (tr "labels.role")] [:div {:class (stl/css :title-field-role)} (tr "labels.role")]
[:div {:class (stl/css :title-field-status)} (tr "labels.status")]] [:div {:class (stl/css :title-field-status)} (tr "labels.status")]]
(if (empty? invitations) (if (empty? invitations)
[:& empty-invitation-table {:can-invite? can-invite?}] [:& empty-invitation-table {:can-invite can-invite}]
[:div {:class (stl/css :table-rows)} [:div {:class (stl/css :table-rows)}
(for [invitation invitations] (for [invitation invitations]
[:& invitation-row [:> invitation-row*
{:key (:email invitation) {:key (:email invitation)
:invitation invitation :invitation invitation
:can-invite? can-invite? :can-invite can-invite
:team-id team-id}])])])) :team-id team-id}])])]))
(mf/defc team-invitations-page (mf/defc team-invitations-page
[{:keys [team] :as props}] [{:keys [team]}]
(let [invitations (mf/deref refs/dashboard-team-invitations)] (let [invitations (mf/deref refs/dashboard-team-invitations)]
(mf/with-effect [team] (mf/with-effect [team]
@ -794,7 +799,7 @@
(mf/defc webhook-modal (mf/defc webhook-modal
{::mf/register modal/components {::mf/register modal/components
::mf/register-as :webhook} ::mf/register-as :webhook}
[{:keys [webhook] :as props}] [{:keys [webhook]}]
(let [initial (mf/with-memo [] (let [initial (mf/with-memo []
(or (some-> webhook (update :uri str)) (or (some-> webhook (update :uri str))
@ -905,7 +910,7 @@
(tr "modals.create-webhook.submit-label"))}]]]]]])) (tr "modals.create-webhook.submit-label"))}]]]]]]))
(mf/defc webhooks-hero (mf/defc webhooks-hero
{::mf/wrap-props false} {::mf/props :obj}
[] []
[:div {:class (stl/css :webhooks-hero-container)} [:div {:class (stl/css :webhooks-hero-container)}
[:h2 {:class (stl/css :hero-title)} [:h2 {:class (stl/css :hero-title)}
@ -939,18 +944,9 @@
:class (stl/css :menu-disabled)} :class (stl/css :menu-disabled)}
[:> icon* {:id "menu"}]]))) [:> icon* {:id "menu"}]])))
(mf/defc last-delivery-icon
{::mf/wrap-props false}
[{:keys [success? text]}]
[:div {:class (stl/css :last-delivery-icon)
:title text}
(if success?
success-icon
warning-icon)])
(mf/defc webhook-item (mf/defc webhook-item
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
[{:keys [webhook permissions] :as props}] [{:keys [webhook permissions]}]
(let [error-code (:error-code webhook) (let [error-code (:error-code webhook)
id (:id webhook) id (:id webhook)
creator-id (:profile-id webhook) creator-id (:profile-id webhook)
@ -1014,14 +1010,14 @@
:can-edit can-edit}]]])) :can-edit can-edit}]]]))
(mf/defc webhooks-list (mf/defc webhooks-list
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [webhooks permissions]}] [{:keys [webhooks permissions]}]
[:div {:class (stl/css :table-rows :webhook-table)} [:div {:class (stl/css :table-rows :webhook-table)}
(for [webhook webhooks] (for [webhook webhooks]
[:& webhook-item {:webhook webhook :key (:id webhook) :permissions permissions}])]) [:& webhook-item {:webhook webhook :key (:id webhook) :permissions permissions}])])
(mf/defc team-webhooks-page (mf/defc team-webhooks-page
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [team]}] [{:keys [team]}]
(let [webhooks (mf/deref refs/dashboard-team-webhooks)] (let [webhooks (mf/deref refs/dashboard-team-webhooks)]
@ -1051,7 +1047,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(mf/defc team-settings-page (mf/defc team-settings-page
{::mf/wrap-props false} {::mf/props :obj}
[{:keys [team]}] [{:keys [team]}]
(let [finput (mf/use-ref) (let [finput (mf/use-ref)

View file

@ -91,6 +91,8 @@
(defn- create-form-mutator (defn- create-form-mutator
[internal-state rerender-fn wrap-update-fn initial opts] [internal-state rerender-fn wrap-update-fn initial opts]
(mf/set-ref-val! internal-state initial)
(reify (reify
IDeref IDeref
(-deref [_] (-deref [_]
@ -128,6 +130,12 @@
[& {:keys [initial] :as opts}] [& {:keys [initial] :as opts}]
(let [rerender-fn (use-rerender-fn) (let [rerender-fn (use-rerender-fn)
initial
(mf/with-memo [initial]
{:data (if (fn? initial) (initial) initial)
:errors {}
:touched {}})
internal-state internal-state
(mf/use-ref nil) (mf/use-ref nil)
@ -136,11 +144,8 @@
(create-form-mutator internal-state rerender-fn wrap-update-schema-fn initial opts))] (create-form-mutator internal-state rerender-fn wrap-update-schema-fn initial opts))]
;; Initialize internal state once ;; Initialize internal state once
(mf/with-effect [] (mf/with-layout-effect []
(mf/set-ref-val! internal-state (mf/set-ref-val! internal-state initial))
{:data {}
:errors {}
:touched {}}))
(mf/with-effect [initial] (mf/with-effect [initial]
(if (fn? initial) (if (fn? initial)