|  | @@ -0,0 +1,75 @@
 | 
	
		
			
				|  |  | +(ns frontend.worker.commands
 | 
	
		
			
				|  |  | +  "Invoke commands based on user settings"
 | 
	
		
			
				|  |  | +  (:require [datascript.core :as d]
 | 
	
		
			
				|  |  | +            [logseq.db.frontend.property.type :as db-property-type]
 | 
	
		
			
				|  |  | +            [cljs-time.core :as t]
 | 
	
		
			
				|  |  | +            [cljs-time.coerce :as tc]
 | 
	
		
			
				|  |  | +            [logseq.db.frontend.property :as db-property]))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +;; TODO: allow users to add command or configure it through #Command (which parent should be #Code)
 | 
	
		
			
				|  |  | +(def *commands
 | 
	
		
			
				|  |  | +  (atom
 | 
	
		
			
				|  |  | +   [[:repeated-task
 | 
	
		
			
				|  |  | +     {:title "Repeated task"
 | 
	
		
			
				|  |  | +      :entity-conditions [{:property :logseq.task/repeated?
 | 
	
		
			
				|  |  | +                           :value true}]
 | 
	
		
			
				|  |  | +      :tx-conditions [{:property :logseq.task/status
 | 
	
		
			
				|  |  | +                       :value :logseq.task/status.done}]
 | 
	
		
			
				|  |  | +      :actions [[:reschedule :logseq.task/scheduled]
 | 
	
		
			
				|  |  | +                [:set-property :logseq.task/status :logseq.task/status.todo]]}]]))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +(defn sastify-condition?
 | 
	
		
			
				|  |  | +  "Whether entity or updated datoms satisfy the `condition`"
 | 
	
		
			
				|  |  | +  [db entity {:keys [property value]} datoms]
 | 
	
		
			
				|  |  | +  (when-let [property-entity (d/entity db property)]
 | 
	
		
			
				|  |  | +    (let [value-matches? (fn [value]
 | 
	
		
			
				|  |  | +                           (cond
 | 
	
		
			
				|  |  | +                             (qualified-keyword? value)
 | 
	
		
			
				|  |  | +                             (= (:db/ident (get entity property)) value)
 | 
	
		
			
				|  |  | +                           ;; ref type
 | 
	
		
			
				|  |  | +                             (and (int? value) (contains? db-property-type/all-ref-property-types (:type (:block/schema property-entity))))
 | 
	
		
			
				|  |  | +                             (= (:db/id (get entity property)) value)
 | 
	
		
			
				|  |  | +                             :else
 | 
	
		
			
				|  |  | +                             (= (get entity property) value)))]
 | 
	
		
			
				|  |  | +      (if datoms
 | 
	
		
			
				|  |  | +        (some (fn [d] (value-matches? (:v d))) datoms)
 | 
	
		
			
				|  |  | +        (value-matches? value)))))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +(defmulti handle-command (fn [action-id & _others] action-id))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +(defmethod handle-command :reschedule [_ entity property]
 | 
	
		
			
				|  |  | +  (let [frequency (db-property/property-value-content  (:logseq.task/recur-frequency entity))
 | 
	
		
			
				|  |  | +        unit (:logseq.task/recur-unit entity)]
 | 
	
		
			
				|  |  | +    (when (and frequency unit)
 | 
	
		
			
				|  |  | +      (let [interval (case (:db/ident unit)
 | 
	
		
			
				|  |  | +                       :logseq.task/recur-unit.minute t/minutes
 | 
	
		
			
				|  |  | +                       :logseq.task/recur-unit.hour t/hours
 | 
	
		
			
				|  |  | +                       :logseq.task/recur-unit.day t/days
 | 
	
		
			
				|  |  | +                       :logseq.task/recur-unit.week t/weeks
 | 
	
		
			
				|  |  | +                       :logseq.task/recur-unit.month t/months
 | 
	
		
			
				|  |  | +                       :logseq.task/recur-unit.year t/years)
 | 
	
		
			
				|  |  | +            next-time (tc/to-long (t/plus (t/now) (interval frequency)))]
 | 
	
		
			
				|  |  | +        [[:db/add (:db/id entity) property next-time]]))))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +(defmethod handle-command :set-property [_ entity property value]
 | 
	
		
			
				|  |  | +  [[:db/add (:db/id entity) property value]])
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +(defn execute-command
 | 
	
		
			
				|  |  | +  "Build tx-data"
 | 
	
		
			
				|  |  | +  [entity [_command {:keys [actions]}]]
 | 
	
		
			
				|  |  | +  (mapcat (fn [action]
 | 
	
		
			
				|  |  | +            (apply handle-command (first action) entity (rest action))) actions))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +(defn run-commands
 | 
	
		
			
				|  |  | +  [{:keys [tx-data db-after]}]
 | 
	
		
			
				|  |  | +  (let [db db-after]
 | 
	
		
			
				|  |  | +    (mapcat (fn [[e datoms]]
 | 
	
		
			
				|  |  | +              (let [entity (d/entity db e)
 | 
	
		
			
				|  |  | +                    commands (filter (fn [[_command {:keys [entity-conditions tx-conditions]}]]
 | 
	
		
			
				|  |  | +                                       (and (every? #(sastify-condition? db entity % nil) entity-conditions)
 | 
	
		
			
				|  |  | +                                            (every? #(sastify-condition? db entity % datoms) tx-conditions))) @*commands)]
 | 
	
		
			
				|  |  | +                (mapcat
 | 
	
		
			
				|  |  | +                 (fn [command]
 | 
	
		
			
				|  |  | +                   (execute-command entity command))
 | 
	
		
			
				|  |  | +                 commands)))
 | 
	
		
			
				|  |  | +            (group-by :e tx-data))))
 |