1
0
mirror of https://github.com/garraflavatra/rolens.git synced 2025-01-18 21:17:59 +00:00

Custom views!

This commit is contained in:
Romein van Buren 2023-01-23 20:47:43 +01:00
parent 29e93dcab1
commit fa924f087d
Signed by: romein
GPG Key ID: 0EFF8478ADDF6C49
11 changed files with 437 additions and 126 deletions

View File

@ -40,6 +40,7 @@
} }
function select(itemKey) { function select(itemKey) {
if (activeKey !== itemKey) {
activeKey = itemKey; activeKey = itemKey;
if (level === 0) { if (level === 0) {
activePath = [ itemKey ]; activePath = [ itemKey ];
@ -49,6 +50,7 @@
} }
dispatch('select', { level, itemKey }); dispatch('select', { level, itemKey });
} }
}
function closeAll() { function closeAll() {
childrenOpen = {}; childrenOpen = {};

View File

@ -1,8 +1,10 @@
<script> <script>
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import Icon from './icon.svelte';
export let tabs = []; export let tabs = [];
export let selectedKey = {}; export let selectedKey = {};
export let canAddTab = false;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
@ -15,24 +17,52 @@
<nav class="tabs"> <nav class="tabs">
<ul> <ul>
{#each tabs as tab (tab.key)} {#each tabs as tab (tab.key)}
<li class="tab" class:active={tab.key === selectedKey}> <li class:active={tab.key === selectedKey}>
<button on:click={() => select(tab.key)}>{tab.title}</button> <button class="tab" on:click={() => select(tab.key)}>{tab.title}</button>
{#if tab.closable}
<button class="close" on:click={() => dispatch('closeTab', tab.key)}>
<Icon name="x" />
</button>
{/if}
</li> </li>
{/each} {/each}
{#if canAddTab}
<li class="tab add">
<button class="tab" on:click={() => dispatch('addTab')}>
<Icon name="+" />
</button>
</li>
{/if}
</ul> </ul>
</nav> </nav>
<style> <style>
.tabs ul { ul {
overflow-x: scroll; overflow-x: scroll;
display: flex; display: flex;
list-style: none; list-style: none;
} }
.tabs li { li {
display: inline-block; display: inline-block;
flex-grow: 1; flex-grow: 1;
position: relative;
} }
.tabs li button {
li.add {
flex: 0 1;
}
.tabs :global(svg) {
width: 13px;
height: 13px;
vertical-align: bottom;
}
li.active :global(svg) {
color: #fff;
}
button.tab {
width: 100%; width: 100%;
padding: 0.7rem 1rem; padding: 0.7rem 1rem;
border: 1px solid #ccc; border: 1px solid #ccc;
@ -40,13 +70,33 @@
cursor: pointer; cursor: pointer;
background-color: #fff; background-color: #fff;
} }
.tabs li:last-child button { button.tab:hover {
background-color: #eee;
}
button.tab:active {
background-color: #ddd;
}
li:last-child button.tab {
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
} }
.tabs li.active button { li.active button.tab {
color: #fff; color: #fff;
background-color: #00008b; background-color: #00008b;
border-color: #00008b; border-color: #00008b;
cursor: not-allowed; cursor: not-allowed;
} }
button.close {
position: absolute;
right: 7px;
top: 7px;
padding: 3px;
border-radius: 2px;
}
button.close:hover {
background-color: rgba(0, 0, 0, 0.1);
}
button.close:active {
background-color: rgba(0, 0, 0, 0.2);
}
</style> </style>

View File

@ -2,32 +2,69 @@
import TabBar from '../../../components/tabbar.svelte'; import TabBar from '../../../components/tabbar.svelte';
import Modal from '../../../components/modal.svelte'; import Modal from '../../../components/modal.svelte';
import Icon from '../../../components/icon.svelte'; import Icon from '../../../components/icon.svelte';
import { views } from '../../../stores';
import { randomString } from '../../../utils';
import { input } from '../../../actions';
export let collection;
export let show = false; export let show = false;
export let activeView = 'list'; export let activeViewKey = 'list';
export let config = {
hideObjectIndicators: false,
columns: [],
};
export let firstItem = {}; export let firstItem = {};
let activeTab = activeView || 'list'; $: tabs = Object.entries($views).filter(v => (
v[0] === 'list' || (
v[1].host === collection.hostKey &&
v[1].database === collection.dbKey &&
v[1].collection === collection.key
)
)).sort((a, b) => sortTabKeys(a[0], b[0]))
.map(([ key, v ]) => ({ key, title: v.name, closable: key !== 'list' }));
$: activeView && (activeTab = activeView); function sortTabKeys(a, b) {
$: if (!config.columns || (config.columns.length === 0)) { if (a === 'list') {
config.columns = [ { key: '_id' } ]; return -1;
}
if (b === 'list') {
return 1;
}
else {
return a.localeCompare(b);
}
}
function createView() {
const newViewKey = randomString();
$views[newViewKey] = {
name: 'Table view',
host: collection.hostKey,
database: collection.dbKey,
collection: collection.key,
type: 'table',
columns: [ { key: '_id' } ],
};
activeViewKey = newViewKey;
}
function removeView(viewKey) {
const keys = Object.keys($views).sort(sortTabKeys);
const oldIndex = keys.indexOf(viewKey);
const newKey = keys[oldIndex - 1];
console.log(keys, oldIndex, newKey);
activeViewKey = newKey;
delete $views[viewKey];
$views = $views;
} }
function addColumn(before) { function addColumn(before) {
if (typeof before === 'number') { if (typeof before === 'number') {
config.columns = [ $views[activeViewKey].columns = [
...config.columns.slice(0, before), ...$views[activeViewKey].columns.slice(0, before),
{}, {},
...config.columns.slice(before), ...$views[activeViewKey].columns.slice(before),
]; ];
} }
else { else {
config.columns = [ ...config.columns, {} ]; $views[activeViewKey].columns = [ ...$views[activeViewKey].columns, {} ];
} }
} }
@ -35,46 +72,63 @@
if ((typeof firstItem !== 'object') || (firstItem === null)) { if ((typeof firstItem !== 'object') || (firstItem === null)) {
return; return;
} }
config.columns = Object.keys(firstItem).map(key => ({ key })); $views[activeViewKey].columns = Object.keys(firstItem).map(key => ({ key }));
} }
function moveColumn(oldIndex, delta) { function moveColumn(oldIndex, delta) {
const column = config.columns[oldIndex]; const column = $views[activeViewKey].columns[oldIndex];
const newIndex = oldIndex + delta; const newIndex = oldIndex + delta;
config.columns.splice(oldIndex, 1); $views[activeViewKey].columns.splice(oldIndex, 1);
config.columns.splice(newIndex, 0, column); $views[activeViewKey].columns.splice(newIndex, 0, column);
config.columns = config.columns; $views[activeViewKey].columns = $views[activeViewKey].columns;
} }
function removeColumn(index) { function removeColumn(index) {
config.columns.splice(index, 1); $views[activeViewKey].columns.splice(index, 1);
config.columns = config.columns; $views[activeViewKey].columns = $views[activeViewKey].columns;
} }
</script> </script>
<Modal title="View configuration" bind:show contentPadding={false}> <Modal title="View configuration" bind:show contentPadding={false}>
<TabBar <TabBar
tabs={[ {tabs}
{ key: 'list', title: 'List view' }, canAddTab={true}
{ key: 'table', title: 'Table view columns' }, on:addTab={createView}
]} on:closeTab={e => removeView(e.detail)}
bind:selectedKey={activeTab} bind:selectedKey={activeViewKey}
/> />
<div class="options"> <div class="options">
{#if activeTab === 'list'} {#if $views[activeViewKey]}
<div class="meta">
{#key activeViewKey}
<label class="field">
<span class="label">View name</span>
<input type="text" use:input={{ autofocus: true }} bind:value={$views[activeViewKey].name} disabled={activeViewKey === 'list'} />
</label>
{/key}
<label class="field">
<span class="label">View type</span>
<select bind:value={$views[activeViewKey].type} disabled={activeViewKey === 'list'}>
<option value="list">List view</option>
<option value="table">Table view</option>
</select>
</label>
</div>
{#if $views[activeViewKey].type === 'list'}
<div class="flex"> <div class="flex">
<input type="checkbox" id="hideObjectIndicators" bind:checked={config.hideObjectIndicators} /> <input type="checkbox" id="hideObjectIndicators" bind:checked={$views[activeViewKey].hideObjectIndicators} />
<label for="hideObjectIndicators"> <label for="hideObjectIndicators">
Hide object indicators ({'{...}'} and [...]) in list view and show nothing instead Hide object indicators ({'{...}'} and [...]) in list view and show nothing instead
</label> </label>
</div> </div>
{:else if activeTab === 'table'} {:else if $views[activeViewKey].type === 'table'}
{#each config.columns as column, columnIndex} {#each $views[activeViewKey].columns as column, columnIndex}
<div class="column"> <div class="column">
<label class="field"> <label class="field">
<input type="text" bind:value={column.key} placeholder="Column keypath" /> <input type="text" use:input bind:value={column.key} placeholder="Column keypath" />
</label> </label>
<button class="btn" type="button" on:click={() => addColumn(columnIndex)} title="Add column before this one"> <button class="btn" type="button" on:click={() => addColumn(columnIndex)} title="Add column before this one">
<Icon name="+" /> <Icon name="+" />
@ -82,7 +136,7 @@
<button class="btn" type="button" on:click={() => moveColumn(columnIndex, -1)} disabled={columnIndex === 0} title="Move column one position up"> <button class="btn" type="button" on:click={() => moveColumn(columnIndex, -1)} disabled={columnIndex === 0} title="Move column one position up">
<Icon name="chev-u" /> <Icon name="chev-u" />
</button> </button>
<button class="btn" type="button" on:click={() => moveColumn(columnIndex, 1)} disabled={columnIndex === config.columns.length - 1} title="Move column one position down"> <button class="btn" type="button" on:click={() => moveColumn(columnIndex, 1)} disabled={columnIndex === $views[activeViewKey].columns.length - 1} title="Move column one position down">
<Icon name="chev-d" /> <Icon name="chev-d" />
</button> </button>
<button class="btn danger" type="button" on:click={() => removeColumn(columnIndex)} title="Remove this column"> <button class="btn danger" type="button" on:click={() => removeColumn(columnIndex)} title="Remove this column">
@ -97,6 +151,7 @@
<Icon name="zap" /> Add suggested columns <Icon name="zap" /> Add suggested columns
</button> </button>
{/if} {/if}
{/if}
</div> </div>
</Modal> </Modal>
@ -105,9 +160,17 @@
padding: 1rem; padding: 1rem;
} }
.meta {
display: grid;
grid-template: 1fr / 1fr 1fr;
gap: 0.5rem;
margin-bottom: 1rem;
}
.flex { .flex {
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;
align-items: center;
} }
.column { .column {

View File

@ -1,5 +1,5 @@
<script> <script>
import { FindItems, Hosts, RemoveItemById, UpdateHost } from '../../../../wailsjs/go/app/App'; import { FindItems, RemoveItemById } from '../../../../wailsjs/go/app/App';
import CodeExample from '../../../components/code-example.svelte'; import CodeExample from '../../../components/code-example.svelte';
import { input } from '../../../actions'; import { input } from '../../../actions';
import ObjectGrid from '../../../components/objectgrid.svelte'; import ObjectGrid from '../../../components/objectgrid.svelte';
@ -8,7 +8,7 @@
import FindViewConfigModal from './find-viewconfig.svelte'; import FindViewConfigModal from './find-viewconfig.svelte';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import Grid from '../../../components/grid.svelte'; import Grid from '../../../components/grid.svelte';
import { applicationSettings } from '../../../stores'; import { applicationSettings, views } from '../../../stores';
export let collection; export let collection;
@ -21,46 +21,18 @@
}; };
let form = { ...defaults }; let form = { ...defaults };
let view = 'list'; let activeViewKey = 'list';
let result = {}; let result = {};
let submittedForm = {}; let submittedForm = {};
let queryField; let queryField;
let activePath = []; let activePath = [];
let objectViewerData; let objectViewerData;
let viewConfigModalOpen = false; let viewConfigModalOpen = false;
let viewConfig = {};
$: code = `db.${collection.key}.find(${form.query || '{}'}${form.fields && form.fields !== '{}' ? `, ${form.fields}` : ''}).sort(${form.sort})${form.skip ? `.skip(${form.skip})` : ''}${form.limit ? `.limit(${form.limit})` : ''};`; $: code = `db.${collection.key}.find(${form.query || '{}'}${form.fields && form.fields !== '{}' ? `, ${form.fields}` : ''}).sort(${form.sort})${form.skip ? `.skip(${form.skip})` : ''}${form.limit ? `.limit(${form.limit})` : ''};`;
$: lastPage = (submittedForm.limit && result?.results?.length) ? Math.max(0, Math.ceil((result.total - submittedForm.limit) / submittedForm.limit)) : 0; $: lastPage = (submittedForm.limit && result?.results?.length) ? Math.max(0, Math.ceil((result.total - submittedForm.limit) / submittedForm.limit)) : 0;
$: activePage = (submittedForm.limit && submittedForm.skip && result?.results?.length) ? submittedForm.skip / submittedForm.limit : 0; $: activePage = (submittedForm.limit && submittedForm.skip && result?.results?.length) ? submittedForm.skip / submittedForm.limit : 0;
$: collection && refresh(); $: collection && refresh();
$: updateConfig(viewConfig);
async function getViewConfig() {
try {
const hosts = await Hosts();
viewConfig = hosts?.[collection.hostKey]?.databases?.[collection.dbKey]?.collections?.[collection.key]?.viewConfig || {};
console.log(hosts, viewConfig);
}
catch (e) {
console.error(e);
}
}
async function updateConfig(viewConfig) {
try {
const hosts = await Hosts();
hosts[collection.hostKey].databases = hosts[collection.hostKey].databases || {};
hosts[collection.hostKey].databases[collection.dbKey] = hosts[collection.hostKey].databases[collection.dbKey] || {};
hosts[collection.hostKey].databases[collection.dbKey].collections = hosts[collection.hostKey].databases[collection.dbKey].collections || {};
hosts[collection.hostKey].databases[collection.dbKey].collections[collection.key] = hosts[collection.hostKey].databases[collection.dbKey].collections[collection.key] || {};
hosts[collection.hostKey].databases[collection.dbKey].collections[collection.key].viewConfig = viewConfig;
await UpdateHost(collection.hostKey, JSON.stringify(hosts[collection.hostKey]));
}
catch (e) {
console.error(e);
}
}
async function submitQuery() { async function submitQuery() {
activePath = []; activePath = [];
@ -72,7 +44,6 @@
} }
async function refresh() { async function refresh() {
await getViewConfig();
if ($applicationSettings.autosubmitQuery) { if ($applicationSettings.autosubmitQuery) {
await submitQuery(); await submitQuery();
} }
@ -121,10 +92,6 @@
objectViewerData = item; objectViewerData = item;
} }
function toggleView() {
view = view === 'table' ? 'list' : 'table';
}
export function performQuery(q) { export function performQuery(q) {
form = { ...defaults, ...q }; form = { ...defaults, ...q };
submitQuery(); submitQuery();
@ -172,19 +139,19 @@
<div class="result"> <div class="result">
<div class="grid"> <div class="grid">
{#key result} {#key result}
{#if view === 'table'} {#if activeViewKey === 'table'}
<Grid <Grid
key="_id" key="_id"
columns={viewConfig.columns?.map(c => ({ key: c.key, title: c.key })) || []} columns={$views[activeViewKey]?.columns?.map(c => ({ key: c.key, title: c.key })) || []}
showHeaders={true} showHeaders={true}
items={result.results || []} items={result.results || []}
bind:activePath bind:activePath
on:trigger={e => openJson(e.detail?.itemKey)} on:trigger={e => openJson(e.detail?.itemKey)}
/> />
{:else if view === 'list'} {:else if activeViewKey === 'list'}
<ObjectGrid <ObjectGrid
data={result.results} data={result.results}
hideObjectIndicators={viewConfig?.hideObjectIndicators} hideObjectIndicators={$views[activeViewKey]?.hideObjectIndicators}
bind:activePath bind:activePath
on:trigger={e => openJson(e.detail?.itemKey)} on:trigger={e => openJson(e.detail?.itemKey)}
/> />
@ -202,9 +169,6 @@
<button class="btn" on:click={() => viewConfigModalOpen = true} title="Configure view"> <button class="btn" on:click={() => viewConfigModalOpen = true} title="Configure view">
<Icon name="cog" /> <Icon name="cog" />
</button> </button>
<button class="btn" on:click={toggleView} title="Toggle view">
<Icon name={view === 'table' ? 'list' : 'table'} />
</button>
<button class="btn danger" on:click={removeActive} disabled={!activePath?.length} title="Drop selected item"> <button class="btn danger" on:click={removeActive} disabled={!activePath?.length} title="Drop selected item">
<Icon name="-" /> <Icon name="-" />
</button> </button>
@ -226,7 +190,12 @@
</div> </div>
<ObjectViewer bind:data={objectViewerData} /> <ObjectViewer bind:data={objectViewerData} />
<FindViewConfigModal bind:show={viewConfigModalOpen} activeView={view} bind:config={viewConfig} firstItem={result.results?.[0]} /> <FindViewConfigModal
bind:show={viewConfigModalOpen}
bind:activeViewKey
firstItem={result.results?.[0]}
{collection}
/>
<datalist id="limits"> <datalist id="limits">
{#each [ 1, 5, 10, 25, 50, 100, 200 ] as value} {#each [ 1, 5, 10, 25, 50, 100, 200 ] as value}

View File

@ -1,6 +1,6 @@
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import { Environment } from '../wailsjs/runtime/runtime'; import { Environment } from '../wailsjs/runtime/runtime';
import { Settings, UpdateSettings } from '../wailsjs/go/app/App'; import { Settings, UpdateSettings, UpdateViewStore, Views } from '../wailsjs/go/app/App';
export const busy = (() => { export const busy = (() => {
const { update, subscribe } = writable(0); const { update, subscribe } = writable(0);
@ -63,3 +63,19 @@ export const environment = (() => {
reload(); reload();
return { reload, subscribe }; return { reload, subscribe };
})(); })();
export const views = (() => {
const { set, subscribe } = writable({});
const reload = async() => {
const newViewStore = await Views();
set(newViewStore);
return newViewStore;
};
reload();
subscribe(newViewStore => {
UpdateViewStore(JSON.stringify(newViewStore));
});
return { reload, set, subscribe };
})();

View File

@ -44,6 +44,13 @@ p strong {
cursor: wait; cursor: wait;
} }
input:disabled,
select:disabled {
cursor: not-allowed;
opacity: 1;
color: #444;
}
.field { .field {
display: flex; display: flex;
white-space: nowrap; white-space: nowrap;

View File

@ -33,3 +33,18 @@ export function controlKeyDown(event) {
return event?.ctrlKey; return event?.ctrlKey;
} }
} }
function randInt(min, max) {
return Math.round(Math.random() * (max - min) + min);
}
export function randomString(length = 12) {
const chars = 'qwertyuiopasdfghjklzxcvbnm1234567890';
let output = '';
Array(length).fill('').forEach(() => {
output += chars[randInt(0, chars.length - 1)];
});
return output;
}

View File

@ -37,6 +37,8 @@ export function RemoveItemById(arg1:string,arg2:string,arg3:string,arg4:string):
export function RemoveItems(arg1:string,arg2:string,arg3:string,arg4:string,arg5:boolean):Promise<number>; export function RemoveItems(arg1:string,arg2:string,arg3:string,arg4:string,arg5:boolean):Promise<number>;
export function RemoveView(arg1:string):Promise<void>;
export function RenameCollection(arg1:string,arg2:string,arg3:string,arg4:string):Promise<boolean>; export function RenameCollection(arg1:string,arg2:string,arg3:string,arg4:string):Promise<boolean>;
export function Settings():Promise<app.Settings>; export function Settings():Promise<app.Settings>;
@ -48,3 +50,7 @@ export function UpdateHost(arg1:string,arg2:string):Promise<void>;
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>;
export function UpdateSettings(arg1:string):Promise<app.Settings>; export function UpdateSettings(arg1:string):Promise<app.Settings>;
export function UpdateViewStore(arg1:string):Promise<void>;
export function Views():Promise<app.ViewStore>;

View File

@ -66,6 +66,10 @@ export function RemoveItems(arg1, arg2, arg3, arg4, arg5) {
return window['go']['app']['App']['RemoveItems'](arg1, arg2, arg3, arg4, arg5); return window['go']['app']['App']['RemoveItems'](arg1, arg2, arg3, arg4, arg5);
} }
export function RemoveView(arg1) {
return window['go']['app']['App']['RemoveView'](arg1);
}
export function RenameCollection(arg1, arg2, arg3, arg4) { export function RenameCollection(arg1, arg2, arg3, arg4) {
return window['go']['app']['App']['RenameCollection'](arg1, arg2, arg3, arg4); return window['go']['app']['App']['RenameCollection'](arg1, arg2, arg3, arg4);
} }
@ -89,3 +93,11 @@ export function UpdateItems(arg1, arg2, arg3, arg4) {
export function UpdateSettings(arg1) { export function UpdateSettings(arg1) {
return window['go']['app']['App']['UpdateSettings'](arg1); return window['go']['app']['App']['UpdateSettings'](arg1);
} }
export function UpdateViewStore(arg1) {
return window['go']['app']['App']['UpdateViewStore'](arg1);
}
export function Views() {
return window['go']['app']['App']['Views']();
}

View File

@ -0,0 +1,182 @@
package app
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
type ViewType string
const (
TableView ViewType = "table"
ListView ViewType = "list"
)
type ViewColumn struct {
Key string `json:"key"`
Width int64 `json:"width"`
}
type View struct {
Name string `json:"name"`
Host string `json:"host"`
Database string `json:"database"`
Collection string `json:"collection"`
Type ViewType `json:"type"`
HideObjectIndicators bool `json:"hideObjectIndicators"`
Columns []ViewColumn `json:"columns"`
}
var BuiltInListView = View{
Name: "List",
Type: ListView,
}
type ViewStore map[string]View
func updateViewStore(newData ViewStore) error {
filePath, err := appDataFilePath("views.json")
if err != nil {
return err
}
jsonData, err := json.MarshalIndent(newData, "", "\t")
if err != nil {
return err
}
err = ioutil.WriteFile(filePath, jsonData, os.ModePerm)
return err
}
func (a *App) Views() (ViewStore, error) {
views := make(ViewStore, 0)
filePath, err := appDataFilePath("views.json")
if err != nil {
fmt.Println(err.Error())
return views, err
}
jsonData, err := ioutil.ReadFile(filePath)
if err != nil {
// It's ok if the file cannot be opened, for example if it is not accessible.
// Therefore no error is returned.
fmt.Println(err.Error())
return views, nil
}
if len(jsonData) > 0 {
err = json.Unmarshal(jsonData, &views)
if err != nil {
fmt.Println(err.Error())
return nil, errors.New("views.json file contains malformatted JSON data")
}
}
views["list"] = BuiltInListView
return views, nil
}
// func (a *App) AddView(jsonData string) error {
// views, err := a.Views()
// if err != nil {
// runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
// Type: runtime.InfoDialog,
// Title: "Could not retrieve views",
// })
// return errors.New("could not retrieve existing view store")
// }
// var newView View
// err = json.Unmarshal([]byte(jsonData), &newView)
// if err != nil {
// runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
// Type: runtime.InfoDialog,
// Title: "Malformed JSON",
// })
// return errors.New("invalid JSON")
// }
// id, err := uuid.NewRandom()
// if err != nil {
// runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
// Type: runtime.InfoDialog,
// Title: "Failed to generate a UUID",
// })
// return errors.New("could not generate a UUID")
// }
// views[id.String()] = newView
// err = updateViewStore(views)
// if err != nil {
// runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
// Type: runtime.InfoDialog,
// Title: "Could not update view store",
// })
// return errors.New("could not update view store")
// }
// return nil
// }
func (a *App) UpdateViewStore(jsonData string) error {
var viewStore ViewStore
err := json.Unmarshal([]byte(jsonData), &viewStore)
if err != nil {
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.InfoDialog,
Title: "Malformed JSON",
})
return errors.New("invalid JSON")
}
err = updateViewStore(viewStore)
if err != nil {
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.InfoDialog,
Title: "Could not update view store",
})
return errors.New("could not update view store")
}
return nil
}
func (a *App) RemoveView(viewKey string) error {
views, err := a.Views()
if err != nil {
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.InfoDialog,
Title: "Could not retrieve views",
})
return errors.New("could not retrieve existing view store")
}
sure, _ := runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Title: "Confirm",
Message: "Are you sure you want to remove " + views[viewKey].Name + "?",
Buttons: []string{"Yes", "No"},
DefaultButton: "Yes",
CancelButton: "No",
})
if sure != "Yes" {
return errors.New("operation aborted")
}
delete(views, viewKey)
err = updateViewStore(views)
if err != nil {
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.InfoDialog,
Title: "Could not update view store",
})
return errors.New("could not update view store")
}
return nil
}

View File

@ -14,17 +14,6 @@ import (
type Host struct { type Host struct {
Name string `json:"name"` Name string `json:"name"`
URI string `json:"uri"` URI string `json:"uri"`
Databases map[string]struct {
Collections map[string]struct {
ViewConfig struct {
HideObjectIndicators bool `json:"hideObjectIndicators"`
Columns []struct {
Key string `json:"key"`
Width int64 `json:"width"`
} `json:"columns"`
} `json:"viewConfig"`
} `json:"collections"`
} `json:"databases"`
} }
func updateHostsFile(newData map[string]Host) error { func updateHostsFile(newData map[string]Host) error {