Added the Node notation to define paths to screens..
This commit is contained in:
parent
c96c2a7559
commit
061add76a8
7 changed files with 199 additions and 158 deletions
184
cmd/test/main.go
184
cmd/test/main.go
|
@ -51,10 +51,7 @@ func ExtractSessionData(c *tg.Context) *SessionData {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
startScreenButton = tg.NewButton("Back").ActionFunc(func(c *tg.Context){
|
startScreenButton = tg.NewButton("Home").Go("/")
|
||||||
c.GoUp()
|
|
||||||
})
|
|
||||||
|
|
||||||
incDecKeyboard = tg.NewKeyboard().Row(
|
incDecKeyboard = tg.NewKeyboard().Row(
|
||||||
tg.NewButton("+").ActionFunc(func(c *tg.Context) {
|
tg.NewButton("+").ActionFunc(func(c *tg.Context) {
|
||||||
d := ExtractSessionData(c)
|
d := ExtractSessionData(c)
|
||||||
|
@ -71,14 +68,14 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
navKeyboard = tg.NewKeyboard().Row(
|
navKeyboard = tg.NewKeyboard().Row(
|
||||||
tg.NewButton("Inc/Dec").ScreenChange("/start/inc-dec"),
|
tg.NewButton("Inc/Dec").Go("/inc-dec"),
|
||||||
).Row(
|
).Row(
|
||||||
tg.NewButton("Upper case").ActionFunc(func(c *tg.Context){
|
tg.NewButton("Upper case").ActionFunc(func(c *tg.Context){
|
||||||
c.Go("/start/upper-case", "this shit", "works")
|
c.Go("/upper-case", "this shit", "works")
|
||||||
}),
|
}),
|
||||||
tg.NewButton("Lower case").ScreenChange("/start/lower-case"),
|
tg.NewButton("Lower case").Go("/case"),
|
||||||
).Row(
|
).Row(
|
||||||
tg.NewButton("Send location").ScreenChange("/start/send-location"),
|
tg.NewButton("Send location").Go("/send-location"),
|
||||||
).Reply().WithOneTime(true)
|
).Reply().WithOneTime(true)
|
||||||
|
|
||||||
sendLocationKeyboard = tg.NewKeyboard().Row(
|
sendLocationKeyboard = tg.NewKeyboard().Row(
|
||||||
|
@ -106,124 +103,106 @@ var (
|
||||||
).Reply()
|
).Reply()
|
||||||
)
|
)
|
||||||
|
|
||||||
var theNode = tg.NewNode(
|
|
||||||
"/", tg.WidgetFunc(func(c *tg.Context){
|
|
||||||
c.Go("/start")
|
|
||||||
}),
|
|
||||||
tg.NewNode(
|
|
||||||
"start", tg.WidgetFunc(func(c *tg.Context){}),
|
|
||||||
tg.NewNode(
|
|
||||||
"profile", tg.WidgetFunc(func(c *tg.Context){}),
|
|
||||||
),
|
|
||||||
tg.NewNode(
|
|
||||||
"upper-case", tg.WidgetFunc(func(c *tg.Context){}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
var beh = tg.NewBehaviour().
|
var beh = tg.NewBehaviour().
|
||||||
WithInitFunc(func(c *tg.Context) {
|
WithInitFunc(func(c *tg.Context) {
|
||||||
// The session initialization.
|
// The session initialization.
|
||||||
c.Session.Data = &SessionData{}
|
c.Session.Data = &SessionData{}
|
||||||
}).WithScreens(
|
}).WithRootNode(tg.NewRootNode(
|
||||||
tg.NewScreen("/start", tg.NewPage(
|
// The "/" widget.
|
||||||
"",
|
tg.NewPage().
|
||||||
).WithInline(
|
WithInline(
|
||||||
tg.NewKeyboard().Row(
|
tg.NewKeyboard().Row(
|
||||||
tg.NewButton("GoT Github page").
|
tg.NewButton("GoT Github page").
|
||||||
WithUrl("https://github.com/mojosa-software/got"),
|
WithUrl("https://github.com/mojosa-software/got"),
|
||||||
).Inline().Widget("The bot started!"),
|
).Inline().Widget("The bot started!"),
|
||||||
).WithReply(
|
).WithReply(
|
||||||
navKeyboard.Widget("Choose what you are interested in"),
|
navKeyboard.Widget("Choose what you are interested in"),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
tg.NewScreen("/start/inc-dec", tg.NewPage(
|
|
||||||
"The screen shows how "+
|
tg.NewNode(
|
||||||
"user separated data works "+
|
"inc-dec", tg.NewPage().WithReply(
|
||||||
"by saving the counter for each of users "+
|
|
||||||
"separately. ",
|
|
||||||
).WithReply(
|
|
||||||
incDecKeyboard.Reply().Widget("Press the buttons to increment and decrement"),
|
incDecKeyboard.Reply().Widget("Press the buttons to increment and decrement"),
|
||||||
).ActionFunc(func(c *tg.Context) {
|
).ActionFunc(func(c *tg.Context) {
|
||||||
// The function will be calleb before serving page.
|
// The function will be calleb before serving page.
|
||||||
d := ExtractSessionData(c)
|
d := ExtractSessionData(c)
|
||||||
c.Sendf("Current counter value = %d", d.Counter)
|
c.Sendf("Current counter value = %d", d.Counter)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
||||||
tg.NewScreen("/start/upper-case", tg.NewPage(
|
tg.NewNode(
|
||||||
|
"upper-case", tg.NewPage().WithText(
|
||||||
"Type text and the bot will send you the upper case version to you",
|
"Type text and the bot will send you the upper case version to you",
|
||||||
).WithReply(
|
).WithReply(
|
||||||
navToStartKeyboard.Widget(""),
|
navToStartKeyboard.Widget(""),
|
||||||
).WithSub(
|
).WithSub(
|
||||||
NewMutateMessageWidget(strings.ToUpper),
|
NewMutateMessageWidget(strings.ToUpper),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
tg.NewScreen("/start/lower-case", tg.NewPage(
|
tg.NewNode(
|
||||||
|
"lower-case", tg.NewPage().WithText(
|
||||||
"Type text and the bot will send you the lower case version",
|
"Type text and the bot will send you the lower case version",
|
||||||
).WithReply(
|
).WithReply(
|
||||||
navToStartKeyboard.Widget(""),
|
navToStartKeyboard.Widget(""),
|
||||||
).WithSub(
|
).WithSub(
|
||||||
NewMutateMessageWidget(strings.ToLower),
|
NewMutateMessageWidget(strings.ToLower),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
tg.NewScreen("/start/send-location", tg.NewPage(
|
tg.NewNode(
|
||||||
"",
|
"send-location", tg.NewPage().WithReply(
|
||||||
).WithReply(
|
sendLocationKeyboard.Widget("Press the button to send your location!"),
|
||||||
sendLocationKeyboard.Widget("Press the button to send your location!"),
|
).WithInline(
|
||||||
).WithInline(
|
tg.NewKeyboard().Row(
|
||||||
tg.NewKeyboard().Row(
|
tg.NewButton(
|
||||||
tg.NewButton(
|
"Check",
|
||||||
"Check",
|
).WithData(
|
||||||
).WithData(
|
"check",
|
||||||
"check",
|
).ActionFunc(func(c *tg.Context) {
|
||||||
).ActionFunc(func(c *tg.Context) {
|
d := ExtractSessionData(c)
|
||||||
d := ExtractSessionData(c)
|
c.Sendf("Counter = %d", d.Counter)
|
||||||
c.Sendf("Counter = %d", d.Counter)
|
}),
|
||||||
}),
|
).Inline().Widget("Press the button to display your counter"),
|
||||||
).Inline().Widget("Press the button to display your counter"),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
).WithCommands(
|
),
|
||||||
tg.NewCommand("start").
|
)).WithCommands(
|
||||||
Desc("start or restart the bot or move to the start screen").
|
tg.NewCommand("start").
|
||||||
ActionFunc(func(c *tg.Context){
|
Desc("start or restart the bot or move to the start screen").
|
||||||
c.Sendf("Your username is %q", c.Message.From.UserName)
|
ActionFunc(func(c *tg.Context){
|
||||||
c.Go("/start")
|
c.Sendf("Your username is %q", c.Message.From.UserName)
|
||||||
}),
|
c.Go("/start")
|
||||||
tg.NewCommand("hello").
|
}),
|
||||||
Desc("sends the 'Hello, World!' message back").
|
tg.NewCommand("hello").
|
||||||
ActionFunc(func(c *tg.Context) {
|
Desc("sends the 'Hello, World!' message back").
|
||||||
c.Sendf("Hello, World!")
|
ActionFunc(func(c *tg.Context) {
|
||||||
}),
|
c.Sendf("Hello, World!")
|
||||||
tg.NewCommand("read").
|
}),
|
||||||
Desc("reads a string and sends it back").
|
tg.NewCommand("read").
|
||||||
WidgetFunc(func(c *tg.Context) {
|
Desc("reads a string and sends it back").
|
||||||
c.Sendf("Type text and I will send it back to you")
|
WidgetFunc(func(c *tg.Context) {
|
||||||
for u := range c.Input() {
|
c.Sendf("Type text and I will send it back to you")
|
||||||
if u.Message == nil {
|
for u := range c.Input() {
|
||||||
continue
|
if u.Message == nil {
|
||||||
}
|
continue
|
||||||
c.Sendf("You typed %q", u.Message.Text)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
c.Sendf("Done")
|
c.Sendf("You typed %q", u.Message.Text)
|
||||||
}),
|
break
|
||||||
tg.NewCommand("image").
|
}
|
||||||
Desc("sends a sample image").
|
c.Sendf("Done")
|
||||||
ActionFunc(func(c *tg.Context) {
|
}),
|
||||||
img := tg.NewFile("media/cat.jpg").Image().Caption("A cat!")
|
tg.NewCommand("image").
|
||||||
c.Send(img)
|
Desc("sends a sample image").
|
||||||
}),
|
ActionFunc(func(c *tg.Context) {
|
||||||
tg.NewCommand("botname").
|
img := tg.NewFile("media/cat.jpg").Image().Caption("A cat!")
|
||||||
Desc("get the bot name").
|
c.Send(img)
|
||||||
ActionFunc(func(c *tg.Context) {
|
}),
|
||||||
bd := c.Bot.Data.(*BotData)
|
tg.NewCommand("botname").
|
||||||
c.Sendf("My name is %q", bd.Name)
|
Desc("get the bot name").
|
||||||
}),
|
ActionFunc(func(c *tg.Context) {
|
||||||
)
|
bd := c.Bot.Data.(*BotData)
|
||||||
|
c.Sendf("My name is %q", bd.Name)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
var gBeh = tg.NewGroupBehaviour().
|
var gBeh = tg.NewGroupBehaviour().
|
||||||
InitFunc(func(c *tg.GC) {
|
InitFunc(func(c *tg.GC) {
|
||||||
|
@ -239,8 +218,7 @@ var gBeh = tg.NewGroupBehaviour().
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.Println(theNode.ScreenMap())
|
log.Println(beh.Screens)
|
||||||
return
|
|
||||||
token := os.Getenv("BOT_TOKEN")
|
token := os.Getenv("BOT_TOKEN")
|
||||||
|
|
||||||
bot, err := tg.NewBot(token)
|
bot, err := tg.NewBot(token)
|
||||||
|
|
20
tg/beh.go
20
tg/beh.go
|
@ -37,8 +37,13 @@ func (b *Behaviour) WithInitFunc(
|
||||||
return b.WithInit(fn)
|
return b.WithInit(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Behaviour) WithRootNode(node *RootNode) *Behaviour {
|
||||||
|
b.Screens = node.ScreenMap()
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
// The function sets screens.
|
// The function sets screens.
|
||||||
func (b *Behaviour) WithScreens(
|
/*func (b *Behaviour) WithScreens(
|
||||||
screens ...*Screen,
|
screens ...*Screen,
|
||||||
) *Behaviour {
|
) *Behaviour {
|
||||||
for _, screen := range screens {
|
for _, screen := range screens {
|
||||||
|
@ -52,7 +57,7 @@ func (b *Behaviour) WithScreens(
|
||||||
b.Screens[screen.Id] = screen
|
b.Screens[screen.Id] = screen
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// The function sets as the standard root widget CommandWidget
|
// The function sets as the standard root widget CommandWidget
|
||||||
// and its commands..
|
// and its commands..
|
||||||
|
@ -69,18 +74,19 @@ func (b *Behaviour) WithCommands(cmds ...*Command) *Behaviour {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether the screen exists in the behaviour.
|
// Check whether the screen exists in the behaviour.
|
||||||
func (beh *Behaviour) ScreenExist(id ScreenId) bool {
|
func (beh *Behaviour) PathExist(pth Path) bool {
|
||||||
_, ok := beh.Screens[id]
|
_, ok := beh.Screens[pth]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the screen by it's ID.
|
// Returns the screen by it's ID.
|
||||||
func (beh *Behaviour) GetScreen(id ScreenId) *Screen {
|
func (beh *Behaviour) GetScreen(pth Path) *Screen {
|
||||||
if !beh.ScreenExist(id) {
|
pth = pth.Clean()
|
||||||
|
if !beh.PathExist(pth) {
|
||||||
panic(ScreenNotExistErr)
|
panic(ScreenNotExistErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
screen := beh.Screens[id]
|
screen := beh.Screens[pth]
|
||||||
return screen
|
return screen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ func (btn *Button) ActionFunc(fn ActionFunc) *Button {
|
||||||
return btn.WithAction(fn)
|
return btn.WithAction(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (btn *Button) ScreenChange(sc ScreenChange) *Button {
|
func (btn *Button) Go(sc ScreenChange) *Button {
|
||||||
return btn.WithAction(sc)
|
return btn.WithAction(sc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -165,7 +165,7 @@ func (widget *CommandWidget) Serve(c *Context) {
|
||||||
|
|
||||||
var cmdUpdates *UpdateChan
|
var cmdUpdates *UpdateChan
|
||||||
for u := range c.Input() {
|
for u := range c.Input() {
|
||||||
if c.CurScreen() == "" && u.Message != nil {
|
if c.Path() == "" && u.Message != nil {
|
||||||
// Skipping and executing the preinit action
|
// Skipping and executing the preinit action
|
||||||
// while we have the empty screen.
|
// while we have the empty screen.
|
||||||
// E. g. the session did not start.
|
// E. g. the session did not start.
|
||||||
|
|
|
@ -15,12 +15,16 @@ type Page struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return new page with the specified text.
|
// Return new page with the specified text.
|
||||||
func NewPage(text string) *Page {
|
func NewPage() *Page {
|
||||||
ret := &Page{}
|
ret := &Page{}
|
||||||
ret.Text = text
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Page) WithText(text string) *Page {
|
||||||
|
p.Text = text
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
// Set the inline keyboard.
|
// Set the inline keyboard.
|
||||||
func (p *Page) WithInline(inline *InlineKeyboardWidget) *Page {
|
func (p *Page) WithInline(inline *InlineKeyboardWidget) *Page {
|
||||||
p.Inline = inline
|
p.Inline = inline
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
//tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
//tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
"path"
|
//"path"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ContextType string
|
type ContextType string
|
||||||
|
@ -25,7 +25,7 @@ type context struct {
|
||||||
Bot *Bot
|
Bot *Bot
|
||||||
skippedUpdates *UpdateChan
|
skippedUpdates *UpdateChan
|
||||||
// Current screen ID.
|
// Current screen ID.
|
||||||
screenId, prevScreenId ScreenId
|
path, prevPath Path
|
||||||
}
|
}
|
||||||
|
|
||||||
// Goroutie function to handle each user.
|
// Goroutie function to handle each user.
|
||||||
|
@ -42,17 +42,17 @@ func (c *context) run(a Action, u *Update) {
|
||||||
a.Act(&Context{context: c, Update: u})
|
a.Act(&Context{context: c, Update: u})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) CurScreen() ScreenId {
|
func (c *Context) Path() Path {
|
||||||
return c.screenId
|
return c.path
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) PrevScreen() ScreenId {
|
func (c *Context) PrevPath() Path {
|
||||||
return c.prevScreenId
|
return c.prevPath
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) Run(a Action, u *Update) {
|
func (c *Context) Run(a Action, u *Update) {
|
||||||
if a != nil {
|
if a != nil {
|
||||||
a.Act(&Context{context: c.context, Update: u})
|
a.Act(c.Copy().WithUpdate(u))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,13 +144,13 @@ func (af ActionFunc) Act(c *Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The type implements changing screen to the underlying ScreenId
|
// The type implements changing screen to the underlying ScreenId
|
||||||
type ScreenChange ScreenId
|
type ScreenChange Path
|
||||||
|
|
||||||
func (sc ScreenChange) Act(c *Context) {
|
func (sc ScreenChange) Act(c *Context) {
|
||||||
if !c.Bot.behaviour.ScreenExist(ScreenId(sc)) {
|
if !c.Bot.behaviour.PathExist(Path(sc)) {
|
||||||
panic(ScreenNotExistErr)
|
panic(ScreenNotExistErr)
|
||||||
}
|
}
|
||||||
err := c.Go(ScreenId(sc))
|
err := c.Go(Path(sc))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -159,18 +159,22 @@ func (sc ScreenChange) Act(c *Context) {
|
||||||
type C = Context
|
type C = Context
|
||||||
|
|
||||||
// Changes screen of user to the Id one.
|
// Changes screen of user to the Id one.
|
||||||
func (c *Context) Go(screenId ScreenId, args ...any) error {
|
func (c *Context) Go(pth Path, args ...any) error {
|
||||||
if !c.Bot.behaviour.ScreenExist(screenId) {
|
if !c.PathExist(pth) {
|
||||||
return ScreenNotExistErr
|
return ScreenNotExistErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getting the screen and changing to
|
// Getting the screen and changing to
|
||||||
// then executing its widget.
|
// then executing its widget.
|
||||||
screen := c.Bot.behaviour.Screens[screenId]
|
if !pth.IsAbs() {
|
||||||
c.prevScreenId = c.screenId
|
pth = (c.Path() + "/" + pth).Clean()
|
||||||
c.screenId = screenId
|
}
|
||||||
|
|
||||||
|
c.prevPath = c.path
|
||||||
|
c.path = pth
|
||||||
|
|
||||||
// Stopping the current widget.
|
// Stopping the current widget.
|
||||||
|
screen := c.Bot.behaviour.Screens[pth]
|
||||||
c.skippedUpdates.Close()
|
c.skippedUpdates.Close()
|
||||||
if screen.Widget != nil {
|
if screen.Widget != nil {
|
||||||
c.skippedUpdates = c.RunWidget(screen.Widget, args...)
|
c.skippedUpdates = c.RunWidget(screen.Widget, args...)
|
||||||
|
@ -181,6 +185,10 @@ func (c *Context) Go(screenId ScreenId, args ...any) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) PathExist(pth Path) bool {
|
||||||
|
return c.Bot.behaviour.PathExist(pth)
|
||||||
|
}
|
||||||
|
|
||||||
// Run widget in background returning the new input channel for it.
|
// Run widget in background returning the new input channel for it.
|
||||||
func (c *Context) RunWidget(widget Widget, args ...any) *UpdateChan {
|
func (c *Context) RunWidget(widget Widget, args ...any) *UpdateChan {
|
||||||
if widget == nil {
|
if widget == nil {
|
||||||
|
@ -209,12 +217,18 @@ func (c *Context) RunWidget(widget Widget, args ...any) *UpdateChan {
|
||||||
return updates
|
return updates
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Go to the root screen.
|
||||||
|
func (c *Context) GoRoot() {
|
||||||
|
c.Go("/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go one level upper in the screen hierarchy.
|
||||||
func (c *Context) GoUp() {
|
func (c *Context) GoUp() {
|
||||||
c.Go(ScreenId(path.Dir(string(c.CurScreen()))))
|
c.Go(c.Path().Dir())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change screen to the previous.
|
// Change screen to the previous.
|
||||||
// To get to the parent screen use GoUp.
|
// To get to the parent screen use GoUp.
|
||||||
func (c *Context) GoPrev() {
|
func (c *Context) GoPrev() {
|
||||||
c.Go(c.PrevScreen())
|
c.Go(c.PrevPath())
|
||||||
}
|
}
|
||||||
|
|
93
tg/screen.go
93
tg/screen.go
|
@ -1,60 +1,100 @@
|
||||||
package tg
|
package tg
|
||||||
|
|
||||||
// Unique identifier for the screen.
|
import (
|
||||||
type ScreenId string
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Unique identifier for the screen
|
||||||
|
// and relative paths to the screen.
|
||||||
|
type Path string
|
||||||
|
|
||||||
|
// Returns true if the path is empty.
|
||||||
|
func (p Path) IsEmpty() bool {
|
||||||
|
return p == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the path is absolute.
|
||||||
|
func (p Path) IsAbs() bool {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return p[0] == '/'
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Path) Dir() Path {
|
||||||
|
return Path(path.Dir(string(p)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean the path deleting exceed ., .. and / .
|
||||||
|
func (p Path) Clean() Path {
|
||||||
|
return Path(path.Clean(string(p)))
|
||||||
|
}
|
||||||
|
|
||||||
// Screen statement of the bot.
|
// Screen statement of the bot.
|
||||||
// Mostly what buttons to show.
|
// Mostly what buttons to show.
|
||||||
type Screen struct {
|
type Screen struct {
|
||||||
// Unique identifer to change to the screen
|
|
||||||
// via Context.ChangeScreen method.
|
|
||||||
Id ScreenId
|
|
||||||
// The widget to run when reaching the screen.
|
// The widget to run when reaching the screen.
|
||||||
Widget Widget
|
Widget Widget
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The first node with the "/" path.
|
||||||
|
type RootNode struct {
|
||||||
|
Screen *Screen
|
||||||
|
Subs []*Node
|
||||||
|
}
|
||||||
|
|
||||||
// The node is a simple way to represent
|
// The node is a simple way to represent
|
||||||
// tree-like structured applications.
|
// tree-like structured applications.
|
||||||
type Node struct {
|
type Node struct {
|
||||||
|
Path Path
|
||||||
Screen *Screen
|
Screen *Screen
|
||||||
Subs []*Node
|
Subs []*Node
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNode(id ScreenId, widget Widget, subs ...*Node) *Node {
|
// Return new root node with the specified widget in the screen.
|
||||||
ret := &Node{}
|
func NewRootNode(widget Widget, subs ...*Node) *RootNode {
|
||||||
ret.Screen = NewScreen(id, widget)
|
ret := &RootNode{}
|
||||||
|
ret.Screen = NewScreen(widget)
|
||||||
ret.Subs = subs
|
ret.Subs = subs
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) ScreenMap() ScreenMap {
|
func NewNode(relPath Path, widget Widget, subs ...*Node) *Node {
|
||||||
|
ret := &Node{}
|
||||||
|
ret.Path = relPath
|
||||||
|
ret.Screen = NewScreen(widget)
|
||||||
|
ret.Subs = subs
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *RootNode) ScreenMap() ScreenMap {
|
||||||
m := make(ScreenMap)
|
m := make(ScreenMap)
|
||||||
id := n.Screen.Id
|
var root Path = "/"
|
||||||
m[id] = n.Screen
|
m[root] = n.Screen
|
||||||
n.Screen.Id = id
|
|
||||||
var root ScreenId
|
|
||||||
if id == "/" {
|
|
||||||
root = ""
|
|
||||||
} else {
|
|
||||||
root = id
|
|
||||||
}
|
|
||||||
for _, sub := range n.Subs {
|
for _, sub := range n.Subs {
|
||||||
buf := sub.screenMap(root + "/")
|
buf := sub.ScreenMap(root)
|
||||||
for k, v := range buf {
|
for k, v := range buf {
|
||||||
|
_, ok := m[k]
|
||||||
|
if ok {
|
||||||
|
panic("duplicate paths in node definition")
|
||||||
|
}
|
||||||
m[k] = v
|
m[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) screenMap(root ScreenId) ScreenMap {
|
func (n *Node) ScreenMap(root Path) ScreenMap {
|
||||||
m := make(ScreenMap)
|
m := make(ScreenMap)
|
||||||
id := root+ n.Screen.Id
|
pth := (root + n.Path).Clean()
|
||||||
m[id] = n.Screen
|
m[pth] = n.Screen
|
||||||
n.Screen.Id = id
|
|
||||||
for _, sub := range n.Subs {
|
for _, sub := range n.Subs {
|
||||||
buf := sub.screenMap(id + "/")
|
buf := sub.ScreenMap((pth + "/").Clean())
|
||||||
for k, v := range buf {
|
for k, v := range buf {
|
||||||
|
_, ok := m[k]
|
||||||
|
if ok {
|
||||||
|
panic("duplicate paths in node definition")
|
||||||
|
}
|
||||||
m[k] = v
|
m[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,12 +102,11 @@ func (n *Node) screenMap(root ScreenId) ScreenMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map structure for the screens.
|
// Map structure for the screens.
|
||||||
type ScreenMap map[ScreenId]*Screen
|
type ScreenMap map[Path] *Screen
|
||||||
|
|
||||||
// Returns the new screen with specified name and widget.
|
// Returns the new screen with specified name and widget.
|
||||||
func NewScreen(id ScreenId, widget Widget) *Screen {
|
func NewScreen(widget Widget) *Screen {
|
||||||
return &Screen{
|
return &Screen{
|
||||||
Id: id,
|
|
||||||
Widget: widget,
|
Widget: widget,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue