summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
Diffstat (limited to 'ui')
-rw-r--r--ui/.rahannarc1
-rw-r--r--ui/views/auth.go590
-rw-r--r--ui/views/play.go427
-rw-r--r--ui/views/tabs.go32
-rw-r--r--ui/views/views.go144
5 files changed, 0 insertions, 1194 deletions
diff --git a/ui/.rahannarc b/ui/.rahannarc
deleted file mode 100644
index 572a8a5..0000000
--- a/ui/.rahannarc
+++ /dev/null
@@ -1 +0,0 @@
-eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo5LCJleHAiOjE3NDM5NjE2OTF9.4d0HpvRiwMD5IfNoctRXAg8DI6CxdZvouxKL4--LTJU \ No newline at end of file
diff --git a/ui/views/auth.go b/ui/views/auth.go
deleted file mode 100644
index a695466..0000000
--- a/ui/views/auth.go
+++ /dev/null
@@ -1,590 +0,0 @@
-package views
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "net/http"
- "os"
-
- "github.com/charmbracelet/bubbles/textinput"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
-)
-
-const (
- SignInTab TabType = iota
- SignUpTab
-)
-
-// AuthModel is the main container model for both login and signup tabsuth
-type AuthModel struct {
- loginModel loginModel
- signupModel signupModel
- activeTab TabType
- width int
- height int
-}
-
-// Model holds the state for login page
-type loginModel struct {
- username textinput.Model
- password textinput.Model
- focus int
- err error
- isLoading bool
- token string
- width int
- height int
-}
-
-// Model holds the state for signup page
-type signupModel struct {
- loginModel
- confirmPassword textinput.Model
-}
-
-// Response from API
-type authResponse struct {
- Token string `json:"token"`
- Error string `json:"error"`
-}
-
-// Initialize AuthModel which contains both tabs
-func NewAuthModel(width, height int) AuthModel {
- return AuthModel{
- loginModel: initLoginModel(width, height),
- signupModel: initSignupModel(width, height),
- activeTab: SignInTab,
- width: width,
- height: height,
- }
-}
-
-// Initialize loginModel
-func initLoginModel(width, height int) loginModel {
- username := textinput.New()
- username.Prompt = " "
- username.TextStyle = inputStyle
- username.Placeholder = "mario.rossi"
- username.Focus()
- username.CharLimit = 156
- username.Width = 30
-
- password := textinput.New()
- password.Prompt = " "
- password.TextStyle = inputStyle
- password.Placeholder = "*****"
- password.EchoMode = textinput.EchoPassword
- password.CharLimit = 156
- password.Width = 30
-
- return loginModel{
- username: username,
- password: password,
- focus: 0,
- err: nil,
- isLoading: false,
- token: "",
- width: width,
- height: height,
- }
-}
-
-// Initialize signupModel
-func initSignupModel(width, height int) signupModel {
- inputStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#7EE2A8"))
-
- username := textinput.New()
- username.Prompt = " "
- username.TextStyle = inputStyle
- username.Placeholder = "mario.rossi"
- username.Focus()
- username.CharLimit = 156
- username.Width = 30
-
- password := textinput.New()
- password.Prompt = " "
- password.TextStyle = inputStyle
- password.Placeholder = "*****"
- password.EchoMode = textinput.EchoPassword
- password.CharLimit = 156
- password.Width = 30
-
- confirmPassword := textinput.New()
- confirmPassword.Prompt = " "
- confirmPassword.TextStyle = inputStyle
- confirmPassword.Placeholder = "*****"
- confirmPassword.EchoMode = textinput.EchoPassword
- confirmPassword.CharLimit = 156
- confirmPassword.Width = 30
-
- return signupModel{
- loginModel: loginModel{
- username: username,
- password: password,
- focus: 0,
- err: nil,
- isLoading: false,
- token: "",
- width: width,
- height: height,
- },
- confirmPassword: confirmPassword,
- }
-}
-
-// Init function for AuthModel
-func (m AuthModel) Init() tea.Cmd {
- ClearScreen()
- return textinput.Blink
-}
-
-func (m AuthModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
- var cmds []tea.Cmd
-
- if exit := handleExit(msg); exit != nil {
- return m, exit
- }
-
- switch msg := msg.(type) {
- case tea.WindowSizeMsg:
- m.width = msg.Width
- m.height = msg.Height
- return m, nil
-
- case tea.KeyMsg:
- switch msg.String() {
- case "alt+1":
- // Switch to sign-in tab
- if m.activeTab != SignInTab {
- m.activeTab = SignInTab
- m.loginModel.focus = 0
- m.loginModel.username.Focus()
- m.loginModel.password.Blur()
- m.signupModel.username.Blur()
- m.signupModel.password.Blur()
- m.signupModel.confirmPassword.Blur()
- }
- return m, nil
-
- case "alt+2":
- // Switch to sign-up tab
- if m.activeTab != SignUpTab {
- m.activeTab = SignUpTab
- m.signupModel.focus = 0
- m.signupModel.username.Focus()
- m.signupModel.password.Blur()
- m.signupModel.confirmPassword.Blur()
- m.loginModel.username.Blur()
- m.loginModel.password.Blur()
- }
- return m, nil
-
- }
- }
-
- if m.activeTab == SignInTab {
- var cmd tea.Cmd
- m.loginModel, cmd = m.loginModel.Update(msg)
- cmds = append(cmds, cmd)
- } else {
- var cmd tea.Cmd
- m.signupModel, cmd = m.signupModel.Update(msg)
- cmds = append(cmds, cmd)
- }
-
- return m, tea.Batch(cmds...)
-}
-
-// View function for AuthModel
-func (m AuthModel) View() string {
- width, height := m.width, m.height
-
- // Get the content of the active tab
- var tabContent string
- if m.activeTab == SignInTab {
- tabContent = m.loginModel.renderContent()
- } else {
- tabContent = m.signupModel.renderContent()
- }
-
- // Create the window with tab content
- ui := lipgloss.JoinVertical(lipgloss.Center,
- getTabsRow([]string{"Sign In", "Sign Up"}, m.activeTab),
- windowStyle.Width(getFormWidth(width)).Render(tabContent),
- )
-
- // Center logo and form in available space
- contentHeight := lipgloss.Height(logo) + lipgloss.Height(ui) + 2
- paddingTop := (height - contentHeight) / 2
- if paddingTop < 0 {
- paddingTop = 0
- }
-
- // Combine logo and tabs with vertical centering
- output := lipgloss.NewStyle().
- MarginTop(paddingTop).
- Render(
- lipgloss.JoinVertical(lipgloss.Center,
- getLogo(m.width),
- lipgloss.PlaceHorizontal(width, lipgloss.Center, ui),
- ),
- )
-
- return output
-}
-
-// Update function for loginModel
-func (m loginModel) Update(msg tea.Msg) (loginModel, tea.Cmd) {
- switch msg := msg.(type) {
- case tea.KeyMsg:
- switch msg.Type {
- case tea.KeyUp:
- m.focus = (m.focus - 1) % 2
- if m.focus < 0 {
- m.focus = 1
- }
- m.updateFocus()
- case tea.KeyDown:
- m.focus = (m.focus + 1) % 2
- m.updateFocus()
- case tea.KeyEnter:
- if !m.isLoading {
- m.isLoading = true
- return m, m.loginCallback()
- }
- case tea.KeyTab:
- m.focus = (m.focus + 1) % 2
- m.updateFocus()
- }
- case authResponse:
- m.isLoading = false
- if msg.Error != "" {
- m.err = fmt.Errorf(msg.Error)
- m.focus = 0
- m.updateFocus()
- } else {
- m.token = msg.Token
- ClearScreen()
- f, err := os.OpenFile(".rahannarc", os.O_WRONLY|os.O_CREATE, 0644)
- if err != nil {
- m.err = err
- break
- }
- defer f.Close()
- f.Write([]byte(m.token))
- return m, SwitchModelCmd(NewPlayModel(m.width, m.height))
- }
- case error:
- m.isLoading = false
- m.err = msg
- m.focus = 0
- m.updateFocus()
- }
-
- var cmd tea.Cmd
- m.username, cmd = m.username.Update(msg)
- cmdPassword := tea.Batch(cmd)
- m.password, cmd = m.password.Update(msg)
- return m, tea.Batch(cmd, cmdPassword)
-}
-
-// Update function for signupModel
-func (m signupModel) Update(msg tea.Msg) (signupModel, tea.Cmd) {
- switch msg := msg.(type) {
- case tea.KeyMsg:
- switch msg.Type {
- case tea.KeyUp:
- m.focus = (m.focus - 1) % 3
- if m.focus < 0 {
- m.focus = 2
- }
- m.updateFocus()
- case tea.KeyDown:
- m.focus = (m.focus + 1) % 3
- m.updateFocus()
- case tea.KeyEnter:
- if !m.isLoading {
- m.isLoading = true
- return m, m.signupCallback()
- }
- case tea.KeyTab:
- m.focus = (m.focus + 1) % 3
- m.updateFocus()
- }
- case authResponse:
- m.isLoading = false
- if msg.Error != "" {
- m.err = fmt.Errorf(msg.Error)
- m.focus = 0
- m.updateFocus()
- } else {
- m.token = msg.Token
- ClearScreen()
- f, err := os.OpenFile(".rahannarc", os.O_WRONLY|os.O_CREATE, 0644)
- if err != nil {
- m.err = err
- break
- }
- defer f.Close()
- f.Write([]byte(m.token))
- return m, SwitchModelCmd(NewPlayModel(m.width, m.height))
- }
- case error:
- m.isLoading = false
- m.err = msg
- m.focus = 0
- m.updateFocus()
- }
-
- var cmds []tea.Cmd
- var cmd tea.Cmd
-
- m.username, cmd = m.username.Update(msg)
- cmds = append(cmds, cmd)
-
- m.password, cmd = m.password.Update(msg)
- cmds = append(cmds, cmd)
-
- m.confirmPassword, cmd = m.confirmPassword.Update(msg)
- cmds = append(cmds, cmd)
-
- return m, tea.Batch(cmds...)
-}
-
-// Helper function to update input focus for signup
-func (m *signupModel) updateFocus() {
- m.username.Blur()
- m.password.Blur()
- m.confirmPassword.Blur()
-
- switch m.focus {
- case 0:
- m.username.Focus()
- case 1:
- m.password.Focus()
- case 2:
- m.confirmPassword.Focus()
- }
-}
-
-// Helper function to update input focus for signin
-func (m *loginModel) updateFocus() {
- m.username.Blur()
- m.password.Blur()
-
- switch m.focus {
- case 0:
- m.username.Focus()
- case 1:
- m.password.Focus()
- }
-}
-
-// Login API callback
-func (m loginModel) loginCallback() tea.Cmd {
- return func() tea.Msg {
- url := os.Getenv("API_BASE") + "/auth/login"
-
- payload, err := json.Marshal(map[string]string{
- "username": m.username.Value(),
- "password": m.password.Value(),
- })
-
- if err != nil {
- return authResponse{Error: err.Error()}
- }
-
- resp, err := http.Post(url, "application/json", bytes.NewReader(payload))
- if err != nil {
- return authResponse{Error: err.Error()}
- }
- defer resp.Body.Close()
-
- if resp.StatusCode != http.StatusOK {
- var response authResponse
- err = json.NewDecoder(resp.Body).Decode(&response)
- if err != nil {
- return authResponse{Error: fmt.Sprintf("HTTP error: %d, unable to decode body", resp.StatusCode)}
- }
- return authResponse{Error: response.Error}
- }
-
- var response authResponse
- err = json.NewDecoder(resp.Body).Decode(&response)
- if err != nil {
- return authResponse{Error: fmt.Sprintf("Error decoding JSON: %v", err)}
- }
-
- return response
- }
-}
-
-// Signup API callback
-func (m signupModel) signupCallback() tea.Cmd {
- return func() tea.Msg {
- // Validate that passwords match
- if m.password.Value() != m.confirmPassword.Value() {
- return authResponse{Error: "Passwords do not match"}
- }
-
- url := os.Getenv("API_BASE") + "/auth/register"
-
- payload, err := json.Marshal(map[string]string{
- "username": m.username.Value(),
- "password": m.password.Value(),
- })
-
- if err != nil {
- return authResponse{Error: err.Error()}
- }
-
- resp, err := http.Post(url, "application/json", bytes.NewReader(payload))
- if err != nil {
- return authResponse{Error: err.Error()}
- }
- defer resp.Body.Close()
-
- if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
- var response authResponse
- err = json.NewDecoder(resp.Body).Decode(&response)
- if err != nil {
- return authResponse{Error: fmt.Sprintf("HTTP error: %d, unable to decode body", resp.StatusCode)}
- }
- return authResponse{Error: response.Error}
- }
-
- var response authResponse
- err = json.NewDecoder(resp.Body).Decode(&response)
- if err != nil {
- return authResponse{Error: fmt.Sprintf("Error decoding JSON: %v", err)}
- }
-
- return response
- }
-}
-
-// Render content of the login tab
-func (m loginModel) renderContent() string {
- formWidth := getFormWidth(m.width)
-
- // Styles
- titleStyle := lipgloss.NewStyle().
- Bold(true).
- Foreground(lipgloss.Color("#7ee2a8")).
- Align(lipgloss.Center).
- Width(formWidth - 4) // Account for padding
-
- labelStyle := lipgloss.NewStyle().
- Width(10).
- Align(lipgloss.Right)
-
- inputWrapStyle := lipgloss.NewStyle().
- Align(lipgloss.Center).
- Width(formWidth - 4) // Account for padding
-
- statusStyle := lipgloss.NewStyle().
- Align(lipgloss.Center).
- Bold(true).
- Width(formWidth - 4) // Account for padding
-
- // Error message
- formError := ""
- if m.err != nil {
- formError = fmt.Sprintf("Error: %v", m.err.Error())
- }
-
- // Status message
- statusMsg := fmt.Sprintf("Press %s to login", lipgloss.NewStyle().Italic(true).Render("Enter"))
- if m.isLoading {
- statusMsg = "Logging in..."
- }
-
- form := lipgloss.JoinVertical(lipgloss.Center,
- titleStyle.Render("Sign in to your account"),
- "\n",
- errorStyle.Align(lipgloss.Center).Width(formWidth-4).Render(formError),
- inputWrapStyle.Render(
- lipgloss.JoinHorizontal(lipgloss.Left,
- labelStyle.Render("Username:"),
- m.username.View(),
- ),
- ),
- inputWrapStyle.Render(
- lipgloss.JoinHorizontal(lipgloss.Left,
- labelStyle.Render("Password:"),
- m.password.View(),
- ),
- ),
- "\n",
- statusStyle.Render(statusMsg),
- )
-
- return form
-}
-
-// Render content of the signup tab
-func (m signupModel) renderContent() string {
- formWidth := getFormWidth(m.width)
-
- // Styles
- titleStyle := lipgloss.NewStyle().
- Bold(true).
- Foreground(lipgloss.Color("#7ee2a8")).
- Align(lipgloss.Center).
- Width(formWidth - 4) // Account for padding
-
- labelStyle := lipgloss.NewStyle().
- Width(16).
- Align(lipgloss.Right)
-
- inputWrapStyle := lipgloss.NewStyle().
- Align(lipgloss.Center).
- Width(formWidth - 4) // Account for padding
-
- statusStyle := lipgloss.NewStyle().
- Align(lipgloss.Center).
- Bold(true).
- Width(formWidth - 4) // Account for padding
-
- // Error message
- formError := ""
- if m.err != nil {
- formError = fmt.Sprintf("Error: %v", m.err.Error())
- }
-
- // Status message
- statusMsg := fmt.Sprintf("Press %s to register", lipgloss.NewStyle().Italic(true).Render("Enter"))
- if m.isLoading {
- statusMsg = "Creating account..."
- }
-
- form := lipgloss.JoinVertical(lipgloss.Center,
- titleStyle.Render("Create a new account"),
- "\n",
- errorStyle.Align(lipgloss.Center).Width(formWidth-4).Render(formError),
- inputWrapStyle.Render(
- lipgloss.JoinHorizontal(lipgloss.Left,
- labelStyle.Render("Username:"),
- m.username.View(),
- ),
- ),
- inputWrapStyle.Render(
- lipgloss.JoinHorizontal(lipgloss.Left,
- labelStyle.Render("Password:"),
- m.password.View(),
- ),
- ),
- inputWrapStyle.Render(
- lipgloss.JoinHorizontal(lipgloss.Left,
- labelStyle.Render("Confirm:"),
- m.confirmPassword.View(),
- ),
- ),
- "\n",
- statusStyle.Render(statusMsg),
- )
-
- return form
-}
diff --git a/ui/views/play.go b/ui/views/play.go
deleted file mode 100644
index 389c302..0000000
--- a/ui/views/play.go
+++ /dev/null
@@ -1,427 +0,0 @@
-package views
-
-import (
- "bufio"
- "bytes"
- "encoding/json"
- "fmt"
- "net/http"
- "os"
-
- "github.com/boozec/rahanna/api/database"
- "github.com/boozec/rahanna/network"
- "github.com/charmbracelet/bubbles/key"
- "github.com/charmbracelet/bubbles/textinput"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
-)
-
-var chess string = `
- A B C D E F G H
-+---------------+
-8 |♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜| 8
-7 |♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟| 7
-6 |. . . . . . . .| 6
-5 |. . . . . . . .| 5
-4 |. . . . . . . .| 4
-3 |. . . . . . . .| 3
-2 |♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙| 2
-1 |♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖| 1
-+---------------+
- A B C D E F G H
-`
-
-type playKeyMap struct {
- EnterNewGame key.Binding
- StartNewGame key.Binding
- GoLogout key.Binding
- Quit key.Binding
-}
-
-type playResponse struct {
- Name string `json:"name"`
- Error string `json:"error"`
-}
-
-var defaultGameKeyMap = playKeyMap{
- EnterNewGame: key.NewBinding(
- key.WithKeys("alt+E", "alt+e"),
- key.WithHelp("Alt+E", "Enter a play using code"),
- ),
- StartNewGame: key.NewBinding(
- key.WithKeys("alt+s", "alt+s"),
- key.WithHelp("Alt+S", "Start a new play"),
- ),
- GoLogout: key.NewBinding(
- key.WithKeys("alt+Q", "alt+q"),
- key.WithHelp("Alt+Q", "Logout"),
- ),
- Quit: key.NewBinding(
- key.WithKeys("Q", "q"),
- key.WithHelp(" Q", "Quit"),
- ),
-}
-
-type PlayModelPage int
-
-const (
- LandingPage PlayModelPage = iota
- InsertCodePage
- StartGamePage
-)
-
-type PlayModel struct {
- width int
- height int
- err error
- keys playKeyMap
- namePrompt textinput.Model
- page PlayModelPage
- isLoading bool
- playName string
- play *database.Game
-}
-
-func NewPlayModel(width, height int) PlayModel {
- namePrompt := textinput.New()
- namePrompt.Prompt = " "
- namePrompt.TextStyle = inputStyle
- namePrompt.Placeholder = "rectangular-lake"
- namePrompt.Focus()
- namePrompt.CharLimit = 23
- namePrompt.Width = 23
-
- return PlayModel{
- width: width,
- height: height,
- err: nil,
- keys: defaultGameKeyMap,
- namePrompt: namePrompt,
- page: LandingPage,
- isLoading: false,
- playName: "",
- play: nil,
- }
-}
-
-func (m PlayModel) Init() tea.Cmd {
- ClearScreen()
- return textinput.Blink
-}
-
-func (m PlayModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
- if exit := handleExit(msg); exit != nil {
- return m, exit
- }
-
- switch msg := msg.(type) {
- case tea.WindowSizeMsg:
- m.width = msg.Width
- m.height = msg.Height
- return m, nil
-
- case tea.KeyMsg:
- switch {
- case key.Matches(msg, m.keys.EnterNewGame):
- m.page = InsertCodePage
- return m, nil
- case key.Matches(msg, m.keys.StartNewGame):
- m.page = StartGamePage
- if !m.isLoading {
- m.isLoading = true
- return m, m.newGameCallback()
- }
- case key.Matches(msg, m.keys.GoLogout):
- return m, m.logout()
- case key.Matches(msg, m.keys.Quit):
- return m, tea.Quit
- case msg.Type == tea.KeyEnter:
- if m.page == InsertCodePage {
- if !m.isLoading {
- m.isLoading = true
- return m, m.enterGame()
- }
- }
- }
- case playResponse:
- m.isLoading = false
- m.err = nil
- if msg.Error != "" {
- m.err = fmt.Errorf(msg.Error)
- if msg.Error == "unauthorized" {
- return m, m.logout()
- }
- } else {
- m.playName = msg.Name
- }
- return m, nil
- case database.Game:
- m.isLoading = false
- m.play = &msg
- m.err = nil
- return m, nil
- case error:
- m.isLoading = false
- m.err = msg
- }
-
- var cmd tea.Cmd = nil
-
- if m.page == InsertCodePage {
- m.namePrompt, cmd = m.namePrompt.Update(msg)
- }
-
- return m, tea.Batch(cmd)
-}
-
-func (m PlayModel) View() string {
- formWidth := getFormWidth(m.width)
-
- var content string
- base := lipgloss.NewStyle().Align(lipgloss.Center).Width(m.width)
-
- switch m.page {
- case LandingPage:
- content = chess
- m.namePrompt.Blur()
- case InsertCodePage:
- m.namePrompt.Focus()
- var statusMsg string
- if m.isLoading {
- statusMsg = "Loading..."
- content = base.
- Render(
- lipgloss.NewStyle().
- Align(lipgloss.Center).
- Bold(true).
- Render(statusMsg),
- )
- } else if m.play != nil {
- statusMsg = fmt.Sprintf("You are playing versus %s", lipgloss.NewStyle().Foreground(lipgloss.Color("#e67e22")).Render(m.play.Player1.Username))
- content = base.
- Render(
- lipgloss.NewStyle().
- Align(lipgloss.Center).
- Width(m.width).
- Bold(true).
- Render(statusMsg),
- )
- } else {
- statusMsg = fmt.Sprintf("Press %s to join", lipgloss.NewStyle().Italic(true).Render("Enter"))
- content = base.
- Render(
- lipgloss.JoinVertical(lipgloss.Left,
- lipgloss.NewStyle().Width(23).Render("Insert play code:"),
- m.namePrompt.View(),
- lipgloss.NewStyle().
- Align(lipgloss.Center).
- PaddingTop(2).
- Width(23).
- Bold(true).
- Render(statusMsg),
- ),
- )
- }
-
- case StartGamePage:
- var statusMsg string
- if m.isLoading {
- statusMsg = "Loading..."
- } else if m.playName != "" {
- statusMsg = fmt.Sprintf("Share `%s` to your friend", lipgloss.NewStyle().Italic(true).Foreground(lipgloss.Color("#F39C12")).Render(m.playName))
- }
-
- content = base.
- Render(statusMsg)
- }
-
- var windowContent string
- if m.err != nil {
- formError := fmt.Sprintf("Error: %v", m.err.Error())
- windowContent = lipgloss.JoinVertical(
- lipgloss.Center,
- windowStyle.Width(formWidth).Render(lipgloss.JoinVertical(
- lipgloss.Center,
- errorStyle.Align(lipgloss.Center).Width(formWidth-4).Render(formError),
- content,
- )),
- )
- } else {
- windowContent = lipgloss.JoinVertical(
- lipgloss.Center,
- windowStyle.Width(formWidth).Render(lipgloss.JoinVertical(
- lipgloss.Center,
- content,
- )),
- )
- }
-
- enterKey := fmt.Sprintf("%s %s", altCodeStyle.Render(m.keys.EnterNewGame.Help().Key), m.keys.EnterNewGame.Help().Desc)
- startKey := fmt.Sprintf("%s %s", altCodeStyle.Render(m.keys.StartNewGame.Help().Key), m.keys.StartNewGame.Help().Desc)
- logoutKey := fmt.Sprintf("%s %s", altCodeStyle.Render(m.keys.GoLogout.Help().Key), m.keys.GoLogout.Help().Desc)
- quitKey := fmt.Sprintf("%s %s", altCodeStyle.Render(m.keys.Quit.Help().Key), m.keys.Quit.Help().Desc)
-
- // Vertically align the buttons
- buttons := lipgloss.JoinVertical(
- lipgloss.Left,
- enterKey,
- startKey,
- logoutKey,
- quitKey,
- )
-
- centeredContent := lipgloss.JoinVertical(
- lipgloss.Center,
- getLogo(m.width),
- windowContent,
- lipgloss.NewStyle().MarginTop(2).Render(buttons),
- )
-
- return lipgloss.Place(
- m.width,
- m.height,
- lipgloss.Center,
- lipgloss.Center,
- centeredContent,
- )
-}
-
-func (m PlayModel) newGameCallback() tea.Cmd {
- return func() tea.Msg {
- f, err := os.Open(".rahannarc")
- if err != nil {
- return playResponse{Error: err.Error()}
- }
- defer f.Close()
-
- scanner := bufio.NewScanner(f)
- var authorization string
- for scanner.Scan() {
- authorization = scanner.Text()
- }
-
- if err := scanner.Err(); err != nil {
- fmt.Println("Error during scanning:", err)
- }
-
- url := os.Getenv("API_BASE") + "/play"
-
- port, err := network.GetRandomAvailablePort()
- if err != nil {
- return playResponse{Error: err.Error()}
- }
-
- payload, err := json.Marshal(map[string]string{
- "ip": fmt.Sprintf("%s:%d", network.GetOutboundIP().String(), port),
- })
-
- if err != nil {
- return playResponse{Error: err.Error()}
- }
-
- req, err := http.NewRequest("POST", url, bytes.NewReader(payload))
- if err != nil {
- return playResponse{Error: err.Error()}
- }
-
- req.Header.Set("Content-Type", "application/json")
- req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", authorization))
-
- client := &http.Client{}
-
- resp, err := client.Do(req)
-
- defer resp.Body.Close()
-
- if resp.StatusCode != http.StatusOK {
- var response playResponse
- err = json.NewDecoder(resp.Body).Decode(&response)
- if err != nil {
- return playResponse{Error: fmt.Sprintf("HTTP error: %d, unable to decode body", resp.StatusCode)}
- }
- return playResponse{Error: response.Error}
- }
-
- var response playResponse
- err = json.NewDecoder(resp.Body).Decode(&response)
- if err != nil {
- return playResponse{Error: fmt.Sprintf("Error decoding JSON: %v", err)}
- }
-
- return response
- }
-}
-
-func (m PlayModel) enterGame() tea.Cmd {
- return func() tea.Msg {
- f, err := os.Open(".rahannarc")
- if err != nil {
- return playResponse{Error: err.Error()}
- }
- defer f.Close()
-
- scanner := bufio.NewScanner(f)
- var authorization string
- for scanner.Scan() {
- authorization = scanner.Text()
- }
-
- if err := scanner.Err(); err != nil {
- fmt.Println("Error during scanning:", err)
- }
-
- url := os.Getenv("API_BASE") + "/enter-game"
-
- port, err := network.GetRandomAvailablePort()
- if err != nil {
- return playResponse{Error: err.Error()}
- }
-
- payload, err := json.Marshal(map[string]string{
- "ip": fmt.Sprintf("%s:%d", network.GetOutboundIP().String(), port),
- "name": m.namePrompt.Value(),
- })
-
- if err != nil {
- return playResponse{Error: err.Error()}
- }
-
- req, err := http.NewRequest("POST", url, bytes.NewReader(payload))
- if err != nil {
- return playResponse{Error: err.Error()}
- }
-
- req.Header.Set("Content-Type", "application/json")
- req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", authorization))
-
- client := &http.Client{}
-
- resp, err := client.Do(req)
-
- defer resp.Body.Close()
-
- if resp.StatusCode != http.StatusOK {
- var response playResponse
- err = json.NewDecoder(resp.Body).Decode(&response)
- if err != nil {
- return playResponse{Error: fmt.Sprintf("HTTP error: %d, unable to decode body", resp.StatusCode)}
- }
- return playResponse{Error: response.Error}
- }
-
- var response database.Game
- err = json.NewDecoder(resp.Body).Decode(&response)
- if err != nil {
- return playResponse{Error: fmt.Sprintf("Error decoding JSON: %v", err)}
- }
-
- return response
- }
-}
-
-func (m PlayModel) logout() tea.Cmd {
- if err := os.Remove(".rahannarc"); err != nil {
- return nil
- }
- return SwitchModelCmd(NewAuthModel(m.width, m.height+1))
-}
diff --git a/ui/views/tabs.go b/ui/views/tabs.go
deleted file mode 100644
index 13e3672..0000000
--- a/ui/views/tabs.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package views
-
-import (
- "fmt"
-
- "github.com/charmbracelet/lipgloss"
-)
-
-type TabType int
-
-var (
- tabStyle = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(highlightColor).Padding(0, 2)
- inactiveTabStyle = tabStyle
- activeTabStyle = tabStyle
-)
-
-func getTabsRow(tabsText []string, activeTab TabType) string {
- tabs := make([]string, len(tabsText))
-
- for i, tab := range tabsText {
- if TabType(i) == activeTab {
- tabs[i] = fmt.Sprintf("%s %s", altCodeStyle.Render(fmt.Sprintf("Alt+%d", i+1)), lipgloss.NewStyle().Bold(true).Foreground(highlightColor).Render(tab))
- tabs[i] = activeTabStyle.Foreground(highlightColor).Render(tabs[i])
- } else {
- tabs[i] = fmt.Sprintf("%s %s", altCodeStyle.Render(fmt.Sprintf("Alt+%d", i+1)), lipgloss.NewStyle().Render(tab))
- tabs[i] = inactiveTabStyle.Foreground(highlightColor).Render(tabs[i])
- }
- }
-
- return lipgloss.JoinHorizontal(lipgloss.Top, tabs...)
-
-}
diff --git a/ui/views/views.go b/ui/views/views.go
deleted file mode 100644
index fa70035..0000000
--- a/ui/views/views.go
+++ /dev/null
@@ -1,144 +0,0 @@
-package views
-
-import (
- "errors"
- "os"
-
- "os/exec"
-
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
- "golang.org/x/term"
-)
-
-var logo = `
-▗▄▄▖ ▗▄▖ ▗▖ ▗▖ ▗▄▖ ▗▖ ▗▖▗▖ ▗▖ ▗▄▖
-▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▛▚▖▐▌▐▛▚▖▐▌▐▌ ▐▌
-▐▛▀▚▖▐▛▀▜▌▐▛▀▜▌▐▛▀▜▌▐▌ ▝▜▌▐▌ ▝▜▌▐▛▀▜▌
-▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌
-`
-
-var (
- highlightColor = lipgloss.Color("#7ee2a8")
- errorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#ff0000"))
- altCodeStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#666666")).Bold(true)
- windowStyle = lipgloss.NewStyle().BorderForeground(highlightColor).Padding(2, 0).Align(lipgloss.Center).Border(lipgloss.RoundedBorder())
- inputStyle = lipgloss.NewStyle().Foreground(highlightColor)
-)
-
-// Get terminal size dynamically
-func GetTerminalSize() (width, height int) {
- fd := int(os.Stdin.Fd())
- if w, h, err := term.GetSize(fd); err == nil {
- return w, h
- }
- return 80, 24 // Default size if detection fails
-}
-
-// Clear terminal screen
-func ClearScreen() {
- if len(os.Getenv("DEBUG")) == 0 {
- cmd := exec.Command("clear")
- if os.Getenv("OS") == "Windows_NT" {
- cmd = exec.Command("cmd", "/c", "cls")
- }
- cmd.Stdout = os.Stdout
- cmd.Run()
- }
-}
-
-func getFormWidth(width int) int {
- formWidth := width * 2 / 3
- if formWidth > 80 {
- formWidth = 80 // Cap at 80 chars for readability
- } else if formWidth < 40 {
- formWidth = width - 4 // For small terminals
- }
-
- return formWidth
-}
-
-type RahannaModel struct {
- width int
- height int
- currentModel tea.Model
- auth AuthModel
- play PlayModel
-}
-
-func NewRahannaModel() RahannaModel {
- width, height := GetTerminalSize()
-
- auth := NewAuthModel(width, height)
- play := NewPlayModel(width, height)
-
- var currentModel tea.Model = auth
-
- if _, err := os.Stat(".rahannarc"); !errors.Is(err, os.ErrNotExist) {
- currentModel = play
- }
-
- return RahannaModel{
- width: width,
- height: height,
- currentModel: currentModel,
- auth: auth,
- play: play,
- }
-}
-
-func (m RahannaModel) Init() tea.Cmd {
- return m.currentModel.Init()
-}
-
-type switchModel struct {
- model tea.Model
-}
-
-func SwitchModelCmd(model tea.Model) tea.Cmd {
- s := switchModel{
- model: model,
- }
-
- return func() tea.Msg {
- return s
- }
-}
-
-func (m RahannaModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
- switch msg := msg.(type) {
- case switchModel:
- m.currentModel = msg.model
- return m, nil
- }
- var cmd tea.Cmd
- m.currentModel, cmd = m.currentModel.Update(msg)
- return m, cmd
-}
-
-func (m RahannaModel) View() string {
- return m.currentModel.View()
-}
-
-func handleExit(msg tea.Msg) tea.Cmd {
- switch msg := msg.(type) {
- case tea.KeyMsg:
- switch msg.String() {
- case "ctrl+c":
- return tea.Quit
- }
- }
-
- return nil
-}
-
-func getLogo(width int) string {
- logoStyle := lipgloss.NewStyle().
- Foreground(lipgloss.Color("#7ee2a8")).
- Bold(true).
- Align(lipgloss.Center).
- Width(width)
-
- return logoStyle.Render(logo)
-
-}