~reesmichael1/roman

5982bd368aba9e317fe1b5e9929dffefa586ccb7 — Michael Rees 8 months ago c066910
Return user to feed selection after viewing a post

This commit adds a new error type, InterruptError, to handle the user
asking us nicely to exit during the selection. This lets us check if
this has been raised after each selection, and if not, return the user
to the beginning. It also fixes a few bugs in the menu paging
implementation.
4 files changed, 42 insertions(+), 14 deletions(-)

M src/romanpkg/errors.nim
M src/romanpkg/feeds.nim
M src/romanpkg/main.nim
M src/romanpkg/termask.nim
M src/romanpkg/errors.nim => src/romanpkg/errors.nim +6 -2
@@ 1,2 1,6 @@
type RomanError* =
  object of Exception
type
  RomanError* =
    object of Exception
  InterruptError* =
    object of Exception


M src/romanpkg/feeds.nim => src/romanpkg/feeds.nim +6 -2
@@ 1,3 1,4 @@
import options
import sequtils
import tables
import terminal


@@ 27,7 28,7 @@ proc formatTitle*(feed: Feed): string {.raises: [].} =
  feed.title & " [" & $feed.unreadPosts & "/" & $feed.posts.len & "]"


proc displayFeed*(feed: var Feed) {.raises: [RomanError].} =
proc displayFeed*(feed: var Feed) {.raises: [RomanError, InterruptError].} =
  try:
    under(feed.title & "\n", sty = {styleBright})



@@ 37,8 38,11 @@ proc displayFeed*(feed: var Feed) {.raises: [RomanError].} =
      display[p.title] = p.formatTitle()
      titles.add(p.title)

    let title = promptList("Select Post", titles, show = 10,
    let selectedTitle = promptList("Select Post", titles, show = 10,
        displayNames = display)
    if selectedTitle.isNone():
      raise newException(InterruptError, "no feed selected")
    let title = selectedTitle.unsafeGet()
    let post = filter(feed.posts, proc(p: Post): bool = p.title == title)[0]
    displayPost(post)
    post.markAsRead()

M src/romanpkg/main.nim => src/romanpkg/main.nim +20 -6
@@ 1,4 1,5 @@
import os
import options
import sequtils
import tables



@@ 8,13 9,17 @@ import subscriptions
import termask


proc chooseFeed(feeds: seq[Feed]): Feed {.raises: [RomanError].} =
proc chooseFeed(feeds: seq[Feed]): Feed {.raises: [RomanError,
    InterruptError].} =
  var displayNames = initTable[string, string]()
  for feed in feeds:
    displayNames[feed.title] = feed.formatTitle()
  try:
    let name = promptList("Select Feed", toSeq(displayNames.keys),
    let selectedName = promptList("Select Feed", toSeq(displayNames.keys),
        displayNames = displayNames, show = 10)
    if selectedName.isNone:
      raise newException(InterruptError, "no feed selected")
    let name = selectedName.unsafeGet()
    result = filter(feeds, proc(f: Feed): bool = f.title == name)[0]
  except ValueError as e:
    raise newException(RomanError, e.msg)


@@ 22,8 27,9 @@ proc chooseFeed(feeds: seq[Feed]): Feed {.raises: [RomanError].} =
    raise newException(RomanError, e.msg)


proc runMainPath() {.raises: [RomanError].} =
proc runMainPath() {.raises: [RomanError, InterruptError].} =
  let subs = getSubscriptions()
  var feeds: seq[Feed]
  var feed: Feed
  if subs.len == 0:
    echo "You aren't subscribed to any feeds yet! ",


@@ 31,11 37,16 @@ proc runMainPath() {.raises: [RomanError].} =
    return
  elif subs.len == 1:
    feed = getFeed(subs[0].url)
    feeds = @[feed]
  else:
    let feeds = map(subs, proc(s: Subscription): Feed = getFeed(s.url))
    feed = chooseFeed(feeds)
    feeds = map(subs, proc(s: Subscription): Feed = getFeed(s.url))

  displayFeed(feed)
  while true:
    if feeds.len == 1:
      displayFeed(feed)
    else:
      feed = chooseFeed(feeds)
      displayFeed(feed)


proc main*(subscribeURL: string = "") {.raises: [].} =


@@ 47,3 58,6 @@ proc main*(subscribeURL: string = "") {.raises: [].} =
  except RomanError as e:
    echo "error: ", e.msg
    quit(1)

  except InterruptError:
    quit(0)

M src/romanpkg/termask.nim => src/romanpkg/termask.nim +10 -4
@@ 1,3 1,4 @@
import options
import sequtils
import strutils
import terminal


@@ 70,7 71,7 @@ proc goBackPage(currentArgs: var seq[string], selectedIx: var int,
    return
  # Go back to the last set of results and reset
  sliceIx -= 1
  if sliceIx <= 0:
  if sliceIx < 0:
    sliceIx = argSlices.len - 1
  selectedIx = 0
  currentArgs = argSlices[sliceIx]


@@ 78,7 79,7 @@ proc goBackPage(currentArgs: var seq[string], selectedIx: var int,

proc promptList*(question: string, args: openarray[string],
    displayNames: Table[string, string] = initTable[string, string](),
        show: int = -1): string {.raises: [ValueError, IOError].} =
        show: int = -1): Option[string] {.raises: [ValueError, IOError].} =
  var
    selectedIx = 0
    selectionMade = false


@@ 91,9 92,12 @@ proc promptList*(question: string, args: openarray[string],
    if args.len <= show:
      argSlices = @[toSeq(args)]
    else:
      # Split the arguments into chunks of length show
      # Store those chunks in argSlices
      var counter = 0
      while counter < args.len:
        let top = min(counter + show, args.len - 1)
        # Subtract 2 because both counter and show are 1 indexed
        let top = min(counter + show - 1, args.len - 1)
        let nextArgs = args[counter..top]
        argSlices.add(nextArgs)
        counter += show


@@ 173,6 177,8 @@ proc promptList*(question: string, args: openarray[string],
      of 'P':
        goBackPage(currentArgs, selectedIx, sliceIx, argSlices)
        break
      of 'q':
        return none(string)
      of '\3':
        showCursor(stdout)
        # Move the cursor down to the end of the arguments list


@@ 189,4 195,4 @@ proc promptList*(question: string, args: openarray[string],
  for i in 0..<currentArgs.len():
    cursorUp(stdout)
  showCursor(stdout)
  return currentArgs[selectedIx]
  return some(currentArgs[selectedIx])