2
1
Fork 0

Viewing stories now works

This commit is contained in:
sloum 2021-03-24 15:43:40 -07:00
parent a3bc3814e0
commit 401526bccc
4 changed files with 205 additions and 18 deletions

View File

@ -2,7 +2,6 @@ package main
import (
"fmt"
"os"
"reflect"
"strings"
"time"
@ -31,6 +30,7 @@ type Board struct {
Zoom int
}
func (b *Board) Run() {
defer termios.Restore()
termios.SetCharMode()
@ -42,20 +42,16 @@ func (b *Board) Run() {
switch ch {
case 'Q':
termios.Restore()
fmt.Print("\033[?25h")
os.Exit(0)
Quit()
case 'N':
b.CreateLane()
case 'n':
b.CreateStory()
case '\n':
// View current story
b.ViewStory()
case 'h', 'j', 'k', 'l', 'H', 'L', 'K', 'J':
b.ClearMessage()
b.Move(ch)
case 'c':
// Comment on current story
case 'D':
// Delete current story
case 'e':
@ -66,6 +62,8 @@ func (b *Board) Run() {
b.ZoomIn()
case '-':
b.ZoomOut()
default:
b.SetMessage(fmt.Sprintf("There is no action bount to '%c'", ch), true)
}
}
}
@ -94,6 +92,10 @@ func (b *Board) CreateLane() {
}
func (b *Board) CreateStory() {
if b.Current < 0 {
b.SetMessage("You must create a lane first", true)
return
}
b.Lanes[b.Current].CreateStory(b)
b.SetMessage("Story created", false)
}
@ -115,7 +117,7 @@ func (b Board) GetLaneSlices(width int) [][]string {
} else {
s = make([]string, 0)
}
laneText[i] = s
laneText[i-b.LaneOff] = s
}
return laneText
}
@ -198,7 +200,6 @@ func (b *Board) ZoomOut() {
func (b *Board) Move(ch rune) {
if len(b.Lanes) == 0 {
b.SetMessage("You cannot move what does not exist", true)
return
}
switch ch {
@ -206,6 +207,9 @@ func (b *Board) Move(ch rune) {
// move left a lane
if b.Current > 0 {
b.Current -= 1
if b.Current < b.LaneOff {
b.LaneOff -= 1
}
} else {
b.SetMessage("Cannot move further left", true)
}
@ -213,6 +217,9 @@ func (b *Board) Move(ch rune) {
// move selection right a lane
if b.Current < len(b.Lanes)-1 {
b.Current += 1
if b.Current > b.LaneOff + b.Zoom - 1 {
b.LaneOff += 1
}
} else {
b.SetMessage("Cannot move further right", true)
}
@ -220,6 +227,9 @@ func (b *Board) Move(ch rune) {
// move selection down a story
if b.Lanes[b.Current].Current < len(b.Lanes[b.Current].Stories)-1 {
b.Lanes[b.Current].Current += 1
if b.Lanes[b.Current].Current * 2 + 1 > b.Height-5 {
b.Lanes[b.Current].StoryOff += 1
}
} else {
b.SetMessage("Cannot move further down", true)
}
@ -227,6 +237,9 @@ func (b *Board) Move(ch rune) {
// move selection up a story
if b.Lanes[b.Current].Current > 0 {
b.Lanes[b.Current].Current -= 1
if b.Lanes[b.Current].Current < b.Lanes[b.Current].StoryOff {
b.Lanes[b.Current].StoryOff -= 1
}
} else {
b.SetMessage("Cannot move further up", true)
}
@ -271,6 +284,7 @@ func (b *Board) Move(ch rune) {
b.Lanes[b.Current+1].Current = len(b.Lanes[b.Current+1].Stories)-1
MoveRight: b.Move('l')
case 'K':
// moves story up
storyIndex := b.Lanes[b.Current].Current
if storyIndex <= 0 {
b.SetMessage("Cannot move story up", true)
@ -280,6 +294,7 @@ func (b *Board) Move(ch rune) {
swapper(storyIndex, storyIndex-1)
b.Move('k')
case 'J':
// moves story down
storyIndex := b.Lanes[b.Current].Current
if storyIndex > len(b.Lanes[b.Current].Stories)-2 {
b.SetMessage("Cannot move story down", true)
@ -292,6 +307,17 @@ func (b *Board) Move(ch rune) {
}
}
func (b *Board) ViewStory() {
if b.Current > -1 {
if b.Lanes[b.Current].Current > -1 {
b.Lanes[b.Current].Stories[b.Lanes[b.Current].Current].View(b)
}
} else {
b.SetMessage("There is no story to view", true)
}
}
func (b Board) Draw() {
var out strings.Builder
out.WriteString(cursorHome)

14
lane.go
View File

@ -5,9 +5,10 @@ import (
)
type Lane struct {
Title string
Stories []Story
Current int // Index of current story
Title string
Stories []Story
Current int // Index of current story
StoryOff int // offset for the lane slice
}
func (l *Lane) CreateStory(b *Board) {
@ -39,16 +40,15 @@ func (l Lane) Header(width int, selected bool) string {
}
}
func (l Lane) StringSlice(width int, selected bool) []string {
out := make([]string, 0, len(l.Stories) * 3 + 1)
for i, story := range l.Stories {
for i := l.StoryOff; i < len(l.Stories); i++ {
leadIn := " "
if selected && l.Current == i {
leadIn = "\033[1m➜\033[21m "
}
out = append(out, fmt.Sprintf("%s%*.*s%s", style.Lane, width, width, " ", styleOff))
out = append(out, fmt.Sprintf("%s %s%s%-*.*s%s %s", style.Lane, style.Input, leadIn, width-4, width-4, story.Title, style.Lane, styleOff))
out = append(out, fmt.Sprintf("%s %s%s%-*.*s%s %s", style.Lane, style.Input, leadIn, width-4, width-4, l.Stories[i].Title, style.Lane, styleOff))
}
if len(out) > 0 {
out = append(out, fmt.Sprintf("%s%*.*s%s", style.Lane, width, width, " ", styleOff))
@ -57,5 +57,5 @@ func (l Lane) StringSlice(width int, selected bool) []string {
}
func MakeLane(title string) Lane {
return Lane{title, make([]Story,0,5), -1}
return Lane{title, make([]Story,0,5), -1, 0}
}

34
main.go
View File

@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"os"
"strings"
"tildegit.org/sloum/swim/termios"
"time"
)
@ -56,7 +57,7 @@ func GetAndConfirmCommandLine(prefix string) (string, error) {
fmt.Print(style.Input)
fmt.Printf("%s%s%sIs %q correct? (y/n/c)", cursorEnd, upAndLeft, style.Input, line)
conf = Getch()
if conf == 'y' {
if conf == 'y' || conf == '\n' {
break
} else if conf == 'n' {
fmt.Print(cursorEnd)
@ -72,6 +73,37 @@ func GetAndConfirmCommandLine(prefix string) (string, error) {
return line, err
}
// Adapted From:
// https://gist.github.com/kennwhite/306317d81ab4a885a965e25aa835b8ef
func WrapText(text string, lineWidth int) string {
var wrapped strings.Builder
words := strings.Fields(strings.TrimSpace(text))
if len(words) == 0 {
return text
}
wrapped.WriteString(words[0])
spaceLeft := lineWidth - wrapped.Len()
for _, word := range words[1:] {
if len(word)+1 > spaceLeft {
wrapped.WriteRune('\n')
wrapped.WriteString(word)
spaceLeft = lineWidth - len(word)
} else {
wrapped.WriteRune(' ')
wrapped.WriteString(word)
spaceLeft -= 1 + len(word)
}
}
return wrapped.String()
}
func Quit() {
termios.Restore()
fmt.Print("\033[?25h")
os.Exit(0)
}
func main() {
style.Init(SimpleColor)
cols, rows := termios.GetWindowSize()

131
story.go
View File

@ -1,6 +1,9 @@
package main
import (
"fmt"
"os/user"
"strings"
"time"
)
@ -13,6 +16,132 @@ type Story struct {
Comments []Comment
Created time.Time
Updated time.Time
Offset int
StSlice []string
Points int
}
func (s *Story) View(b *Board) {
s.BuildStorySlice(b.Width)
var ch rune
for {
s.Draw(b)
ch = Getch()
switch ch {
case 'j', 'k':
s.Scroll(ch, b)
case ':':
b.EnterCommand()
case 'x':
s.Offset = 0
return
case 'Q':
Quit()
case 'c', 'C':
s.AddComment(b)
}
}
}
func (s *Story) AddComment(b *Board) {
comment, err := GetAndConfirmCommandLine("Comment: ")
if err != nil {
b.SetMessage(err.Error(), true)
return
}
u, err := user.Current()
if err != nil {
b.SetMessage(err.Error(), true)
return
}
s.Comments = append(s.Comments, Comment{u.Name, comment, time.Now()})
s.BuildStorySlice(b.Width)
}
func (s *Story) BuildStorySlice(width int) {
var out strings.Builder
out.WriteRune('\n')
out.WriteString(WrapText(s.Title, width-7))
out.WriteRune('\n')
out.WriteRune('\n')
out.WriteString("Updated: ")
out.WriteString(s.Updated.Format(time.UnixDate))
pts := s.Points
if pts < 1 {
out.WriteString("\nPoints: -\n\n")
} else {
out.WriteString(fmt.Sprintf("\nPoints: %d\n\n", s.Points))
}
out.WriteString("Users: ")
out.WriteString(WrapText(strings.Join(s.Users, ", "), width-7))
out.WriteRune('\n')
out.WriteRune('\n')
out.WriteString("Description:\n\n")
out.WriteString(WrapText(s.Body, width))
out.WriteRune('\n')
out.WriteRune('\n')
for i, task := range s.Tasks {
out.WriteString(fmt.Sprintf("%2d. ", i+1))
if task.Complete {
out.WriteString("☒ ")
} else {
out.WriteString("☐ ")
}
out.WriteString(WrapText(task.Body, width-6))
out.WriteRune('\n')
}
out.WriteRune('\n')
out.WriteString(strings.Repeat("━", width))
out.WriteString("\n\nComments:\n\n")
for _, c := range s.Comments {
out.WriteString(c.User)
out.WriteRune('\n')
out.WriteString(WrapText(c.Created.Format(time.UnixDate), width))
out.WriteRune('\n')
out.WriteRune('\n')
out.WriteString(WrapText(c.Body, width))
out.WriteRune('\n')
out.WriteRune('\n')
out.WriteString(strings.Repeat("╍", width))
out.WriteRune('\n')
out.WriteRune('\n')
}
s.StSlice = strings.Split(out.String(), "\n")
}
func (s Story) Draw(b *Board) {
var out strings.Builder
out.WriteString(cursorHome)
out.WriteString(b.PrintHeader())
for i := 0; i < b.Height-3; i++ {
index := i+s.Offset
if index >= len(s.StSlice) {
out.WriteString(fmt.Sprintf("%*.*s\n", b.Width, b.Width, ""))
} else {
out.WriteString(fmt.Sprintf("%-*.*s\n", b.Width, b.Width, s.StSlice[index]))
}
}
out.WriteString(b.PrintInputArea())
out.WriteString(b.PrintMessage())
fmt.Print(out.String())
}
func (s *Story) Scroll(dir rune, b *Board) {
switch dir {
case 'j':
if s.Offset + b.Height - 3 < len(s.StSlice) {
s.Offset += 1
} else {
b.SetMessage("Cannot move further down", true)
}
case 'k':
if s.Offset > 0 {
s.Offset -= 1
} else {
b.SetMessage("Cannot move further up", true)
}
}
}
func (s Story) Duplicate() Story {
@ -32,5 +161,5 @@ func (s Story) Duplicate() Story {
}
func MakeStory(title string) Story {
return Story{title,"", make([]string,0,2), -1, make([]Task,0,2), make([]Comment,0,2), time.Now(), time.Now()}
return Story{title,"", make([]string,0,2), -1, make([]Task,0,2), make([]Comment,0,2), time.Now(), time.Now(), 0, []string{}, -1}
}