summaryrefslogtreecommitdiff
path: root/internal/client/ui
diff options
context:
space:
mode:
authorKyren223 <Kyren223@proton.me>2025-01-16 17:36:06 +0200
committerKyren223 <Kyren223@proton.me>2025-01-16 17:36:06 +0200
commit9ebd017160ceac890b8e03b213064ae00c9fc158 (patch)
treef702ab32186e281d15edf30fcb899f0532738d22 /internal/client/ui
parent5bc8a1ff7aae46225084a08d6f2c96d076860574 (diff)
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
Diffstat (limited to 'internal/client/ui')
-rw-r--r--internal/client/ui/core/chat/chat.go130
-rw-r--r--internal/client/ui/viminput/viminput.go60
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
+}