1
0
mirror of https://github.com/garraflavatra/rolens.git synced 2025-01-18 13:07:58 +00:00

Multiple improvements to the frontend

* Consistent usage of modal footer
* Remove hosts
* Moved hosts to dedicated store
This commit is contained in:
Romein van Buren 2023-05-31 20:20:39 +02:00
parent 27dc1f9117
commit 415efe9ac4
Signed by: romein
GPG Key ID: 0EFF8478ADDF6C49
17 changed files with 163 additions and 112 deletions

View File

@ -1,6 +1,4 @@
--- # Installing Rolens
title: Installation
---
## System requirements ## System requirements

View File

@ -1,9 +1,10 @@
<script> <script>
import BlankState from '$components/blankstate.svelte'; import BlankState from '$components/blankstate.svelte';
import ContextMenu from '$components/contextmenu.svelte'; import ContextMenu from '$components/contextmenu.svelte';
import { connections } from '$lib/stores/connections'; import connections from '$lib/stores/connections';
import contextMenu from '$lib/stores/contextmenu'; import contextMenu from '$lib/stores/contextmenu';
import environment from '$lib/stores/environment'; import environment from '$lib/stores/environment';
import hosts from '$lib/stores/hosts';
import applicationInited from '$lib/stores/inited'; import applicationInited from '$lib/stores/inited';
import About from '$organisms/about.svelte'; import About from '$organisms/about.svelte';
import Connection from '$organisms/connection/index.svelte'; import Connection from '$organisms/connection/index.svelte';
@ -11,18 +12,22 @@
import { EventsEmit, EventsOn } from '$wails/runtime'; import { EventsEmit, EventsOn } from '$wails/runtime';
import { tick } from 'svelte'; import { tick } from 'svelte';
const hosts = {};
const activeHostKey = ''; const activeHostKey = '';
let activeDbKey = ''; let activeDbKey = '';
let activeCollKey = ''; let activeCollKey = '';
let settingsModalOpen = false; let settingsModalOpen = false;
let aboutModalOpen = false; let aboutModalOpen = false;
let connectionManager; let connectionManager;
let showWelcomeScreen = false; let showWelcomeScreen = undefined;
$: host = hosts[activeHostKey]; $: host = hosts[activeHostKey];
$: connection = $connections[activeHostKey]; $: connection = $connections[activeHostKey];
$: showWelcomeScreen = !Object.keys(hosts).length;
hosts.subscribe(h => {
if (h && (showWelcomeScreen === undefined)) {
showWelcomeScreen = !Object.keys($hosts || {}).length;
}
});
async function createFirstHost() { async function createFirstHost() {
showWelcomeScreen = false; showWelcomeScreen = false;
@ -39,14 +44,14 @@
<div id="root" class="platform-{$environment?.platform}"> <div id="root" class="platform-{$environment?.platform}">
<div class="titlebar"></div> <div class="titlebar"></div>
{#if $applicationInited} {#if $applicationInited && $hosts && (showWelcomeScreen !== undefined)}
<main class:empty={showWelcomeScreen}> <main class:empty={showWelcomeScreen}>
{#if showWelcomeScreen} {#if showWelcomeScreen}
<BlankState label="Welcome to Rolens!" image="/logo.png" pale={false} big={true}> <BlankState label="Welcome to Rolens!" image="/logo.png" pale={false} big={true}>
<button class="btn" on:click={createFirstHost}>Add your first host</button> <button class="btn" on:click={createFirstHost}>Add your first host</button>
</BlankState> </BlankState>
{:else} {:else}
<Connection {hosts} {activeHostKey} bind:activeCollKey bind:activeDbKey bind:this={connectionManager} /> <Connection {activeHostKey} bind:activeCollKey bind:activeDbKey bind:this={connectionManager} />
{/if} {/if}
</main> </main>

View File

@ -1,3 +1,5 @@
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
export const connections = writable({}); const connections = writable({});
export default connections;

View File

@ -0,0 +1,11 @@
import { Hosts } from "$wails/go/app/App";
import { writable } from "svelte/store";
import applicationInited from "./inited";
const { set, subscribe } = writable();
const update = async () => set(await Hosts());
applicationInited.defer(update);
const hosts = { update, subscribe };
export default hosts;

View File

@ -2,13 +2,35 @@ import { derived } from 'svelte/store';
import environment from './environment'; import environment from './environment';
import applicationSettings from './settings'; import applicationSettings from './settings';
const applicationInited = derived([ environment, applicationSettings ], ([ env, settings ], set) => { let alreadyInited = false;
const listeners = [];
const defer = listener => {
if (alreadyInited) {
listener();
}
else {
listeners.push(listener)
}
};
const { subscribe } = derived([ environment, applicationSettings ], ([ env, settings ], set) => {
if (alreadyInited) {
return;
}
if (env && settings) { if (env && settings) {
set(true); set(true);
alreadyInited = true;
// Remove loading spinner. // Remove loading spinner.
document.getElementById('app-loading')?.remove(); document.getElementById('app-loading')?.remove();
// Call hooks
listeners.forEach(l => l());
} }
}, false); }, false);
const applicationInited = { defer, subscribe };
export default applicationInited; export default applicationInited;

View File

@ -56,12 +56,13 @@
<Icon name="cog" /> <Icon name="cog" />
</button> </button>
</label> </label>
<button class="btn" type="submit">
<Icon name="play" />
Start export
</button>
</form> </form>
<svelte:fragment slot="footer">
<button class="btn" on:click={performExport}>
<Icon name="play" /> Start export
</button>
</svelte:fragment>
</Modal> </Modal>
<style> <style>

View File

@ -86,16 +86,16 @@
No rules No rules
{/each} {/each}
</div> </div>
<div class="buttons">
<button class="btn" type="button" on:click={addRule} disabled={index.model.some(r => r.sort === 'hashed')}>
<Icon name="+" /> Add rule
</button>
<button class="btn" type="submit" disabled={!index.model.length || index.model.some(r => !r.key)}>
<Icon name="+" /> Create index
</button>
</div>
</form> </form>
<div class="buttons" slot="footer">
<button class="btn" on:click={addRule} disabled={index.model.some(r => r.sort === 'hashed')}>
<Icon name="+" /> Add rule
</button>
<button class="btn" on:click={create} disabled={!index.model.length || index.model.some(r => !r.key)}>
<Icon name="+" /> Create index
</button>
</div>
</Modal> </Modal>
<style> <style>
@ -128,7 +128,7 @@
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;
} }
.buttons button[type="submit"] { .buttons:nth-child(2) {
margin-left: auto; margin-left: auto;
} }
</style> </style>

View File

@ -4,13 +4,13 @@
import Icon from '$components/icon.svelte'; import Icon from '$components/icon.svelte';
import Modal from '$components/modal.svelte'; import Modal from '$components/modal.svelte';
import input from '$lib/actions/input'; import input from '$lib/actions/input';
import hosts from '$lib/stores/hosts';
import queries from '$lib/stores/queries'; import queries from '$lib/stores/queries';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
export let queryToSave = undefined; export let queryToSave = undefined;
export let collection = {}; export let collection = {};
export let show = false; export let show = false;
export let hosts = {};
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let gridSelectedPath = []; let gridSelectedPath = [];
@ -88,7 +88,7 @@
columns={[ { key: 'n', title: 'Query name' }, { key: 'h', title: 'Host' }, { key: 'ns', title: 'Namespace' } ]} columns={[ { key: 'n', title: 'Query name' }, { key: 'h', title: 'Host' }, { key: 'ns', title: 'Namespace' } ]}
key="n" key="n"
items={Object.entries($queries).reduce((object, [ name, query ]) => { items={Object.entries($queries).reduce((object, [ name, query ]) => {
object[query.name] = { n: name, h: hosts[query.hostKey]?.name || '?', ns: `${query.dbKey}.${query.collKey}` }; object[query.name] = { n: name, h: $hosts[query.hostKey]?.name || '?', ns: `${query.dbKey}.${query.collKey}` };
return object; return object;
}, {})} }, {})}
showHeaders={true} showHeaders={true}
@ -100,23 +100,25 @@
/> />
</div> </div>
{#if queryToSave && Object.keys($queries).includes(queryToSave.name)}
<Hint>
You are about to <strong>overwrite</strong> a saved query. Give it
another name if you do not want to overwrite.
</Hint>
{/if}
</form>
<svelte:fragment slot="footer">
{#if queryToSave} {#if queryToSave}
<button class="btn" type="submit"> <button class="btn" on:click={submit}>
<Icon name="save" /> Save query <Icon name="save" /> Save query
</button> </button>
{#if Object.keys($queries).includes(queryToSave.name)}
<Hint>
You are about to <strong>overwrite</strong> a saved query. Give it
another name if you do not want to overwrite.
</Hint>
{/if}
{:else} {:else}
<button class="btn" type="submit" disabled={!selectedKey}> <button class="btn" on:click={submit} disabled={!selectedKey}>
<Icon name="upload" /> Load query <Icon name="upload" /> Load query
</button> </button>
{/if} {/if}
</form> </svelte:fragment>
</Modal> </Modal>
<style> <style>

View File

@ -16,7 +16,6 @@
import QueryChooser from './components/querychooser.svelte'; import QueryChooser from './components/querychooser.svelte';
export let collection; export let collection;
export let hosts = {};
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
const defaults = { const defaults = {
@ -252,7 +251,6 @@
bind:queryToSave bind:queryToSave
bind:show={showQueryChooser} bind:show={showQueryChooser}
on:select={queryChosen} on:select={queryChosen}
{hosts}
{collection} {collection}
/> />

View File

@ -16,7 +16,6 @@
export let hostKey; export let hostKey;
export let dbKey; export let dbKey;
export let collectionKey; export let collectionKey;
export let hosts = {};
let tab = 'find'; let tab = 'find';
let find; let find;
@ -62,7 +61,7 @@
<div class="container"> <div class="container">
{#if tab === 'stats'} <Stats {collection} /> {#if tab === 'stats'} <Stats {collection} />
{:else if tab === 'find'} <Find {collection} {hosts} bind:this={find} on:openViewConfig={openViewConfig} /> {:else if tab === 'find'} <Find {collection} bind:this={find} on:openViewConfig={openViewConfig} />
{:else if tab === 'insert'} <Insert {collection} on:performFind={catchQuery} on:openViewConfig={openViewConfig} /> {:else if tab === 'insert'} <Insert {collection} on:performFind={catchQuery} on:openViewConfig={openViewConfig} />
{:else if tab === 'update'} <Update {collection} on:performFind={catchQuery} /> {:else if tab === 'update'} <Update {collection} on:performFind={catchQuery} />
{:else if tab === 'remove'} <Remove {collection} /> {:else if tab === 'remove'} <Remove {collection} />

View File

@ -3,12 +3,12 @@
import Grid from '$components/grid.svelte'; import Grid from '$components/grid.svelte';
import Modal from '$components/modal.svelte'; import Modal from '$components/modal.svelte';
import { startProgress } from '$lib/progress'; import { startProgress } from '$lib/progress';
import { connections } from '$lib/stores/connections'; import connections from '$lib/stores/connections';
import hosts from '$lib/stores/hosts';
import applicationSettings from '$lib/stores/settings'; import applicationSettings from '$lib/stores/settings';
import { OpenConnection, OpenDatabase, PerformDump } from '$wails/go/app/App'; import { OpenConnection, OpenDatabase, PerformDump } from '$wails/go/app/App';
export let info; export let info;
export let hosts = {};
$: if (info) { $: if (info) {
info.outdir = info.outdir || $applicationSettings.defaultExportDirectory; info.outdir = info.outdir || $applicationSettings.defaultExportDirectory;
@ -83,7 +83,7 @@
hideChildrenToggles hideChildrenToggles
items={[ items={[
{ id: undefined, name: '(localhost)' }, { id: undefined, name: '(localhost)' },
...Object.keys(hosts).map(id => ({ id, name: hosts[id]?.name })), ...Object.keys($hosts).map(id => ({ id, name: $hosts[id]?.name })),
]} ]}
on:select={e => selectHost(e.detail?.itemKey)} on:select={e => selectHost(e.detail?.itemKey)}
/> />
@ -123,11 +123,11 @@
/> />
</div> </div>
</div> </div>
<div>
<button type="submit" class="btn">Perform dump</button>
</div>
</form> </form>
<svelte:fragment slot="footer">
<button class="btn" on:click={performDump}>Perform dump</button>
</svelte:fragment>
</Modal> </Modal>
<style> <style>

View File

@ -1,18 +1,18 @@
<script> <script>
import Modal from '$components/modal.svelte'; import Modal from '$components/modal.svelte';
import input from '$lib/actions/input'; import input from '$lib/actions/input';
import hosts from '$lib/stores/hosts';
import { AddHost, UpdateHost } from '$wails/go/app/App'; import { AddHost, UpdateHost } from '$wails/go/app/App';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
export let show = false; export let show = false;
export let hostKey = ''; export let hostKey = '';
export let hosts = {};
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let form = {}; let form = {};
let error = ''; let error = '';
$: valid = validate(form); $: valid = validate(form);
$: host = hosts[hostKey]; $: host = $hosts[hostKey];
$: if (show || !show) { $: if (show || !show) {
init(); init();
@ -36,7 +36,10 @@
await UpdateHost(hostKey, JSON.stringify(form)); await UpdateHost(hostKey, JSON.stringify(form));
} }
else { else {
await AddHost(JSON.stringify(form)); const newHostKey = await AddHost(JSON.stringify(form));
if (newHostKey) {
hostKey = newHostKey;
}
} }
show = false; show = false;
dispatch('reload'); dispatch('reload');
@ -58,18 +61,18 @@
<span class="label">Connection string</span> <span class="label">Connection string</span>
<input type="text" placeholder="mongodb://..." bind:value={form.uri} spellcheck="false" use:input /> <input type="text" placeholder="mongodb://..." bind:value={form.uri} spellcheck="false" use:input />
</label> </label>
<div class="result">
<div>
{#if error}
<div class="error">{error}</div>
{/if}
</div>
<button class="btn" disabled={!valid} type="submit">
{host ? 'Save' : 'Create'}
</button>
</div>
</form> </form>
<div class="result" slot="footer">
<div>
{#if error}
<div class="error">{error}</div>
{/if}
</div>
<button class="btn" disabled={!valid} on:click={submit}>
{host ? 'Save' : 'Create'}
</button>
</div>
</Modal> </Modal>
<style> <style>

View File

@ -1,12 +1,13 @@
<script> <script>
import Grid from '$components/grid.svelte'; import Grid from '$components/grid.svelte';
import { startProgress } from '$lib/progress'; import { startProgress } from '$lib/progress';
import { connections } from '$lib/stores/connections'; import connections from '$lib/stores/connections';
import { WindowSetTitle } from '$wails/runtime/runtime'; import { WindowSetTitle } from '$wails/runtime/runtime';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import { DropCollection, DropDatabase, OpenCollection, OpenConnection, OpenDatabase, TruncateCollection } from '../../../wailsjs/go/app/App'; import { DropCollection, DropDatabase, OpenCollection, OpenConnection, OpenDatabase, RemoveHost, TruncateCollection } from '../../../wailsjs/go/app/App';
import hosts from '$lib/stores/hosts';
import { tick } from 'svelte';
export let hosts = {};
export let activeHostKey = ''; export let activeHostKey = '';
export let activeDbKey = ''; export let activeDbKey = '';
export let activeCollKey = ''; export let activeCollKey = '';
@ -16,7 +17,7 @@
$: activeHostKey = activeGridPath[0] || activeHostKey; $: activeHostKey = activeGridPath[0] || activeHostKey;
$: activeDbKey = activeGridPath[1]; $: activeDbKey = activeGridPath[1];
$: activeCollKey = activeGridPath[2]; $: activeCollKey = activeGridPath[2];
$: host = hosts[activeHostKey]; $: host = $hosts[activeHostKey];
$: connection = $connections[activeHostKey]; $: connection = $connections[activeHostKey];
$: database = connection?.databases[activeDbKey]; $: database = connection?.databases[activeDbKey];
$: collection = database?.collections?.[activeCollKey]; $: collection = database?.collections?.[activeCollKey];
@ -38,12 +39,21 @@
}); });
activeHostKey = hostKey; activeHostKey = hostKey;
dispatch('connected', hostKey); dispatch('connected', hostKey);
WindowSetTitle(`${hosts[activeHostKey].name} - Rolens`); WindowSetTitle(`${$hosts[activeHostKey].name} - Rolens`);
} }
progress.end(); progress.end();
} }
async function removeHost(hostKey) {
activeCollKey = '';
activeDbKey = '';
activeHostKey = '';
await tick();
await RemoveHost(hostKey);
hosts.update();
}
async function openDatabase(dbKey) { async function openDatabase(dbKey) {
const progress = startProgress(`Opening database "${dbKey}"…`); const progress = startProgress(`Opening database "${dbKey}"…`);
const collections = await OpenDatabase(activeHostKey, dbKey); const collections = await OpenDatabase(activeHostKey, dbKey);
@ -88,9 +98,9 @@
<Grid <Grid
striped={false} striped={false}
columns={[ { key: 'name' }, { key: 'count', right: true } ]} columns={[ { key: 'name' }, { key: 'count', right: true } ]}
items={Object.keys(hosts).map(hostKey => ({ items={Object.keys($hosts).map(hostKey => ({
id: hostKey, id: hostKey,
name: hosts[hostKey].name, name: $hosts[hostKey].name,
icon: 'server', icon: 'server',
children: Object.keys(connection?.databases || {}).sort().map(dbKey => ({ children: Object.keys(connection?.databases || {}).sort().map(dbKey => ({
id: dbKey, id: dbKey,
@ -122,7 +132,8 @@
menu: [ menu: [
{ label: 'New database…', fn: () => dispatch('newDatabase') }, { label: 'New database…', fn: () => dispatch('newDatabase') },
{ separator: true }, { separator: true },
{ label: `Edit host ${hosts[hostKey].name}`, fn: () => dispatch('editHost', hostKey) }, { label: `Edit host ${$hosts[hostKey].name}`, fn: () => dispatch('editHost', hostKey) },
{ label: `Remove host…`, fn: () => removeHost(hostKey) },
], ],
}))} }))}
bind:activePath={activeGridPath} bind:activePath={activeGridPath}

View File

@ -1,6 +1,6 @@
<script> <script>
import { startProgress } from '$lib/progress'; import { startProgress } from '$lib/progress';
import { connections } from '$lib/stores/connections'; import connections from '$lib/stores/connections';
import { Hosts, RenameCollection } from '$wails/go/app/App'; import { Hosts, RenameCollection } from '$wails/go/app/App';
import { EnterText } from '$wails/go/ui/UI'; import { EnterText } from '$wails/go/ui/UI';
import { EventsOn } from '$wails/runtime/runtime'; import { EventsOn } from '$wails/runtime/runtime';
@ -10,8 +10,9 @@
import HostDetail from './hostdetail.svelte'; import HostDetail from './hostdetail.svelte';
import HostTree from './hosttree.svelte'; import HostTree from './hosttree.svelte';
import sharedState from '$lib/stores/sharedstate'; import sharedState from '$lib/stores/sharedstate';
import Icon from '$components/icon.svelte';
import hosts from '$lib/stores/hosts';
export let hosts = {};
export let activeHostKey = ''; export let activeHostKey = '';
export let activeDbKey = ''; export let activeDbKey = '';
export let activeCollKey = ''; export let activeCollKey = '';
@ -25,10 +26,6 @@
$: sharedState.currentDb.set(activeDbKey); $: sharedState.currentDb.set(activeDbKey);
$: sharedState.currentColl.set(activeCollKey); $: sharedState.currentColl.set(activeCollKey);
async function getHosts() {
hosts = await Hosts();
}
export function createHost() { export function createHost() {
hostDetailKey = ''; hostDetailKey = '';
showHostDetail = true; showHostDetail = true;
@ -89,12 +86,16 @@
EventsOn('CreateHost', createHost); EventsOn('CreateHost', createHost);
EventsOn('CreateDatabase', createDatabase); EventsOn('CreateDatabase', createDatabase);
EventsOn('CreateCollection', createCollection); EventsOn('CreateCollection', createCollection);
onMount(getHosts);
</script> </script>
<div class="tree"> <div class="tree">
<div class="tree-buttons">
<button class="button-small" on:click={createHost}>
<Icon name="+" /> New host
</button>
</div>
<HostTree <HostTree
{hosts}
bind:activeHostKey bind:activeHostKey
bind:activeCollKey bind:activeCollKey
bind:activeDbKey bind:activeDbKey
@ -114,21 +115,22 @@
hostKey={activeHostKey} hostKey={activeHostKey}
dbKey={activeDbKey} dbKey={activeDbKey}
collectionKey={activeCollKey} collectionKey={activeCollKey}
{hosts}
/> />
<HostDetail <HostDetail
bind:show={showHostDetail} bind:show={showHostDetail}
on:reload={getHosts} on:reload={hosts.update}
hostKey={activeHostKey} hostKey={activeHostKey}
{hosts}
/> />
<DumpInfo bind:info={exportInfo} {hosts} /> <DumpInfo bind:info={exportInfo} />
<style> <style>
.tree { .tree {
padding: 0.5rem; padding: 0.5rem;
background-color: #fff; background-color: #fff;
} }
.tree-buttons {
margin-bottom: 1rem;
}
</style> </style>

6
frontend/wailsjs/go/app/App.d.ts generated vendored
View File

@ -7,7 +7,7 @@ import {menu} from '../models';
import {context} from '../models'; import {context} from '../models';
import {ui} from '../models'; import {ui} from '../models';
export function AddHost(arg1:string):Promise<void>; export function AddHost(arg1:string):Promise<string>;
export function Aggregate(arg1:string,arg2:string,arg3:string,arg4:string,arg5:string):Promise<void>; export function Aggregate(arg1:string,arg2:string,arg3:string,arg4:string,arg5:string):Promise<void>;
@ -43,7 +43,7 @@ export function PerformFindExport(arg1:string,arg2:string,arg3:string,arg4:strin
export function PurgeLogDirectory():Promise<void>; export function PurgeLogDirectory():Promise<void>;
export function RemoveHost(arg1:string):Promise<void>; export function RemoveHost(arg1:string):Promise<boolean>;
export function RemoveItemById(arg1:string,arg2:string,arg3:string,arg4:string):Promise<boolean>; export function RemoveItemById(arg1:string,arg2:string,arg3:string,arg4:string):Promise<boolean>;
@ -67,7 +67,7 @@ export function Startup(arg1:context.Context,arg2:ui.UI):Promise<void>;
export function TruncateCollection(arg1:string,arg2:string,arg3:string):Promise<boolean>; export function TruncateCollection(arg1:string,arg2:string,arg3:string):Promise<boolean>;
export function UpdateHost(arg1:string,arg2:string):Promise<void>; export function UpdateHost(arg1:string,arg2:string):Promise<boolean>;
export function UpdateItems(arg1:string,arg2:string,arg3:string,arg4:string):Promise<number>; export function UpdateItems(arg1:string,arg2:string,arg3:string,arg4:string):Promise<number>;

View File

@ -45,6 +45,7 @@ func (a *App) Menu() *menu.Menu {
fileMenu.AddText("Update", keys.Combo("u", keys.CmdOrCtrlKey, keys.OptionOrAltKey), menuCallbackEmit(a, "OpenCollectionTab", "update")) fileMenu.AddText("Update", keys.Combo("u", keys.CmdOrCtrlKey, keys.OptionOrAltKey), menuCallbackEmit(a, "OpenCollectionTab", "update"))
fileMenu.AddText("Remove", keys.Combo("r", keys.CmdOrCtrlKey, keys.OptionOrAltKey), menuCallbackEmit(a, "OpenCollectionTab", "remove")) fileMenu.AddText("Remove", keys.Combo("r", keys.CmdOrCtrlKey, keys.OptionOrAltKey), menuCallbackEmit(a, "OpenCollectionTab", "remove"))
fileMenu.AddText("Indexes", keys.Combo("x", keys.CmdOrCtrlKey, keys.OptionOrAltKey), menuCallbackEmit(a, "OpenCollectionTab", "indexes")) fileMenu.AddText("Indexes", keys.Combo("x", keys.CmdOrCtrlKey, keys.OptionOrAltKey), menuCallbackEmit(a, "OpenCollectionTab", "indexes"))
fileMenu.AddText("Aggregate", keys.Combo("a", keys.CmdOrCtrlKey, keys.OptionOrAltKey), menuCallbackEmit(a, "OpenCollectionTab", "aggregate"))
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
appMenu.Append(menu.EditMenu()) appMenu.Append(menu.EditMenu())

View File

@ -34,8 +34,7 @@ func (a *App) Hosts() (map[string]Host, error) {
if err != nil { if err != nil {
// It's ok if the file cannot be opened, for example if it is not accessible. // It's ok if the file cannot be opened, for example if it is not accessible.
// Therefore no error is returned. // Therefore no error is returned.
runtime.LogInfo(a.ctx, "Could not open hosts.json") runtime.LogInfof(a.ctx, "Could not open hosts.json (%s), trying to create it.", err.Error())
runtime.LogInfo(a.ctx, err.Error())
return make(map[string]Host, 0), nil return make(map[string]Host, 0), nil
} }
@ -46,84 +45,80 @@ func (a *App) Hosts() (map[string]Host, error) {
err = json.Unmarshal(jsonData, &hosts) err = json.Unmarshal(jsonData, &hosts)
if err != nil { if err != nil {
runtime.LogInfo(a.ctx, "host.json file contains malformatted JSON data") runtime.LogInfof(a.ctx, "host.json file contains malformatted JSON data: %s", err.Error())
runtime.LogInfo(a.ctx, err.Error())
return nil, errors.New("host.json file contains malformatted JSON data") return nil, errors.New("host.json file contains malformatted JSON data")
} }
return hosts, nil return hosts, nil
} }
} }
func (a *App) AddHost(jsonData string) error { func (a *App) AddHost(jsonData string) string {
hosts, err := a.Hosts() hosts, err := a.Hosts()
if err != nil { if err != nil {
zenity.Error(err.Error(), zenity.Title("Error while retrieving hosts"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Error while retrieving hosts"), zenity.ErrorIcon)
return errors.New("could not retrieve existing host list") return ""
} }
var newHost Host var newHost Host
err = json.Unmarshal([]byte(jsonData), &newHost) err = json.Unmarshal([]byte(jsonData), &newHost)
if err != nil { if err != nil {
runtime.LogError(a.ctx, "Add host: malformed form") runtime.LogErrorf(a.ctx, "Add host: malformed form: %s", err.Error())
runtime.LogError(a.ctx, err.Error())
zenity.Error(err.Error(), zenity.Title("Could not parse JSON"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Could not parse JSON"), zenity.ErrorIcon)
return errors.New("invalid JSON") return ""
} }
id, err := uuid.NewRandom() id, err := uuid.NewRandom()
if err != nil { if err != nil {
runtime.LogError(a.ctx, "Add host: failed to generate a UUID") runtime.LogErrorf(a.ctx, "Add host: failed to generate a UUID: %s", err.Error())
runtime.LogError(a.ctx, err.Error())
zenity.Error(err.Error(), zenity.Title("Error while generating UUID"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Error while generating UUID"), zenity.ErrorIcon)
return errors.New("could not generate a UUID") return ""
} }
hosts[id.String()] = newHost hosts[id.String()] = newHost
err = updateHostsFile(a, hosts) err = updateHostsFile(a, hosts)
if err != nil { if err != nil {
zenity.Error(err.Error(), zenity.Title("Error while updating host list"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Error while updating host list"), zenity.ErrorIcon)
return errors.New("could not update host list") return ""
} }
return nil return id.String()
} }
func (a *App) UpdateHost(hostKey string, jsonData string) error { func (a *App) UpdateHost(hostKey string, jsonData string) bool {
hosts, err := a.Hosts() hosts, err := a.Hosts()
if err != nil { if err != nil {
zenity.Error(err.Error(), zenity.Title("Error while getting hosts"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Error while getting hosts"), zenity.ErrorIcon)
return errors.New("could not retrieve existing host list") return false
} }
var host Host var host Host
err = json.Unmarshal([]byte(jsonData), &host) err = json.Unmarshal([]byte(jsonData), &host)
if err != nil { if err != nil {
runtime.LogError(a.ctx, "Could not parse update host JSON") runtime.LogErrorf(a.ctx, "Could not parse update host JSON: %s", err.Error())
runtime.LogError(a.ctx, err.Error())
zenity.Error(err.Error(), zenity.Title("Could not parse JSON"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Could not parse JSON"), zenity.ErrorIcon)
return errors.New("invalid JSON") return false
} }
hosts[hostKey] = host hosts[hostKey] = host
err = updateHostsFile(a, hosts) err = updateHostsFile(a, hosts)
if err != nil { if err != nil {
zenity.Error(err.Error(), zenity.Title("Error while updating hosts"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Error while updating hosts"), zenity.ErrorIcon)
return errors.New("could not update host list") return false
} }
return nil return true
} }
func (a *App) RemoveHost(key string) error { func (a *App) RemoveHost(key string) bool {
hosts, err := a.Hosts() hosts, err := a.Hosts()
if err != nil { if err != nil {
zenity.Error(err.Error(), zenity.Title("Error while retrieving hosts"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Error while retrieving hosts"), zenity.ErrorIcon)
return errors.New("could not retrieve existing host list") return false
} }
err = zenity.Question("Are you sure you want to remove "+hosts[key].Name+"?", zenity.Title("Confirm"), zenity.WarningIcon) err = zenity.Question("Are you sure you want to remove "+hosts[key].Name+"?", zenity.Title("Confirm"), zenity.WarningIcon)
if err == zenity.ErrCanceled { if err == zenity.ErrCanceled {
return errors.New("operation aborted") return false
} }
delete(hosts, key) delete(hosts, key)
@ -131,7 +126,8 @@ func (a *App) RemoveHost(key string) error {
if err != nil { if err != nil {
zenity.Error(err.Error(), zenity.Title("Error while updating hosts"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Error while updating hosts"), zenity.ErrorIcon)
return errors.New("could not update host list") return false
} }
return nil
return true
} }