[codex] improve sync onboarding (#12676)

* enhance: improve sync onboarding

* chore: update sync onboarding local changes

* fix: show sync upload in native header

* fix: route native local sync taps

* fix: use upload icon for native local sync

* chore: remove debug log

* fix: compile cljs tests

* test: remove header compile dependency

* test: remove mobile header compile dependency
This commit is contained in:
Tienson Qin
2026-05-18 23:27:44 +08:00
committed by GitHub
parent 4cdb14ef8f
commit 466647ef3d
10 changed files with 402 additions and 40 deletions

View File

@@ -79,6 +79,7 @@ object MaterialIconResolver {
"go-to", "goto" -> Icons.Outlined.Explore
"bookmark" -> Icons.Filled.Bookmarks
"sync" -> Icons.Outlined.Equalizer
"cloud", "cloud-upload", "icloud-and-arrow-up" -> Icons.Filled.CloudUpload
else -> null
}
}

View File

@@ -10,6 +10,7 @@
[frontend.components.export :as export]
[frontend.components.page-menu :as page-menu]
[frontend.components.plugins :as plugins]
[frontend.components.repo :as repo]
[frontend.components.right-sidebar :as sidebar]
[frontend.components.rtc.indicator :as rtc-indicator]
[frontend.components.server :as server]
@@ -53,6 +54,24 @@
(t :nav/home)
{:trigger-props {:as-child true}}))
(defn current-local-uploadable-graph
[]
(let [current-repo (state/get-current-repo)]
(some (fn [{:keys [url] :as graph}]
(when (and (= current-repo url)
(repo/local-uploadable-graph? graph))
graph))
(state/get-repos))))
(defn local-graph-sync-button
[graph]
(ui/tooltip
(shui/button-ghost-icon :cloud
{:class "local-graph-sync-btn"
:on-click #(repo/upload-local-graph-with-confirm! graph)})
(t :graph/use-sync-beta)
{:trigger-props {:as-child true}}))
(rum/defcs rtc-collaborators <
rum/reactive
(rum/local nil ::online-users)
@@ -449,6 +468,9 @@
(rtc-indicator/uploading-detail))
(search-index-progress)
(when-let [graph (current-local-uploadable-graph)]
(local-graph-sync-button graph))
(when (and (not= (state/get-current-route) :home)
(not custom-home-page?))
(home-button))

View File

@@ -4,13 +4,13 @@
[frontend.config :as config]
[frontend.context.i18n :as i18n :refer [t]]
[frontend.db :as db]
[frontend.handler.db-based.rtc-flows :as rtc-flows]
[frontend.handler.db-based.sync :as rtc-handler]
[frontend.handler.graph :as graph]
[frontend.handler.notification :as notification]
[frontend.handler.repo :as repo-handler]
[frontend.handler.route :as route-handler]
[frontend.handler.user :as user-handler]
[frontend.mobile.util :as mobile-util]
[frontend.state :as state]
[frontend.ui :as ui]
[frontend.util :as util]
@@ -25,16 +25,72 @@
[promesa.core :as p]
[rum.core :as rum]))
(defn graph-sync-icon-name
[{:keys [remote? graph-e2ee?]}]
(when remote?
(if graph-e2ee? "lock" "cloud")))
(defn local-uploadable-graph?
[{:keys [root remote?]}]
(and (or root
(mobile-util/native-platform?))
(not remote?)
(user-handler/logged-in?)
(user-handler/rtc-group?)))
(defn- graph-e2ee-enabled?
[{:keys [url graph-e2ee?] :as graph}]
(if (contains? graph :graph-e2ee?)
(true? graph-e2ee?)
(if (= url (state/get-current-repo))
(let [e2ee? (ldb/get-graph-rtc-e2ee? (db/get-db))]
(if (nil? e2ee?) true (true? e2ee?)))
true)))
(defn- <ensure-current-graph-for-upload!
[repo]
(if (= repo (state/get-current-repo))
(p/resolved nil)
(state/pub-event! [:graph/switch repo])))
(defn upload-local-graph-with-confirm!
[{:keys [url] :as graph}]
(let [graph-name (config/db-graph-name url)
dialog-config {:cancel-label (t :ui/cancel)
:ok-label (t :ui/confirm)}]
(-> (shui/dialog-confirm!
[:p.font-medium.-my-4 (t :graph/upload-local-confirm-desc graph-name)]
dialog-config)
(p/then
(fn []
(p/let [_ (<ensure-current-graph-for-upload! url)
graph-e2ee? (graph-e2ee-enabled? graph)]
(let [mobile? (util/mobile?)
hide-upload-log! (fn []
(when mobile?
(shui/popup-hide! :rtc-graph-upload-log)))]
(when mobile?
(shui/popup-show! nil
(fn []
(rtc-indicator/uploading-logs))
{:id :rtc-graph-upload-log}))
(rtc-indicator/on-upload-finished-task
hide-upload-log!)
(-> (rtc-handler/<rtc-upload-graph! url graph-e2ee?)
(p/finally hide-upload-log!)))))))))
(rum/defc normalized-graph-label
[{:keys [url remote? graph-e2ee?] :as graph} on-click]
[{:keys [url remote?] :as graph} on-click]
(when graph
[:span.flex.items-center
(let [local-dir (config/get-local-dir url)
graph-name (text-util/get-graph-name-from-path url)]
[:a.flex.items-center {:title local-dir
:on-click #(on-click graph)}
[:span graph-name]
(when remote? [:strong.px-1.flex.items-center (ui/icon (if graph-e2ee? "lock" "cloud"))])])]))
[:<>
[:a.flex.items-center {:title local-dir
:on-click #(on-click graph)}
[:span graph-name]
(when remote?
[:strong.px-1.flex.items-center (ui/icon (graph-sync-icon-name graph))])]])]))
(defn sort-repos-with-metadata-local
[repos]
@@ -122,6 +178,7 @@
:class "delete-local-graph-menu-item"
:on-click #(delete-local-graph! repo)}
(t :graph/delete-local-action)))
(when (and root
(user-handler/logged-in?)
(user-handler/rtc-group?)
@@ -130,26 +187,7 @@
(shui/dropdown-menu-item
{:key "logseq-sync"
:class "use-logseq-sync-menu-item"
:on-click (fn []
(let [repo (state/get-current-repo)
token (state/get-auth-id-token)
remote-graph-name (config/db-graph-name (state/get-current-repo))
graph-e2ee? (let [e2ee? (ldb/get-graph-rtc-e2ee? (db/get-db))]
(if (nil? e2ee?) true (true? e2ee?)))]
(when (and token remote-graph-name)
(rtc-handler/<rtc-upload-graph! repo graph-e2ee?)
(when (util/mobile?)
(shui/popup-show! nil
(fn []
(rtc-indicator/uploading-logs))
{:id :rtc-graph-upload-log}))
(rtc-indicator/on-upload-finished-task
(fn []
(when (util/mobile?) (shui/popup-hide! :rtc-graph-upload-log))
(p/do!
(rtc-flows/trigger-rtc-start repo)
(rtc-handler/<get-remote-graphs)))))))}
:on-click #(upload-local-graph-with-confirm! repo)}
(t :graph/use-sync-beta)))
(when (and remote?
@@ -448,7 +486,8 @@
(ui/tooltip
[:a.item.flex.items-center.gap-1.select-none
selector-opts
[:span.thumb (shui/tabler-icon (if remote? "cloud" "topology-star") {:size 16})]
[:span.thumb
(shui/tabler-icon (if remote? "cloud" "topology-star") {:size 16})]
[:strong short-repo-name]
(shui/tabler-icon "selector" {:size 18})]
current-repo)]))

