summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--defer_load.go60
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--image.go448
-rw-r--r--main.go13
-rw-r--r--tag.go2
6 files changed, 394 insertions, 135 deletions
diff --git a/defer_load.go b/defer_load.go
new file mode 100644
index 0000000..7619c62
--- /dev/null
+++ b/defer_load.go
@@ -0,0 +1,60 @@
+package main
+
+import (
+ "context"
+ "log"
+)
+
+var (
+ deferredLoadCancel context.CancelFunc
+ deferredLoadCallback func()
+)
+
+func registerDeferredLoad(callbacks []func(), immediate bool) {
+ deferredLoadCallback = func() {
+ deferredLoadCallback = nil
+
+ func() {
+ giantLock.Lock()
+ defer giantLock.Unlock()
+ if deferredLoadCancel != nil {
+ deferredLoadCancel()
+ deferredLoadCancel = nil
+ }
+ }()
+
+ ctx, cancel := context.WithCancel(context.TODO())
+ deferredLoadCancel = cancel
+ proceed := true
+ done := make(chan bool)
+
+ go func() {
+ for i, callback := range callbacks {
+ if !proceed {
+ log.Printf("Deferred load cancelled.")
+ return
+ }
+
+ if callback == nil {
+ log.Printf("Callback %v is nil.", i)
+ continue
+ }
+ log.Printf("Firing deferred loading callback %v.", i)
+ callback()
+ }
+ done <- true
+ }()
+
+ select {
+ case <-ctx.Done():
+ deferredLoadCancel = nil
+ proceed = false
+ case <-done:
+ deferredLoadCancel = nil
+ log.Printf("Deferred load done.")
+ }
+ }
+ if immediate {
+ go deferredLoadCallback()
+ }
+}
diff --git a/go.mod b/go.mod
index 5a9b602..8a4b0ab 100644
--- a/go.mod
+++ b/go.mod
@@ -8,5 +8,5 @@ require (
github.com/bwmarrin/snowflake v0.3.0
github.com/fyne-io/mobile v0.1.3-0.20210412090810-650a3139866a // indirect
github.com/sqweek/dialog v0.0.0-20211002065838-9a201b55ab91
- random.chars.jp/git/image-board v1.4.0
+ random.chars.jp/git/image-board v1.4.2
)
diff --git a/go.sum b/go.sum
index 47bb4a3..8f8a82f 100644
--- a/go.sum
+++ b/go.sum
@@ -470,4 +470,8 @@ random.chars.jp/git/image-board v1.3.10 h1:aoN5qsHzIuwxcQnWCl56LJQz8pHzi3Stcs9wO
random.chars.jp/git/image-board v1.3.10/go.mod h1:fKoOuCTZ+1F4DCciH+fywbzD+go1GXgStZ+OF7hdr8c=
random.chars.jp/git/image-board v1.4.0 h1:JxRJNczJ5ojIusXAz12mAJD3QF/cjsL0Pxrqs2pI2+Q=
random.chars.jp/git/image-board v1.4.0/go.mod h1:fKoOuCTZ+1F4DCciH+fywbzD+go1GXgStZ+OF7hdr8c=
+random.chars.jp/git/image-board v1.4.1 h1:HsodNrCL76h6HXvCCVGG6qd+n7tvZN4CyWqfxr44a+c=
+random.chars.jp/git/image-board v1.4.1/go.mod h1:fKoOuCTZ+1F4DCciH+fywbzD+go1GXgStZ+OF7hdr8c=
+random.chars.jp/git/image-board v1.4.2 h1:mCdm8K2fzzxWlNRf8NSurgoMXyEW5+2C2htigqyPK9c=
+random.chars.jp/git/image-board v1.4.2/go.mod h1:fKoOuCTZ+1F4DCciH+fywbzD+go1GXgStZ+OF7hdr8c=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
diff --git a/image.go b/image.go
index d3867bb..b45abd1 100644
--- a/image.go
+++ b/image.go
@@ -21,6 +21,7 @@ import (
"random.chars.jp/git/image-board/api"
"random.chars.jp/git/image-board/store"
"strings"
+ "sync"
"time"
)
@@ -67,9 +68,10 @@ var (
tp *int
)
var (
- imageCache = make(map[string]*imageEntry)
- groupCache = make(map[string][]store.Image)
- pageCache = make(map[int][]store.Image)
+ imageCache = make(map[string]*imageEntry)
+ imageCacheLock = sync.RWMutex{}
+ groupCache = make(map[string][]store.Image)
+ pageCache = make(map[int][]store.Image)
)
// imageMeta represents image metadata not included in the API payload.
@@ -91,6 +93,7 @@ func (i *imageEntry) resource(preview bool) (*fyne.StaticResource, error) {
if i == nil {
return nil, errors.New("did not get image entry")
}
+
switch preview {
case true:
if i.preview != nil {
@@ -116,14 +119,17 @@ func (i *imageEntry) resource(preview bool) (*fyne.StaticResource, error) {
}
}
+ s := time.Now()
if res, err := remote.ImageFile(i.image.Snowflake, preview); err != nil {
return nil, err
} else {
switch preview {
case true:
+ log.Printf("Fetched image %s preview in %s.", i.image.Snowflake, time.Now().Sub(s))
i.preview = fyne.NewStaticResource(i.image.Snowflake+"-preview.jpg", res)
return i.preview, nil
case false:
+ log.Printf("Fetched image %s full in %s.", i.image.Snowflake, time.Now().Sub(s))
i.file = fyne.NewStaticResource(i.image.Snowflake+"."+i.image.Type, res)
return i.file, nil
}
@@ -223,15 +229,58 @@ func totalPages() (int, error) {
}
func getImage(image store.Image) *imageEntry {
+ imageCacheLock.RLock()
+ defer imageCacheLock.RUnlock()
if entry, ok := imageCache[image.Snowflake]; !ok {
+ imageCacheLock.RUnlock()
+ imageCacheLock.Lock()
imageCache[image.Snowflake] = &imageEntry{image: image}
+ imageCacheLock.Unlock()
+ imageCacheLock.RLock()
return imageCache[image.Snowflake]
} else {
return entry
}
}
+func getImageFlake(flake string) *imageEntry {
+ imageCacheLock.RLock()
+ defer imageCacheLock.RUnlock()
+ if entry, ok := imageCache[flake]; !ok {
+ imageCacheLock.RUnlock()
+ imageCacheLock.Lock()
+ fetch:
+ s := time.Now()
+ if img, err := remote.Image(flake); err != nil {
+ retry := make(chan bool)
+ diag := dialog.NewError(err, window)
+ diag.SetDismissText("Retry")
+ diag.SetOnClosed(func() {
+ retry <- true
+ })
+ <-retry
+ goto fetch
+ } else {
+ log.Printf("Fetched image %s in %s.", flake, time.Now().Sub(s))
+ imageCache[flake] = &imageEntry{image: img}
+ }
+ imageCacheLock.Unlock()
+ imageCacheLock.RLock()
+ return imageCache[flake]
+ } else {
+ return entry
+ }
+}
+
func getImageGroup(image store.Image) (entry *imageEntry, group []*imageEntry) {
+ entry = getImage(image)
+
+ if image.Parent == image.Child {
+ log.Printf("Ungrouped image %s returned as-is.", image.Snowflake)
+ group = []*imageEntry{entry}
+ return
+ }
+
if g, ok := groupCache[image.Snowflake]; !ok {
s := time.Now()
if gr, err := remote.ImageGroup(image.Snowflake); err != nil {
@@ -240,7 +289,6 @@ func getImageGroup(image store.Image) (entry *imageEntry, group []*imageEntry) {
} else {
e := time.Now().Sub(s)
grFlakes := make([]string, len(gr))
- entry = getImage(image)
group = make([]*imageEntry, len(gr))
for i, img := range gr {
group[i] = getImage(img)
@@ -248,15 +296,15 @@ func getImageGroup(image store.Image) (entry *imageEntry, group []*imageEntry) {
groupCache[img.Snowflake] = gr
}
log.Printf("Fetched group %s in %s.", strings.Join(grFlakes, ", "), e)
+ return
}
} else {
- entry = getImage(image)
group = make([]*imageEntry, len(g))
for i, img := range g {
group[i] = getImage(img)
}
+ return
}
- return
}
func makeImageBorder(data []byte, c color.Color, thickness float64) (image.Image, error) {
@@ -294,19 +342,40 @@ func makeImageBorder(data []byte, c color.Color, thickness float64) (image.Image
return newImg, nil
}
-func makeGallery(images []store.Image) (*fyne.Container, error) {
- var loadImage func(gallery *fyne.Container, m store.Image) error
- switch getCollapseCategory() {
+func makeGallery(images []store.Image, noGroup bool) (*fyne.Container, error) {
+ var callbacks = make([]func(), len(images))
+ defer func() { go registerDeferredLoad(callbacks, false) }()
+ var (
+ flakeImage map[string]*store.Image
+ imageGroups map[string]*[]string
+ groupLocks map[*[]string]*sync.Mutex
+ )
+
+ cc := getCollapseCategory()
+ if noGroup {
+ cc = false
+ }
+
+ var loadImage func(gallery *fyne.Container, m store.Image, i int) error
+ switch cc {
case false:
- loadImage = func(gallery *fyne.Container, m store.Image) error {
+ loadImage = func(gallery *fyne.Container, m store.Image, i int) error {
img := &canvas.Image{
FillMode: canvas.ImageFillContain,
ScaleMode: canvas.ImageScaleFastest,
}
- if res, err := getImage(m).resource(true); err != nil {
- return err
- } else {
- img.Resource = res
+
+ callbacks[i] = func() {
+ go func(m store.Image, img *canvas.Image) {
+ defer img.Refresh()
+
+ if res, err := getImage(m).resource(true); err != nil {
+ go dialog.ShowError(err, window)
+ } else {
+ log.Printf("Populated image %s.", m.Snowflake)
+ img.Resource = res
+ }
+ }(m, img)
}
gallery.Add(newImageButton(img, m))
@@ -314,34 +383,54 @@ func makeGallery(images []store.Image) (*fyne.Container, error) {
return nil
}
case true:
- loadImage = func(gallery *fyne.Container, m store.Image) error {
- entry, group := getImageGroup(m)
- if len(group) == 0 || m.Snowflake != group[0].image.Snowflake {
- log.Printf("Skipped secondary image %s.", m.Snowflake)
- return nil
+ loadImage = func(gallery *fyne.Container, m store.Image, i int) error {
+ var (
+ entry = getImage(m)
+ group []string
+ )
+ if imageGroups[m.Snowflake] != nil {
+ group = *imageGroups[m.Snowflake]
+ if len(group) == 0 || m.Snowflake != group[0] {
+ log.Printf("Skipped secondary image %s.", m.Snowflake)
+ return nil
+ }
+ log.Printf("Adding primary image %s to view.", m.Snowflake)
+ } else {
+ group = []string{entry.image.Snowflake}
+ log.Printf("Adding ungrouped image %s to view.", m.Snowflake)
}
img := &canvas.Image{
FillMode: canvas.ImageFillContain,
ScaleMode: canvas.ImageScaleFastest,
}
- if res, err := entry.resource(true); err != nil {
- return err
- } else {
- if len(group) == 1 {
- img.Resource = res
- } else {
- var i image.Image
- if i, err = makeImageBorder(res.Content(), color.RGBA{R: 0x00, G: 0x00, B: 0xff, A: 0xff}, 0.03); err != nil {
- return err
+
+ callbacks[i] = func() {
+ go func(m store.Image, img *canvas.Image) {
+ defer img.Refresh()
+
+ if res, err := entry.resource(true); err != nil {
+ go dialog.ShowError(err, window)
} else {
- img.Image = i
+ if len(group) == 1 {
+ img.Resource = res
+ log.Printf("Populated ungrouped image %s.", m.Snowflake)
+ } else {
+ var newImg image.Image
+ if newImg, err = makeImageBorder(res.Content(),
+ color.RGBA{R: 0x00, G: 0x00, B: 0xff, A: 0xff}, 0.03); err != nil {
+ go dialog.ShowError(err, window)
+ } else {
+ img.Image = newImg
+ log.Printf("Populated primary image %s with group size %v.",
+ m.Snowflake, len(group))
+ }
+ }
}
- }
+ }(m, img)
}
gallery.Add(newImageButton(img, m))
- log.Printf("Added primary image %s to view.", m.Snowflake)
return nil
}
}
@@ -352,8 +441,53 @@ func makeGallery(images []store.Image) (*fyne.Container, error) {
Width: 128,
Height: 128,
})
+ if cc {
+ log.Printf("Starting local group discovery.")
+
+ flakeImage = make(map[string]*store.Image)
+ imageGroups = make(map[string]*[]string)
+ groupLocks = make(map[*[]string]*sync.Mutex)
+
+ for _, img := range images {
+ a := img
+ flakeImage[img.Snowflake] = &a
+ }
+
+ for flake, img := range flakeImage {
+ if imageGroups[flake] != nil {
+ log.Printf("Skipping local group discovery for image %s since it already belongs to one.", flake)
+ continue
+ }
+ if img.Child == img.Parent {
+ log.Printf("Skipping local group discovery for image %s since it is not grouped.", flake)
+ continue
+ }
+
+ group := []string{img.Snowflake}
+ // Iterate backwards
+ if flakeImage[img.Parent] != nil {
+ for parent := flakeImage[img.Parent]; parent != nil; parent = flakeImage[parent.Parent] {
+ group = append([]string{parent.Snowflake}, group...)
+ }
+ }
+ // Iterate forwards
+ if flakeImage[img.Child] != nil {
+ for child := flakeImage[img.Child]; child != nil; child = flakeImage[child.Child] {
+ group = append(group, child.Snowflake)
+ }
+ }
+
+ for _, f := range group {
+ imageGroups[f] = &group
+ }
+ groupLocks[&group] = &sync.Mutex{}
+ log.Printf("Discovered local group %s.", strings.Join(group, ", "))
+ }
+ log.Printf("Local group discovery done.")
+ }
+
for i, img := range images {
- if err := loadImage(gallery, img); err != nil {
+ if err := loadImage(gallery, img, i); err != nil {
return nil, err
}
go value(float64(i+1) / float64(len(images)))
@@ -380,7 +514,7 @@ func makePageView(page int) (*fyne.Container, error) {
return nil, err
}
var gallery *fyne.Container
- if g, err := makeGallery(images); err != nil {
+ if g, err := makeGallery(images, false); err != nil {
return nil, err
} else {
gallery = g
@@ -447,44 +581,100 @@ func makeImageView(image store.Image) (*fyne.Container, error) {
if remote == nil {
return nil, errors.New("attempting to make image with nil remote")
}
+ var br *fyne.Container
+ loadingLabelImage := widget.NewLabel("Loading...")
+
+ var callbacks []func()
+ defer func() { registerDeferredLoad(callbacks, false) }()
- entry, group := getImageGroup(image)
+ entry := getImage(image)
img := &canvas.Image{
FillMode: canvas.ImageFillContain,
ScaleMode: canvas.ImageScaleSmooth,
}
+ download := widget.NewButton(strings.ToUpper(image.Type), func() {
+ go fileSave(image.Snowflake, image.Type, img.Resource.Content())
+ })
- s := time.Now()
- if res, err := entry.resource(false); err != nil {
- return nil, err
- } else {
- img.Resource = res
- }
- log.Printf("Fetched image %s in %s.", image.Snowflake, time.Now().Sub(s).String())
+ callbacks = append(callbacks, func() {
+ go func() {
+ log.Printf("Starting fetch image %s.", image.Snowflake)
+ if res, err := entry.resource(false); err != nil {
+ go dialog.ShowError(err, window)
+ } else {
+ img.Resource = res
+ }
+ log.Printf("Finish fetch image %s.", image.Snowflake)
+ br.Remove(loadingLabelImage)
+ br.Add(img)
+ download.Enable()
+ }()
+ })
var metadata *imageMeta
- s = time.Now()
- if meta, err := entry.metadata(); err != nil {
- return nil, err
- } else {
- metadata = meta
- }
- log.Printf("Fetched image %s metadata in %s.", image.Snowflake, time.Now().Sub(s).String())
+ loadingLabel := widget.NewLabel("Loading...")
+ ts := widget.NewForm()
+ tv := container.NewBorder(widget.NewSeparator(), nil, nil, nil, loadingLabel)
+ info := widget.NewForm()
+
+ callbacks = append(callbacks, func() {
+ go func() {
+ log.Printf("Starting metadata fetch for image %s.", image.Snowflake)
+ s := time.Now()
+ if meta, err := entry.metadata(); err != nil {
+ diag := dialog.NewError(err, window)
+ diag.SetOnClosed(func() { setPage(currentPage) })
+ diag.SetDismissText("Return to previous view")
+ } else {
+ metadata = meta
+ log.Printf("Fetched image %s metadata in %s.", image.Snowflake, time.Now().Sub(s).String())
+ }
+ info.Items[1].Widget.(*link).Text = metadata.owner.Username
+ info.Items[1].Widget.(*link).onTap = func() { displayUserInfo(metadata.owner) }
+ info.Refresh()
+ log.Printf("Uploader field of image %s populated with %s.",
+ image.Snowflake, userString(metadata.owner))
+
+ // Make tag list
+ makeTags := func(tagType string) *fyne.Container {
+ a := container.NewVBox()
+ for _, tag := range metadata.tags[tagType] {
+ func(tag string) { a.Add(newLink(tag, func() { tagSearch([]string{tag}, func() { setImage(image) }) })) }(tag)
+ }
+ return a
+ }
+ appendTags := func(tagType string) {
+ a := makeTags(tagType)
+ if len(a.Objects) > 0 {
+ ts.Append(strings.Title(tagType), a)
+ }
+ }
+ appendTags(store.ArtistType)
+ appendTags(store.CharacterType)
+ appendTags(store.CopyrightType)
+ appendTags(store.MetaType)
+ appendTags(store.GenericType)
+
+ log.Printf("Finish metadata fetch for image %s.", image.Snowflake)
+ tv.Remove(loadingLabel)
+ tv.Add(ts)
+ }()
+ })
- info := widget.NewForm(
- widget.NewFormItem("ID", newLink(image.Snowflake, func() { window.Clipboard().SetContent(image.Snowflake) })),
- widget.NewFormItem("Uploader", newLink(metadata.owner.Username, func() { displayUserInfo(metadata.owner) })),
- )
+ // Populate info section
+ info.Append("ID", newLink(image.Snowflake, func() { window.Clipboard().SetContent(image.Snowflake) }))
+ info.Append("Uploader", newLink("Loading...", func() {}))
if image.Source != "" {
- u, err := url.Parse(image.Source)
- if err != nil {
- return nil, err
+ if u, err := url.Parse(image.Source); err != nil {
+ log.Printf("Invalid source URL on image %s.", image.Snowflake)
+ go dialog.ShowInformation("Warning", "This image has an invalid source URL.\n"+
+ "If this dialog reoccurs, contact your system administrator.", window)
+ } else {
+ src := widget.NewHyperlink(u.Host, u)
+ src.Wrapping = fyne.TextWrapWord
+ info.Append("Source", src)
}
- src := widget.NewHyperlink(u.Host, u)
- src.Wrapping = fyne.TextWrapWord
- info.Append("Source", src)
}
-
cL := widget.NewLabel(image.Commentary)
cL.Wrapping = fyne.TextWrapBreak
ctL := widget.NewLabel(image.CommentaryTranslation)
@@ -499,37 +689,14 @@ func makeImageView(image store.Image) (*fyne.Container, error) {
info.Append("Translated Commentary", ctL)
}
}
-
- info.Append("Download", widget.NewButton(strings.ToUpper(image.Type), func() {
- go fileSave(image.Snowflake, image.Type, img.Resource.Content())
- }))
-
- // Make tag list
- makeTags := func(tagType string) *fyne.Container {
- a := container.NewVBox()
- for _, tag := range metadata.tags[tagType] {
- func(tag string) { a.Add(newLink(tag, func() { tagSearch([]string{tag}, func() { setImage(image) }) })) }(tag)
- }
- return a
- }
- ts := widget.NewForm()
- appendTags := func(tagType string) {
- a := makeTags(tagType)
- if len(a.Objects) > 0 {
- ts.Append(strings.Title(tagType), a)
- }
- }
- appendTags(store.ArtistType)
- appendTags(store.CharacterType)
- appendTags(store.CopyrightType)
- appendTags(store.MetaType)
- appendTags(store.GenericType)
+ info.Append("Download", download)
+ download.Disable()
sc := container.NewVScroll(container.NewBorder(nil, nil, nil, widget.NewSeparator(),
- container.NewVBox(info, container.NewBorder(widget.NewSeparator(), nil, nil, nil, ts))))
+ container.NewVBox(info, tv)))
- br := container.NewBorder(nil, nil, sc, nil,
- img, sc)
+ br = container.NewBorder(nil, nil, sc, nil,
+ loadingLabelImage)
const (
retract = "<<"
@@ -630,7 +797,7 @@ func makeImageView(image store.Image) (*fyne.Container, error) {
if submit {
defer loading("Submitting", "Waiting for server...")()
- s = time.Now()
+ s := time.Now()
if err := remote.ImageUpdate(image.Snowflake, source, parent, commentary, commentaryTranslation); err != nil {
go dialog.ShowError(err, window)
}
@@ -706,7 +873,7 @@ func makeImageView(image store.Image) (*fyne.Container, error) {
del := widget.NewFormItem("Delete", widget.NewButton("Delete image", func() {
go dialog.ShowConfirm("Confirmation", "Are you sure you want to destroy this image?", func(okay bool) {
if okay {
- s = time.Now()
+ s := time.Now()
if err := remote.ImageDestroy(image.Snowflake); err != nil {
go dialog.ShowError(err, window)
return
@@ -736,68 +903,85 @@ func makeImageView(image store.Image) (*fyne.Container, error) {
ops := container.NewBorder(nil, nil, fwd, cnf)
var border *fyne.Container
- switch len(group) == 1 {
+ switch image.Child == image.Parent {
case true:
border = container.NewBorder(ops, nil, nil, nil,
container.NewBorder(widget.NewSeparator(), nil, nil, nil, lf))
case false:
- var ent int
- for i, im := range group {
- if im.image.Snowflake == image.Snowflake {
- ent = i
- }
- }
b := widget.NewButton("Previous", func() {
- setImage(group[ent-1].image)
+ setImage(getImageFlake(image.Parent).image)
})
r := widget.NewButton("Next", func() {
- setImage(group[ent+1].image)
+ setImage(getImageFlake(image.Child).image)
})
- if ent == 0 {
+ if image.Parent == "" {
b.Disable()
}
- if ent == len(group)-1 {
+ if image.Child == "" {
r.Disable()
}
var (
- groupView *container.Scroll
- groupViewBox = container.NewGridWrap(fyne.Size{Width: 128, Height: 128})
+ groupView *container.Scroll
+ groupViewBox = container.NewGridWrap(fyne.Size{Width: 128, Height: 128})
+ groupViewDiag dialog.Dialog
)
- groupView = container.NewHScroll(groupViewBox)
- for _, e := range group {
- eImg := &canvas.Image{
- FillMode: canvas.ImageFillContain,
- ScaleMode: canvas.ImageScaleFastest,
- }
- if res, err := e.resource(true); err != nil {
- return nil, err
- } else {
- if e.image.Snowflake == entry.image.Snowflake {
- if i, err := makeImageBorder(res.Content(), color.RGBA{R: 0x00, G: 0x00, B: 0xff, A: 0xff}, 0.03); err != nil {
- return nil, err
- } else {
- eImg.Image = i
- }
- } else {
- eImg.Resource = res
- }
- }
- groupViewBox.Add(newImageButton(eImg, e.image))
- log.Printf("Added image %s to group view of %s.", e.image.Snowflake, entry.image.Snowflake)
- }
- groupViewDiag := dialog.NewCustom("Image group", "Close", groupView, window)
- groupViewDiag.Resize(fyne.Size{Width: window.Content().Size().Width - 16})
- groupView.SetMinSize(fyne.Size{Height: 128})
+ ops.Add(widget.NewButton("Show group", func() {
+ if groupViewDiag == nil {
+ func() {
+ defer loading("Loading", "Fetching group...")()
+ log.Printf("Making group view for image %s.", image.Snowflake)
+ _, group := getImageGroup(image)
+ groupView = container.NewHScroll(groupViewBox)
+
+ callback := make([]func(), len(group))
+ defer registerDeferredLoad(callback, true)
+ for i, e := range group {
+ eImg := &canvas.Image{
+ FillMode: canvas.ImageFillContain,
+ ScaleMode: canvas.ImageScaleFastest,
+ }
+ e := e
+
+ callback[i] = func() {
+ go func(eImg *canvas.Image) {
+ defer eImg.Refresh()
+ if res, err := e.resource(true); err != nil {
+ go dialog.ShowError(err, window)
+ return
+ } else {
+ if e.image.Snowflake == entry.image.Snowflake {
+ if i, err := makeImageBorder(res.Content(), color.RGBA{R: 0x00, G: 0x00, B: 0xff, A: 0xff}, 0.03); err != nil {
+ go dialog.ShowError(err, window)
+ return
+ } else {
+ eImg.Image = i
+ }
+ } else {
+ eImg.Resource = res
+ }
+ }
+ }(eImg)
+ }
- var groupViewToggle *widget.Button
- groupViewToggle = widget.NewButton("Show group", func() {
+ groupViewBox.Add(newImageButton(eImg, e.image))
+ log.Printf("Added image %s to group view of %s.", e.image.Snowflake, entry.image.Snowflake)
+ }
+
+ groupViewDiag = dialog.NewCustom("Image group", "Close", groupView, window)
+ groupViewDiag.Resize(fyne.Size{Width: window.Content().Size().Width - 16})
+ groupView.SetMinSize(fyne.Size{Height: 128})
+ log.Printf("Done group view make for image %s.", image.Snowflake)
+ }()
+ }
groupViewDiag.Show()
- })
- ops.Add(groupViewToggle)
+ }))
+
registerImageSwitchCallback(func() {
- groupViewDiag.Hide()
+ if groupViewDiag != nil {
+ groupViewDiag.Hide()
+ }
})
border = container.NewBorder(ops, container.NewBorder(nil, nil, b, r), nil, nil,
diff --git a/main.go b/main.go
index 93d190d..67c78f8 100644
--- a/main.go
+++ b/main.go
@@ -5,6 +5,7 @@ import (
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
+ "log"
"random.chars.jp/git/image-board/client"
"sync"
)
@@ -15,7 +16,7 @@ var (
remote *client.Remote
application = app.NewWithID("jp.chars.rand.imageboard")
window = application.NewWindow("Image Board")
- giantLock = sync.Mutex{}
+ giantLock = sync.RWMutex{}
menu *fyne.MainMenu
inPageView = false
)
@@ -61,7 +62,17 @@ func mainMenuRefresh() {
}
func setContent(o fyne.CanvasObject) {
+ if deferredLoadCancel != nil {
+ log.Printf("Cancelling deferred load due to content set.")
+ deferredLoadCancel()
+ }
+
inPageView = false
window.SetContent(container.NewBorder(tagSearchBar, nil, nil, nil,
tagSearchBar, o))
+
+ if deferredLoadCallback != nil {
+ log.Printf("Initiating deferred load after content set.")
+ go deferredLoadCallback()
+ }
}
diff --git a/tag.go b/tag.go
index 0d03b9d..835bf1e 100644
--- a/tag.go
+++ b/tag.go
@@ -406,7 +406,7 @@ func tagSearchPageSet(page int, back func()) error {
}
var gallery *fyne.Container
- if g, err := makeGallery(images); err != nil {
+ if g, err := makeGallery(images, true); err != nil {
return err
} else {
gallery = g