util.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. package ui
  2. import (
  3. "cmp"
  4. "slices"
  5. "strings"
  6. "github.com/ayn2op/discordo/internal/config"
  7. "github.com/ayn2op/tview"
  8. "github.com/diamondburned/arikawa/v3/discord"
  9. "github.com/diamondburned/ningen/v3"
  10. "github.com/gdamore/tcell/v3"
  11. )
  12. // ConfigureBox configures the provided box according to the provided theme.
  13. func ConfigureBox(box *tview.Box, cfg *config.Theme) *tview.Box {
  14. border := cfg.Border
  15. normalBorderStyle, activeBorderStyle := border.NormalStyle.Style, border.ActiveStyle.Style
  16. normalBorderSet, activeBorderSet := border.NormalSet.BorderSet, border.ActiveSet.BorderSet
  17. title := cfg.Title
  18. normalTitleStyle, activeTitleStyle := title.NormalStyle.Style, title.ActiveStyle.Style
  19. footer := cfg.Footer
  20. normalFooterStyle, activeFooterStyle := footer.NormalStyle.Style, footer.ActiveStyle.Style
  21. padding := border.Padding
  22. box.
  23. SetBorderStyle(normalBorderStyle).
  24. SetBorderSet(normalBorderSet).
  25. SetBorderPadding(padding[0], padding[1], padding[2], padding[3]).
  26. SetTitleStyle(normalTitleStyle).
  27. SetTitleAlignment(title.Alignment.Alignment).
  28. SetFooterStyle(normalFooterStyle).
  29. SetFooterAlignment(footer.Alignment.Alignment).
  30. SetBlurFunc(func() {
  31. box.
  32. SetBorderStyle(normalBorderStyle).
  33. SetBorderSet(normalBorderSet)
  34. box.SetTitleStyle(normalTitleStyle).SetFooterStyle(normalFooterStyle)
  35. }).
  36. SetFocusFunc(func() {
  37. box.
  38. SetBorderStyle(activeBorderStyle).
  39. SetBorderSet(activeBorderSet)
  40. box.SetTitleStyle(activeTitleStyle).SetFooterStyle(activeFooterStyle)
  41. })
  42. if border.Enabled {
  43. box.SetBorders(tview.BordersAll)
  44. }
  45. return box
  46. }
  47. // Centered creates a new grid with provided primitive aligned in the center.
  48. func Centered(p tview.Primitive, width, height int) tview.Primitive {
  49. return tview.NewGrid().
  50. SetColumns(0, width, 0).
  51. SetRows(0, height, 0).
  52. AddItem(p, 1, 1, 1, 1, 0, 0, true)
  53. }
  54. func ChannelToString(channel discord.Channel, icons config.Icons, state *ningen.State) string {
  55. var icon string
  56. switch channel.Type {
  57. case discord.DirectMessage, discord.GroupDM:
  58. if channel.Name != "" {
  59. return channel.Name
  60. }
  61. recipients := make([]string, len(channel.DMRecipients))
  62. for i, r := range channel.DMRecipients {
  63. if state != nil && channel.Type == discord.DirectMessage {
  64. if rel, ok := state.RelationshipState.FullRelationship(r.ID); ok && rel.Type == discord.FriendRelationship {
  65. if rel.Nickname != nil && *rel.Nickname != "" {
  66. recipients[i] = *rel.Nickname
  67. continue
  68. }
  69. }
  70. }
  71. recipients[i] = r.DisplayOrUsername()
  72. }
  73. return strings.Join(recipients, ", ")
  74. case discord.GuildCategory:
  75. icon = icons.GuildCategory
  76. case discord.GuildText:
  77. icon = icons.GuildText
  78. case discord.GuildVoice:
  79. icon = icons.GuildVoice
  80. case discord.GuildStageVoice:
  81. icon = icons.GuildStageVoice
  82. case discord.GuildAnnouncementThread:
  83. icon = icons.GuildAnnouncementThread
  84. case discord.GuildPublicThread:
  85. icon = icons.GuildPublicThread
  86. case discord.GuildPrivateThread:
  87. icon = icons.GuildPrivateThread
  88. case discord.GuildAnnouncement:
  89. icon = icons.GuildAnnouncement
  90. case discord.GuildForum:
  91. icon = icons.GuildForum
  92. case discord.GuildStore:
  93. icon = icons.GuildStore
  94. }
  95. return icon + channel.Name
  96. }
  97. func SortGuildChannels(channels []discord.Channel) {
  98. slices.SortFunc(channels, func(a, b discord.Channel) int {
  99. return cmp.Compare(a.Position, b.Position)
  100. })
  101. }
  102. func SortPrivateChannels(channels []discord.Channel) {
  103. slices.SortFunc(channels, func(a, b discord.Channel) int {
  104. // Descending order
  105. return cmp.Compare(getMessageIDFromChannel(b), getMessageIDFromChannel(a))
  106. })
  107. }
  108. func getMessageIDFromChannel(channel discord.Channel) discord.MessageID {
  109. if channel.LastMessageID.IsValid() {
  110. return channel.LastMessageID
  111. }
  112. return discord.MessageID(channel.ID)
  113. }
  114. func MergeStyle(base, overlay tcell.Style) tcell.Style {
  115. fg := overlay.GetForeground()
  116. if fg == tcell.ColorDefault {
  117. fg = base.GetForeground()
  118. }
  119. bg := overlay.GetBackground()
  120. if bg == tcell.ColorDefault {
  121. bg = base.GetBackground()
  122. }
  123. style := base.Foreground(fg).Background(bg)
  124. style = style.Bold(base.HasBold() || overlay.HasBold())
  125. style = style.Dim(base.HasDim() || overlay.HasDim())
  126. style = style.Italic(base.HasItalic() || overlay.HasItalic())
  127. style = style.Blink(base.HasBlink() || overlay.HasBlink())
  128. style = style.Reverse(base.HasReverse() || overlay.HasReverse())
  129. style = style.StrikeThrough(base.HasStrikeThrough() || overlay.HasStrikeThrough())
  130. if base.HasUnderline() || overlay.HasUnderline() {
  131. style = style.Underline(true)
  132. }
  133. return style
  134. }