View File

@@ -355,8 +355,10 @@
(defn rtc-group?
[]
(boolean (or (some? (config/get-custom-sync-server-url))
(seq (set/intersection (state/user-groups) #{"team" "rtc_2025_07_10"})))))
(boolean (or
config/dev?
(some? (config/get-custom-sync-server-url))
(seq (set/intersection (state/user-groups) #{"team" "rtc_2025_07_10"})))))
(defn alpha-user?
[]

View File

@@ -16,6 +16,7 @@
(defonce ^:private *graph->aes-key (atom {}))
(defonce ^:private *user-rsa-key-pair-inflight (atom {}))
(defonce ^:private *ensure-user-rsa-key-pair-inflight (atom {}))
(defonce ^:private node-default-auth-file "~/logseq/auth.json")
(defonce ^:private e2ee-password-secret-key "logseq-encrypted-password")
(def ^:private invalid-transit ::invalid-transit)
@@ -377,7 +378,8 @@
exported-public-key (crypt/<export-public-key publicKey)
public-key-str (ldb/write-transit-str exported-public-key)
encrypted-private-key-str (ldb/write-transit-str encrypted-private-key)]
(p/let [_ (<upload-user-rsa-key-pair! base public-key-str encrypted-private-key-str)]
(p/let [_ (<save-e2ee-password password)
_ (<upload-user-rsa-key-pair! base public-key-str encrypted-private-key-str)]
{:public-key public-key-str
:encrypted-private-key encrypted-private-key-str
:password password})))
@@ -404,13 +406,31 @@
:else
(<generate-and-upload-user-rsa-key-pair! base opts))))
(defn- <ensure-user-rsa-key-pair
[base {:keys [password ensure-server? server-rsa-keys-exists?] :as opts}]
(if (seq password)
(<ensure-user-rsa-key-pair-raw base opts)
(p/let [user-id (<resolve-user-uuid)]
(when-not (and (string? base) (string? user-id))
(fail-fast :db-sync/missing-field {:base base
:user-id user-id
:field :user-rsa-key-pair}))
(let [k [base user-id ensure-server? server-rsa-keys-exists?]]
(if-let [inflight (get @*ensure-user-rsa-key-pair-inflight k)]
inflight
(let [task (-> (<ensure-user-rsa-key-pair-raw base opts)
(p/finally (fn []
(swap! *ensure-user-rsa-key-pair-inflight dissoc k))))]
(swap! *ensure-user-rsa-key-pair-inflight assoc k task)
task))))))
(defn ensure-user-rsa-keys!
([]
(ensure-user-rsa-keys! nil))
([opts]
(let [base (e2ee-base)]
(if (string? base)
(<ensure-user-rsa-key-pair-raw base opts)
(<ensure-user-rsa-key-pair base opts)
(do
(log/info :db-sync/skip-ensure-user-rsa-keys {:reason :missing-e2ee-base})
(p/resolved nil))))))

View File

@@ -31,6 +31,8 @@
[rum.core :as rum]))
(defonce native-top-bar-listener? (atom false))
(defonce native-top-bar-listener-version (atom nil))
(def ^:private native-top-bar-listener-current-version :sync-upload-v2)
(defn- open-journal-calendar! []
(let [apply-date! (fn [date]
@@ -113,10 +115,19 @@
(t :mobile.header/create-graph))])
{:default-height false}))
(defn current-local-uploadable-graph
[]
(let [current-repo (state/get-current-repo)]
(some (fn [{:keys [url] :as graph}]
(when (and (= current-repo url)
(repo/local-uploadable-graph? graph))
graph))
(state/get-repos))))
(defn- register-native-top-bar-events! [*configure-top-bar-f]
(when (and (mobile-util/native-platform?)
mobile-util/native-top-bar
(not @native-top-bar-listener?))
(not= native-top-bar-listener-current-version @native-top-bar-listener-version))
(.addListener ^js mobile-util/native-top-bar "buttonTapped"
(fn [^js e]
(case (.-id e)
@@ -130,9 +141,11 @@
"add-graph" (state/pub-event! [:graph/new-db-graph])
"home-setting" (open-home-settings-actions!)
"graph-setting" (open-graph-settings-actions!)
"sync" (shui/popup-show! nil
(rtc-indicator/details)
{})
"sync" (if-let [graph (current-local-uploadable-graph)]
(repo/upload-local-graph-with-confirm! graph)
(shui/popup-show! nil
(rtc-indicator/details)
{}))
"favorite" (when-let [id (state/get-current-page)]
(when (common-util/uuid-string? id)
(when-let [block (db/entity [:block/uuid (uuid id)])]
@@ -150,10 +163,11 @@
(open-page-settings block))))
nil)))
(reset! native-top-bar-listener? true)))
(reset! native-top-bar-listener? true)
(reset! native-top-bar-listener-version native-top-bar-listener-current-version)))
(defn- configure-native-top-bar!
[{:keys [tab title route-name route-view sync-color favorited? show-sync?]}]
[{:keys [tab title route-name route-view sync-color favorited? show-sync? show-local-upload?]}]
(when (and (mobile-util/native-platform?)
mobile-util/native-top-bar)
(let [hidden? (and (mobile-util/native-ios?) (= tab "search"))
@@ -177,7 +191,10 @@
(cond-> []
(nil? route-view)
(conj {:id "home-setting" :systemIcon "ellipsis"})
(and show-sync? (not page?))
(and show-local-upload? (not page?))
(conj {:id "sync" :systemIcon "icloud.and.arrow.up"
:size "medium"})
(and (not show-local-upload?) show-sync? (not page?))
(conj {:id "sync" :systemIcon "circle.fill" :color sync-color
:size "small"}))
@@ -212,7 +229,9 @@
rtc-state (:rtc-state detail-info)
graph-uuid (or (:graph-uuid detail-info)
(ldb/get-graph-rtc-uuid (db/get-db)))
local-uploadable-graph (current-local-uploadable-graph)
show-sync? (and current-repo graph-uuid (user-handler/logged-in?))
show-local-upload? (some? local-uploadable-graph)
unpushed-block-update-count (:pending-local-ops detail-info)
pending-asset-ops (:pending-asset-ops detail-info)
fallback-title (cond
@@ -253,11 +272,12 @@
:route-view route-view
:sync-color sync-color
:show-sync? show-sync?
:show-local-upload? show-local-upload?
:favorited? favorited?}))]
(reset! *configure-top-bar-f f)
(f favorited?)))
nil)
[current-repo tab route-name route-view route-id fallback-title sync-color show-sync? page-route?])
[current-repo tab route-name route-view route-id fallback-title sync-color show-sync? show-local-upload? page-route?])
(hooks/use-effect!
(fn []
@@ -282,13 +302,14 @@
:route-view route-view
:sync-color sync-color
:show-sync? show-sync?
:show-local-upload? show-local-upload?
:favorited? favorited?}))]
(reset! *configure-top-bar-f f)
(f favorited?)))))
(p/catch (fn [_] nil)))
#(reset! cancelled? true))
nil))
[current-repo tab route-name route-view route-id sync-color show-sync? page-route?])
[current-repo tab route-name route-view route-id sync-color show-sync? show-local-upload? page-route?])
[:<>]))

