#+PROPERTY: header-args:emacs-lisp :tangle "~/.emacs.d/config-org.el" :comments both

* The all-mighty org configuration
#+begin_src emacs-lisp
(require 'my-org)
(require 'my-org-agenda-files)
#+end_src
* Plugins
** fstree
#+BEGIN_SRC
  (add-to-list 'load-path
               (ef "submodule/org-fstree"))
  (require 'org-fstree)
#+END_SRC
** org-bullets
#+BEGIN_SRC
(use-package org-bullets)
(when (not (eq system-type 'windows-nt))
(add-hook 'org-mode-hook (lambda () (org-bullets-mode 1))))
#+END_SRC
** calfw-org
#+BEGIN_SRC emacs-lisp
  (use-package calfw)
  (use-package calfw-ical)
  (use-package calfw-gcal)
  (use-package calfw-org)
  (global-set-key (kbd "C-c A") 'cfw:open-org-calendar)
  (setq cfw:org-overwrite-default-keybinding t)
#+END_SRC
** sync with google calendar
#+BEGIN_SRC emacs-lisp#
  (defvar url-http-method)
  (defvar url-http-data)
  (defvar url-http-extra-headers)
  (defvar oauth--token-data)
  (defvar url-callback-function)

  (require 'url-http)
  (unless (package-installed-p 'org-caldav)
    (use-package oauth2)
    (use-package org-caldav))
  (setq epa-pinentry-mode 'loopback)
  (setq plstore-cache-passphrase-for-symmetric-encryption t)

  (save-excursion
    (let ((filename (ef "google-calendar-secret.el")))
      (when (file-exists-p filename)
        (set-buffer (find-file-noselect filename))
        (let ((var (eval (read (buffer-string)))))
          (setq org-caldav-oauth2-client-id (car var)
                org-caldav-oauth2-client-secret (cadr var)))
        (kill-buffer))))

  ;; (setq org-caldav-url 'google
  ;;       org-caldav-calendar-id "jqeua8pamjrclakq3bg8mpnlis@group.calendar.google.com"
  ;;       org-caldav-inbox "~/MEGA/org/agenda/test.org"
  ;;       org-caldav-files '("~/MEGA/org/agenda/agenda.org")
  ;;       org-icalendar-include-todo nil
  ;;       org-icalendar-include-sexps t
  ;;       org-icalendar-categories '(all-tags category)
  ;;       org-icalendar-use-deadline '(event-if-todo event-if-not-todo todo-due)
  ;;       org-icalendar-use-scheduled '(event-if-todo event-if-not-todo todo-start)
  ;;       org-icalendar-with-timestamps nil
  ;;       org-caldav-delete-org-entries 'never)

  (setq org-caldav-url "https://99.57.234.31/remote.php/dav/calendars/bchu"
        org-caldav-calendar-id "orgmode"
        org-caldav-inbox "~/MEGA/org/agenda/test.org"
        org-caldav-files '("~/MEGA/org/agenda/agenda.org" "~/MEGA/org/agenda/classes_caldav_workaround.org"))

  (setq org-icalendar-alarm-time 30
        org-icalendar-include-todo nil
        org-icalendar-include-sexps t
        org-icalendar-categories '(all-tags category)
        org-icalendar-use-deadline '(event-if-todo event-if-not-todo todo-due)
        org-icalendar-use-scheduled '(todo-start event-if-todo)
        org-icalendar-with-timestamps nil
        org-caldav-delete-org-entries 'never)

  (setq org-caldav-skip-conditions '(nottodo ("TODO" "NEXT"))
        org-caldav-exclude-tags '("ARCHIVE" "_nosync_"))
#+END_SRC
** Reveal.js
#+begin_src
  (use-package org-re-reveal)
  (setq org-re-reveal-root "file:///home/benson/.reveal.js")
#+end_src
*** Old ox-reveal package
#+BEGIN_SRC
  (add-to-list 'load-path
               (ef "submodule/org-reveal"))
  (require 'ox-reveal)
  (setq org-reveal-root "file:///home/benson/.reveal.js")
  (setq org-structure-template-alist (remove-if (lambda (c) (string= (car c) "n")) org-structure-template-alist))
#+END_SRC
** org-timeline
#+BEGIN_SRC emacs-lisp
  (use-package org-timeline)
  (remove-hook 'org-agenda-finalize-hook 'org-timeline-insert-timeline)
#+END_SRC
** Code-blocks
#+BEGIN_SRC emacs-lisp
  (require 'ob-core)
  (require 'ob-clojure)
  (require 'ob-plantuml)
  (use-package plantuml-mode)

  (setq org-babel-clojure-backend 'cider)
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((clojure . t)
     (plantuml . t)
     (emacs-lisp . t)
     (shell . t)
     (dot . t)))

  (defun my-org-confirm-babel-evaluate (lang body)
    (not (member lang '("plantuml"))))

  (setq org-confirm-babel-evaluate 'my-org-confirm-babel-evaluate)
  (setq org-plantuml-jar-path "/usr/share/java/plantuml/plantuml.jar")
#+END_SRC
** helm-org-rifle
#+begin_src emacs-lisp
  (use-package helm-org-rifle)
  (global-set-key (kbd "C-c o r") 'helm-org-rifle)
  (setq helm-org-rifle-test-against-path t)
#+end_src
** org-mru-clock
#+begin_src emacs-lisp
  (use-package org-mru-clock)
#+end_src
** org-clock-convenience
#+begin_src emacs-lisp
  (defun my/org-clock-move-to-other ()
    (interactive)
    (forward-char 6)
    (while (condition-case nil
               (progn
                 (previous-line)
                 (org-clock-convenience-goto-ts)
                 nil)
             (error t))))

  (defun my/org-clock-move-up ()
    (interactive)
    (org-clock-convenience-timestamp-up)
    (my/org-clock-move-to-other)
    (org-clock-convenience-timestamp-up))

  (use-package org-clock-convenience
    :ensure t
    :bind (:map org-agenda-mode-map
                ("<S-up>" . org-clock-convenience-timestamp-up)
                ("<S-down>" . org-clock-convenience-timestamp-down)
                ("<S-M-up>" . org-clock-convenience-timestamp-up)
                ("<S-M-down>" . org-clock-convenience-timestamp-down)
                ("ö" . org-clock-convenience-fill-gap)
                ("é" . org-clock-convenience-fill-gap-both)))
#+end_src
** org-clock-consisitency
#+begin_src emacs-lisp
  (setq org-agenda-clock-consistency-checks
        '(:max-duration "10:00"
                        :min-duration 0
                        :max-gap 0
                        :gap-ok-around ("4:00")
                        ;; :default-face ((:background "DarkRed")
                        ;;                (:foreground "white"))
                        ;; :overlap-face nil
                        ;; :gap-face ((:background "DarkRed")
                        ;;            (:foreground "white"))
                        ;; :no-end-time-face nil
                        ;; :long-face nil
                        ;; :short-face nil
                        ))
#+end_src
** org-clock stuff
#+begin_src emacs-lisp
  (org-clock-persistence-insinuate)
  (setq org-clock-in-resume t)
  (setq org-clock-mode-line-total 'today)
  (setq org-clock-persist t)
  (org-clock-persistence-insinuate)
  (setq org-clock-continuously t)
#+end_src
** org-brain
#+begin_src
  (use-package org-brain :ensure t
    :init
    (global-set-key (kbd "M-'") 'org-brain-visualize)
    (setq org-brain-path "~/MEGA/org/brain/")
    ;; For Evil users
    (with-eval-after-load 'evil
      (evil-set-initial-state 'org-brain-visualize-mode 'emacs))
    :config
    (setq org-id-track-globally t)
    (setq org-id-locations-file (ef ".org-id-locations"))
    (push '("b" "Brain" plain (function org-brain-goto-end)
            "* %i%?" :empty-lines 1)
          org-capture-templates)
    (setq org-brain-visualize-default-choices 'all)
    (setq org-brain-title-max-length 0)
    (define-key org-brain-visualize-mode-map (kbd "^") 'org-brain-visualize-back))
#+end_src
** Open links with firefox
#+begin_src emacs-lisp
  (when (not my-ec/is-wsl)
    (setq browse-url-browser-function 'browse-url-firefox))
#+end_src
** org-export
#+begin_src emacs-lisp
  (require 'ox-latex)
  (require 'ox-beamer)
#+end_src

** org-jira
#+BEGIN_SRC emacs-lisp
  (use-package org-jira)
  (setq jiralib-url "https://wenningbai.atlassian.net/")
#+END_SRC
** org-now
#+begin_src emacs-lisp
  (add-to-list 'load-path (ef "submodule/org-now"))
  (require 'org-now)
  (setq org-now-location
        nil)
#+end_src
* More alternative views
#+begin_src emacs-lisp
  (defun cfw:open-org-calendar-no-projects (&args)
    "Open an org schedule calendar in the new buffer."
    (interactive)
    (save-excursion
      (let ((buf (get-buffer "*cfw-calendar*")))
        (if buf
            (switch-to-buffer buf)
          (let* ((org-agenda-skip-function 'my/agenda-custom-skip)
                 (source1 (cfw:org-create-source))
                 (curr-keymap (if cfw:org-overwrite-default-keybinding cfw:org-custom-map cfw:org-schedule-map))
                 (cp (cfw:create-calendar-component-buffer
                      :view 'two-weeks
                      :contents-sources (list source1)
                      :custom-map curr-keymap
                      :sorter 'cfw:org-schedule-sorter)))
            (switch-to-buffer (cfw:cp-get-buffer cp))
            (set (make-variable-buffer-local 'org-agenda-skip-function)
                 'my/agenda-custom-skip)
            (when (not org-todo-keywords-for-agenda)
              (message "Warn : open org-agenda buffer first.")))
          ))))
#+end_src
* Stuff                                                                 :FIX:
#+begin_src
    (setq org-agenda-tags-todo-honor-ignore-options t)

    (defun bh/org-auto-exclude-function (tag)
      "Automatic task exclusion in the agenda with / RET"
      (when (string= tag "online")
        (concat "-" tag)))

    (org-defkey org-agenda-mode-map
                "A"
                'org-agenda)

    (setq org-agenda-auto-exclude-function 'bh/org-auto-exclude-function)
    (setq org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled)
    (setq org-agenda-skip-scheduled-if-deadline-is-shown nil)
    (setq org-agenda-log-mode-items '(clock closed))

    (defun org-agenda-add-separater-between-project ()
      (setq buffer-read-only nil)
      (save-excursion
        (goto-char (point-min))
        (let ((start-pos (point))
              (previous t))
          (re-search-forward " +agenda: +[^\\. ]" nil t)
          (while (re-search-forward " +agenda: +[^\\. ]" nil t)
            (beginning-of-line)
            (insert "=============================================\n")
            (forward-line)))))

    ;; I don't think this code is necessary
    ;; (add-to-list 'org-agenda-entry-types :deadlines*)

    (setq org-agenda-hide-tags-regexp "NOT_TASKS\\|PROJECT")

    (use-package htmlize)
    (org-super-agenda-mode)
    (setq org-super-agenda-header-separator "")
#+end_src
* Checkbox hack
#+BEGIN_SRC emacs-lisp
  (defun my/org-checkbox-todo ()
    "Switch header TODO state to DONE when all checkboxes are ticked, to TODO otherwise"
    (let ((todo-state (org-get-todo-state)) beg end)
      (unless (not todo-state)
        (save-excursion
          (org-back-to-heading t)
          (setq beg (point))
          (end-of-line)
          (setq end (point))
          (goto-char beg)
          (if (re-search-forward "\\[\\([0-9]*%\\)\\]\\|\\[\\([0-9]*\\)/\\([0-9]*\\)\\]"
                                 end t)
              (if (match-end 1)
                  (if (equal (match-string 1) "100%")
                      (unless (string-equal todo-state "DONE")
                        (org-todo 'done))
                    (unless (string-equal todo-state "TASK")
                      (org-todo 'todo)))
                (if (and (> (match-end 2) (match-beginning 2))
                         (equal (match-string 2) (match-string 3)))
                    (unless (string-equal todo-state "DONE")
                      (org-todo 'done))
                  (unless (string-equal todo-state "TASK")
                    (org-todo "TASK")))))))))

  (add-hook 'org-checkbox-statistics-hook 'my/org-checkbox-todo)
#+END_SRC
* View org files
#+BEGIN_SRC emacs-lisp
  (defun make-org-file (filename)
    "Make an org buffer in folder for all new incoming org files"
    (interactive "MName: ")
    (switch-to-buffer (find-file-noselect (concat "~/MEGA/org/random/" filename ".org"))))

  (defun make-encrypted-org-file (filename)
    (interactive "MName: ")
    (switch-to-buffer (find-file-noselect (concat "~/MEGA/org/random/" filename ".gpg")))
    (insert "# -*- mode:org; epa-file-encrypt-to: (\"bensonchu457@gmail.com\") -*-\n\n")
    (org-mode))


  (defun view-org-files ()
    "Convenient way for openning up org folder in dired"
    (interactive)
    (dired "~/MEGA/org/"))
#+END_SRC
* Parallel org-tags-views
#+begin_src emacs-lisp
  ;; TODO
#+end_src
* refile to datetree
#+begin_src emacs-lisp
  (defun my/org-read-datetree-date (d)
    "Parse a time string D and return a date to pass to the datetree functions."
    (let ((dtmp (nthcdr 3 (parse-time-string d))))
      (list (cadr dtmp) (car dtmp) (caddr dtmp))))

  (defun my/org-refile-to-archive-datetree (&optional bfn)
    "Refile an entry to a datetree under an archive."
    (interactive)
    (require 'org-datetree)
    (let* ((org-read-date-prefer-future nil)
           (bfn (or bfn (find-file-noselect (expand-file-name (my/agenda-file "datetree.org")))))
           (datetree-date (my/org-read-datetree-date (org-read-date t nil))))
      (org-refile nil nil (list nil (buffer-file-name bfn) nil
                                (with-current-buffer bfn
                                  (save-excursion
                                    (org-datetree-find-date-create datetree-date)
                                    (point))))))
    (setq this-command 'my/org-refile-to-journal))
#+end_src
* org-link use qutebrowser
#+begin_src emacs-lisp
  (defun my/browse-url-qutebrowser (url &optional new-window)
    (interactive)
    (start-process (concat "qutebrowser " url)
                   nil
                   "qutebrowser"
                   url))

  ;;(setq browse-url-browser-function #'my/browse-url-qutebrowser)
  ;;(setq browse-url-browser-function #'browse-url-firefox)
#+end_src
* new headline set property
#+begin_src emacs-lisp
  (defun my/org-set-created-property (&rest args)
    (when-let (f (buffer-file-name))
      (let ((fname (expand-file-name f)))
        (when (remove-if-not (lambda (x) (string= fname (expand-file-name x))) org-agenda-files)
          (let ((ts (format-time-string "[%Y-%m-%d %a %H:%M]")))
            (org-set-property "CREATED" ts))))))

  (advice-add #'org-insert-heading
              :after
              #'my/org-set-created-property)
#+end_src
* Code for deleting empty blocks
#+begin_src emacs-lisp
  (defvar my/delete-blocks t)

  (defun org-agenda-delete-empty-compact-blocks ()
    "Function removes empty compact blocks.
   If two lines next to each other have the
   org-agenda-structure face, then delete the
   previous block."
    (unless org-agenda-compact-blocks
      (user-error "Compact blocks must be on"))
    (when my/delete-blocks
      (setq buffer-read-only nil)
      (save-excursion
        (goto-char (point-min))
        (let ((start-pos (point))
              (previous nil))
          (while (not (eobp))
            (cond
             ((let ((face (get-char-property (point) 'face)))
                (or (eq face 'org-agenda-structure)
                    (eq face 'org-agenda-date-today)))
              (if previous
                  (delete-region start-pos
                                 (point))
                (setq start-pos (point)))
              (unless (org-agenda-check-type nil 'agenda)
                (setq previous t)))
             (t (setq previous nil)))
            (forward-line))))
      (setq buffer-read-only t)))

  (add-hook 'org-agenda-finalize-hook #'org-agenda-delete-empty-compact-blocks)
#+end_src
* Highlight top priority projects
#+begin_src emacs-lisp
  (defvar my/highlight-top-priority t)

  (defun org-agenda-highlight-top-priority ()
    (when my/highlight-top-priority
      (setq buffer-read-only nil)
      (save-excursion
        (goto-char (point-min))
        (let ((start-pos (point))
              (previous nil))
          (while (re-search-forward "\\[#A\\]" nil t)
            (add-face-text-property (point-at-bol) (point-at-eol) '(:background "color-19")))))
      (setq buffer-read-only t)))

  (add-hook 'org-agenda-finalize-hook #'org-agenda-highlight-top-priority)
#+end_src
* org-notmuch
#+begin_src emacs-lisp
  (use-package notmuch
    :config
    (use-package ol-notmuch))
#+end_src
* remove inherited tags
#+begin_src emacs-lisp
  (defun my/org-remove-inherited-tag-strings ()
    "Removes inherited tags from the headline-at-point's tag string.
  Note this does not change the inherited tags for a headline,
  just the tag string."
    (interactive)
    (org-set-tags (seq-remove (lambda (tag)
                                (get-text-property 0 'inherited tag))
                              (org-get-tags))))

  (defun my/org-clean-tags ()
    "Visit last refiled headline and remove inherited tags from tag string."
    (save-window-excursion
      (org-refile-goto-last-stored)
      (my/org-remove-inherited-tag-strings)))

  (defun my/org-refile-preserve-tags (orig &rest args)
    (let ((tags (org-get-tags)))
      (apply orig args)))

  (add-hook 'org-after-refile-insert-hook 'my/org-clean-tags)
#+end_src
* archive sibling remove sub archive sibling
#+begin_src emacs-lisp
  (defun my/is-archive-tree ()
    (and (string= "Archive"
                  (org-get-heading t t t t))
         (member "ARCHIVE" (org-get-tags))))

  (defun my/archive-remove-all-sibling (&rest args)
    (save-excursion
      (let (points)
        (ol/descendants
          (when (my/is-archive-tree)
            (push (point) points)))
        (mapcar (lambda (p)
                  (goto-char p)
                  (my/org-delete-promote))
                points))))

  (advice-add #'org-archive-to-archive-sibling
              :before
              #'my/archive-remove-all-sibling)
#+end_src
* Learning chinese, setup org-drill
#+begin_src emacs-lisp
  (use-package org-drill)

  (defun org-drill-present-one-side-always (session)
    (org-drill-with-hidden-comments
     (org-drill-with-hidden-cloze-hints
      (org-drill-with-hidden-cloze-text
       (let ((drill-sections (org-drill-hide-all-subheadings-except nil)))
         (when drill-sections
           (save-excursion
             (goto-char (nth 0 drill-sections))
             (org-show-subtree)))
         (org-drill--show-latex-fragments)
         (ignore-errors
           (org-display-inline-images t))
         (org-cycle-hide-drawers 'all)
         (prog1 (org-drill-presentation-prompt session)
           (org-drill-hide-subheadings-if 'org-drill-entry-p)))))))

  (add-to-list 'org-drill-card-type-alist
               '("oneside" org-drill-present-one-side-always nil t))

  ;; (pop org-drill-card-type-alist)
#+end_src
* Insert inactive timestamp after last org-datetree--find-create
#+begin_src emacs-lisp
  (defun org-datetree--find-create-add-timestamp (&rest args)
    (save-excursion
      (when day
        (let ((lnum (line-number-at-pos)))
          (next-line)
          (when (= lnum (line-number-at-pos))
            (end-of-line)
            (insert "\n")))
        (unless (looking-at-p (rx "[" (repeat 4 digit) "-" (repeat 2 digit) "-"
                                  (repeat 2 digit) " " (repeat 3 alpha) "]"))
          (insert (format-time-string "[%Y-%m-%d %a]"))))))

  (advice-add #'org-datetree--find-create
              :after
              #'org-datetree--find-create-add-timestamp)
#+end_src

* I'm bored
#+begin_src emacs-lisp
  (defun im-bored ()
    (interactive)
    (org-ql-search (append org-agenda-files
                           (list (my/agenda-file "when_im_bored.org")
                                 (my/agenda-file "eternal.org")))
      '(and (tags-local "bored"))))
#+end_src
* org-noter
#+begin_src emacs-lisp
  (use-package org-noter
    :config
    ;; (unless (eq 'hash-table (type-of face-new-frame-defaults))
    ;;   (require 'face-copier)
    ;;   (def-face-copier x-show-tip-faces (sym)
    ;;     nil
    ;;     tooltip)

    ;;   (defun dont-copy-faces-for-x-show-tip (orig &rest args)
    ;;     (override1-face x-show-tip-faces
    ;;       (apply orig args)))

    ;;   (advice-add #'x-show-tip
    ;;               :around
    ;;               #'dont-copy-faces-for-x-show-tip))
    )

  (use-exwm
    :config
    (setq org-noter-always-create-frame nil))
#+end_src
* turn into tickle
#+begin_src emacs-lisp
  (defun my/tickle-todo ()
    (interactive)
    (org-agenda-todo "TICKLER")
    (org-agenda-schedule))

  (define-key org-agenda-mode-map (kbd "T") #'my/tickle-todo)
#+end_src
* org-wiki                                                              :FIX:
#+begin_src
  (require 'org-wiki)
  (setq org-wiki-location-list
        '("~/MEGA/org/wiki"))

  (setq org-wiki-template
        "#+TITLE: %n\n#+DESCRIPTION:\n#+KEYWORDS:\n#+STARTUP:  content\n\n\n- Related: \n\n* Backlinks\n#+STARTUP: folded\n\n* %n\n")

  (defun org-wiki-insert-backlink (back-from back-to)
    (let ((wiki-link (format "[[wiki:%s][%s]]"
                             back-to back-to))
          (file (save-window-excursion (org-wiki--open-page back-from)
                                       (current-buffer))))
      (with-current-buffer file
        (beginning-of-buffer)
        (when (not (save-excursion
                     (search-forward wiki-link nil t)))
          (search-forward "* Backlinks")
          (forward-line 2)
          (beginning-of-line)
          (insert "- " wiki-link "\n")
          (save-buffer)))))


  (defun my/org-wiki-insert-new ()
    (interactive)
    (let ((page-name (read-string  "Page: ")))
      (save-excursion (insert (org-make-link-string (concat "wiki:" page-name)
                                                    page-name
                                                    )))
      (org-wiki-insert-backlink
       page-name
       (org-wiki--current-page))))

  (advice-add #'org-wiki-insert-new
              :override
              #'my/org-wiki-insert-new)

  (defun my/org-wiki-insert-link ()
    "Insert a Wiki link at point for a existing page."
    (interactive)
    (org-wiki--helm-selection
     (lambda (page)
       (insert (org-wiki--make-link page))
       (org-wiki-insert-backlink
        page
        (org-wiki--current-page)))))

  (advice-add #'org-wiki-insert-link
              :override
              #'my/org-wiki-insert-link)
#+end_src
* Variable pitch org-mode
#+begin_src emacs-lisp
  ;; (mapcar
  ;;  (lambda (face)
  ;;    (set-face-attribute face nil :inherit 'fixed-pitch))
  ;;  '(org-block org-block-begin-line org-block-end-line org-code
  ;;              org-document-info-keyword org-done org-formula org-indent
  ;;              org-meta-line org-special-keyword org-table org-todo
  ;;              org-verbatim org-date org-drawer))
#+end_src
* Restriction from org-agenda
#+begin_src emacs-lisp
  (define-key org-agenda-mode-map (kbd "N") #'(lambda ()
                                                (interactive)
                                                (org-agenda-set-restriction-lock-from-agenda nil)
                                                (org-agenda-redo)))
  (define-key org-agenda-mode-map (kbd "U") #'org-agenda-remove-restriction-lock)
#+end_src
* Org mode magit update commands
#+begin_src
  (when (executable-find "~/bin/gitwatch")
    (defun start-gitwatch ()
      (interactive)
      (if (and gitwatch-process
               (process-live-p gitwatch-process))
          (message "gitwatch already exists")
        (setq gitwatch-process
              (start-process-shell-command "gitwatch"
                                           nil
                                           "~/bin/gitwatch -r origin -b laptop -m 'Gitwatch commit: %d' ~/MEGA/org/agenda"))))
    (defun kill-gitwatch ()
      (interactive)
      (when (and gitwatch-process
                 (process-live-p gitwatch-process))
        (kill-process gitwatch-process)))

    (defvar gitwatch-process nil)

    (add-to-list 'emacs-startup-hook
                 #'start-gitwatch))

  (defvar magit-sentinel-after-function nil)

  (defun magit-sentinel-after (&rest args)
    (while
        (when-let (fun (pop magit-sentinel-after-function))
          (funcall fun)
          t)))

  (advice-add #'magit-process-sentinel
              :after
              #'magit-sentinel-after)

  (defun org-update-main ()
    (interactive)
    (let ((default-directory "~/MEGA/org/agenda"))
      (kill-gitwatch)
      ;; Make sure local changes are committed
      (when (magit-changed-files "HEAD")
        (magit-stage-modified t)
        (magit-commit-create `("-m" ,(format "Pre-merge commit: %s" (format-time-string "%D %T")))))
      ;; Update all submodules
      (shell-command "git submodule foreach git pull origin master")
      (when (magit-changed-files "HEAD")
        (magit-stage-modified t)
        (magit-commit-create `("-m" ,(format "Updated submodules: %s" (format-time-string "%D %T")))))
      ;; Do a fetch
      (push #'org-update-initiate-merge magit-sentinel-after-function)
      (magit-run-git-async '("fetch" "--all"))))

  (defun org-update-initiate-merge ()
    ;; Check if mobile has updated
    (let ((base (vc-git-mergebase "laptop" "origin/mobile"))
          (laptop (vc-git--rev-parse "laptop"))
          (origin-mobile (vc-git--rev-parse "origin/mobile")))
      ;; If so, turn off gitwatch and initiate a merge.
      (when (and (not (string= base laptop))
                 (not (string= base origin-mobile)))
        (add-hook 'git-commit-post-finish-hook ;;magit-post-commit-hook
                  #'org-update-post-commit)
        (magit-merge-plain "origin/mobile"))))

  (defun org-update-post-commit ()
    ;; After merge, do a push to both laptop and mobile
    ;; Also restart gitwatch
    (call-interactively #'magit-push-current-to-upstream)
    (magit-push-current "origin/mobile" nil)
    (start-gitwatch)
    (remove-hook 'git-commit-post-finish-hook
                 #'org-update-post-commit))

  (magit-run-git-async '("fetch" "--all"))

  ;; (setq debug-the-process (magit-run-git-async '("fetch" "--all")
  ;;                                              :sentinel '(lambda (process event) (message "Done"))))
#+end_src
* elgantt
#+begin_src emacs-lisp
  (add-to-list 'load-path (ef "submodule/elgantt"))
  (require 'elgantt)
#+end_src
* org-kanban
#+begin_src emacs-lisp
  (use-package org-kanban)
  (defun my/org-dblock-write:kanban (params)
    "Create the kanban dynamic block.
  PARAMS may contain `:mirrored`, `:match`, `:scope`, `:layout`, `:range`, `:depth` and `:compressed`."
    (insert
     (let*
         (
          (mirrored (plist-get params :mirrored))
          (compressed (plist-get params :compressed))
          (match (plist-get params :match))
          (range (plist-get params :range))
          (depth (org-kanban--params-depth params))
          (layout (org-kanban//params-layout params))
          (files (org-kanban//params-files params))
          (scope (org-kanban//params-scope params files))
          (todo-keywords (split-string (plist-get params :todo) "|"))
          (sort-spec-string (plist-get params :sort))
          (sort-spec (org-kanban--prepare-comparator sort-spec-string todo-keywords))
          (todo-infos (org-map-entries 'org-kanban//todo-info-extract match scope))
          (sorted-todo-infos (if sort-spec (-sort sort-spec todo-infos) todo-infos))
          (filtered-todo-infos (-filter (lambda (todo-info)
                                          (org-kanban//range-fun
                                           (org-kanban--todo-info-get-keyword todo-info)
                                           (org-kanban//todo-info-get-keywords todo-info)
                                           (car range)
                                           (cdr range)))
                                        sorted-todo-infos))
          (filtered-todo-infos (-filter (lambda (todo-info)
                                          (if (eq scope 'tree)
                                              (let* (
                                                     (tree-info (nth 0 todo-infos))
                                                     (tree-level (org-kanban--todo-info-get-level tree-info)))
                                                (< (org-kanban--todo-info-get-level todo-info) (+ depth tree-level)))
                                            (<= (org-kanban--todo-info-get-level todo-info) depth))) filtered-todo-infos))
          (filtered-todo-infos (-filter (lambda (todo-info) (nth 4 (org-kanban//todo-info-get-heading todo-info))) filtered-todo-infos))
          (row-for (lambda (todo-info) (org-kanban//row-for todo-info todo-keywords layout)))
          (table-title (string-join todo-keywords "|"))
          (filtered (-filter (lambda (todo-info)
                               (-intersection
                                (list (org-kanban//heading-get-todo-keyword (org-kanban//todo-info-get-heading todo-info)))
                                (org-kanban//todo-info-get-keywords todo-info)))
                             filtered-todo-infos))
          (table (if compressed
                     (org-kanban//compressed-rows (-map row-for filtered))
                   (let* ((rows (-map row-for filtered)))
                     (if rows
                         (--reduce (format "%s\n%s" acc it) rows)
                       ""
                       )))))
       (format "|%s|\n|--|\n%s" table-title table)))
    (org-table-align))

  (advice-add #'org-dblock-write:kanban
              :override
              #'my/org-dblock-write:kanban)
#+end_src
* Modern views
#+begin_src emacs-lisp
  (quelpa `(org-timeblock :repo "ichernyshovvv/org-timeblock" :fetcher github))
  (use-package org-hyperscheduler)
#+end_src
* Pretty symbols
#+begin_src emacs-lisp
  ;; (add-hook 'org-mode-hook (lambda ()
  ;;  "Beautify Org Checkbox Symbol"
  ;;  (push '("[ ]" .  "☐") prettify-symbols-alist)
  ;;  (push '("[X]" . "☑" ) prettify-symbols-alist)
  ;;  (push '("[-]" . "❍" ) prettify-symbols-alist)
  ;;  (prettify-symbols-mode)))
#+end_src
