ui.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. package ui
  2. import (
  3. "mimicry/pub"
  4. "mimicry/ansi"
  5. "mimicry/feed"
  6. "fmt"
  7. "sync"
  8. )
  9. type State struct {
  10. /* the 0 index is special; it is rendered in full, not as a preview.
  11. the others are all rendered as previews. negative indexes represent
  12. parents of the 0th element (found via `inReplyTo`) and positive
  13. elements represent children (found via the pertinent collection,
  14. e.g. `replies` for a post or `outbox` for an actor) */
  15. feed *feed.Feed
  16. index int
  17. context int
  18. page pub.Container
  19. basepoint uint
  20. }
  21. func (s *State) View(width int, height uint) string {
  22. //return s.feed.Get(0).String(width)
  23. var top, center, bottom string
  24. //TODO: this should be bounded based on size of feed
  25. for i := s.index - s.context; i <= s.index + s.context; i++ {
  26. if !s.feed.Contains(i) {
  27. continue
  28. }
  29. var serialized string
  30. if i == 0 {
  31. serialized = s.feed.Get(i).String(width - 4)
  32. } else if i > 0 {
  33. serialized = "╰ " + ansi.Indent(s.feed.Get(i).Preview(width - 4), " ", false)
  34. } else {
  35. serialized = s.feed.Get(i).Preview(width - 4)
  36. }
  37. if i == s.index {
  38. center = ansi.Indent(serialized, "┃ ", true)
  39. } else if i < s.index {
  40. if top != "" { top += "\n" }
  41. top += ansi.Indent(serialized + "\n│", " ", true)
  42. } else {
  43. if bottom != "" { bottom += "\n" }
  44. bottom += ansi.Indent("│\n" + serialized, " ", true)
  45. }
  46. }
  47. return ansi.CenterVertically(top, center, bottom, height)
  48. }
  49. func (s *State) Update(input byte) {
  50. /* Interesting problem, but you will succeed! */
  51. switch input {
  52. case 'k': // up
  53. mayNeedLoading := s.index - s.context - 1
  54. if !s.feed.Contains(mayNeedLoading) {
  55. if s.feed.Contains(mayNeedLoading + 1) {
  56. s.feed.Prepend(s.feed.Get(mayNeedLoading + 1).Parents(1))
  57. }
  58. }
  59. if s.feed.Contains(s.index - 1) {
  60. s.index -= 1
  61. /* Preload more into the HTTP cache */
  62. s.PreloadUp(s.context)
  63. }
  64. case 'j': // down
  65. mayNeedLoading := s.index + 1 + s.context
  66. if !s.feed.Contains(mayNeedLoading) {
  67. if s.page != nil {
  68. var children []pub.Tangible
  69. children, s.page, s.basepoint = s.page.Harvest(1, s.basepoint)
  70. s.feed.Append(children)
  71. }
  72. }
  73. if s.feed.Contains(s.index + 1) {
  74. s.index += 1
  75. /* Preload more into the HTTP cache */
  76. s.PreloadDown(s.context)
  77. }
  78. }
  79. // TODO: the catchall down here will be to look at s.feed.Get(s.index).References()
  80. // for urls to switch to
  81. }
  82. func (s *State) SwitchTo(item pub.Any) {
  83. switch narrowed := item.(type) {
  84. case pub.Tangible:
  85. s.feed = feed.Create(narrowed)
  86. var parents, children []pub.Tangible
  87. var wg sync.WaitGroup
  88. wg.Add(2)
  89. go func() {parents = narrowed.Parents(uint(s.context)); wg.Done()}()
  90. go func() {children, s.page, s.basepoint = narrowed.Children(uint(s.context)); wg.Done()}()
  91. wg.Wait()
  92. s.feed.Prepend(parents)
  93. s.feed.Append(children)
  94. s.PreloadUp(s.context)
  95. s.PreloadDown(s.context)
  96. case pub.Container:
  97. var children []pub.Tangible
  98. children, s.page, s.basepoint = narrowed.Harvest(uint(s.context), 0)
  99. s.feed = feed.CreateAndAppend(children)
  100. s.PreloadDown(s.context)
  101. default:
  102. panic(fmt.Sprintf("unrecognized non-Tangible non-Container: %T", item))
  103. }
  104. }
  105. func (s *State) PreloadDown(amount int) {
  106. if s.page != nil {
  107. go s.page.Harvest(uint(amount), s.basepoint)
  108. }
  109. }
  110. func (s *State) PreloadUp(amount int) {
  111. if s.feed.Contains(s.index - s.context) {
  112. go s.feed.Get(s.index - s.context).Parents(uint(amount))
  113. }
  114. }
  115. func Start(input string) *State {
  116. item := pub.FetchUserInput(input)
  117. s := &State{
  118. feed: &feed.Feed{},
  119. index: 0,
  120. context: 5,
  121. }
  122. s.SwitchTo(item)
  123. return s
  124. }