diff --git a/backend/src/app/rpc/mutations/management.clj b/backend/src/app/rpc/mutations/management.clj index f4a7b1e45..012ec050c 100644 --- a/backend/src/app/rpc/mutations/management.clj +++ b/backend/src/app/rpc/mutations/management.clj @@ -28,6 +28,7 @@ (s/def ::project-id ::us/uuid) (s/def ::file-id ::us/uuid) (s/def ::team-id ::us/uuid) +(s/def ::new-name ::us/string) (defn- remap-id [item index key] @@ -72,7 +73,7 @@ (blob/encode)))))) (defn- duplicate-file - [conn {:keys [profile-id file index project-id]} {:keys [reset-shared-flag] :as opts}] + [conn {:keys [profile-id file index project-id new-name]} {:keys [reset-shared-flag] :as opts}] (let [flibs (db/query conn :file-library-rel {:file-id (:id file)}) fmeds (db/query conn :file-media-object {:file-id (:id file)}) @@ -94,12 +95,15 @@ (some? project-id) (assoc :project-id project-id) + (some? new-name) + (assoc :name new-name) + (true? reset-shared-flag) (assoc :is-shared false)) file (-> file - (update :id #(get index %)) - (process-file index))] + (update :id #(get index %)) + (process-file index))] (db/insert! conn :file file) (db/insert! conn :file-profile-rel @@ -123,10 +127,11 @@ (declare duplicate-file) (s/def ::duplicate-file - (s/keys :req-un [::profile-id ::file-id])) + (s/keys :req-un [::profile-id ::file-id] + :opt-un [::new-name])) (sv/defmethod ::duplicate-file - [{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}] + [{:keys [pool] :as cfg} {:keys [profile-id file-id new-name] :as params}] (db/with-atomic [conn pool] (let [file (db/get-by-id conn :file file-id) index {file-id (uuid/next)} @@ -141,23 +146,29 @@ (declare duplicate-project) (s/def ::duplicate-project - (s/keys :req-un [::profile-id ::project-id])) + (s/keys :req-un [::profile-id ::project-id] + :opt-un [::new-name])) (sv/defmethod ::duplicate-project - [{:keys [pool] :as cfg} {:keys [profile-id project-id] :as params}] + [{:keys [pool] :as cfg} {:keys [profile-id project-id new-name] :as params}] (db/with-atomic [conn pool] (let [project (db/get-by-id conn :project project-id)] (teams/check-edition-permissions! conn profile-id (:team-id project)) (duplicate-project conn (assoc params :project project))))) (defn duplicate-project - [conn {:keys [profile-id project] :as params}] + [conn {:keys [profile-id project new-name] :as params}] (let [files (db/query conn :file {:project-id (:id project)} {:columns [:id]}) index (reduce #(assoc %1 (:id %2) (uuid/next)) {} files) - project (assoc project :id (uuid/next)) + project (cond-> project + new-name + (assoc :name new-name) + + :always + (assoc :id (uuid/next))) params (assoc params :project-id (:id project) :index index)] @@ -170,7 +181,9 @@ :can-edit true}) (doseq [{:keys [id]} files] (let [file (db/get-by-id conn :file id) - params (assoc params :file file)] + params (-> params + (assoc :file file) + (dissoc :new-name))] (duplicate-file conn params {:reset-shared-flag false :remap-libraries true}))) project)) diff --git a/backend/tests/app/tests/test_services_management.clj b/backend/tests/app/tests/test_services_management.clj index bc60d9e4f..d68aeb093 100644 --- a/backend/tests/app/tests/test_services_management.clj +++ b/backend/tests/app/tests/test_services_management.clj @@ -49,7 +49,8 @@ (let [data {::th/type :duplicate-file :profile-id (:id profile) - :file-id (:id file1)} + :file-id (:id file1) + :new-name "file 1 (copy)"} out (th/mutation! data)] ;; (th/print-result! out) @@ -58,9 +59,9 @@ (t/is (nil? (:error out))) (let [result (:result out)] - ;; Check that the returned result is a file but has different + ;; Check that the returned result is a file but has different id ;; and different name. - (t/is (= (:name file1) (:name result))) + (t/is (= "file 1 (copy)" (:name result))) (t/is (not= (:id file1) (:id result))) ;; Check that the new file has a correct file library relation @@ -120,15 +121,16 @@ (let [data {::th/type :duplicate-project :profile-id (:id profile) - :project-id (:id project)} + :project-id (:id project) + :new-name "project 1 (copy)"} out (th/mutation! data)] ;; Check tha tresult is correct (t/is (nil? (:error out))) (let [result (:result out)] - ;; Check that they are the same project but different ids - (t/is (= (:name project) (:name result))) + ;; Check that they are the same project but different id and name + (t/is (= "project 1 (copy)" (:name result))) (t/is (not= (:id project) (:id result))) ;; Check the total number of projects (previously is 2, now is 3) diff --git a/frontend/resources/locales.json b/frontend/resources/locales.json index a390eee62..61168ec02 100644 --- a/frontend/resources/locales.json +++ b/frontend/resources/locales.json @@ -400,6 +400,13 @@ }, "used-in" : [ "src/app/main/ui/settings/profile.cljs" ] }, + "dashboard.copy-suffix" : { + "translations" : { + "en" : "(copy)", + "es" : "(copia)" + }, + "used-in" : [ "src/app/main/data/dashboard.cljs" ] + }, "dashboard.create-new-team" : { "translations" : { "ca" : "+ Crear un nou equip", @@ -441,6 +448,13 @@ }, "used-in" : [ "src/app/main/ui/dashboard/files.cljs" ] }, + "dashboard.duplicate" : { + "translations" : { + "en" : "Duplicate", + "es" : "Duplicar" + }, + "used-in" : [ "src/app/main/ui/dashboard/file_menu.cljs" ] + }, "dashboard.empty-files" : { "translations" : { "ca" : "Encara no hi ha cap arxiu aquĆ­", diff --git a/frontend/src/app/main/data/dashboard.cljs b/frontend/src/app/main/data/dashboard.cljs index 812c4fe0a..c6fa043c6 100644 --- a/frontend/src/app/main/data/dashboard.cljs +++ b/frontend/src/app/main/data/dashboard.cljs @@ -12,6 +12,7 @@ [app.common.uuid :as uuid] [app.main.repo :as rp] [app.main.data.users :as du] + [app.util.i18n :as i18n :refer [tr]] [app.util.router :as rt] [app.util.time :as dt] [app.util.timers :as ts] @@ -347,6 +348,28 @@ (rx/map #(partial created %)) (rx/catch on-error))))))) +(defn duplicate-project + [{:keys [id name] :as params}] + (us/assert ::us/uuid id) + (letfn [(duplicated [project state] + (-> state + (assoc-in [:projects (:team-id project) (:id project)] project) + (assoc-in [:dashboard-local :project-for-edit] (:id project))))] + (ptk/reify ::duplicate-project + ptk/WatchEvent + (watch [_ state stream] + (let [{:keys [on-success on-error] + :or {on-success identity + on-error identity}} (meta params) + + new-name (str name " " (tr "dashboard.copy-suffix"))] + + (->> (rp/mutation! :duplicate-project {:project-id id + :new-name new-name}) + (rx/tap on-success) + (rx/map #(partial duplicated %)) + (rx/catch on-error))))))) + (def clear-project-for-edit (ptk/reify ::clear-project-for-edit ptk/UpdateEvent @@ -494,3 +517,24 @@ (-> state (assoc-in [:files project-id id] file) (update-in [:recent-files project-id] (fnil conj #{}) id))))) + +;; --- Duplicate File + +(defn duplicate-file + [{:keys [id name] :as params}] + (us/assert ::us/uuid id) + (ptk/reify ::duplicate-file + ptk/WatchEvent + (watch [_ state stream] + (let [{:keys [on-success on-error] + :or {on-success identity + on-error identity}} (meta params) + + new-name (str name " " (tr "dashboard.copy-suffix"))] + + (->> (rp/mutation! :duplicate-file {:file-id id + :new-name new-name}) + (rx/tap on-success) + (rx/map file-created) + (rx/catch on-error)))))) + diff --git a/frontend/src/app/main/ui/dashboard/file_menu.cljs b/frontend/src/app/main/ui/dashboard/file_menu.cljs index d9f82403d..474202df7 100644 --- a/frontend/src/app/main/ui/dashboard/file_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/file_menu.cljs @@ -36,6 +36,11 @@ qparams {:page-id (first (get-in file [:data :pages]))}] (st/emit! (rt/nav-new-window :workspace pparams qparams))))) + on-duplicate + (mf/use-callback + (mf/deps file) + (st/emitf (dd/duplicate-file file))) + delete-fn (mf/use-callback (mf/deps file) @@ -100,6 +105,7 @@ :left left :options [[(tr "dashboard.open-in-new-tab") on-new-tab] [(tr "labels.rename") on-edit] + [(tr "dashboard.duplicate") on-duplicate] [(tr "labels.delete") on-delete] (if (:is-shared file) [(tr "dashboard.remove-shared") on-del-shared] diff --git a/frontend/src/app/main/ui/dashboard/project_menu.cljs b/frontend/src/app/main/ui/dashboard/project_menu.cljs index 71aad431c..672e73c34 100644 --- a/frontend/src/app/main/ui/dashboard/project_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/project_menu.cljs @@ -26,6 +26,17 @@ (let [top (or top 0) left (or left 0) + on-duplicate + (mf/use-callback + (mf/deps project) + #(let [on-success + (fn [new-project] + (st/emit! (rt/nav :dashboard-files + {:team-id (:team-id new-project) + :project-id (:id new-project)})))] + (st/emit! (dd/duplicate-project + (with-meta project {:on-success on-success}))))) + delete-fn (mf/use-callback (mf/deps project) @@ -49,5 +60,6 @@ :top top :left left :options [[(tr "labels.rename") on-edit] + [(tr "dashboard.duplicate") on-duplicate] [(tr "labels.delete") on-delete]]}]))