;;; slack-dialog-buffer.el --- -*- lexical-binding: t; -*- ;; Copyright (C) 2018 南優也 ;; Author: 南優也 ;; Keywords: ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; ;;; Code: (require 'eieio) (require 'slack-buffer) (require 'slack-dialog) (define-derived-mode slack-dialog-buffer-mode fundamental-mode "Slack Dialog Buffer" (setq-local default-directory slack-default-directory) (setq-local buffer-read-only t)) (defvar slack-dialog-submit-button-map (let ((map (make-sparse-keymap))) (define-key map (kbd "RET") #'slack-dialog-buffer-submit) (define-key map [mouse-1] #'slack-dialog-buffer-submit) map)) (defvar slack-dialog-cancel-button-map (let ((map (make-sparse-keymap))) (define-key map (kbd "RET") #'slack-dialog-buffer-cancel) (define-key map [mouse-1] #'slack-dialog-buffer-cancel) map)) (defvar slack-dialog-select-element-map (let ((map (make-sparse-keymap))) (define-key map (kbd "RET") #'slack-dialog-buffer-select) (define-key map [mouse-1] #'slack-dialog-buffer-select) map)) (defface slack-dialog-element-placeholder-face '((t (:inherit font-lock-comment-face :slant normal ;; :box (:line-width 1 :color "#fff") ))) "Used to dialog's element placeholder" :group 'slack) (defface slack-dialog-element-error-face '((t (:inherit font-lock-warning-face))) "Used to dialog's element error message" :group 'slack) (defface slack-dialog-element-hint-face '((t (:inherit font-lock-comment-face :slant italic))) "Used to dialog's element hint" :group 'slack) (defface slack-dialog-element-label-face '((t (:weight bold))) "Used to dialog's element label" :group 'slack) (defface slack-dialog-select-element-input-face '((t (:box (:line-width 1 :style released-button)))) "Used to dialog's select element input" :group 'slack) (defface slack-dialog-title-face '((t (:weight bold :height 1.2))) "Used to dialog's title" :group 'slack) (defface slack-dialog-submit-button-face '((t (:box (:line-width 1 :style released-button) :foreground "#2aa198"))) "Used to dialog's submit button" :group 'slack) (defface slack-dialog-cancel-button-face '((t (:box (:line-width 1 :style released-button)))) "Used to dialog's cancel button" :group 'slack) (defclass slack-dialog-buffer (slack-buffer) ((dialog-id :initarg :dialog-id :type string) (dialog :initarg :dialog :type slack-dialog))) (defmethod slack-buffer-name :static ((_class slack-dialog-buffer) dialog-id dialog team) (with-slots (title) dialog (format "*Slack Dialog - %s [%s] : %s*" title dialog-id (slack-team-name team)))) (defmethod slack-buffer-name ((this slack-dialog-buffer)) (with-slots (dialog-id dialog team) this (slack-buffer-name 'slack-dialog-buffer dialog-id dialog team))) (defmethod slack-buffer-find :static ((class slack-dialog-buffer) dialog-id dialog team) (slack-buffer-find-4 class dialog-id dialog team)) (defmethod slack-buffer-insert-label ((this slack-dialog-element)) (with-slots (label optional) this (insert (propertize label 'face 'slack-dialog-element-label-face)) (when optional (insert " (optional)")))) (defmethod slack-buffer-insert-hint ((this slack-dialog-text-element)) (with-slots (hint) this (when hint (insert "\n") (insert (propertize hint 'face 'slack-dialog-element-hint-face)) (insert "\n")))) (defvar slack-dialog-element-edit-button-map (let ((map (make-sparse-keymap))) (define-key map (kbd "RET") #'slack-dialog-buffer-open-edit-element-buffer) (define-key map [mouse-1] #'slack-dialog-buffer-open-edit-element-buffer) map)) (defmethod slack-buffer-insert-edit-button ((this slack-dialog-text-element)) (insert (propertize " Edit " 'face '(:box (:line-width 1 :style released-button)) 'keymap slack-dialog-element-edit-button-map 'slack-dialog-element this))) (defun slack-dialog-buffer-open-edit-element-buffer () (interactive) (slack-if-let* ((element (get-text-property (point) 'slack-dialog-element)) (buffer slack-current-buffer) (team (oref buffer team)) (edit-buffer (slack-create-dialog-element-edit-buffer buffer element team))) (slack-buffer-display edit-buffer))) (defmethod slack-buffer-insert-placeholder ((this slack-dialog-text-element)) (with-slots (placeholder) this (insert (propertize placeholder 'face 'slack-dialog-element-placeholder-face)))) (defmethod slack-buffer-insert-errors ((this slack-dialog-element)) (with-slots (errors) this (mapc #'(lambda (err) (insert (propertize (oref err error-message) 'face 'slack-dialog-element-error-face)) (insert "\n")) errors))) (defmethod slack-buffer-insert ((this slack-dialog-text-element)) (with-slots (value placeholder errors) this (slack-buffer-insert-label this) (insert " ") (slack-buffer-insert-edit-button this) (insert "\n") (if value (insert value) (if placeholder (slack-buffer-insert-placeholder this) (insert "Click Edit"))) (insert "\n") (slack-buffer-insert-errors this) (slack-buffer-insert-hint this))) (defmethod slack-buffer-insert ((this slack-dialog-textarea-element)) (with-slots (value placeholder) this (slack-buffer-insert-label this) (insert " ") (slack-buffer-insert-edit-button this) (insert "\n") (if value (insert value) (if placeholder (slack-buffer-insert-placeholder this) (insert "Click Edit"))) (insert "\n") (slack-buffer-insert-errors this) (slack-buffer-insert-hint this))) (defun slack-dialog-buffer-select () (interactive) (slack-if-let* ((buffer slack-current-buffer) (team (oref buffer team)) (dialog (oref buffer dialog)) (dialog-id (oref buffer dialog-id)) (element-name (get-text-property (point) 'slack-dialog-element-name)) (dialog-element (cl-find-if #'(lambda (el) (string= element-name (oref el name))) (oref dialog elements))) (selected (slack-dialog--execute dialog-element dialog-id team)) (label (car selected)) (value (cdr selected)) (option (make-instance 'slack-dialog-select-option :label label :value value))) (progn (oset dialog-element selected-options (list option)) (oset dialog-element value value) (slack-dialog-buffer-redisplay buffer)))) (defmethod slack-buffer-insert-select-button ((this slack-dialog-select-element)) (let ((label (slack-if-let* ((selected (slack-dialog-selected-option this))) (slack-selectable-text selected) "Choose an option..."))) (insert (propertize (format " %s " label) 'face 'slack-dialog-select-element-input-face 'keymap slack-dialog-select-element-map 'slack-dialog-element-name (oref this name))))) (defmethod slack-buffer-insert ((this slack-dialog-select-element)) (slack-buffer-insert-label this) (insert "\n") (slack-buffer-insert-select-button this) (insert "\n") (slack-buffer-insert-errors this)) (defun slack-dialog-buffer-submit () (interactive) (slack-if-let* ((buffer slack-current-buffer)) (slack-dialog-buffer--submit buffer))) (defmethod slack-dialog-buffer--submit ((this slack-dialog-buffer)) (with-slots (dialog dialog-id team) this (with-slots (elements) dialog (dolist (element elements) (let ((value (slack-dialog-element-value element))) (slack-dialog-element-validate element value))) (let ((params (mapcar #'(lambda (element) (cons (oref element name) (slack-dialog-element-value element))) elements))) (cl-labels ((create-dialog-element-error (payload) (make-instance #'slack-dialog-element-error :name (plist-get payload :name) :error-message (plist-get payload :error))) (set-dialog-element-error (dialog-error elements) (slack-if-let* ((element (cl-find-if #'(lambda (el) (string= (oref el name) (oref dialog-error name))) elements)) (new-errors (cons dialog-error (cl-remove-if #'(lambda (e) (string= (oref e name) (oref dialog-error name))) (oref element errors))))) (oset element errors new-errors))) (after-success (data) (slack-if-let* ((err (plist-get data :error))) (progn (oset dialog error-message err) (dolist (dialog-error (mapcar #'create-dialog-element-error (plist-get data :dialog_errors))) (set-dialog-element-error dialog-error elements)) (slack-dialog-buffer-redisplay this)) (slack-dialog-buffer-kill-buffer this)))) (slack-dialog-clear-errors dialog) (slack-dialog--submit dialog dialog-id team params #'after-success)))))) (defun slack-dialog-buffer-cancel () (interactive) (slack-if-let* ((buffer slack-current-buffer)) (with-slots (dialog dialog-id team) buffer (slack-dialog-notify-cancel dialog dialog-id team) (slack-dialog-buffer-kill-buffer buffer)))) (defmethod slack-dialog-buffer-kill-buffer ((this slack-dialog-buffer)) (slack-if-let* ((buffer-name (slack-buffer-name this)) (buf (get-buffer buffer-name)) (win (get-buffer-window buf))) (progn (kill-buffer buf) (when (< 1 (count-windows)) (delete-window win))))) (defmethod slack-buffer-insert ((this slack-dialog-buffer)) (with-slots (dialog) this (with-slots (error-message title elements submit-label) dialog (let ((inhibit-read-only t)) (insert (propertize title 'face 'slack-dialog-title-face)) (when error-message (insert "\n") (insert (propertize error-message 'face 'slack-dialog-element-error-face))) (insert "\n\n") (mapc #'(lambda (el) (slack-buffer-insert el) (insert "\n")) elements) (insert "\n") (insert (propertize " Cancel " 'face 'slack-dialog-cancel-button-face 'keymap slack-dialog-cancel-button-map)) (insert "\t") (insert (propertize (format " %s " submit-label) 'face 'slack-dialog-submit-button-face 'keymap slack-dialog-submit-button-map)) (goto-char (point-min)))))) (defmethod slack-buffer-init-buffer ((this slack-dialog-buffer)) (let* ((buf (generate-new-buffer (slack-buffer-name this))) (dialog (oref this dialog)) (dialog-id (oref this dialog-id)) (team (oref this team))) (with-current-buffer buf (slack-dialog-buffer-mode) (slack-buffer-set-current-buffer this) (slack-buffer-insert this)) (slack-buffer-push-new-4 'slack-dialog-buffer dialog-id dialog team))) (defun slack-create-dialog-buffer (dialog-id dialog team) (slack-if-let* ((buf (slack-buffer-find 'slack-dialog-buffer dialog-id dialog team))) buf (make-instance 'slack-dialog-buffer :dialog-id dialog-id :dialog dialog :team team))) (defmethod slack-dialog-buffer-save-element-value ((this slack-dialog-buffer) name value) (with-slots (dialog) this (with-slots (elements) dialog (let ((element (cl-find-if #'(lambda (el) (string= name (oref el name))) elements))) (oset element value value) (slack-dialog-buffer-redisplay this))))) (defmethod slack-dialog-buffer-redisplay ((this slack-dialog-buffer)) (slack-if-let* ((bufname (slack-buffer-name this)) (buf (get-buffer bufname))) (with-current-buffer buf (let ((inhibit-read-only t) (cur-point (point))) (delete-region (point-min) (point-max)) (slack-buffer-insert this) (when (and (< (point-min) cur-point) (< cur-point (point-max))) (goto-char cur-point)))))) (provide 'slack-dialog-buffer) ;;; slack-dialog-buffer.el ends here