Add feedback components 106-130; inert scope, focus return, two-step deploy
This commit is contained in:
87 files changed
+2492
-147
No files matched your search
+2
-2
@@ -123,10 +123,10 @@ func TestLostPageAndLocale(t *testing.T) {
|
||||
t.Fatalf("lost page texts %q", tt.Texts())
|
||||
}
|
||||
click(t, tt, "language")
|
||||
if a.locale != pixel.Zh || !tt.HasText("继续?") {
|
||||
if a.locale != pixel.Zh || !tt.HasText("重试") {
|
||||
t.Fatal("language toggle did not switch built-in copy")
|
||||
}
|
||||
click(t, tt, "继续?")
|
||||
click(t, tt, "重试")
|
||||
if a.Path() != "/" {
|
||||
t.Fatalf("continue went to %q", a.Path())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
package gallery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/feedback"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||||
)
|
||||
|
||||
var feedbackEntries = []Entry{
|
||||
{106, "Progress", "进度条", "feedback", "A bar that fills chunk by chunk. Below zero, a block runs back and forth.", demoProgress},
|
||||
{107, "Spinner", "旋转器", "feedback", "Eight frames of beads going round. It keeps turning under Reduce Motion: it means work.", demoSpinner},
|
||||
{108, "Meter", "计量条", "feedback", "A level that turns Warn past a mark and Bad past the next.", demoMeter},
|
||||
{109, "Skeleton", "骨架屏", "feedback", "Dithered stand-ins with a shine sweeping across in steps.", demoSkeleton},
|
||||
{110, "Alert", "提示框", "feedback", "A framed callout: info, success, warning or danger, with an optional ×.", demoAlert},
|
||||
{111, "Banner", "横幅", "feedback", "A full-width strip with actions and a ×.", demoBanner},
|
||||
{112, "EmptyState", "空状态", "feedback", "NO ITEMS over an empty chest, and something to do about it.", demoEmptyState},
|
||||
{113, "ErrorState", "错误态", "feedback", "GAME OVER, a CONTINUE? countdown and RETRY. The gallery's own errors use it.", demoErrorState},
|
||||
{114, "SuccessState", "通关", "feedback", "STAGE CLEAR! The stars pop in one by one.", demoSuccessState},
|
||||
{115, "LoadingScreen", "加载画面", "feedback", "NOW LOADING with stepping dots and a tip every four seconds.", demoLoadingScreen},
|
||||
{116, "DotsLoader", "点点加载", "feedback", "Three dots hopping in turn.", demoDotsLoader},
|
||||
{117, "BarsLoader", "柱状加载", "feedback", "Five bars rising and falling, a step apart.", demoBarsLoader},
|
||||
{118, "Hourglass", "沙漏", "feedback", "Sand falls in steps, then the glass flips.", demoHourglass},
|
||||
{119, "Toast", "吐司", "feedback", "Short messages at the bottom, three at a time, each for a few seconds. Some can be undone.", demoToast},
|
||||
{120, "InlineMessage", "行内提示", "feedback", "A small ! or ✓ note under a field.", demoInlineMessage},
|
||||
{121, "CountUp", "计数", "feedback", "Numbers count up in steps, the way scores should.", demoCountUp},
|
||||
{122, "Pulse", "脉冲", "feedback", "A dithered ring grows around what wants attention.", demoPulse},
|
||||
{123, "LevelUp", "升级", "feedback", "LEVEL UP! slides in with sparkles when the level rises.", demoLevelUp},
|
||||
{124, "Achievement", "成就", "feedback", "A trophy card slides in, waits, and slides away.", demoAchievement},
|
||||
{125, "ComboCounter", "连击", "feedback", "×12 COMBO! It pops a dot bigger with every hit.", demoComboCounter},
|
||||
{126, "ContinuePrompt", "继续?", "feedback", "CONTINUE? YES or NO before the count runs out.", demoContinuePrompt},
|
||||
{127, "SaveIndicator", "存档指示", "feedback", "A floppy spins while saving, then SAVED ✓.", demoSaveIndicator},
|
||||
{128, "BusyOverlay", "忙碌遮罩", "feedback", "A checker veil and spinner over busy content; it takes the pointer and the focus.", demoBusyOverlay},
|
||||
{129, "ReadyGo", "预备开始", "feedback", "3, 2, 1, GO! in big digits, a second each.", demoReadyGo},
|
||||
{130, "Shake", "抖动", "feedback", "Jolts its child side to side, for a wrong answer.", demoShake},
|
||||
}
|
||||
|
||||
type feedbackDemo struct {
|
||||
progress, hp float64
|
||||
closed bool
|
||||
tries, replays, prompts, score int
|
||||
level, cue, combo, start, shake int
|
||||
toasts []feedback.Toast
|
||||
hero, secret, motto string
|
||||
saving, busy time.Time
|
||||
}
|
||||
|
||||
func feedbackState(c *ui.Context) *feedbackDemo {
|
||||
return ui.Local(c.Root(), demoKey("feedback"), func() feedbackDemo {
|
||||
return feedbackDemo{progress: 0.3, hp: 0.5, level: 4}
|
||||
})
|
||||
}
|
||||
|
||||
func demoProgress(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
ui.Column(c).Gap(k.D(6)).Children(func() {
|
||||
feedback.Progress(c, "Download", s.progress, feedback.ProgressOptions{Percent: true})
|
||||
feedback.Progress(c, "Searching", -1, feedback.ProgressOptions{})
|
||||
ui.Row(c).Gap(k.D(4)).Children(func() {
|
||||
if input.Button(c, "+10%", input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.progress = min(1, s.progress+0.1)
|
||||
log(fmt.Sprintf("%.0f%%", s.progress*100))
|
||||
}
|
||||
if input.Button(c, "RESET", input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.progress = 0
|
||||
log("reset")
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func demoSpinner(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
ui.Row(c).Gap(k.D(12)).AlignItems(ui.Center).Children(func() {
|
||||
for sc := 1; sc <= 3; sc++ {
|
||||
feedback.Spinner(c, fmt.Sprintf("Loading ×%d", sc), feedback.SpinnerOptions{Scale: sc})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoMeter(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
ui.Column(c).Gap(k.D(6)).Children(func() {
|
||||
feedback.Meter(c, "Heat", s.hp, feedback.MeterOptions{Warning: 0.6, Critical: 0.85})
|
||||
if input.Slider(c, "Heat level", &s.hp, input.SliderOptions{}).Changed {
|
||||
log(fmt.Sprintf("heat %.2f", s.hp))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoSkeleton(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
ui.Column(c).Gap(k.D(10)).Children(func() {
|
||||
feedback.Skeleton(c, feedback.SkeletonOptions{Avatar: true})
|
||||
feedback.Skeleton(c, feedback.SkeletonOptions{Lines: 2, Width: 120})
|
||||
})
|
||||
}
|
||||
|
||||
func demoAlert(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
ui.Column(c).Gap(k.D(4)).Children(func() {
|
||||
feedback.Alert(c, "Did you know?", "Pressing P paints the world anew.", feedback.AlertOptions{Kind: feedback.Info})
|
||||
feedback.Alert(c, "Quest complete", "The princess was in this castle.", feedback.AlertOptions{Kind: feedback.Success})
|
||||
feedback.Alert(c, "Low on potions", "Two left. Shop before the boss.", feedback.AlertOptions{Kind: feedback.Warning})
|
||||
if !s.closed && feedback.Alert(c, "Save corrupted", "Just kidding. Close me.", feedback.AlertOptions{Kind: feedback.Danger, Closable: true}).Closed {
|
||||
s.closed = true
|
||||
log("closed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoBanner(c *ui.Context, log func(string)) {
|
||||
r := feedback.Banner(c, "A new patch is out: slimes now bounce higher.", feedback.BannerOptions{Kind: feedback.Info, Actions: []string{"UPDATE", "LATER"}, Dismissable: true})
|
||||
if r.Action != "" {
|
||||
log(r.Action)
|
||||
}
|
||||
if r.Dismissed {
|
||||
log("dismissed")
|
||||
}
|
||||
}
|
||||
|
||||
func demoEmptyState(c *ui.Context, log func(string)) {
|
||||
if feedback.EmptyState(c, "Your inventory is empty.", feedback.EmptyStateOptions{Action: "VISIT SHOP"}).Acted {
|
||||
log("off to the shop")
|
||||
}
|
||||
}
|
||||
|
||||
func demoErrorState(c *ui.Context, log func(string)) {
|
||||
s := feedbackState(c)
|
||||
// A new key starts a fresh countdown.
|
||||
ui.Box(c).Key(s.tries).Children(func() {
|
||||
if feedback.ErrorState(c, "The slime won this round.", feedback.ErrorStateOptions{}).Continue {
|
||||
s.tries++
|
||||
log(fmt.Sprintf("continue #%d", s.tries))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoSuccessState(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
ui.Column(c).Gap(k.D(4)).AlignItems(ui.Center).Children(func() {
|
||||
ui.Box(c).Key(s.replays).Children(func() {
|
||||
feedback.SuccessState(c, "All 99 coins found.", feedback.SuccessStateOptions{})
|
||||
})
|
||||
if input.Button(c, "REPLAY", input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.replays++
|
||||
log("replay")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoLoadingScreen(c *ui.Context, log func(string)) {
|
||||
feedback.LoadingScreen(c, feedback.LoadingScreenOptions{Tips: []string{
|
||||
"Tip: slimes fear the color red.", "Tip: press P to change the palette.", "Tip: save often. The dragon does not wait.",
|
||||
}})
|
||||
}
|
||||
|
||||
func demoDotsLoader(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
ui.Row(c).Gap(k.D(6)).AlignItems(ui.Center).Children(func() {
|
||||
feedback.DotsLoader(c, "Thinking", feedback.DotsLoaderOptions{})
|
||||
pixel.Text(c, "The wizard is thinking", pixel.TextOptions{Slot: pixel.Faint})
|
||||
})
|
||||
}
|
||||
|
||||
func demoBarsLoader(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
ui.Row(c).Gap(k.D(6)).AlignItems(ui.Center).Children(func() {
|
||||
feedback.BarsLoader(c, "Tuning", feedback.BarsLoaderOptions{})
|
||||
pixel.Text(c, "Tuning the chiptune", pixel.TextOptions{Slot: pixel.Faint})
|
||||
})
|
||||
}
|
||||
|
||||
func demoHourglass(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
ui.Row(c).Gap(k.D(6)).AlignItems(ui.Center).Children(func() {
|
||||
feedback.Hourglass(c, "Waiting", feedback.HourglassOptions{})
|
||||
pixel.Text(c, "The turn passes", pixel.TextOptions{Slot: pixel.Faint})
|
||||
})
|
||||
}
|
||||
|
||||
func demoToast(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
ui.Row(c).Gap(k.D(4)).Children(func() {
|
||||
if input.Button(c, "SAVE GAME", input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.toasts = append(s.toasts, feedback.Toast{Message: "Game saved."})
|
||||
log("toast: saved")
|
||||
}
|
||||
if input.Button(c, "DROP SWORD", input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.toasts = append(s.toasts, feedback.Toast{Message: "You dropped the sword.", Action: "UNDO", For: 5 * time.Second})
|
||||
log("toast: dropped")
|
||||
}
|
||||
})
|
||||
if r := feedback.Toasts(c, &s.toasts, feedback.ToastsOptions{}); r.Acted != nil {
|
||||
log(r.Acted.Action + ": " + r.Acted.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func demoInlineMessage(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
ui.Column(c).Gap(k.D(3)).Children(func() {
|
||||
if input.TextInput(c, "Hero name", &s.hero, input.TextInputOptions{Placeholder: "Your name"}).Changed {
|
||||
log("name " + s.hero)
|
||||
}
|
||||
if s.hero == "" {
|
||||
feedback.InlineMessage(c, "A hero needs a name.", feedback.InlineMessageOptions{Kind: feedback.Danger})
|
||||
} else {
|
||||
feedback.InlineMessage(c, "That name is free!", feedback.InlineMessageOptions{Kind: feedback.Success})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoCountUp(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
ui.Row(c).Gap(k.D(8)).AlignItems(ui.Center).Children(func() {
|
||||
feedback.CountUp(c, s.score, feedback.CountUpOptions{Scale: 2, Slot: pixel.Accent})
|
||||
for _, add := range []int{100, 1000} {
|
||||
if input.Button(c, fmt.Sprintf("+%d", add), input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.score += add
|
||||
log(fmt.Sprintf("score %d", s.score))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoPulse(c *ui.Context, log func(string)) {
|
||||
feedback.Pulse(c, feedback.PulseOptions{}, func() {
|
||||
if input.IconButton(c, icon.Mail, "New mail", input.IconButtonOptions{}).Clicked {
|
||||
log("mail opened")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoLevelUp(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
ui.Column(c).Gap(k.D(4)).Children(func() {
|
||||
if input.Button(c, "GAIN XP", input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.level++
|
||||
log(fmt.Sprintf("level %d", s.level))
|
||||
}
|
||||
feedback.LevelUp(c, s.level, feedback.LevelUpOptions{})
|
||||
})
|
||||
}
|
||||
|
||||
func demoAchievement(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
titles := []string{"Pressed Start", "Slime Tamer", "Touched Grass", "Read the Manual"}
|
||||
ui.Column(c).Gap(k.D(4)).Children(func() {
|
||||
if input.Button(c, "UNLOCK", input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.cue++
|
||||
log("unlocked " + titles[s.cue%len(titles)])
|
||||
}
|
||||
feedback.Achievement(c, titles[s.cue%len(titles)], feedback.AchievementOptions{Cue: s.cue})
|
||||
})
|
||||
}
|
||||
|
||||
func demoComboCounter(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
ui.Column(c).Gap(k.D(4)).Children(func() {
|
||||
ui.Row(c).Gap(k.D(4)).Children(func() {
|
||||
if input.Button(c, "HIT", input.ButtonOptions{Size: input.Small, Variant: input.Primary}).Clicked {
|
||||
s.combo++
|
||||
log(fmt.Sprintf("combo %d", s.combo))
|
||||
}
|
||||
if input.Button(c, "MISS", input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.combo = 0
|
||||
log("combo broken")
|
||||
}
|
||||
})
|
||||
feedback.ComboCounter(c, s.combo, feedback.ComboCounterOptions{})
|
||||
})
|
||||
}
|
||||
|
||||
func demoContinuePrompt(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
ui.Column(c).Gap(k.D(4)).AlignItems(ui.Center).Children(func() {
|
||||
ui.Box(c).Key(s.prompts).Children(func() {
|
||||
r := feedback.ContinuePrompt(c, feedback.ContinuePromptOptions{})
|
||||
switch {
|
||||
case r.Yes:
|
||||
log("yes: one more go")
|
||||
case r.No:
|
||||
log("no: thanks for playing")
|
||||
case r.TimedOut:
|
||||
log("time up")
|
||||
}
|
||||
})
|
||||
if input.Button(c, "AGAIN", input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.prompts++
|
||||
log("again")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoSaveIndicator(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
saving := k.Now().Before(s.saving)
|
||||
if saving {
|
||||
k.Tick(c, 4)
|
||||
}
|
||||
ui.Row(c).Gap(k.D(8)).AlignItems(ui.Center).Children(func() {
|
||||
if input.Button(c, "SAVE", input.ButtonOptions{Size: input.Small, Disabled: saving}).Clicked {
|
||||
s.saving = k.Now().Add(1500 * time.Millisecond)
|
||||
log("saving")
|
||||
}
|
||||
feedback.SaveIndicator(c, saving, feedback.SaveIndicatorOptions{})
|
||||
})
|
||||
}
|
||||
|
||||
func demoBusyOverlay(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
busy := k.Now().Before(s.busy)
|
||||
if busy {
|
||||
k.Tick(c, 4)
|
||||
}
|
||||
feedback.BusyOverlay(c, busy, feedback.BusyOverlayOptions{}, func() {
|
||||
ui.Column(c).Gap(k.D(4)).Padding(k.D(4)).Children(func() {
|
||||
if input.TextInput(c, "Guild motto", &s.motto, input.TextInputOptions{Placeholder: "We came, we saved"}).Changed {
|
||||
log("motto " + s.motto)
|
||||
}
|
||||
if input.Button(c, "SEND", input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.busy = k.Now().Add(2 * time.Second)
|
||||
log("sending…")
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func demoReadyGo(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
ui.Column(c).Gap(k.D(4)).AlignItems(ui.Center).Children(func() {
|
||||
if input.Button(c, "START", input.ButtonOptions{Size: input.Small, Variant: input.Primary}).Clicked {
|
||||
s.start++
|
||||
log("ready")
|
||||
}
|
||||
if feedback.ReadyGo(c, feedback.ReadyGoOptions{Cue: s.start}).Done {
|
||||
log("GO!")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoShake(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := feedbackState(c)
|
||||
ui.Column(c).Gap(k.D(4)).Children(func() {
|
||||
feedback.Shake(c, feedback.ShakeOptions{Cue: s.shake}, func() {
|
||||
input.PasswordInput(c, "Password", &s.secret, input.PasswordInputOptions{Placeholder: "Speak, friend"})
|
||||
})
|
||||
if input.Button(c, "ENTER", input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.shake++
|
||||
log("wrong password")
|
||||
}
|
||||
})
|
||||
}
|
||||
+3
-18
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/display"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/feedback"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/layout"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||||
@@ -187,24 +187,9 @@ func (a *App) results(c *ui.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// lost answers unknown paths with GAME OVER and CONTINUE.
|
||||
// lost answers unknown paths with GAME OVER.
|
||||
func (a *App) lost(c *ui.Context, path string) {
|
||||
if GameOver(c, "No page at "+path) {
|
||||
if feedback.ErrorState(c, "No page at "+path, feedback.ErrorStateOptions{}).Continue {
|
||||
a.Go("/")
|
||||
}
|
||||
}
|
||||
|
||||
// GameOver shows why the game ended; true on CONTINUE.
|
||||
func GameOver(c *ui.Context, why string) bool {
|
||||
k := pixel.K(c)
|
||||
cont := false
|
||||
ui.Column(c).Gap(k.D(8)).AlignItems(ui.Center).Padding(k.D(24)).Children(func() {
|
||||
ui.Box(c).Size(k.D(pixel.Measure(k.Msg("game_over"))*3), k.D(48)).Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
shadow(k.Canvas(p, r), 0, 0, k.Msg("game_over"), pixel.Bad, 3)
|
||||
})
|
||||
pixel.Text(c, why, pixel.TextOptions{Slot: pixel.Faint, Wrap: true, MaxLines: 6}).Width(k.D(min(pixel.Measure(why), 280)))
|
||||
display.Icon(c, icon.Skull, display.IconOptions{Scale: 3})
|
||||
cont = input.Button(c, k.Msg("continue"), input.ButtonOptions{}).Clicked
|
||||
})
|
||||
return cont
|
||||
}
|
||||
+1
-1
@@ -56,7 +56,7 @@ var Categories = []Category{
|
||||
{"tabletop", "Tabletop", "桌游", icon.Crown},
|
||||
}
|
||||
|
||||
var entries = slices.Concat(foundationEntries, layoutEntries, textEntries, displayEntries, inputEntries, controlEntries)
|
||||
var entries = slices.Concat(foundationEntries, layoutEntries, textEntries, displayEntries, inputEntries, controlEntries, feedbackEntries)
|
||||
|
||||
func Entries() []Entry { return entries }
|
||||
|
||||
|
||||
Reference in new issue
Block a user