desktop.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. // package desktop -- консольный клиент для вар-танка
  2. package desktop
  3. import (
  4. "fmt"
  5. "os"
  6. "time"
  7. "github.com/charmbracelet/bubbles/textinput"
  8. tea "github.com/charmbracelet/bubbletea"
  9. "wartank/desktop/client_serv"
  10. )
  11. // Desktop -- консольный клиент для вар-танка
  12. type Desktop struct {
  13. TeaModel
  14. p *tea.Program // Объект приложения
  15. client *client_serv.ClientServ // Клиент подключния к бото-серверу
  16. isConnect bool // Признак подключения к бот-серверу
  17. isLogin bool // Признак логина на сервер
  18. isMakeAdmin bool // Признак создания админа
  19. }
  20. var desk *Desktop
  21. func NewDesktop() *Desktop {
  22. sf := &Desktop{
  23. client: client_serv.NewClientServ(),
  24. }
  25. sf.TeaModel = NewModel()
  26. go sf.tick()
  27. sf.p = tea.NewProgram(sf, tea.WithAltScreen(), tea.WithANSICompressor())
  28. desk = sf
  29. if _, err := sf.p.Run(); err != nil {
  30. fmt.Printf("Опа! здесь какая-то ошибочка: %v", err)
  31. os.Exit(1)
  32. }
  33. return sf
  34. }
  35. // AMode -- режим обработки клавиш
  36. type AMode int
  37. const (
  38. ModeDefault AMode = iota
  39. ModeConfig // Режим конфигурирования
  40. ModeConfigServer // Режим конфигуррования сервера
  41. ModeInputHostServer // Режим ввода хоста сервера
  42. ModeInputLoginServer // Режим ввода логина на сервер
  43. ModeInputPassServer // Режим ввода пароля входа на сервер
  44. ModeConnectServer // Режим подключения к серверу
  45. ModeLoginServer // Режим логина на сервер
  46. ModeServerMakeAdmin // Создать админа для бот-сервера
  47. )
  48. type TeaModel struct {
  49. choices []string // выбранный пункт в списке
  50. cursor int // позиция курсора в списке
  51. selected map[int]struct{} // что выбрано из списка
  52. mode AMode // Режим показа на экране
  53. textInput textinput.Model
  54. host string // Имя хоста сервера (с портом)
  55. login string // Логин для сервера
  56. pass string // Пароль для сервера
  57. }
  58. func NewModel() TeaModel {
  59. ti := textinput.New()
  60. ti.Placeholder = "localhost"
  61. ti.Focus()
  62. ti.CharLimit = 156
  63. ti.Width = 20
  64. m := TeaModel{
  65. // Список дел
  66. choices: []string{"Купить картошку", "Оплатить мобилку", "Заплатить за кредит"},
  67. selected: make(map[int]struct{}),
  68. textInput: ti,
  69. }
  70. return m
  71. }
  72. func (sf *Desktop) tick() {
  73. for {
  74. time.Sleep(time.Second * 1)
  75. msg := tea.MouseEvent{}
  76. // m.Update(msg)
  77. sf.p.Send(msg)
  78. }
  79. }
  80. // Run -- запускает в работу консольный клиент
  81. func (sf *Desktop) Run() error {
  82. return nil
  83. }
  84. func (sf Desktop) Init() tea.Cmd {
  85. // Просто вернуть `nil`, т.е. никаких команд
  86. return textinput.Blink
  87. }
  88. func (sf Desktop) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
  89. var cmd tea.Cmd
  90. switch msg := msg.(type) {
  91. // Может клавиша нажата
  92. case tea.KeyMsg:
  93. switch msg.String() { // Да, нажато, а что именно?
  94. case "ctrl+q": // Надо выйти из программы
  95. return sf, tea.Quit
  96. }
  97. }
  98. switch desk.mode {
  99. case ModeDefault:
  100. switch msg := msg.(type) {
  101. // Может клавиша нажата
  102. case tea.KeyMsg:
  103. switch msg.String() { // Да, нажато, а что именно?
  104. case "up", "k": // Если "up" или "k" перемещение курсора вверх
  105. if desk.cursor > 0 {
  106. desk.cursor--
  107. }
  108. case "down", "j": // Если "down" или "j" перемещение курсора вниз
  109. if desk.cursor < len(desk.choices)-1 {
  110. desk.cursor++
  111. }
  112. case "enter", " ": // Нажатие "enter" или "пробел" переключает выбранный элемент списка
  113. _, ok := desk.selected[desk.cursor]
  114. if ok {
  115. delete(desk.selected, desk.cursor)
  116. } else {
  117. desk.selected[desk.cursor] = struct{}{}
  118. }
  119. case "w": // Настройки графического клиента
  120. desk.mode = ModeConfig
  121. }
  122. }
  123. case ModeConfig:
  124. switch msg := msg.(type) {
  125. // Может клавиша нажата
  126. case tea.KeyMsg:
  127. switch msg.String() { // Да, нажато, а что именно?
  128. case "esc": // Вернуться на шаг назад
  129. desk.mode = ModeDefault
  130. case "1": // Выбор один
  131. desk.mode = ModeConfigServer
  132. case "2": // Вход на сервер
  133. if !desk.isConnect {
  134. desk.mode = ModeConnectServer
  135. }
  136. case "3": // Логин на сервер
  137. if !desk.isLogin {
  138. desk.mode = ModeLoginServer
  139. }
  140. case "4": // Создать админа
  141. desk.mode = ModeServerMakeAdmin
  142. }
  143. }
  144. case ModeConfigServer:
  145. switch msg := msg.(type) {
  146. // Может клавиша нажата
  147. case tea.KeyMsg:
  148. switch msg.String() { // Да, нажато, а что именно?
  149. case "esc": // Вернуться на шаг назад
  150. desk.mode = ModeConfig
  151. case "1": // Выбор хоста сервера
  152. desk.mode = ModeInputHostServer
  153. desk.textInput.Reset()
  154. desk.textInput.Placeholder = "localhost:18050"
  155. case "2": // Выбор логина для входа
  156. desk.mode = ModeInputLoginServer
  157. desk.textInput.Reset()
  158. desk.textInput.Placeholder = "login"
  159. case "3": // Выбор пароля для входа
  160. desk.mode = ModeInputPassServer
  161. desk.textInput.Reset()
  162. desk.textInput.Placeholder = "pass"
  163. }
  164. }
  165. case ModeInputHostServer:
  166. switch msg := msg.(type) {
  167. // Может клавиша нажата
  168. case tea.KeyMsg:
  169. switch msg.String() { // Да, нажато, а что именно?
  170. case "esc": // Вернуться на шаг назад
  171. desk.mode = ModeConfigServer
  172. case "enter": // Нажатие "enter" или "пробел" переключает выбранный элемент списка
  173. desk.host = desk.textInput.Value()
  174. desk.mode = ModeConfigServer
  175. default:
  176. desk.textInput, cmd = desk.textInput.Update(msg)
  177. }
  178. }
  179. case ModeInputLoginServer:
  180. switch msg := msg.(type) {
  181. // Может клавиша нажата
  182. case tea.KeyMsg:
  183. switch msg.String() { // Да, нажато, а что именно?
  184. case "esc": // Вернуться на шаг назад
  185. desk.mode = ModeConfigServer
  186. case "enter": // Нажатие "enter" или "пробел" переключает выбранный элемент списка
  187. desk.login = desk.textInput.Value()
  188. desk.mode = ModeConfigServer
  189. default:
  190. desk.textInput, cmd = desk.textInput.Update(msg)
  191. }
  192. }
  193. case ModeInputPassServer:
  194. switch msg := msg.(type) {
  195. // Может клавиша нажата
  196. case tea.KeyMsg:
  197. switch msg.String() { // Да, нажато, а что именно?
  198. case "esc": // Вернуться на шаг назад
  199. desk.mode = ModeConfigServer
  200. case "enter": // Нажатие "enter" или "пробел" переключает выбранный элемент списка
  201. desk.pass = desk.textInput.Value()
  202. desk.mode = ModeConfigServer
  203. default:
  204. desk.textInput, cmd = desk.textInput.Update(msg)
  205. }
  206. }
  207. case ModeConnectServer:
  208. switch msg := msg.(type) {
  209. // Может клавиша нажата
  210. case tea.KeyMsg:
  211. switch msg.String() { // Да, нажато, а что именно?
  212. case "esc": // Нажатие "esc" прекращает подключение к серверу
  213. desk.mode = ModeConfigServer
  214. }
  215. }
  216. case ModeLoginServer:
  217. switch msg := msg.(type) {
  218. // Может клавиша нажата
  219. case tea.KeyMsg:
  220. switch msg.String() { // Да, нажато, а что именно?
  221. case "esc": // Нажатие "esc" прекращает логин на сервер
  222. desk.mode = ModeConfig
  223. }
  224. }
  225. case ModeServerMakeAdmin:
  226. switch msg := msg.(type) {
  227. // Может клавиша нажата
  228. case tea.KeyMsg:
  229. switch msg.String() { // Да, нажато, а что именно?
  230. case "esc": // Нажатие "esc" прекращает логин на сервер
  231. desk.mode = ModeConfig
  232. }
  233. }
  234. }
  235. // Возвращает обновлённую модель дляe Bubble Tea runtime для обработки.
  236. //Здесь команду возвращать надо.
  237. return desk, cmd
  238. }
  239. func (sf Desktop) View() string {
  240. s := time.Now().Local().Format("[ WarTank ] = 2006-01-02 15:04:05.000")
  241. s += fmt.Sprintf("\tХост=%q\tЛогин=%q\tПароль=%q\tПодключено=%v\tЛогин=%v\tЕстьАдмин=%v\n\n", desk.host, desk.login, desk.pass, desk.isConnect, desk.isLogin, desk.isMakeAdmin)
  242. switch desk.mode {
  243. case ModeDefault: // Исходное содержимое
  244. s += desk.winDefault()
  245. case ModeConfig: // Показать настройки клиента
  246. s += desk.winConfig()
  247. case ModeConfigServer: // Конфигурация сервера
  248. s += desk.winConfigServer()
  249. case ModeInputHostServer: // Ввод хоста для сервера
  250. s += desk.winHostServer()
  251. case ModeInputLoginServer: // Ввод логина для входа на сервер
  252. s += desk.winLoginServer()
  253. case ModeInputPassServer: // ввод пароля
  254. s += desk.winPassServer()
  255. case ModeConnectServer: // Режим подключения к серверу
  256. s += desk.winConnectServer()
  257. case ModeLoginServer: // Режим логина на сервер
  258. s += desk.winServerLogin()
  259. case ModeServerMakeAdmin: // Режим содания админа на бот-сервере
  260. s += desk.winServerMakeAdmin()
  261. }
  262. return s
  263. }
  264. func (sf *Desktop) winServerMakeAdmin() string {
  265. s := "=Создание админа на бот-сервере вар-танк=\n\n"
  266. if !desk.isMakeAdmin {
  267. err := desk.client.MakeAdmin(desk.pass)
  268. if err != nil {
  269. s += fmt.Sprintf("Desktop.winServerMakeAdmin(): err=\n\t%v", err)
  270. s += "\n\n[ctrl+q]-выход [esc]-отмена\n"
  271. return s
  272. }
  273. }
  274. s += "Desktop.winServerMakeAdmin(): ok\n"
  275. desk.isMakeAdmin = true
  276. s += "\n\n[ctrl+q]-выход [esc]-отмена\n"
  277. return s
  278. }
  279. func (sf *Desktop) winServerLogin() string {
  280. s := "=Логин на бот-сервер вар-танк=\n\n"
  281. err := desk.client.Login(desk.login, desk.pass)
  282. if err != nil {
  283. s += fmt.Sprintf("Desktop.winServerLogin(): err=\n\t%v", err)
  284. s += "\n\n[ctrl+q]-выход [esc]-отмена\n"
  285. return s
  286. }
  287. s += "Desktop.winServerLogin(): ok\n"
  288. desk.isLogin = true
  289. s += "\n\n[ctrl+q]-выход [esc]-отмена\n"
  290. return s
  291. }
  292. func (sf *Desktop) winConnectServer() string {
  293. s := "=Подключение к бот-серверу вар-танк=\n\n"
  294. if !desk.isConnect {
  295. err := desk.client.Connect(desk.host)
  296. if err != nil {
  297. s += fmt.Sprintf("Desktop.winConnectServer(): err=\n\t%v", err)
  298. s += "\n\n[ctrl+q]-выход [esc]-отмена\n"
  299. return s
  300. }
  301. }
  302. s += "Desktop.winConnectServer(): ok\n"
  303. desk.isConnect = true
  304. s += "\n\n[ctrl+q]-выход [esc]-отмена\n"
  305. return s
  306. }
  307. func (sf *Desktop) winPassServer() string {
  308. s := "=Ввод пароля для бот-сервера вар-танк=\n\n"
  309. s += fmt.Sprintf(">%v\n", desk.textInput.View())
  310. s += "\n\n[ctrl+q]-выход [esc]-назад\n"
  311. return s
  312. }
  313. func (sf *Desktop) winLoginServer() string {
  314. s := "=Ввод логина для сервера вар-танк=\n\n"
  315. s += fmt.Sprintf(">%v\n", desk.textInput.View())
  316. s += "\n\n[ctrl+q]-выход [esc]-назад\n"
  317. return s
  318. }
  319. func (sf *Desktop) winHostServer() string {
  320. s := "=Ввод хоста для сервера вар-танк=\n\n"
  321. s += fmt.Sprintf(">%v\n", desk.textInput.View())
  322. s += "\n\n[ctrl+q]-выход [esc]-назад\n"
  323. return s
  324. }
  325. func (sf *Desktop) winConfigServer() string {
  326. s := "Настройки сервера вар-танк\n\n"
  327. s += "[1]-Настройки хоста сервера\n"
  328. s += "[2]-Логин для входа\n"
  329. s += "[3]-Пароль для входа\n"
  330. s += "\n\n[ctrl+q]-выход [esc]-назад\n"
  331. return s
  332. }
  333. func (sf *Desktop) winConfig() string {
  334. s := "Настройки клиента\n\n"
  335. s += "[1]-Настройки сервера\n"
  336. s += "[2]-Вход на сервер\n"
  337. s += "[3]-Логин на сервер\n"
  338. s += "[4]-Сделать админа на бот-сервере\n"
  339. s += "\n\n[ctrl+q]-выход [esc]-назад\n"
  340. return s
  341. }
  342. func (sf *Desktop) winDefault() string {
  343. // Заголовок
  344. s := "Что нам нужно сделать?\n\n"
  345. // Перебор по списку выбора
  346. for i, choice := range desk.choices {
  347. // Курсор указывает на этот выбор?
  348. cursor := " " // нет курсора
  349. if desk.cursor == i {
  350. cursor = ">" // есть курсор
  351. }
  352. // Этот элемент выбран?
  353. checked := " " // не выбран
  354. if _, ok := desk.selected[i]; ok {
  355. checked = "+" // выбран!
  356. }
  357. // отрендерить заново
  358. s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice)
  359. }
  360. // Подвал
  361. s += "\n\n[ctrl+q]-выход [w]-настройки\n"
  362. // Вернуть строку экрана для отрисовки
  363. return s
  364. }