Skip to content
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
158 lines (136 sloc) 4.45 KB
Copyright 2017 The Camlistore Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
See the License for the specific language governing permissions and
limitations under the License.
// Package downloadbutton provides a Button element that is used in the sidebar of
// the web UI, to download as a zip file all selected files.
package downloadbutton
import (
//go:generate reactGen
// New returns the button element. It should be used as the entry point, to
// create the needed React element.
// key is the id for when the button is in a list, see
// config is the web UI config that was fetched from the server.
// cbs is a wrapper around the callback functions required by this component.
func New(key string, config map[string]string, cbs *Callbacks) react.Element {
if config == nil {
fmt.Println("Nil config for DownloadItemsBtn")
return nil
downloadHelper, ok := config["downloadHelper"]
if !ok {
fmt.Println("No downloadHelper in config for DownloadItemsBtn")
return nil
if cbs == nil {
fmt.Println("Nil callbacks for DownloadItemsBtn")
return nil
if cbs.GetSelection == nil {
fmt.Println("Nil getSelection callback for DownloadItemsBtn")
return nil
if key == "" {
// A key is only needed in the context of a list, which is why
// it is up to the caller to choose it. Just creating it here for
// the sake of consistency.
key = "downloadItemsButton"
props := DownloadItemsBtnProps{
Key: key,
Callbacks: cbs,
downloadHelper: downloadHelper,
return buildDownloadItemsBtnElem(props)
// Callbacks defines the callbacks that must be provided when creating a
// DownloadItemsBtn instance.
type Callbacks struct {
o *js.Object
// GetSelection returns the list of files (blobRefs) selected
// for downloading.
GetSelection func() []string `js:"getSelection"`
// DownloadItemsBtnDef is the definition for the button, that Renders as a React
// Button.
type DownloadItemsBtnDef struct {
type DownloadItemsBtnProps struct {
// Key is the id for when the button is in a list, see
Key string
downloadHelper string
func (d DownloadItemsBtnDef) Render() react.Element {
return react.Button(
Key: d.Props().Key,
OnClick: d,
func (d DownloadItemsBtnDef) OnClick(*react.SyntheticMouseEvent) {
// Note: there's a "memleak", as in: until the selection is cleared and
// another one is started, this button stays allocated. It is of no
// consequence in this case as we don't allocate a lot for this element (in
// previous experiments where the zip archive was in memory, the leak was
// definitely noticeable then), but it is something to keep in mind for
// future elements.
go func() {
if err := d.downloadSelection(); err != nil {
dom.GetWindow().Alert(fmt.Sprintf("%v", err))
func (d DownloadItemsBtnDef) downloadSelection() error {
selection := d.Props().Callbacks.GetSelection()
downloadPrefix := d.Props().downloadHelper
fileRefs := []string{}
for _, file := range selection {
ref, ok := blob.Parse(file)
if !ok {
return fmt.Errorf("cannot download %q, not a valid blobRef", file)
fileRefs = append(fileRefs, ref.String())
el := dom.GetWindow().Document().CreateElement("input")
input := el.(*dom.HTMLInputElement)
input.Type = "text"
input.Name = "files"
input.Value = strings.Join(fileRefs, ",")
el = dom.GetWindow().Document().CreateElement("form")
form := el.(*dom.HTMLFormElement)
form.Action = downloadPrefix
form.Method = "POST"
// As per
// step 2., a form must be connected to the DOM for submission.
body := dom.GetWindow().Document().QuerySelector("body")
defer body.RemoveChild(form)
return nil
You can’t perform that action at this time.