View File

@@ -736,6 +736,7 @@
:graph/time-travel-now "Now"
:graph/toggle-tag "Toggle tag {1}"
:graph/updated-switching "Graph updated! Switching to graph ..."
:graph/upload-local-confirm-desc "Upload graph \"{1}\" to Logseq Server?"
:graph/use-sync-beta "Use Logseq Sync (Beta testing)"
:graph/use-sync-label "Use Logseq Sync?"
:graph/view-mode "View mode"

View File

@@ -732,6 +732,7 @@
:graph/time-travel-now "现在"
:graph/toggle-tag "切换标签 {1}"
:graph/updated-switching "图谱已更新!正在切换到图谱……"
:graph/upload-local-confirm-desc "要将图谱“{1}”上传到 Logseq Server 吗?"
:graph/use-sync-beta "使用 Logseq SyncBeta 测试)"
:graph/use-sync-label "使用 Logseq Sync"
:graph/view-mode "视图模式"

View File

@@ -1,14 +1,170 @@
(ns frontend.components.repo-test
(:require [cljs.test :refer [deftest is async]]
[frontend.components.repo :as repo]
[frontend.components.rtc.indicator :as rtc-indicator]
[frontend.db :as db]
[frontend.handler.db-based.sync :as rtc-handler]
[frontend.handler.repo :as repo-handler]
[frontend.handler.user :as user-handler]
[frontend.state :as state]
[frontend.mobile.util :as mobile-util]
[frontend.util :as util]
[logseq.db :as ldb]
[logseq.shui.ui :as shui]
[promesa.core :as p]))
(defn- ensure-rsa-key-fn
[]
#'repo/ensure-e2ee-rsa-key-for-cloud!)
(deftest local-uploadable-graph-allows-native-mobile-local-graph-without-root-test
(with-redefs [mobile-util/native-platform? (constantly true)
user-handler/logged-in? (constantly true)
user-handler/rtc-group? (constantly true)]
(is (true? (repo/local-uploadable-graph? {:url "logseq_db_mobile"})))))
(deftest upload-local-graph-with-confirm-asks-before-upload-test
(async done
(let [upload-fn (some-> (resolve 'frontend.components.repo/upload-local-graph-with-confirm!) deref)
dialogs (atom [])
upload-calls (atom [])
finished-calls (atom 0)]
(if-not upload-fn
(do
(is false "missing upload-local-graph-with-confirm!")
(done))
(-> (p/with-redefs [shui/dialog-confirm! (fn [content opts]
(swap! dialogs conj {:content content
:opts opts})
(p/resolved nil))
rtc-handler/<rtc-upload-graph! (fn [repo graph-e2ee?]
(swap! upload-calls conj [repo graph-e2ee?])
(p/resolved nil))
state/get-current-repo (fn []
"logseq_db_demo")
rtc-indicator/on-upload-finished-task (fn [f]
(swap! finished-calls inc)
(f))
util/mobile? (fn [] false)
shui/popup-show! (fn [& _] nil)
shui/popup-hide! (fn [& _] nil)]
(upload-fn {:url "logseq_db_demo"
:graph-e2ee? true}))
(p/then (fn [_]
(is (= 1 (count @dialogs)))
(is (= [["logseq_db_demo" true]] @upload-calls))
(is (= 1 @finished-calls))
(done)))
(p/catch (fn [error]
(is false (str error))
(done))))))))
(deftest upload-local-graph-switches-before-uploading-non-current-graph-test
(async done
(let [upload-fn (some-> (resolve 'frontend.components.repo/upload-local-graph-with-confirm!) deref)
current-repo (atom "logseq_db_current")
calls (atom [])]
(if-not upload-fn
(do
(is false "missing upload-local-graph-with-confirm!")
(done))
(-> (p/with-redefs [shui/dialog-confirm! (fn [_content _opts]
(p/resolved nil))
state/get-current-repo (fn []
@current-repo)
state/pub-event! (fn [event]
(swap! calls conj event)
(reset! current-repo (second event))
(p/resolved nil))
db/get-db (fn []
:db-after-switch)
ldb/get-graph-rtc-e2ee? (fn [db]
(is (= :db-after-switch db))
false)
rtc-handler/<rtc-upload-graph! (fn [repo graph-e2ee?]
(swap! calls conj [:upload repo graph-e2ee?])
(p/resolved nil))
rtc-indicator/on-upload-finished-task (fn [f]
(f))
util/mobile? (fn [] false)
shui/popup-show! (fn [& _] nil)
shui/popup-hide! (fn [& _] nil)]
(upload-fn {:url "logseq_db_other"}))
(p/then (fn [_]
(is (= [[:graph/switch "logseq_db_other"]
[:upload "logseq_db_other" false]]
@calls))
(done)))
(p/catch (fn [error]
(is false (str error))
(done))))))))
(deftest upload-local-graph-shows-mobile-popup-before-upload-starts-test
(async done
(let [upload-fn (some-> (resolve 'frontend.components.repo/upload-local-graph-with-confirm!) deref)
calls (atom [])]
(if-not upload-fn
(do
(is false "missing upload-local-graph-with-confirm!")
(done))
(-> (p/with-redefs [shui/dialog-confirm! (fn [_content _opts]
(p/resolved nil))
state/get-current-repo (fn []
"logseq_db_demo")
rtc-handler/<rtc-upload-graph! (fn [_repo _graph-e2ee?]
(swap! calls conj :upload)
(is (= [:popup-show :finish-handler :upload] @calls))
(p/resolved nil))
rtc-indicator/on-upload-finished-task (fn [_f]
(swap! calls conj :finish-handler))
util/mobile? (fn [] true)
shui/popup-show! (fn [& _]
(swap! calls conj :popup-show))
shui/popup-hide! (fn [& _]
(swap! calls conj :popup-hide))]
(upload-fn {:url "logseq_db_demo"
:graph-e2ee? true}))
(p/then (fn [_]
(is (= [:popup-show :finish-handler :upload :popup-hide]
@calls))
(done)))
(p/catch (fn [error]
(is false (str error))
(done))))))))
(deftest upload-local-graph-hides-mobile-popup-when-upload-errors-test
(async done
(let [upload-fn (some-> (resolve 'frontend.components.repo/upload-local-graph-with-confirm!) deref)
calls (atom [])]
(if-not upload-fn
(do
(is false "missing upload-local-graph-with-confirm!")
(done))
(-> (p/with-redefs [shui/dialog-confirm! (fn [_content _opts]
(p/resolved nil))
state/get-current-repo (fn []
"logseq_db_demo")
rtc-handler/<rtc-upload-graph! (fn [_repo _graph-e2ee?]
(swap! calls conj :upload)
(p/rejected (ex-info "upload failed" {})))
rtc-indicator/on-upload-finished-task (fn [_f]
(swap! calls conj :finish-handler))
util/mobile? (fn [] true)
shui/popup-show! (fn [& _]
(swap! calls conj :popup-show))
shui/popup-hide! (fn [& _]
(swap! calls conj :popup-hide))]
(upload-fn {:url "logseq_db_demo"
:graph-e2ee? true}))
(p/then (fn [_]
(is false "expected upload failure")
(done)))
(p/catch (fn [error]
(is (= "upload failed" (ex-message error)))
(is (= [:popup-show :finish-handler :upload :popup-hide]
@calls))
(done))))))))
(deftest ensure-rsa-key-does-not-create-graph-test
(async done
(let [ensure-fn (ensure-rsa-key-fn)

View File

@@ -551,6 +551,105 @@
(is (not= ":public-key is not ISeqable" (ex-message e))))))
(p/finally done))))
(deftest ensure-user-rsa-keys-saves-new-ui-password-test
(async done
(let [upload-calls (atom [])
save-calls (atom [])]
(-> (p/with-redefs [sync-crypt/e2ee-base (fn [] "http://base")
sync-crypt/<resolve-user-uuid (fn []
(p/resolved "user-1"))
sync-crypt/<fetch-user-rsa-key-pair-raw (fn [_base]
(p/resolved nil))
sync-crypt/<upload-user-rsa-key-pair! (fn [base public-key encrypted-private-key]
(swap! upload-calls conj [base public-key encrypted-private-key])
(p/resolved {:public-key public-key
:encrypted-private-key encrypted-private-key}))
sync-crypt/<save-e2ee-password (fn [password]
(swap! save-calls conj password)
(p/resolved nil))
platform/current (fn [] {:env {:runtime :browser}})
platform/kv-get (fn [_platform' _k]
(p/resolved nil))
platform/kv-set! (fn [_platform' _k _value]
(p/resolved nil))
ui-request/<request (fn [action payload & _opts]
(is (= :request-e2ee-password action))
(is (= {:reason :generate-user-rsa-key-pair} payload))
(p/resolved {:password "new-password"}))
crypt/<generate-rsa-key-pair (fn []
(p/resolved {:publicKey :public-key
:privateKey :private-key}))
crypt/<encrypt-private-key (fn [password private-key]
(is (= "new-password" password))
(is (= :private-key private-key))
(p/resolved :encrypted-private-key))
crypt/<export-public-key (fn [public-key]
(is (= :public-key public-key))
(p/resolved :exported-public-key))
ldb/write-transit-str pr-str]
(sync-crypt/ensure-user-rsa-keys!))
(p/then (fn [result]
(is (= "new-password" (:password result)))
(is (= [["http://base"
(pr-str :exported-public-key)
(pr-str :encrypted-private-key)]]
@upload-calls))
(is (= ["new-password"] @save-calls))))
(p/catch (fn [e]
(is false (str e))))
(p/finally done)))))
(deftest ensure-user-rsa-keys-deduplicates-concurrent-new-user-password-request-test
(async done
(let [ui-calls (atom 0)
upload-calls (atom [])
save-calls (atom [])]
(-> (p/with-redefs [sync-crypt/e2ee-base (fn [] "http://base")
sync-crypt/<resolve-user-uuid (fn []
(p/resolved "user-1"))
sync-crypt/<fetch-user-rsa-key-pair-raw (fn [_base]
(p/let [_ (p/delay 10)]
nil))
sync-crypt/<upload-user-rsa-key-pair! (fn [base public-key encrypted-private-key]
(swap! upload-calls conj [base public-key encrypted-private-key])
(p/resolved {:public-key public-key
:encrypted-private-key encrypted-private-key}))
sync-crypt/<save-e2ee-password (fn [password]
(swap! save-calls conj password)
(p/resolved nil))
platform/current (fn [] {:env {:runtime :browser}})
platform/kv-get (fn [_platform' _k]
(p/resolved nil))
platform/kv-set! (fn [_platform' _k _value]
(p/resolved nil))
ui-request/<request (fn [action payload & _opts]
(is (= :request-e2ee-password action))
(is (= {:reason :generate-user-rsa-key-pair} payload))
(swap! ui-calls inc)
(p/resolved {:password "new-password"}))
crypt/<generate-rsa-key-pair (fn []
(p/resolved {:publicKey :public-key
:privateKey :private-key}))
crypt/<encrypt-private-key (fn [password private-key]
(is (= "new-password" password))
(is (= :private-key private-key))
(p/resolved :encrypted-private-key))
crypt/<export-public-key (fn [public-key]
(is (= :public-key public-key))
(p/resolved :exported-public-key))
ldb/write-transit-str pr-str]
(let [task-1 (sync-crypt/ensure-user-rsa-keys!)
task-2 (sync-crypt/ensure-user-rsa-keys!)]
(p/all [task-1 task-2])))
(p/then (fn [results]
(is (= 2 (count results)))
(is (= 1 @ui-calls))
(is (= 1 (count @upload-calls)))
(is (= ["new-password"] @save-calls))))
(p/catch (fn [e]
(is false (str e))))
(p/finally done)))))
(deftest decrypt-private-key-headless-ignores-config-e2ee-password-test
(async done
(let [old-sync-config @worker-state/*db-sync-config