forked from Elara6331/itd
		
	
		
			
				
	
	
		
			361 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			361 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"path/filepath"
 | 
						|
 | 
						|
	"fyne.io/fyne/v2"
 | 
						|
	"fyne.io/fyne/v2/container"
 | 
						|
	"fyne.io/fyne/v2/data/binding"
 | 
						|
	"fyne.io/fyne/v2/dialog"
 | 
						|
	"fyne.io/fyne/v2/theme"
 | 
						|
	"fyne.io/fyne/v2/widget"
 | 
						|
	"go.arsenm.dev/itd/api"
 | 
						|
)
 | 
						|
 | 
						|
func fsTab(ctx context.Context, client *api.Client, w fyne.Window, opened chan struct{}) fyne.CanvasObject {
 | 
						|
	c := container.NewVBox()
 | 
						|
 | 
						|
	// Create new binding to store current directory
 | 
						|
	cwdData := binding.NewString()
 | 
						|
	cwdData.Set("/")
 | 
						|
 | 
						|
	// Create new list binding to store fs listing entries
 | 
						|
	lsData := binding.NewUntypedList()
 | 
						|
 | 
						|
	// This goroutine waits until the fs tab is opened to
 | 
						|
	// request the listing from the watch
 | 
						|
	go func() {
 | 
						|
		// Wait for opened signal
 | 
						|
		<-opened
 | 
						|
 | 
						|
		// Show loading pop up
 | 
						|
		loading := newLoadingPopUp(w)
 | 
						|
		loading.Show()
 | 
						|
 | 
						|
		// Read root directory
 | 
						|
		ls, err := client.ReadDir(ctx, "/")
 | 
						|
		if err != nil {
 | 
						|
			guiErr(err, "Error reading directory", false, w)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		// Set ls binding
 | 
						|
		lsData.Set(lsToAny(ls))
 | 
						|
 | 
						|
		// Hide loading pop up
 | 
						|
		loading.Hide()
 | 
						|
	}()
 | 
						|
 | 
						|
	toolbar := widget.NewToolbar(
 | 
						|
		widget.NewToolbarAction(
 | 
						|
			theme.ViewRefreshIcon(),
 | 
						|
			func() {
 | 
						|
				refresh(ctx, cwdData, lsData, client, w, c)
 | 
						|
			},
 | 
						|
		),
 | 
						|
		widget.NewToolbarAction(
 | 
						|
			theme.UploadIcon(),
 | 
						|
			func() {
 | 
						|
				// Create open dialog for file that will be uploaded
 | 
						|
				dlg := dialog.NewFileOpen(func(uc fyne.URIReadCloser, err error) {
 | 
						|
					if err != nil || uc == nil {
 | 
						|
						return
 | 
						|
					}
 | 
						|
					// Get filepath and close
 | 
						|
					localPath := uc.URI().Path()
 | 
						|
					uc.Close()
 | 
						|
 | 
						|
					// Create new entry to store filepath
 | 
						|
					filenameEntry := widget.NewEntry()
 | 
						|
					// Set entry text to the file name of the selected file
 | 
						|
					filenameEntry.SetText(filepath.Base(localPath))
 | 
						|
					// Create new dialog asking for the filename of the file to be stored on the watch
 | 
						|
					uploadDlg := dialog.NewForm("Upload", "Upload", "Cancel", []*widget.FormItem{
 | 
						|
						widget.NewFormItem("Filename", filenameEntry),
 | 
						|
					}, func(ok bool) {
 | 
						|
						if !ok {
 | 
						|
							return
 | 
						|
						}
 | 
						|
 | 
						|
						// Get current directory
 | 
						|
						cwd, _ := cwdData.Get()
 | 
						|
						// Get remote path by joining current directory with filename
 | 
						|
						remotePath := filepath.Join(cwd, filenameEntry.Text)
 | 
						|
 | 
						|
						// Create new progress dialog
 | 
						|
						progressDlg := newProgress(w)
 | 
						|
						progressDlg.Show()
 | 
						|
 | 
						|
						// Upload file
 | 
						|
						progressCh, err := client.Upload(ctx, remotePath, localPath)
 | 
						|
						if err != nil {
 | 
						|
							guiErr(err, "Error uploading file", false, w)
 | 
						|
							return
 | 
						|
						}
 | 
						|
 | 
						|
						for progressEvt := range progressCh {
 | 
						|
							progressDlg.SetTotal(float64(progressEvt.Total))
 | 
						|
							progressDlg.SetValue(float64(progressEvt.Sent))
 | 
						|
							if progressEvt.Sent == progressEvt.Total {
 | 
						|
								break
 | 
						|
							}
 | 
						|
						}
 | 
						|
 | 
						|
						// Close progress dialog
 | 
						|
						progressDlg.Hide()
 | 
						|
 | 
						|
						// Add file to listing (avoids full refresh)
 | 
						|
						lsData.Append(api.FileInfo{
 | 
						|
							IsDir: false,
 | 
						|
							Name:  filepath.Base(remotePath),
 | 
						|
						})
 | 
						|
					}, w)
 | 
						|
					uploadDlg.Show()
 | 
						|
				}, w)
 | 
						|
				dlg.Show()
 | 
						|
 | 
						|
			},
 | 
						|
		),
 | 
						|
		widget.NewToolbarAction(
 | 
						|
			theme.FolderNewIcon(),
 | 
						|
			func() {
 | 
						|
				// Create new entry for filename
 | 
						|
				filenameEntry := widget.NewEntry()
 | 
						|
				// Create new dialog to ask for the filename
 | 
						|
				mkdirDialog := dialog.NewForm("Make Directory", "Create", "Cancel", []*widget.FormItem{
 | 
						|
					widget.NewFormItem("Filename", filenameEntry),
 | 
						|
				}, func(ok bool) {
 | 
						|
					if !ok {
 | 
						|
						return
 | 
						|
					}
 | 
						|
 | 
						|
					// Get current directory
 | 
						|
					cwd, _ := cwdData.Get()
 | 
						|
					// Get remote path by joining current directory and filename
 | 
						|
					remotePath := filepath.Join(cwd, filenameEntry.Text)
 | 
						|
 | 
						|
					// Make directory
 | 
						|
					err := client.Mkdir(ctx, remotePath)
 | 
						|
					if err != nil {
 | 
						|
						guiErr(err, "Error creating directory", false, w)
 | 
						|
						return
 | 
						|
					}
 | 
						|
 | 
						|
					// Add directory to listing (avoids full refresh)
 | 
						|
					lsData.Append(api.FileInfo{
 | 
						|
						IsDir: true,
 | 
						|
						Name:  filepath.Base(remotePath),
 | 
						|
					})
 | 
						|
				}, w)
 | 
						|
				mkdirDialog.Show()
 | 
						|
			},
 | 
						|
		),
 | 
						|
	)
 | 
						|
 | 
						|
	// Add listener to listing data to create the new items on the GUI
 | 
						|
	// whenever the listing changes
 | 
						|
	lsData.AddListener(binding.NewDataListener(func() {
 | 
						|
		c.Objects = makeItems(ctx, client, lsData, cwdData, w, c)
 | 
						|
		c.Refresh()
 | 
						|
	}))
 | 
						|
 | 
						|
	return container.NewBorder(
 | 
						|
		nil,
 | 
						|
		toolbar,
 | 
						|
		nil,
 | 
						|
		nil,
 | 
						|
		container.NewVScroll(c),
 | 
						|
	)
 | 
						|
}
 | 
						|
 | 
						|
// makeItems creates GUI objects from listing data
 | 
						|
func makeItems(
 | 
						|
	ctx context.Context,
 | 
						|
	client *api.Client,
 | 
						|
	lsData binding.UntypedList,
 | 
						|
	cwdData binding.String,
 | 
						|
	w fyne.Window,
 | 
						|
	c *fyne.Container,
 | 
						|
) []fyne.CanvasObject {
 | 
						|
	// Get listing data
 | 
						|
	ls, _ := lsData.Get()
 | 
						|
 | 
						|
	// Create output slice with dame length as listing
 | 
						|
	out := make([]fyne.CanvasObject, len(ls))
 | 
						|
	for index, val := range ls {
 | 
						|
		// Assert value as file info
 | 
						|
		item := val.(api.FileInfo)
 | 
						|
 | 
						|
		var icon fyne.Resource
 | 
						|
		// Decide which icon to use
 | 
						|
		if item.IsDir {
 | 
						|
			if item.Name == ".." {
 | 
						|
				icon = theme.NavigateBackIcon()
 | 
						|
			} else {
 | 
						|
				icon = theme.FolderIcon()
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			icon = theme.FileIcon()
 | 
						|
		}
 | 
						|
 | 
						|
		// Create new button with the decided icon and the item name
 | 
						|
		btn := widget.NewButtonWithIcon(item.Name, icon, nil)
 | 
						|
		// Align left
 | 
						|
		btn.Alignment = widget.ButtonAlignLeading
 | 
						|
		// Decide which callback function to use
 | 
						|
		if item.IsDir {
 | 
						|
			btn.OnTapped = func() {
 | 
						|
				// Get current directory
 | 
						|
				cwd, _ := cwdData.Get()
 | 
						|
				// Join current directory with item name
 | 
						|
				cwd = filepath.Join(cwd, item.Name)
 | 
						|
				// Set new current directory
 | 
						|
				cwdData.Set(cwd)
 | 
						|
				// Refresh GUI to display new directory
 | 
						|
				refresh(ctx, cwdData, lsData, client, w, c)
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			btn.OnTapped = func() {
 | 
						|
				// Get current directory
 | 
						|
				cwd, _ := cwdData.Get()
 | 
						|
				// Join current directory with item name
 | 
						|
				remotePath := filepath.Join(cwd, item.Name)
 | 
						|
				// Create new save dialog
 | 
						|
				dlg := dialog.NewFileSave(func(uc fyne.URIWriteCloser, err error) {
 | 
						|
					if err != nil || uc == nil {
 | 
						|
						return
 | 
						|
					}
 | 
						|
					// Get path of selected file
 | 
						|
					localPath := uc.URI().Path()
 | 
						|
					// Close WriteCloser (it's not needed)
 | 
						|
					uc.Close()
 | 
						|
 | 
						|
					// Create new progress dialog
 | 
						|
					progressDlg := newProgress(w)
 | 
						|
					progressDlg.Show()
 | 
						|
 | 
						|
					// Download file
 | 
						|
					progressCh, err := client.Download(ctx, localPath, remotePath)
 | 
						|
					if err != nil {
 | 
						|
						guiErr(err, "Error downloading file", false, w)
 | 
						|
						return
 | 
						|
					}
 | 
						|
 | 
						|
					// For every progress event
 | 
						|
					for progressEvt := range progressCh {
 | 
						|
						progressDlg.SetTotal(float64(progressEvt.Total))
 | 
						|
						progressDlg.SetValue(float64(progressEvt.Sent))
 | 
						|
					}
 | 
						|
 | 
						|
					// Close progress dialog
 | 
						|
					progressDlg.Hide()
 | 
						|
				}, w)
 | 
						|
				// Set filename to the item name
 | 
						|
				dlg.SetFileName(item.Name)
 | 
						|
				dlg.Show()
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if item.Name == ".." {
 | 
						|
			out[index] = btn
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		moveBtn := widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func() {
 | 
						|
			moveEntry := widget.NewEntry()
 | 
						|
			dlg := dialog.NewForm("Move", "Move", "Cancel", []*widget.FormItem{
 | 
						|
				widget.NewFormItem("New Path", moveEntry),
 | 
						|
			}, func(ok bool) {
 | 
						|
				if !ok {
 | 
						|
					return
 | 
						|
				}
 | 
						|
 | 
						|
				// Get current directory
 | 
						|
				cwd, _ := cwdData.Get()
 | 
						|
				// Join current directory with item name
 | 
						|
				oldPath := filepath.Join(cwd, item.Name)
 | 
						|
 | 
						|
				// Rename file
 | 
						|
				err := client.Rename(ctx, oldPath, moveEntry.Text)
 | 
						|
				if err != nil {
 | 
						|
					guiErr(err, "Error renaming file", false, w)
 | 
						|
					return
 | 
						|
				}
 | 
						|
 | 
						|
				// Refresh GUI
 | 
						|
				refresh(ctx, cwdData, lsData, client, w, c)
 | 
						|
			}, w)
 | 
						|
			dlg.Show()
 | 
						|
		})
 | 
						|
 | 
						|
		removeBtn := widget.NewButtonWithIcon("", theme.DeleteIcon(), func() {
 | 
						|
			// Get current directory
 | 
						|
			cwd, _ := cwdData.Get()
 | 
						|
			// Join current directory with item name
 | 
						|
			path := filepath.Join(cwd, item.Name)
 | 
						|
 | 
						|
			// Remove file
 | 
						|
			err := client.Remove(ctx, path)
 | 
						|
			if err != nil {
 | 
						|
				guiErr(err, "Error removing file", false, w)
 | 
						|
				return
 | 
						|
			}
 | 
						|
 | 
						|
			// Refresh GUI
 | 
						|
			refresh(ctx, cwdData, lsData, client, w, c)
 | 
						|
		})
 | 
						|
 | 
						|
		// Add button to GUI component list
 | 
						|
		out[index] = container.NewBorder(
 | 
						|
			nil,
 | 
						|
			nil,
 | 
						|
			nil,
 | 
						|
			container.NewHBox(moveBtn, removeBtn),
 | 
						|
			btn,
 | 
						|
		)
 | 
						|
	}
 | 
						|
	return out
 | 
						|
}
 | 
						|
 | 
						|
func refresh(
 | 
						|
	ctx context.Context,
 | 
						|
	cwdData binding.String,
 | 
						|
	lsData binding.UntypedList,
 | 
						|
	client *api.Client,
 | 
						|
	w fyne.Window,
 | 
						|
	c *fyne.Container,
 | 
						|
) {
 | 
						|
	// Create and show new loading pop up
 | 
						|
	loading := newLoadingPopUp(w)
 | 
						|
	loading.Show()
 | 
						|
	// Close pop up at the end of the function
 | 
						|
	defer loading.Hide()
 | 
						|
 | 
						|
	// Get current directory
 | 
						|
	cwd, _ := cwdData.Get()
 | 
						|
	// Read directory
 | 
						|
	ls, err := client.ReadDir(ctx, cwd)
 | 
						|
	if err != nil {
 | 
						|
		guiErr(err, "Error reading directory", false, w)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	// Set new listing data
 | 
						|
	lsData.Set(lsToAny(ls))
 | 
						|
	// Create new GUI objects
 | 
						|
	c.Objects = makeItems(ctx, client, lsData, cwdData, w, c)
 | 
						|
	// Refresh GUI
 | 
						|
	c.Refresh()
 | 
						|
}
 | 
						|
 | 
						|
func lsToAny(ls []api.FileInfo) []interface{} {
 | 
						|
	out := make([]interface{}, len(ls)-1)
 | 
						|
	for i, e := range ls {
 | 
						|
		// Skip first element as it is always "."
 | 
						|
		if i == 0 {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		out[i-1] = e
 | 
						|
	}
 | 
						|
	return out
 | 
						|
}
 |