···1515 applicationsDir string
1616 iconsDir string
1717 binDir string
1818+ progress Progress
1819}
19202021// NewApp returns an App wired to the current user's home directory. The
···4344 binDir: binDir,
4445 }
4546}
4747+4848+// WithProgress returns a copy of App that reports download progress to p.
4949+func (a App) WithProgress(p Progress) App {
5050+ a.progress = p
5151+ return a
5252+}
+1-1
internal/appherder/install.go
···106106107107// InstallFromURL downloads an AppImage from url and installs it.
108108func (a App) InstallFromURL(ctx context.Context, url string) (string, error) {
109109- tmpName, err := downloadToTemp(ctx, url, "appherder-install")
109109+ tmpName, err := downloadToTemp(ctx, url, "appherder-install", a.progress, url)
110110 if err != nil {
111111 return "", err
112112 }
+31
internal/appherder/progress.go
···11+package appherder
22+33+import "io"
44+55+// Progress receives download progress updates. total is -1 when the server
66+// reports no content length.
77+type Progress interface {
88+ Download(name string, received, total int64)
99+}
1010+1111+type progressReader struct {
1212+ reader io.Reader
1313+ progress Progress
1414+ name string
1515+ total int64
1616+ received int64
1717+}
1818+1919+func newProgressReader(reader io.Reader, progress Progress, name string, total int64) io.Reader {
2020+ if progress == nil {
2121+ return reader
2222+ }
2323+ return &progressReader{reader: reader, progress: progress, name: name, total: total}
2424+}
2525+2626+func (pr *progressReader) Read(buf []byte) (int, error) {
2727+ bytesRead, err := pr.reader.Read(buf)
2828+ pr.received += int64(bytesRead)
2929+ pr.progress.Download(pr.name, pr.received, pr.total)
3030+ return bytesRead, err
3131+}