From 9873ac91049494cced99b2637e0a5424e8595d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Tue, 22 Sep 2020 12:00:48 +0200 Subject: [PATCH] :tada: Allow to ignore updates, and do it later in libraries dialog --- backend/src/app/migrations.clj | 5 + ...mod-file-library-rel-table-synced-date.sql | 3 + .../sql/0027-mod-file-table-ignore-sync.sql | 3 + backend/src/app/services/mutations/files.clj | 42 +++++ backend/src/app/services/queries/files.clj | 3 +- common/app/common/data.cljc | 7 + frontend/resources/locales.json | 159 +++++++++++------- frontend/src/app/main/data/workspace.cljs | 12 +- .../app/main/data/workspace/libraries.cljs | 57 ++++++- .../main/data/workspace/notifications.cljs | 25 ++- .../src/app/main/ui/workspace/libraries.cljs | 61 ++++--- 11 files changed, 277 insertions(+), 100 deletions(-) create mode 100644 backend/src/app/migrations/sql/0026-mod-file-library-rel-table-synced-date.sql create mode 100644 backend/src/app/migrations/sql/0027-mod-file-table-ignore-sync.sql diff --git a/backend/src/app/migrations.clj b/backend/src/app/migrations.clj index 7f07dc67e..920ad7d27 100644 --- a/backend/src/app/migrations.clj +++ b/backend/src/app/migrations.clj @@ -93,6 +93,11 @@ {:name "0025-del-generic-tokens-table" :fn (mg/resource "app/migrations/sql/0025-del-generic-tokens-table.sql")} + {:name "0026-mod-file-library-rel-table-synced-date" + :fn (mg/resource "app/migrations/sql/0026-mod-file-library-rel-table-synced-date.sql")} + + {:name "0027-mod-file-table-ignore-sync" + :fn (mg/resource "app/migrations/sql/0027-mod-file-table-ignore-sync.sql")} ]}) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/backend/src/app/migrations/sql/0026-mod-file-library-rel-table-synced-date.sql b/backend/src/app/migrations/sql/0026-mod-file-library-rel-table-synced-date.sql new file mode 100644 index 000000000..79fb14271 --- /dev/null +++ b/backend/src/app/migrations/sql/0026-mod-file-library-rel-table-synced-date.sql @@ -0,0 +1,3 @@ +ALTER TABLE file_library_rel + ADD COLUMN synced_at timestamptz NOT NULL DEFAULT clock_timestamp(); + diff --git a/backend/src/app/migrations/sql/0027-mod-file-table-ignore-sync.sql b/backend/src/app/migrations/sql/0027-mod-file-table-ignore-sync.sql new file mode 100644 index 000000000..9d698ba89 --- /dev/null +++ b/backend/src/app/migrations/sql/0027-mod-file-table-ignore-sync.sql @@ -0,0 +1,3 @@ +ALTER TABLE file + ADD COLUMN ignore_sync_until timestamptz NULL; + diff --git a/backend/src/app/services/mutations/files.clj b/backend/src/app/services/mutations/files.clj index e8cb8ef01..d3db46884 100644 --- a/backend/src/app/services/mutations/files.clj +++ b/backend/src/app/services/mutations/files.clj @@ -188,6 +188,47 @@ :library-file-id library-id})) +;; --- Mutation: Update syncrhonization status of a link + +(declare update-sync) + +(s/def ::update-sync + (s/keys :req-un [::profile-id ::file-id ::library-id])) + +(sm/defmutation ::update-sync + [{:keys [profile-id file-id library-id] :as params}] + (db/with-atomic [conn db/pool] + (files/check-edition-permissions! conn profile-id file-id) + (update-sync conn params))) + +(defn- update-sync + [conn {:keys [file-id library-id] :as params}] + (db/update! conn :file-library-rel + {:synced-at (dt/now)} + {:file-id file-id + :library-file-id library-id})) + + +;; --- Mutation: Ignore updates in linked files + +(declare ignore-sync) + +(s/def ::ignore-sync + (s/keys :req-un [::profile-id ::file-id ::date])) + +(sm/defmutation ::ignore-sync + [{:keys [profile-id file-id date] :as params}] + (db/with-atomic [conn db/pool] + (files/check-edition-permissions! conn profile-id file-id) + (ignore-sync conn params))) + +(defn- ignore-sync + [conn {:keys [file-id date] :as params}] + (db/update! conn :file + {:ignore-sync-until date} + {:id file-id})) + + ;; A generic, Changes based (granular) file update method. (s/def ::changes @@ -258,6 +299,7 @@ :file-id (:id file) :session-id sid :revn (:revn file) + :modified-at (:modified-at file) :changes library-changes}] @(redis/run! :publish {:channel (str team-id) diff --git a/backend/src/app/services/queries/files.clj b/backend/src/app/services/queries/files.clj index aef12ec51..61665dff7 100644 --- a/backend/src/app/services/queries/files.clj +++ b/backend/src/app/services/queries/files.clj @@ -235,7 +235,8 @@ ;; --- Query: File Libraries used by a File (def ^:private sql:file-libraries - "select fl.* + "select fl.*, + flr.synced_at as synced_at from file as fl inner join file_library_rel as flr on (flr.library_file_id = fl.id) where flr.file_id = ? diff --git a/common/app/common/data.cljc b/common/app/common/data.cljc index ba0b92dc2..fe84e3f83 100644 --- a/common/app/common/data.cljc +++ b/common/app/common/data.cljc @@ -182,6 +182,13 @@ (assoc m key (apply f found args)) m))) +(defn assoc-in-when + [m key-seq v] + (let [found (get-in m key-seq sentinel)] + (if-not (identical? sentinel found) + (assoc-in m key-seq v) + m))) + (defn assoc-when [m key v] (let [found (get m key sentinel)] diff --git a/frontend/resources/locales.json b/frontend/resources/locales.json index af9bad780..38242cb63 100644 --- a/frontend/resources/locales.json +++ b/frontend/resources/locales.json @@ -1,6 +1,6 @@ { "auth.already-have-account" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:115" ], + "used-in" : [ "src/app/main/ui/auth/register.cljs:114" ], "translations" : { "en" : "Already have an account?", "fr" : "Vous avez déjà un compte?", @@ -18,7 +18,7 @@ } }, "auth.create-demo-profile" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:124", "src/app/main/ui/auth/login.cljs:135" ], + "used-in" : [ "src/app/main/ui/auth/register.cljs:123", "src/app/main/ui/auth/login.cljs:135" ], "translations" : { "en" : "Create demo account", "fr" : "Créer un compte de démonstration", @@ -27,7 +27,7 @@ } }, "auth.create-demo-profile-label" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:121", "src/app/main/ui/auth/login.cljs:132" ], + "used-in" : [ "src/app/main/ui/auth/register.cljs:120", "src/app/main/ui/auth/login.cljs:132" ], "translations" : { "en" : "Just wanna try it?", "fr" : "Vous voulez juste essayer?", @@ -45,7 +45,7 @@ } }, "auth.email-label" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:90", "src/app/main/ui/auth/recovery_request.cljs:45", "src/app/main/ui/auth/login.cljs:81" ], + "used-in" : [ "src/app/main/ui/auth/register.cljs:89", "src/app/main/ui/auth/recovery_request.cljs:45", "src/app/main/ui/auth/login.cljs:81" ], "translations" : { "en" : "Email", "fr" : "Adresse email", @@ -63,7 +63,7 @@ } }, "auth.fullname-label" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:84" ], + "used-in" : [ "src/app/main/ui/auth/register.cljs:83" ], "translations" : { "en" : "Full Name", "fr" : "Nom complet", @@ -90,7 +90,7 @@ } }, "auth.login-here" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:118" ], + "used-in" : [ "src/app/main/ui/auth/register.cljs:117" ], "translations" : { "en" : "Login here", "fr" : "Se connecter ici", @@ -180,13 +180,13 @@ } }, "auth.notifications.validation-email-sent" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:59", "src/app/main/ui/settings/change_email.cljs:55" ], + "used-in" : [ "src/app/main/ui/settings/change_email.cljs:55", "src/app/main/ui/auth/register.cljs:58" ], "translations" : { "en" : "Verification email sent to %s; check your email!" } }, "auth.password-label" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:94", "src/app/main/ui/auth/login.cljs:87" ], + "used-in" : [ "src/app/main/ui/auth/register.cljs:93", "src/app/main/ui/auth/login.cljs:87" ], "translations" : { "en" : "Password", "fr" : "Mot de passe", @@ -195,7 +195,7 @@ } }, "auth.password-length-hint" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:93" ], + "used-in" : [ "src/app/main/ui/auth/register.cljs:92" ], "translations" : { "en" : "At least 8 characters", "fr" : "Au moins 8 caractères", @@ -258,7 +258,7 @@ } }, "auth.register-submit-label" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:98" ], + "used-in" : [ "src/app/main/ui/auth/register.cljs:97" ], "translations" : { "en" : "Create an account", "fr" : "Créer un compte", @@ -267,7 +267,7 @@ } }, "auth.register-subtitle" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:107" ], + "used-in" : [ "src/app/main/ui/auth/register.cljs:106" ], "translations" : { "en" : "It's free, it's Open Source", "fr" : "C'est gratuit, c'est Open Source", @@ -276,7 +276,7 @@ } }, "auth.register-title" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:106" ], + "used-in" : [ "src/app/main/ui/auth/register.cljs:105" ], "translations" : { "en" : "Create an account", "fr" : "Créer un compte", @@ -294,7 +294,7 @@ } }, "dashboard.grid.add-shared" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:175", "src/app/main/ui/dashboard/grid.cljs:165" ], + "used-in" : [ "src/app/main/ui/dashboard/grid.cljs:165", "src/app/main/ui/workspace/header.cljs:175" ], "translations" : { "en" : "Add as Shared Library", "fr" : "", @@ -303,7 +303,7 @@ } }, "dashboard.grid.add-shared-accept" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:98", "src/app/main/ui/dashboard/grid.cljs:94" ], + "used-in" : [ "src/app/main/ui/dashboard/grid.cljs:94", "src/app/main/ui/workspace/header.cljs:98" ], "translations" : { "en" : "Add as Shared Library", "fr" : "", @@ -312,7 +312,7 @@ } }, "dashboard.grid.add-shared-hint" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:97", "src/app/main/ui/dashboard/grid.cljs:93" ], + "used-in" : [ "src/app/main/ui/dashboard/grid.cljs:93", "src/app/main/ui/workspace/header.cljs:97" ], "translations" : { "en" : "Once added as Shared Library, the assets of this file library will be available to be used among the rest of your files.", "fr" : "", @@ -321,7 +321,7 @@ } }, "dashboard.grid.add-shared-message" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:96", "src/app/main/ui/dashboard/grid.cljs:92" ], + "used-in" : [ "src/app/main/ui/dashboard/grid.cljs:92", "src/app/main/ui/workspace/header.cljs:96" ], "translations" : { "en" : "Add “%s” as Shared Library", "fr" : "", @@ -348,7 +348,7 @@ } }, "dashboard.grid.remove-shared" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:173", "src/app/main/ui/dashboard/grid.cljs:164" ], + "used-in" : [ "src/app/main/ui/dashboard/grid.cljs:164", "src/app/main/ui/workspace/header.cljs:173" ], "translations" : { "en" : "Remove as Shared Library", "fr" : "", @@ -357,7 +357,7 @@ } }, "dashboard.grid.remove-shared-accept" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:107", "src/app/main/ui/dashboard/grid.cljs:113" ], + "used-in" : [ "src/app/main/ui/dashboard/grid.cljs:113", "src/app/main/ui/workspace/header.cljs:107" ], "translations" : { "en" : "Remove as Shared Library", "fr" : "", @@ -366,7 +366,7 @@ } }, "dashboard.grid.remove-shared-hint" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:106", "src/app/main/ui/dashboard/grid.cljs:112" ], + "used-in" : [ "src/app/main/ui/dashboard/grid.cljs:112", "src/app/main/ui/workspace/header.cljs:106" ], "translations" : { "en" : "Once removed as Shared Library, the File Library of this file will stop being available to be used among the rest of your files.", "fr" : "", @@ -375,7 +375,7 @@ } }, "dashboard.grid.remove-shared-message" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:105", "src/app/main/ui/dashboard/grid.cljs:111" ], + "used-in" : [ "src/app/main/ui/dashboard/grid.cljs:111", "src/app/main/ui/workspace/header.cljs:105" ], "translations" : { "en" : "Remove “%s” as Shared Library", "fr" : "", @@ -735,7 +735,7 @@ } }, "errors.email-already-exists" : { - "used-in" : [ "src/app/main/ui/auth.cljs:90", "src/app/main/ui/settings/change_email.cljs:46" ], + "used-in" : [ "src/app/main/ui/settings/change_email.cljs:46", "src/app/main/ui/auth.cljs:89" ], "translations" : { "en" : "Email already used", "fr" : "Adresse e-mail déjà utilisée", @@ -744,7 +744,7 @@ } }, "errors.email-already-validated" : { - "used-in" : [ "src/app/main/ui/auth.cljs:95" ], + "used-in" : [ "src/app/main/ui/auth.cljs:94" ], "translations" : { "en" : "Email already validated.", "fr" : "Adresse e-mail déjà validé.", @@ -762,7 +762,7 @@ } }, "errors.generic" : { - "used-in" : [ "src/app/main/ui/auth.cljs:99", "src/app/main/ui/settings/profile.cljs:36" ], + "used-in" : [ "src/app/main/ui/settings/profile.cljs:36", "src/app/main/ui/auth.cljs:98" ], "translations" : { "en" : "Something wrong has happened.", "fr" : "Quelque chose c'est mal passé.", @@ -834,7 +834,7 @@ } }, "errors.registration-disabled" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:49" ], + "used-in" : [ "src/app/main/ui/auth/register.cljs:48" ], "translations" : { "en" : "The registration is currently disabled.", "fr" : "L'enregistrement est actuellement désactivé.", @@ -843,7 +843,7 @@ } }, "errors.unexpected-error" : { - "used-in" : [ "src/app/main/data/media.cljs:64", "src/app/main/ui/workspace/sidebar/options/exports.cljs:66", "src/app/main/ui/auth/register.cljs:55", "src/app/main/ui/settings/change_email.cljs:50" ], + "used-in" : [ "src/app/main/data/media.cljs:64", "src/app/main/ui/settings/change_email.cljs:50", "src/app/main/ui/workspace/sidebar/options/exports.cljs:66", "src/app/main/ui/auth/register.cljs:54" ], "translations" : { "en" : "An unexpected error occurred.", "fr" : "Une erreur inattendue c'est produite", @@ -924,7 +924,7 @@ } }, "settings.cancel-email-change" : { - "used-in" : [ "src/app/main/ui/settings/profile.cljs:81" ], + "used-in" : [ "src/app/main/ui/settings/profile.cljs:79" ], "translations" : { "en" : "Cancel", "fr" : "Annuler", @@ -942,13 +942,13 @@ } }, "settings.change-email-info3" : { + "used-in" : [ "src/app/main/ui/settings/profile.cljs:78" ], "translations" : { "en" : null, "fr" : null, - "es" : null, - "ru" : null - }, - "used-in" : [ "src/app/main/ui/settings/profile.cljs:78" ] + "ru" : null, + "es" : null + } }, "settings.change-email-label" : { "used-in" : [ "src/app/main/ui/settings/profile.cljs:73" ], @@ -1023,7 +1023,7 @@ } }, "settings.email-verification-pending" : { - "used-in" : [ "src/app/main/ui/settings/profile.cljs:86" ], + "used-in" : [ "src/app/main/ui/settings/profile.cljs:88" ], "translations" : { "en" : "There is a pending email validation.", "fr" : "Une validation par e-mail est en attente.", @@ -1098,10 +1098,10 @@ "translations" : { "en" : null, "fr" : null, - "es" : null, - "ru" : null + "ru" : null, + "es" : null }, - "used-in" : [ "src/app/main/ui/dashboard.cljs:100" ] + "unused" : true }, "settings.notifications.email-verified-successfully" : { "used-in" : [ "src/app/main/ui/auth.cljs:57" ], @@ -1131,7 +1131,7 @@ } }, "settings.notifications.profile-saved" : { - "used-in" : [ "src/app/main/ui/settings/profile.cljs:41", "src/app/main/ui/settings/options.cljs:37" ], + "used-in" : [ "src/app/main/ui/settings/options.cljs:37", "src/app/main/ui/settings/profile.cljs:41" ], "translations" : { "en" : "Profile saved successfully!", "fr" : "Profil enregistré avec succès!", @@ -1185,7 +1185,7 @@ } }, "settings.profile-submit-label" : { - "used-in" : [ "src/app/main/ui/settings/profile.cljs:89", "src/app/main/ui/settings/password.cljs:93", "src/app/main/ui/settings/options.cljs:67" ], + "used-in" : [ "src/app/main/ui/settings/password.cljs:93", "src/app/main/ui/settings/options.cljs:67", "src/app/main/ui/settings/profile.cljs:91" ], "translations" : { "en" : "Update settings", "fr" : "Mettre à jour les paramètres", @@ -1194,7 +1194,7 @@ } }, "settings.remove-account-label" : { - "used-in" : [ "src/app/main/ui/settings/profile.cljs:94" ], + "used-in" : [ "src/app/main/ui/settings/profile.cljs:96" ], "translations" : { "en" : "Want to remove your account?", "fr" : "Vous souhaitez supprimer votre compte?", @@ -1230,7 +1230,7 @@ } }, "settings.update-photo-label" : { - "used-in" : [ "src/app/main/ui/settings/profile.cljs:115" ], + "used-in" : [ "src/app/main/ui/settings/profile.cljs:117" ], "translations" : { "en" : "UPDATE", "fr" : "METTRE A JOUR", @@ -1740,7 +1740,7 @@ } }, "workspace.libraries.add" : { - "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:106" ], + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:111" ], "translations" : { "en" : "Add", "fr" : "", @@ -1749,7 +1749,7 @@ } }, "workspace.libraries.colors" : { - "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:68" ], + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:42" ], "translations" : { "en" : "%s colors", "fr" : "", @@ -1764,13 +1764,13 @@ } }, "workspace.libraries.colors.file-library" : { - "used-in" : [ "src/app/main/ui/workspace/colorpicker.cljs:337", "src/app/main/ui/workspace/colorpalette.cljs:150" ], + "used-in" : [ "src/app/main/ui/workspace/colorpalette.cljs:150", "src/app/main/ui/workspace/colorpicker.cljs:337" ], "translations" : { "en" : "File library" } }, "workspace.libraries.colors.recent-colors" : { - "used-in" : [ "src/app/main/ui/workspace/colorpicker.cljs:336", "src/app/main/ui/workspace/colorpalette.cljs:160" ], + "used-in" : [ "src/app/main/ui/workspace/colorpalette.cljs:160", "src/app/main/ui/workspace/colorpicker.cljs:336" ], "translations" : { "en" : "Recent colors" } @@ -1787,8 +1787,17 @@ "en" : "Small thumbnails" } }, + "workspace.libraries.components" : { + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:36" ], + "translations" : { + "en" : "%s components", + "fr" : "", + "ru" : "", + "es" : "%s componentes" + } + }, "workspace.libraries.file-library" : { - "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:75" ], + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:80" ], "translations" : { "en" : "File library", "fr" : "", @@ -1797,7 +1806,7 @@ } }, "workspace.libraries.graphics" : { - "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:65" ], + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:39" ], "translations" : { "en" : "%s graphics", "fr" : "", @@ -1806,7 +1815,7 @@ } }, "workspace.libraries.in-this-file" : { - "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:72" ], + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:77" ], "translations" : { "en" : "LIBRARIES IN THIS FILE", "fr" : "", @@ -1815,7 +1824,7 @@ } }, "workspace.libraries.libraries" : { - "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:149" ], + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:171" ], "translations" : { "en" : "LIBRARIES", "fr" : "", @@ -1823,8 +1832,26 @@ "es" : "BIBLIOTECAS" } }, + "workspace.libraries.library" : { + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:131" ], + "translations" : { + "en" : "LIBRARY", + "fr" : "", + "ru" : "", + "es" : "BIBLIOTECA" + } + }, + "workspace.libraries.no-libraries-need-sync" : { + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:129" ], + "translations" : { + "en" : "There are no Shared Libraries that need update", + "fr" : "", + "ru" : "", + "es" : "No hay bibliotecas que necesiten ser actualizadas" + } + }, "workspace.libraries.no-matches-for" : { - "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:112" ], + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:117" ], "translations" : { "en" : "No matches found for “%s“", "fr" : "Aucune correspondance pour “%s“", @@ -1833,7 +1860,7 @@ } }, "workspace.libraries.no-shared-libraries-available" : { - "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:111" ], + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:116" ], "translations" : { "en" : "There are no Shared Libraries available", "fr" : "", @@ -1842,7 +1869,7 @@ } }, "workspace.libraries.remove" : { - "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:82" ], + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:87" ], "translations" : { "en" : "Remove", "fr" : "", @@ -1851,7 +1878,7 @@ } }, "workspace.libraries.search-shared-libraries" : { - "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:89" ], + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:94" ], "translations" : { "en" : "Search shared libraries", "fr" : "", @@ -1860,7 +1887,7 @@ } }, "workspace.libraries.shared-libraries" : { - "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:86" ], + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:91" ], "translations" : { "en" : "SHARED LIBRARIES", "fr" : "", @@ -1868,8 +1895,17 @@ "es" : "BIBLIOTECAS COMPARTIDAS" } }, + "workspace.libraries.update" : { + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:138" ], + "translations" : { + "en" : "Update", + "fr" : "", + "ru" : "", + "es" : "Actualizar" + } + }, "workspace.libraries.updates" : { - "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:153" ], + "used-in" : [ "src/app/main/ui/workspace/libraries.cljs:175" ], "translations" : { "en" : "UPDATES", "fr" : "", @@ -2670,7 +2706,17 @@ "es" : "Texto (T)" } }, + "workspace.updates.dismiss" : { + "used-in" : [ "src/app/main/data/workspace/libraries.cljs:487" ], + "translations" : { + "en" : "Dismiss", + "fr" : "", + "ru" : "", + "es" : "Ignorar" + } + }, "workspace.updates.there-are-updates" : { + "used-in" : [ "src/app/main/data/workspace/libraries.cljs:483" ], "translations" : { "en" : "There are updates in shared libraries", "fr" : "", @@ -2679,6 +2725,7 @@ } }, "workspace.updates.update" : { + "used-in" : [ "src/app/main/data/workspace/libraries.cljs:485" ], "translations" : { "en" : "Update", "fr" : "", @@ -2686,14 +2733,6 @@ "es" : "Actualizar" } }, - "workspace.updates.dismiss" : { - "translations" : { - "en" : "Dismiss", - "fr" : "", - "ru" : "", - "es" : "Ignorar" - } - }, "workspace.viewport.click-to-close-path" : { "used-in" : [ "src/app/main/ui/workspace/drawarea.cljs:59" ], "translations" : { diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 3e6c37990..657e1e204 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -153,7 +153,17 @@ (fn [file] (if (= (:id file) file-id) (assoc file :initialized true) - file)))))) + file)))) + + ptk/WatchEvent + (watch [_ state stream] + (let [ignore-until (get-in state [:workspace-file :ignore-sync-until]) + needs-update? (some #(and (> (:modified-at %) (:synced-at %)) + (or (not ignore-until) + (> (:modified-at %) ignore-until))) + (vals (get state :workspace-libraries)))] + (when needs-update? + (rx/of (dwl/notify-sync-file file-id))))))) (defn finalize-file [project-id file-id] diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index f0327d0b3..547d0b01b 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -15,6 +15,7 @@ [app.common.pages-helpers :as cph] [app.common.geom.point :as gpt] [app.common.geom.shapes :as geom] + [app.main.data.messages :as dm] [app.main.data.workspace.common :as dwc] [app.main.data.workspace.selection :as dws] [app.common.pages :as cp] @@ -24,6 +25,7 @@ [app.util.color :as color] [app.util.i18n :refer [tr]] [app.util.router :as rt] + [app.util.time :as dt] [beicon.core :as rx] [cljs.spec.alpha :as s] [potok.core :as ptk])) @@ -346,14 +348,16 @@ (st/emit! (rt/nav-new-window :workspace pparams qparams)))))) (defn ext-library-changed - [file-id changes] + [file-id modified-at changes] (us/assert ::us/uuid file-id) (us/assert ::cp/changes changes) (ptk/reify ::ext-library-changed ptk/UpdateEvent (update [_ state] - (d/update-in-when state [:workspace-libraries file-id :data] - cp/process-changes changes)))) + (-> state + (assoc-in [:workspace-libraries file-id :modified-at] modified-at) + (d/update-in-when [:workspace-libraries file-id :data] + cp/process-changes changes))))) (declare generate-sync-file) (declare generate-sync-page) @@ -433,10 +437,55 @@ [file-id] (us/assert (s/nilable ::us/uuid) file-id) (ptk/reify ::sync-file + ptk/UpdateEvent + (update [_ state] + (if file-id + (assoc-in state [:workspace-libraries file-id :synced-at] (dt/now)) + state)) + ptk/WatchEvent (watch [_ state stream] (let [[rchanges uchanges] (generate-sync-file state file-id)] - (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))) + (rx/concat + (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})) + (when file-id + (rp/mutation :update-sync + {:file-id (get-in state [:workspace-file :id]) + :library-id file-id}))))))) + +(def ignore-sync + (ptk/reify ::sync-file + ptk/UpdateEvent + (update [_ state] + (assoc-in state [:workspace-file :ignore-sync-until] (dt/now))) + + ptk/WatchEvent + (watch [_ state stream] + (rp/mutation :ignore-sync + {:file-id (get-in state [:workspace-file :id]) + :date (dt/now)})))) + +(defn notify-sync-file + [file-id] + (us/assert ::us/uuid file-id) + (ptk/reify ::notify-sync-file + ptk/WatchEvent + (watch [_ state stream] + (let [libraries-need-sync (filter #(> (:modified-at %) (:synced-at %)) + (vals (get state :workspace-libraries))) + do-update #(do (apply st/emit! (map (fn [library] + (sync-file (:id library))) + libraries-need-sync)) + (st/emit! dm/hide)) + do-dismiss #(do (st/emit! ignore-sync) + (st/emit! dm/hide))] + (rx/of (dm/info-dialog + (tr "workspace.updates.there-are-updates") + :inline-actions + [{:label (tr "workspace.updates.update") + :callback do-update} + {:label (tr "workspace.updates.dismiss") + :callback do-dismiss}])))))) (defn- generate-sync-file [state file-id] diff --git a/frontend/src/app/main/data/workspace/notifications.cljs b/frontend/src/app/main/data/workspace/notifications.cljs index eea454c78..a0ae74a6b 100644 --- a/frontend/src/app/main/data/workspace/notifications.cljs +++ b/frontend/src/app/main/data/workspace/notifications.cljs @@ -13,7 +13,6 @@ [app.common.geom.point :as gpt] [app.common.pages :as cp] [app.common.spec :as us] - [app.main.data.messages :as dm] [app.main.data.workspace.common :as dwc] [app.main.data.workspace.persistence :as dwp] [app.main.data.workspace.libraries :as dwl] @@ -204,25 +203,21 @@ (rx/from (map dwc/update-indices page-ids)))))))) (s/def ::library-change-event - (s/keys :req-un [::type ::profile-id ::file-id ::session-id ::revn ::changes])) + (s/keys :req-un [::type + ::profile-id + ::file-id + ::session-id + ::revn + ::modified-at + ::changes])) (defn handle-library-change - [{:keys [file-id changes] :as msg}] + [{:keys [file-id modified-at changes] :as msg}] (us/assert ::library-change-event msg) (ptk/reify ::handle-library-change ptk/WatchEvent (watch [_ state stream] (when (contains? (:workspace-libraries state) file-id) - (let [do-update #(do - (st/emit! (dwl/sync-file file-id)) - (st/emit! dm/hide)) - do-dismiss #(st/emit! dm/hide)] - (rx/of (dwl/ext-library-changed file-id changes) - (dm/info-dialog - (tr "workspace.updates.there-are-updates") - :inline-actions - [{:label (tr "workspace.updates.update") - :callback do-update} - {:label (tr "workspace.updates.dismiss") - :callback do-dismiss}]))))))) + (rx/of (dwl/ext-library-changed file-id modified-at changes) + (dwl/notify-sync-file file-id)))))) diff --git a/frontend/src/app/main/ui/workspace/libraries.cljs b/frontend/src/app/main/ui/workspace/libraries.cljs index 04de7e644..10e9a3834 100644 --- a/frontend/src/app/main/ui/workspace/libraries.cljs +++ b/frontend/src/app/main/ui/workspace/libraries.cljs @@ -16,12 +16,32 @@ [app.main.store :as st] [app.main.refs :as refs] [app.main.data.workspace :as dw] + [app.main.data.workspace.libraries :as dwl] [app.main.ui.icons :as i] [app.main.ui.modal :as modal])) (def workspace-file (l/derived :workspace-file st/state)) +(defn contents-str + [library] + (let [components-count (count (get-in library [:data :components] [])) + graphics-count (count (get-in library [:data :media] [])) + colors-count (count (get-in library [:data :colors] []))] + ;; Include a   so this block has always some content + (str + (str/join " · " + (cond-> [] + (< 0 components-count) + (conj (tr "workspace.libraries.components" components-count)) + + (< 0 graphics-count) + (conj (tr "workspace.libraries.graphics" graphics-count)) + + (< 0 colors-count) + (conj (tr "workspace.libraries.colors" colors-count)))) + "\u00A0"))) + (mf/defc libraries-tab [{:keys [file libraries shared-files] :as props}] (let [search-term (mf/use-state "") @@ -51,22 +71,7 @@ (mf/use-callback (mf/deps file) #(st/emit! (dw/link-file-to-library (:id file) %))) unlink-library - (mf/use-callback (mf/deps file) #(st/emit! (dw/unlink-file-from-library (:id file) %))) - - contents-str - (fn [library] - (let [graphics-count (count (get-in library [:data :media] [])) - colors-count (count (get-in library [:data :colors] []))] - ;; Include a   so this block has always some content - (str - (str/join " · " - (cond-> [] - (< 0 graphics-count) - (conj (tr "workspace.libraries.graphics" graphics-count)) - - (< 0 colors-count) - (conj (tr "workspace.libraries.colors" colors-count)))) - "\u00A0")))] + (mf/use-callback (mf/deps file) #(st/emit! (dw/unlink-file-from-library (:id file) %)))] [:* [:div.section [:div.section-title (tr "workspace.libraries.in-this-file")] @@ -113,8 +118,25 @@ (mf/defc updates-tab - [] - [:div]) + [{:keys [file libraries] :as props}] + (let [libraries-need-sync (filter #(> (:modified-at %) (:synced-at %)) + (vals libraries)) + update-library #(st/emit! (dwl/sync-file %))] + [:div.section + (if (empty? libraries-need-sync) + [:div.section-list-empty + i/library + (tr "workspace.libraries.no-libraries-need-sync")] + [:* + [:div.section-title (tr "workspace.libraries.library")] + [:div.section-list + (for [library libraries-need-sync] + [:div.section-list-item {:key (:id library)} + [:div.item-name (:name library)] + [:div.item-contents (contents-str library)] + [:input.item-button {:type "button" + :value (tr "workspace.libraries.update") + :on-click #(update-library (:id library))}]])]])])) (mf/defc libraries-dialog @@ -158,5 +180,6 @@ :libraries libraries :shared-files shared-files}] :updates - [:& updates-tab {}])]]]])) + [:& updates-tab {:file file + :libraries libraries}])]]]]))