feat: add Connection API and make SyncedAtom a promise
Add `connect` to manage store + shared config, and `synced-atom` now
accepts a Connection or raw store (backwards compatible).
SyncedAtom implements IPromiseFactory so it works directly with p/let:
(def conn (pb/connect {:db "my-app" :server "..."}))
(def !todos (pb/synced-atom conn "todo"))
(p/let [todos !todos] (swap! todos assoc "k" v))
- Connection record holds store-promise + default opts (server, interval)
- `connect` accepts :store (CLJ/CLJS) or :db (CLJS, opens IndexedDB)
- `ready?` now resolves to the SyncedAtom itself instead of `true`
- Add `close!` to close a Connection's store
- Store field is now an atom (set when store-promise resolves)
- Simplify TodoMVC example: no more !conn atom or double-deref
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
(ns atomsync.todomvc
|
||||
"TodoMVC built on Atomsync — offline-first, synced, Clojure-native."
|
||||
(:require [atomsync.core :as pb]
|
||||
[atomsync.store.idb :as idb]
|
||||
[atomsync.hiccup :refer [html]]
|
||||
[promesa.core :as p]
|
||||
[clojure.string :as str]))
|
||||
@@ -10,8 +9,10 @@
|
||||
;; State
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defonce !conn (atom nil))
|
||||
(defonce !todos (atom nil)) ;; the SyncedAtom
|
||||
(defonce conn (pb/connect {:db "atomsync-todomvc"
|
||||
:server "http://localhost:8090/sync"
|
||||
:interval 15000}))
|
||||
(defonce !todos (pb/synced-atom conn "todo"))
|
||||
(defonce !filter (atom :all)) ;; :all | :active | :completed
|
||||
(defonce !editing (atom nil)) ;; id of todo being edited, or nil
|
||||
|
||||
@@ -22,7 +23,7 @@
|
||||
(defn- todos-list
|
||||
"Return todos as sorted vec of [id doc] pairs."
|
||||
[]
|
||||
(->> @@!todos
|
||||
(->> @!todos
|
||||
(sort-by (fn [[_ doc]] (:created doc)))
|
||||
vec))
|
||||
|
||||
@@ -50,33 +51,33 @@
|
||||
(let [text (str/trim text)]
|
||||
(when (seq text)
|
||||
(let [id (str "todo:" (random-uuid))]
|
||||
(swap! @!todos assoc id
|
||||
(swap! !todos assoc id
|
||||
{:text text
|
||||
:completed false
|
||||
:created (.now js/Date)})))))
|
||||
|
||||
(defn- toggle-todo! [id]
|
||||
(swap! @!todos update-in [id :completed] not))
|
||||
(swap! !todos update-in [id :completed] not))
|
||||
|
||||
(defn- toggle-all! []
|
||||
(let [target (not (all-completed?))]
|
||||
(swap! @!todos
|
||||
(swap! !todos
|
||||
(fn [m]
|
||||
(reduce-kv (fn [acc k v] (assoc acc k (assoc v :completed target)))
|
||||
{} m)))))
|
||||
|
||||
(defn- destroy-todo! [id]
|
||||
(swap! @!todos dissoc id))
|
||||
(swap! !todos dissoc id))
|
||||
|
||||
(defn- edit-todo! [id new-text]
|
||||
(let [text (str/trim new-text)]
|
||||
(if (seq text)
|
||||
(swap! @!todos assoc-in [id :text] text)
|
||||
(swap! !todos assoc-in [id :text] text)
|
||||
(destroy-todo! id))
|
||||
(reset! !editing nil)))
|
||||
|
||||
(defn- clear-completed! []
|
||||
(swap! @!todos
|
||||
(swap! !todos
|
||||
(fn [m]
|
||||
(into {} (remove (fn [[_ v]] (:completed v))) m))))
|
||||
|
||||
@@ -119,7 +120,7 @@
|
||||
"Clear completed"])]))
|
||||
|
||||
(defn- render-sync-status []
|
||||
(let [pending (when @!todos (pb/pending-count @!todos))
|
||||
(let [pending (pb/pending-count !todos)
|
||||
online? (.-onLine js/navigator)]
|
||||
[:div.sync-bar
|
||||
[:span {:class (str "sync-dot " (if online? "online" "offline"))}]
|
||||
@@ -232,22 +233,16 @@
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defn ^:export init []
|
||||
(p/let [store (idb/open "atomsync-todomvc")]
|
||||
(let [todos (pb/synced-atom store "todo"
|
||||
{:server "http://localhost:8090/sync"
|
||||
:interval 15000})]
|
||||
(reset! !conn store)
|
||||
(reset! !todos todos)
|
||||
;; Render + bind immediately (empty or stale is fine)
|
||||
(render!)
|
||||
(bind-events!)
|
||||
;; Re-render on any data change (fires when IDB loads + server syncs)
|
||||
(add-watch todos :render (fn [_ _ _ _] (render!)))
|
||||
(add-watch !filter :render (fn [_ _ _ _] (render!)))
|
||||
(.addEventListener js/window "online" (fn [_] (render!)))
|
||||
(.addEventListener js/window "offline" (fn [_] (render!)))
|
||||
;; Wait for IDB — watch triggers render automatically
|
||||
(p/let [_ (pb/ready? todos)]
|
||||
(js/console.log "🔶 Atomsync TodoMVC loaded —" (count @todos) "todos")))))
|
||||
;; Render + bind immediately (empty or stale is fine)
|
||||
(render!)
|
||||
(bind-events!)
|
||||
;; Re-render on any data change (fires when IDB loads + server syncs)
|
||||
(add-watch !todos :render (fn [_ _ _ _] (render!)))
|
||||
(add-watch !filter :render (fn [_ _ _ _] (render!)))
|
||||
(.addEventListener js/window "online" (fn [_] (render!)))
|
||||
(.addEventListener js/window "offline" (fn [_] (render!)))
|
||||
;; Wait for IDB — watch triggers render automatically
|
||||
(p/let [todos !todos]
|
||||
(js/console.log "🔶 Atomsync TodoMVC loaded —" (count @todos) "todos")))
|
||||
|
||||
(init)
|
||||
|
||||
Reference in New Issue
Block a user