From 9ebd017160ceac890b8e03b213064ae00c9fc158 Mon Sep 17 00:00:00 2001 From: Kyren223 Date: Thu, 16 Jan 2025 17:36:06 +0200 Subject: Added visual indicators for chat when the frequency is read-only the viminput is grayed out and the cursor is hidden, when muted (untested) the viminput border is red, and when no access it's grayed and a message showing no perms instead of the "this frequency is empty", along with placeholders for "Send message..." that say that you are muted/don't have permission to write message --- internal/client/ui/core/chat/chat.go | 130 +++++++++++++++++++++----------- internal/client/ui/viminput/viminput.go | 60 ++++++++++----- 2 files changed, 129 insertions(+), 61 deletions(-) diff --git a/internal/client/ui/core/chat/chat.go b/internal/client/ui/core/chat/chat.go index 239f823..ef9de7e 100644 --- a/internal/client/ui/core/chat/chat.go +++ b/internal/client/ui/core/chat/chat.go @@ -22,12 +22,17 @@ import ( ) var ( - focusStyle = lipgloss.NewStyle().Foreground(colors.Focus) + blurStyle = lipgloss.NewStyle().Foreground(colors.White) + focusStyle = lipgloss.NewStyle().Foreground(colors.Focus) + readOnlyStyle = lipgloss.NewStyle().Foreground(colors.Gray) + mutedStyle = lipgloss.NewStyle().Foreground(colors.Red) ViBlurredBorder = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder(), true, true, false). Padding(0, 1) - ViFocusedBorder = ViBlurredBorder.BorderForeground(colors.Focus) + ViFocusedBorder = ViBlurredBorder.BorderForeground(colors.Focus) + ViReadOnlyBorder = ViBlurredBorder.BorderForeground(colors.Gray) + ViMutedBorder = ViBlurredBorder.BorderForeground(colors.Red) VimModeStyle = lipgloss.NewStyle().Bold(true) @@ -47,8 +52,13 @@ var ( Foreground(colors.LightGray).Padding(0, PaddingCount, 1). AlignHorizontal(lipgloss.Center).AlignVertical(lipgloss.Bottom). SetString("This frequency has no messages, start transmiting!") + NoAccess = NoMessages.SetString("You do not have permissions to see messages in this frequency") SelectedGap = lipgloss.NewStyle().Background(colors.BackgroundDim) + + SendMessagePlaceholder = "Send a message..." + ReadOnlyPlaceholder = "You do not have permissions to send messages in this frequency" + MutedPlaceholder = "You have been muted by a server adminstrator" ) const ( @@ -66,6 +76,8 @@ type Model struct { focus bool locked bool + hasReadAccess bool + hasWriteAccess bool networkIndex int receiverIndex int frequencyIndex int @@ -80,12 +92,15 @@ type Model struct { prerender string width int + + style lipgloss.Style + borderStyle lipgloss.Style } func New(width int) Model { viWidth := width - PaddingCount*2 - lipgloss.Width(LeftCorner) - lipgloss.Width(RightCorner) vi := viminput.New(viWidth, ui.Height/2) - vi.Placeholder = "Send a message..." + vi.Placeholder = SendMessagePlaceholder vi.PlaceholderStyle = lipgloss.NewStyle().Foreground(colors.Gray) return Model{ @@ -133,6 +148,42 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { // TODO: properly invalidate cache m.messagesCache = nil + networkId := state.NetworkId(m.networkIndex) + if m.frequencyIndex != -1 && networkId != nil { + frequencies := state.State.Frequencies[*networkId] + frequency := frequencies[m.frequencyIndex] + member := state.State.Members[*networkId][*state.UserID] + + m.hasReadAccess = frequency.Perms != packet.PermNoAccess || member.IsAdmin + m.hasWriteAccess = !member.IsMuted && (frequency.Perms == packet.PermReadWrite || member.IsAdmin) + + if member.IsMuted { + m.vi.Placeholder = MutedPlaceholder + m.borderStyle = ViMutedBorder + m.style = mutedStyle + m.vi.SetInactive(true) + m.locked = false + } else if frequency.Perms != packet.PermReadWrite && !member.IsAdmin { + m.vi.Placeholder = ReadOnlyPlaceholder + m.borderStyle = ViReadOnlyBorder + m.style = readOnlyStyle + m.vi.SetInactive(true) + m.locked = false + } else if m.locked { + m.vi.Placeholder = SendMessagePlaceholder + m.borderStyle = ViFocusedBorder + m.style = focusStyle + m.vi.SetInactive(false) + } else { + m.vi.Placeholder = SendMessagePlaceholder + m.borderStyle = ViBlurredBorder + m.style = blurStyle + m.vi.SetInactive(false) + } + } else if m.receiverIndex != -1 { + // TODO: receiver + } + if !m.focus { m.Prerender() return m, nil @@ -168,9 +219,11 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { key := msg.String() switch key { case "i": - m.locked = true - m.vi.SetMode(viminput.InsertMode) - m.SetIndex(Unselected) + if !m.vi.Inactive() { + m.locked = true + m.vi.SetMode(viminput.InsertMode) + m.SetIndex(Unselected) + } case "k": m.Scroll(1) case "j": @@ -187,10 +240,12 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { m.base = SnapToBottom m.SetIndex(Unselected) case "enter": - m.locked = true - m.vi.SetMode(viminput.InsertMode) - m.base = SnapToBottom - m.SetIndex(Unselected) + if !m.vi.Inactive() { + m.locked = true + m.vi.SetMode(viminput.InsertMode) + m.base = SnapToBottom + m.SetIndex(Unselected) + } case "x", "X", "d", "D": if m.selectedMessage != nil { @@ -275,6 +330,11 @@ func (m *Model) SetFrequency(networkIndex, frequencyIndex int) tea.Cmd { } func (m *Model) ResetBeforeSwitch() { + m.vi.Reset() + m.base = SnapToBottom + m.SetIndex(Unselected) + m.maxMessagesHeight = -1 + networkId := state.NetworkId(m.networkIndex) if m.frequencyIndex != -1 && networkId != nil { frequencies := state.State.Frequencies[*networkId] @@ -288,10 +348,6 @@ func (m *Model) ResetBeforeSwitch() { Base: m.base, MaxHeight: m.maxMessagesHeight, } - m.vi.Reset() - m.base = SnapToBottom - m.maxMessagesHeight = -1 - m.SetIndex(Unselected) } else if m.receiverIndex != -1 { // TODO: receiver } @@ -302,9 +358,10 @@ func (m *Model) RestoreAfterSwitch() tea.Cmd { networkId := state.NetworkId(m.networkIndex) if m.frequencyIndex != -1 && networkId != nil { frequencies := state.State.Frequencies[*networkId] - frequencyId := frequencies[m.frequencyIndex].ID - log.Println("Restoring", frequencyId) - if val, ok := msgs[frequencyId]; ok { + frequency := frequencies[m.frequencyIndex] + log.Println("Restoring", frequency.ID) + + if val, ok := msgs[frequency.ID]; ok { m.vi.SetString(val.IncompleteMessage) m.base = val.Base m.SetIndex(Unselected) @@ -312,17 +369,11 @@ func (m *Model) RestoreAfterSwitch() tea.Cmd { // Don't ask for messages if you already visited this frequency return nil - } else { - m.vi.Reset() - m.base = SnapToBottom - m.SetIndex(Unselected) - m.maxMessagesHeight = -1 - - return gateway.Send(&packet.RequestMessages{ - ReceiverID: nil, - FrequencyID: &frequencyId, - }) } + return gateway.Send(&packet.RequestMessages{ + ReceiverID: nil, + FrequencyID: &frequency.ID, + }) } else if m.receiverIndex != -1 { // TODO: receiver } @@ -333,25 +384,16 @@ func (m *Model) RestoreAfterSwitch() tea.Cmd { func (m *Model) renderMessageBox() string { var builder strings.Builder - input := m.vi.View() - if m.locked { - input = ViFocusedBorder.Render(input) - } else { - input = ViBlurredBorder.Render(input) - } + input := m.borderStyle.Render(m.vi.View()) builder.WriteString(input) builder.WriteByte('\n') - style := lipgloss.NewStyle() - if m.locked { - style = focusStyle - } width := lipgloss.Width(input) - leftAngle := style.Render("") - rightAngle := style.Render("") + leftAngle := m.style.Render("") + rightAngle := m.style.Render("") - builder.WriteString(style.Render(LeftCorner)) + builder.WriteString(m.style.Render(LeftCorner)) width -= lipgloss.Width(LeftCorner) builder.WriteString(leftAngle) @@ -390,12 +432,12 @@ func (m *Model) renderMessageBox() string { bottomCount := width / lipgloss.Width(Border.Bottom) bottom := strings.Repeat(Border.Bottom, bottomCount) - builder.WriteString(style.Render(bottom)) + builder.WriteString(m.style.Render(bottom)) builder.WriteString(leftAngle) builder.WriteString(countStr) builder.WriteString(rightAngle) - builder.WriteString(style.Render(RightCorner)) + builder.WriteString(m.style.Render(RightCorner)) result := builder.String() @@ -413,6 +455,10 @@ func (m *Model) renderMessages(screenHeight int) string { // TODO: implement support for receiver id } + if !m.hasReadAccess { + return NoAccess.Width(m.width).Height(screenHeight).String() + "\n" + } + if btree == nil { return NoMessages.Width(m.width).Height(screenHeight).String() + "\n" } diff --git a/internal/client/ui/viminput/viminput.go b/internal/client/ui/viminput/viminput.go index af21915..296ce6d 100644 --- a/internal/client/ui/viminput/viminput.go +++ b/internal/client/ui/viminput/viminput.go @@ -14,8 +14,9 @@ import ( ) var ( - CursorStyle = lipgloss.NewStyle().Background(colors.White).Foreground(colors.Background) - VisualStyle = lipgloss.NewStyle().Background(colors.DarkGray) + CursorStyle = lipgloss.NewStyle().Background(colors.White).Foreground(colors.Background) + InactiveCursorStyle = lipgloss.NewStyle() + VisualStyle = lipgloss.NewStyle().Background(colors.DarkGray) ) const ( @@ -63,6 +64,7 @@ type Model struct { imod bool // only in O-pending amod bool // only in O-pending + inactive bool focus bool width int height int @@ -93,10 +95,12 @@ func New(width, maxHeight int) Model { tlast: false, imod: false, amod: false, + inactive: false, focus: false, width: width, height: 1, maxHeight: maxHeight, + offset: 0, } } @@ -107,6 +111,11 @@ func (m Model) Init() tea.Cmd { func (m Model) View() string { lines := m.lines + cursorStyle := CursorStyle + if m.inactive { + cursorStyle = InactiveCursorStyle + } + var builder strings.Builder switch m.mode { case NormalMode, InsertMode, OpendingMode: @@ -118,7 +127,7 @@ func (m Model) View() string { if len(m.lines) == 1 && len(line) == 0 && m.Placeholder != "" { cursorChar := m.PlaceholderStyle.Render(m.Placeholder[0:1]) rest := m.PlaceholderStyle.Render(m.Placeholder[1:]) - builder.WriteString(CursorStyle.Render(cursorChar)) + builder.WriteString(cursorStyle.Render(cursorChar)) builder.WriteString(rest) builder.WriteByte('\n') continue @@ -128,10 +137,10 @@ func (m Model) View() string { builder.WriteString(string(line)) } else if m.cursorColumn == len(line) { builder.WriteString(string(line)) - builder.WriteString(CursorStyle.Render(" ")) + builder.WriteString(cursorStyle.Render(" ")) } else { builder.WriteString(string(line[:m.cursorColumn])) - builder.WriteString(CursorStyle.Render(string(line[m.cursorColumn]))) + builder.WriteString(cursorStyle.Render(string(line[m.cursorColumn]))) builder.WriteString(string(line[m.cursorColumn+1:])) } @@ -148,7 +157,7 @@ func (m Model) View() string { if len(m.lines) == 1 && len(line) == 0 && m.Placeholder != "" { cursorChar := m.PlaceholderStyle.Render(m.Placeholder[0:1]) rest := m.PlaceholderStyle.Render(m.Placeholder[1:]) - builder.WriteString(CursorStyle.Render(cursorChar)) + builder.WriteString(cursorStyle.Render(cursorChar)) builder.WriteString(rest) builder.WriteByte('\n') continue @@ -172,10 +181,10 @@ func (m Model) View() string { builder.WriteString(string(line[:m.cursorColumn])) if len(line) != 0 { cursorChar := string(line[m.cursorColumn]) - builder.WriteString(CursorStyle.Render(cursorChar)) + builder.WriteString(cursorStyle.Render(cursorChar)) builder.WriteString(string(line[m.cursorColumn+1:])) } else { - builder.WriteString(CursorStyle.Render(" ")) + builder.WriteString(cursorStyle.Render(" ")) } builder.WriteByte('\n') continue @@ -185,13 +194,13 @@ func (m Model) View() string { upper := max(m.cursorColumn, m.vcol) builder.WriteString(string(line[:lower])) if lower == m.cursorColumn && m.cursorColumn < len(line) { - builder.WriteString(CursorStyle.Render(string(line[m.cursorColumn]))) + builder.WriteString(cursorStyle.Render(string(line[m.cursorColumn]))) lower++ } safeUpper := min(upper, len(line)) builder.WriteString(VisualStyle.Render(string(line[lower:safeUpper]))) if m.cursorColumn == safeUpper && m.cursorColumn < len(line) { - builder.WriteString(CursorStyle.Render(string(line[safeUpper]))) + builder.WriteString(cursorStyle.Render(string(line[safeUpper]))) } else if m.vcol == safeUpper && m.vcol < len(line) { builder.WriteString(VisualStyle.Render(string(line[safeUpper]))) } @@ -199,7 +208,7 @@ func (m Model) View() string { builder.WriteString(string(line[safeLower:])) if m.cursorColumn == len(line) { - builder.WriteString(CursorStyle.Render(" ")) + builder.WriteString(cursorStyle.Render(" ")) } else if m.vcol == len(line) { builder.WriteString(VisualStyle.Render(" ")) } @@ -208,19 +217,19 @@ func (m Model) View() string { // cursor line, highlight before cursor col if m.cursorColumn == len(line) { builder.WriteString(VisualStyle.Render(string(line[:m.cursorColumn]))) - builder.WriteString(CursorStyle.Render(" ")) + builder.WriteString(cursorStyle.Render(" ")) } else { builder.WriteString(VisualStyle.Render(string(line[:m.cursorColumn]))) - builder.WriteString(CursorStyle.Render(string(line[m.cursorColumn]))) + builder.WriteString(cursorStyle.Render(string(line[m.cursorColumn]))) builder.WriteString(string(line[m.cursorColumn+1:])) } } else if m.cursorLine == i { // cursor line, highlight after cursor col builder.WriteString(string(line[:m.cursorColumn])) if m.cursorColumn == len(line) { - builder.WriteString(CursorStyle.Render(" ")) + builder.WriteString(cursorStyle.Render(" ")) } else { - builder.WriteString(CursorStyle.Render(string(line[m.cursorColumn]))) + builder.WriteString(cursorStyle.Render(string(line[m.cursorColumn]))) builder.WriteString(VisualStyle.Render(string(line[m.cursorColumn+1:]))) } } else if m.vline == i && isAnchorBefore { @@ -254,7 +263,7 @@ func (m Model) View() string { if len(m.lines) == 1 && len(line) == 0 && m.Placeholder != "" { cursorChar := m.PlaceholderStyle.Render(m.Placeholder[0:1]) rest := m.PlaceholderStyle.Render(m.Placeholder[1:]) - builder.WriteString(CursorStyle.Render(cursorChar)) + builder.WriteString(cursorStyle.Render(cursorChar)) builder.WriteString(rest) builder.WriteByte('\n') continue @@ -272,10 +281,10 @@ func (m Model) View() string { builder.WriteString(string(line)) } else if m.cursorColumn == len(m.lines[m.cursorLine]) { builder.WriteString(VisualStyle.Render(string(line))) - builder.WriteString(CursorStyle.Render(" ")) + builder.WriteString(cursorStyle.Render(" ")) } else { builder.WriteString(VisualStyle.Render(string(line[:m.cursorColumn]))) - builder.WriteString(CursorStyle.Render(string(line[m.cursorColumn]))) + builder.WriteString(cursorStyle.Render(string(line[m.cursorColumn]))) builder.WriteString(VisualStyle.Render(string(line[m.cursorColumn+1:]))) } @@ -336,7 +345,9 @@ func (m Model) Height() int { } func (m *Model) Focus() { - m.focus = true + if !m.inactive { + m.focus = true + } } func (m *Model) Blur() { @@ -2082,3 +2093,14 @@ func (m *Model) Reset() { m.amod = false m.offset = 0 } + +func (m *Model) SetInactive(inactive bool) { + m.inactive = inactive + if m.inactive { + m.Blur() + } +} + +func (m *Model) Inactive() bool { + return m.inactive +} -- cgit v1.3.1