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

Loose JSON parsing. Many UI improvements.

This commit is contained in:
Romein van Buren 2023-02-19 17:26:32 +01:00
parent 7ac1587c95
commit 78540622ee
Signed by: romein
GPG Key ID: 0EFF8478ADDF6C49
25 changed files with 211 additions and 130 deletions

View File

@ -3,7 +3,7 @@
import Icon from './icon.svelte'; import Icon from './icon.svelte';
import FormInput from './forminput.svelte'; import FormInput from './forminput.svelte';
import contextMenu from '$lib/stores/contextmenu'; import contextMenu from '$lib/stores/contextmenu';
import { resolveKeypath, setValue } from '$lib/keypaths'; import { resolveKeypath, setValue } from '$lib/objects';
export let items = []; export let items = [];
export let columns = []; export let columns = [];

View File

@ -3,6 +3,7 @@
import Modal from './modal.svelte'; import Modal from './modal.svelte';
import ObjectTree from './objecttree.svelte'; import ObjectTree from './objecttree.svelte';
import { onDestroy } from 'svelte'; import { onDestroy } from 'svelte';
import { deepClone } from '$lib/objects';
export let data; export let data;
@ -11,7 +12,7 @@
let _data; let _data;
$: if (data) { $: if (data) {
_data = JSON.parse(JSON.stringify(data)); _data = deepClone(data);
for (const key of Object.keys(_data)) { for (const key of Object.keys(_data)) {
if (typeof _data[key] === 'undefined') { if (typeof _data[key] === 'undefined') {
delete _data[key]; delete _data[key];

View File

@ -1,5 +1,6 @@
import { isInt } from '$lib/math'; import { isInt } from '$lib/math';
import { canBeObjectId, int32, int64, uint64 } from '$lib/mongo'; import { canBeObjectId, int32, int64, uint64 } from '$lib/mongo';
import { jsonLooseParse } from '$lib/strings';
export default function input(node, { autofocus, type, onValid, onInvalid, mandatory } = { export default function input(node, { autofocus, type, onValid, onInvalid, mandatory } = {
autofocus: false, autofocus: false,
@ -26,7 +27,7 @@ export default function input(node, { autofocus, type, onValid, onInvalid, manda
switch (type) { switch (type) {
case 'json': case 'json':
try { try {
JSON.parse(node.value); jsonLooseParse(node.value);
return false; return false;
} }
catch { catch {

View File

@ -0,0 +1,29 @@
{
"Fields": {
"$currentDate": "Current Date",
"$inc": "Increment",
"$min": "Min",
"$max": "Max",
"$mul": "Multiply",
"$rename": "Rename",
"$set": "Set",
"$setOnInsert": "Set on Insert",
"$unset": "Unset"
},
"Array": {
"$addToSet": "Add to Set",
"$pop": "Pop",
"$pull": "Pull",
"$push": "Push",
"$pullAll": "Push All"
},
"Modifiers": {
"$each": "Each",
"$position": "Position",
"$slice": "Slice",
"$sort": "Sort"
},
"Bitwise": {
"$bit": "Bit"
}
}

View File

@ -1,8 +1,9 @@
import { ObjectId } from 'bson'; import { ObjectId } from 'bson';
import aggregationStages from './aggregation-stages.json'; import aggregationStages from './aggregation-stages.json';
import atomicUpdateOperators from './atomic-update-operators.json';
import locales from './locales.json'; import locales from './locales.json';
export { aggregationStages, locales }; export { aggregationStages, atomicUpdateOperators, locales };
// Calculate the min and max values of (un)signed integers with n bits // Calculate the min and max values of (un)signed integers with n bits
export const intMin = bits => Math.pow(2, bits - 1) * -1; export const intMin = bits => Math.pow(2, bits - 1) * -1;

View File

@ -56,3 +56,8 @@ export function setValue(object, path, value) {
return object; return object;
} }
export function deepClone(obj) {
// Room for improvement below
return JSON.parse(JSON.stringify(obj));
}

View File

@ -2,3 +2,23 @@ export function capitalise(string = '') {
const capitalised = string.charAt(0).toUpperCase() + string.slice(1); const capitalised = string.charAt(0).toUpperCase() + string.slice(1);
return capitalised; return capitalised;
} }
export function jsonLooseParse(json) {
const obj = new Function(`return (${json})`)();
return obj;
}
export function convertLooseJson(json) {
const j = JSON.stringify(jsonLooseParse(json));
return j;
}
export function looseJsonIsValid(json) {
try {
jsonLooseParse(json);
return true;
}
catch {
return false;
}
}

View File

@ -5,6 +5,7 @@
import MongoCollation from '$components/mongo-collation.svelte'; import MongoCollation from '$components/mongo-collation.svelte';
import input from '$lib/actions/input'; import input from '$lib/actions/input';
import { aggregationStageDocumentationURL, aggregationStages } from '$lib/mongo'; import { aggregationStageDocumentationURL, aggregationStages } from '$lib/mongo';
import { jsonLooseParse, looseJsonIsValid } from '$lib/strings';
import { Aggregate } from '$wails/go/app/App'; import { Aggregate } from '$wails/go/app/App';
import { BrowserOpenURL } from '$wails/runtime/runtime'; import { BrowserOpenURL } from '$wails/runtime/runtime';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
@ -14,15 +15,7 @@
const options = {}; const options = {};
let stages = []; let stages = [];
let settingsModalOpen = false; let settingsModalOpen = false;
$: invalid = !stages.length || stages.some(stage => { $: invalid = !stages.length || stages.some(stage => !stage.data || !looseJsonIsValid(stage.data));
try {
JSON.parse(stage.data);
return false;
}
catch {
return true;
}
});
function addStage() { function addStage() {
stages = [ ...stages, { type: '$match' } ]; stages = [ ...stages, { type: '$match' } ];
@ -38,7 +31,7 @@
} }
async function run() { async function run() {
const pipeline = stages.map(stage => ({ [stage.type]: JSON.parse(stage.data) })); const pipeline = stages.map(stage => ({ [stage.type]: jsonLooseParse(stage.data) }));
await Aggregate(collection.hostKey, collection.dbKey, collection.key, JSON.stringify(pipeline), JSON.stringify(options)); await Aggregate(collection.hostKey, collection.dbKey, collection.key, JSON.stringify(pipeline), JSON.stringify(options));
} }
@ -84,11 +77,10 @@
<Icon name="cog" /> Settings <Icon name="cog" /> Settings
</button> </button>
</div> </div>
</div> </div>
</form> </form>
<Modal title="Advanced settings" bind:show={settingsModalOpen}> <Modal title="Advanced aggregation settings" bind:show={settingsModalOpen}>
<div class="settinggrid"> <div class="settinggrid">
<label for="allowDiskUse">Allow disk use</label> <label for="allowDiskUse">Allow disk use</label>
<div class="field"> <div class="field">

View File

@ -2,7 +2,7 @@
import FormInput from '$components/forminput.svelte'; import FormInput from '$components/forminput.svelte';
import Icon from '$components/icon.svelte'; import Icon from '$components/icon.svelte';
import { inputTypes } from '$lib/mongo'; import { inputTypes } from '$lib/mongo';
import { resolveKeypath, setValue } from '$lib/keypaths'; import { resolveKeypath, setValue } from '$lib/objects';
import Hint from '$components/hint.svelte'; import Hint from '$components/hint.svelte';
export let item = {}; export let item = {};

View File

@ -1,9 +1,10 @@
<script> <script>
import CodeExample from '$components/code-example.svelte'; // import CodeExample from '$components/code-example.svelte';
import Grid from '$components/grid.svelte'; import Grid from '$components/grid.svelte';
import Icon from '$components/icon.svelte'; import Icon from '$components/icon.svelte';
import ObjectGrid from '$components/objectgrid.svelte'; import ObjectGrid from '$components/objectgrid.svelte';
import input from '$lib/actions/input'; import input from '$lib/actions/input';
import { deepClone } from '$lib/objects';
import queries from '$lib/stores/queries'; import queries from '$lib/stores/queries';
import applicationSettings from '$lib/stores/settings'; import applicationSettings from '$lib/stores/settings';
import views from '$lib/stores/views'; import views from '$lib/stores/views';
@ -45,7 +46,7 @@
if (newResult) { if (newResult) {
newResult.results = newResult.results?.map(s => EJSON.parse(s, { relaxed: false })); newResult.results = newResult.results?.map(s => EJSON.parse(s, { relaxed: false }));
result = newResult; result = newResult;
submittedForm = JSON.parse(JSON.stringify(form)); submittedForm = deepClone(form);
} }
resetFocus(); resetFocus();
} }
@ -157,12 +158,12 @@
</div> </div>
<div class="form-row three"> <div class="form-row three">
<CodeExample {code} /> <!-- <CodeExample {code} /> -->
<button class="btn" type="button" on:click={loadQuery} title="Load query…"> <button class="btn" type="button" on:click={loadQuery}>
<Icon name="upload" /> <Icon name="upload" /> Load query…
</button> </button>
<button class="btn" type="button" on:click={saveQuery} title="Save query as…"> <button class="btn" type="button" on:click={saveQuery}>
<Icon name="save" /> <Icon name="save" /> Save as…
</button> </button>
<button type="submit" class="btn" title="Run query"> <button type="submit" class="btn" title="Run query">
<Icon name="play" /> Run <Icon name="play" /> Run
@ -274,7 +275,8 @@
} }
.form-row.three { .form-row.three {
margin-bottom: 0rem; margin-bottom: 0rem;
grid-template: 1fr / 1fr repeat(3, auto); grid-template: 1fr / repeat(3, auto);
justify-content: end;
} }
.result { .result {

View File

@ -29,6 +29,10 @@
collection.key = collectionKey; collection.key = collectionKey;
} }
$: if (hostKey || dbKey || collectionKey) {
tab = 'find';
}
EventsOn('OpenCollectionTab', name => (tab = name || tab)); EventsOn('OpenCollectionTab', name => (tab = name || tab));
async function catchQuery(event) { async function catchQuery(event) {

View File

@ -43,6 +43,16 @@
</script> </script>
<div class="indexes"> <div class="indexes">
<div class="grid">
<ObjectGrid
key="name"
data={indexes}
getRootMenu={(_, idx) => [ { label: 'Drop this index', fn: () => drop(idx.name) } ]}
bind:activePath
on:trigger={e => openJson(e.detail.itemKey)}
/>
</div>
<div class="actions"> <div class="actions">
<button class="btn" on:click={getIndexes}> <button class="btn" on:click={getIndexes}>
<Icon name="reload" /> Reload <Icon name="reload" /> Reload
@ -54,16 +64,6 @@
<Icon name="x" /> Drop selected <Icon name="x" /> Drop selected
</button> </button>
</div> </div>
<div class="grid">
<ObjectGrid
key="name"
data={indexes}
getRootMenu={(_, idx) => [ { label: 'Drop this index', fn: () => drop(idx.name) } ]}
bind:activePath
on:trigger={e => openJson(e.detail.itemKey)}
/>
</div>
</div> </div>
<ObjectViewer bind:data={objectViewerData} /> <ObjectViewer bind:data={objectViewerData} />
@ -73,7 +73,7 @@
.indexes { .indexes {
display: grid; display: grid;
gap: 0.5rem; gap: 0.5rem;
grid-template: auto 1fr / 1fr; grid-template: 1fr auto / 1fr;
} }
.indexes .grid { .indexes .grid {

View File

@ -7,7 +7,7 @@
import { randomString } from '$lib/math'; import { randomString } from '$lib/math';
import { inputTypes } from '$lib/mongo'; import { inputTypes } from '$lib/mongo';
import views from '$lib/stores/views'; import views from '$lib/stores/views';
import { capitalise } from '$lib/strings'; import { capitalise, convertLooseJson, jsonLooseParse } from '$lib/strings';
import { InsertItems } from '$wails/go/app/App'; import { InsertItems } from '$wails/go/app/App';
import { EJSON } from 'bson'; import { EJSON } from 'bson';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
@ -32,7 +32,7 @@
$: { $: {
if (collection.viewKey === 'list') { if (collection.viewKey === 'list') {
try { try {
newItems = EJSON.parse(json, { relaxed: false }); newItems = EJSON.deserialize(jsonLooseParse(json), { relaxed: false });
} }
catch { /* ok */ } catch { /* ok */ }
} }
@ -46,7 +46,7 @@
} }
async function insert() { async function insert() {
insertedIds = await InsertItems(collection.hostKey, collection.dbKey, collection.key, json); insertedIds = await InsertItems(collection.hostKey, collection.dbKey, collection.key, convertLooseJson(json));
if ((collection.viewKey === 'list') && insertedIds) { if ((collection.viewKey === 'list') && insertedIds) {
newItems = []; newItems = [];
} }
@ -95,15 +95,8 @@
<form on:submit|preventDefault={insert}> <form on:submit|preventDefault={insert}>
<div class="items"> <div class="items">
{#if collection.viewKey === 'list'} {#if collection.viewKey === 'list'}
<label class="field"> <label class="field json">
<textarea <textarea placeholder="[]" class="code" bind:value={json} use:input={{ type: 'json', autofocus: true }}></textarea>
cols="30"
rows="10"
placeholder="[]"
class="code"
bind:value={json}
use:input={{ type: 'json', autofocus: true }}
></textarea>
</label> </label>
{:else if viewType === 'form'} {:else if viewType === 'form'}
<div class="form"> <div class="form">
@ -208,4 +201,11 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
.field.json {
height: 100%;
}
.field.json textarea {
resize: none;
}
</style> </style>

View File

@ -1,5 +1,5 @@
<script> <script>
import CodeExample from '$components/code-example.svelte'; // import CodeExample from '$components/code-example.svelte';
import Icon from '$components/icon.svelte'; import Icon from '$components/icon.svelte';
import input from '$lib/actions/input'; import input from '$lib/actions/input';
import { RemoveItems } from '$wails/go/app/App'; import { RemoveItems } from '$wails/go/app/App';
@ -17,15 +17,7 @@
</script> </script>
<form on:submit|preventDefault={removeItems}> <form on:submit|preventDefault={removeItems}>
<div class="options"> <!-- <CodeExample {code} /> -->
<CodeExample {code} />
<label class="field">
<span class="label">Many</span>
<span class="checkbox">
<input type="checkbox" bind:checked={many} />
</span>
</label>
</div>
<label class="field"> <label class="field">
<textarea <textarea
@ -38,35 +30,38 @@
></textarea> ></textarea>
</label> </label>
<div class="flex"> <div class="actions">
<div>
{#key result}
{#if typeof result === 'number'}
<span class="flash-green">Removed {result} item{result === 1 ? '' : 's'}</span>
{/if}
{/key}
</div>
<button type="submit" class="btn danger"> <button type="submit" class="btn danger">
<Icon name="-" /> Remove <Icon name="-" /> Remove
</button> </button>
<label class="field many">
<span class="label">Many</span>
<span class="checkbox">
<input type="checkbox" bind:checked={many} />
</span>
</label>
{#key result}
{#if typeof result === 'number'}
<span class="flash-green">Removed {result} item{result === 1 ? '' : 's'}</span>
{/if}
{/key}
</div> </div>
</form> </form>
<style> <style>
form { form {
display: grid; display: grid;
grid-template-rows: auto 1fr auto; grid-template-rows: 1fr auto;
gap: 0.5rem; gap: 0.5rem;
} }
.options { .many {
display: grid; display: inline-flex;
gap: 0.5rem;
grid-template: 1fr / 1fr auto;
} }
.flex { textarea {
display: flex; resize: none;
justify-content: space-between;
} }
</style> </style>

View File

@ -1,12 +1,12 @@
<script> <script>
import CodeExample from '$components/code-example.svelte'; // import CodeExample from '$components/code-example.svelte';
import ObjectGrid from '$components/objectgrid.svelte'; import ObjectGrid from '$components/objectgrid.svelte';
export let collection; export let collection;
</script> </script>
<div class="stats"> <div class="stats">
<CodeExample code="db.stats()" /> <!-- <CodeExample code="db.stats()" /> -->
<div class="grid"> <div class="grid">
<ObjectGrid data={collection.stats} /> <ObjectGrid data={collection.stats} />
@ -17,7 +17,7 @@
.stats { .stats {
display: grid; display: grid;
gap: 0.5rem; gap: 0.5rem;
grid-template: auto 1fr / 1fr; grid-template: auto / 1fr;
} }
.stats .grid { .stats .grid {

View File

@ -1,45 +1,31 @@
<script> <script>
import CodeExample from '$components/code-example.svelte'; // import CodeExample from '$components/code-example.svelte';
import Icon from '$components/icon.svelte'; import Icon from '$components/icon.svelte';
import input from '$lib/actions/input'; import input from '$lib/actions/input';
import { atomicUpdateOperators } from '$lib/mongo';
import { deepClone } from '$lib/objects';
import { convertLooseJson, jsonLooseParse } from '$lib/strings';
import { UpdateItems } from '$wails/go/app/App'; import { UpdateItems } from '$wails/go/app/App';
export let collection = {}; export let collection = {};
const atomicUpdateOperators = {
'Fields': {
$currentDate: 'Current Date',
$inc: 'Increment',
$min: 'Min',
$max: 'Max',
$mul: 'Multiply',
$rename: 'Rename',
$set: 'Set',
$setOnInsert: 'Set on Insert',
$unset: 'Unset',
},
'Array': {
$addToSet: 'Add to Set',
$pop: 'Pop',
$pull: 'Pull',
$push: 'Push',
$pullAll: 'Push All',
},
'Modifiers': {
$each: 'Each',
$position: 'Position',
$slice: 'Slice',
$sort: 'Sort',
},
'Bitwise': {
$bit: 'Bit',
},
};
const allOperators = Object.values(atomicUpdateOperators).map(Object.keys).flat(); const allOperators = Object.values(atomicUpdateOperators).map(Object.keys).flat();
const form = { query: '{}', parameters: [ { type: '$set' } ] }; const form = { query: '{}', parameters: [ { type: '$set' } ] };
let updatedCount; let updatedCount;
$: code = buildCode(form); $: code = buildCode(form);
$: invalid = !form.query || form.parameters?.some(param => {
if (!param.value) {
return true;
}
try {
jsonLooseParse(param.value);
return false;
}
catch {
return true;
}
});
function buildCode(form) { function buildCode(form) {
let operation = '{ ' + form.parameters.filter(p => p.type).map(p => `${p.type}: ${p.value || '{}'}`).join(', ') + ' }'; let operation = '{ ' + form.parameters.filter(p => p.type).map(p => `${p.type}: ${p.value || '{}'}`).join(', ') + ' }';
@ -58,7 +44,10 @@
} }
async function submitQuery() { async function submitQuery() {
updatedCount = await UpdateItems(collection.hostKey, collection.dbKey, collection.key, JSON.stringify(form)); const f = deepClone(form);
f.query = convertLooseJson(f.query);
f.parameters = f.parameters.map(param => ({ ...param, value: convertLooseJson(param.value) }));
updatedCount = await UpdateItems(collection.hostKey, collection.dbKey, collection.key, JSON.stringify(f));
} }
function removeParam(index) { function removeParam(index) {
@ -92,7 +81,7 @@
</script> </script>
<form class="update" on:submit|preventDefault={submitQuery}> <form class="update" on:submit|preventDefault={submitQuery}>
<CodeExample language="json" {code} /> <!-- <CodeExample language="json" {code} /> -->
<div class="options"> <div class="options">
<label class="field"> <label class="field">
@ -117,7 +106,7 @@
{/if} {/if}
{/key} {/key}
<button class="btn" type="submit"> <button class="btn" type="submit" disabled={invalid}>
<Icon name="check" /> Update <Icon name="check" /> Update
</button> </button>
</div> </div>
@ -135,7 +124,7 @@
{#each Object.entries(atomicUpdateOperators) as [groupName, options]} {#each Object.entries(atomicUpdateOperators) as [groupName, options]}
<optgroup label={groupName}> <optgroup label={groupName}>
{#each Object.entries(options) as [key, label]} {#each Object.entries(options) as [key, label]}
<option value={key} disabled={form.parameters.some(p => p.type === key)}> <option value={key} disabled={form.parameters.some(p => p.type === key) && (key !== param.type)}>
{label} {label}
</option> </option>
{/each} {/each}
@ -145,14 +134,13 @@
<input type="text" class="code" bind:value={param.value} placeholder={'{}'} use:input={{ type: 'json' }} /> <input type="text" class="code" bind:value={param.value} placeholder={'{}'} use:input={{ type: 'json' }} />
</label> </label>
<label class="field"> <button class="btn" disabled={form.parameters.length >= allOperators.length} on:click={() => addParameter()} type="button">
<button class="btn" disabled={form.parameters.length >= allOperators.length} on:click={() => addParameter()} type="button"> <Icon name="+" />
<Icon name="+" /> </button>
</button>
<button class="btn" disabled={form.parameters.length < 2} on:click={() => removeParam(index)} type="button"> <button class="btn" disabled={form.parameters.length < 2} on:click={() => removeParam(index)} type="button">
<Icon name="-" /> <Icon name="-" />
</button> </button>
</label>
</fieldset> </fieldset>
{/each} {/each}
</fieldset> </fieldset>
@ -162,7 +150,7 @@
.update { .update {
display: grid; display: grid;
gap: 0.5rem; gap: 0.5rem;
grid-template: auto auto auto 1fr / 1fr; grid-template: auto auto 1fr / 1fr;
} }
.options { .options {
@ -181,7 +169,7 @@
} }
.parameter { .parameter {
display: grid; display: grid;
grid-template: 1fr / 1fr auto; grid-template: 1fr / 1fr auto auto;
gap: 0.5rem; gap: 0.5rem;
} }

View File

@ -41,6 +41,8 @@ export function OpenDirectory(arg1:string,arg2:string):Promise<string>;
export function PerformExport(arg1:string):Promise<boolean>; export function PerformExport(arg1:string):Promise<boolean>;
export function PurgeLogDirectory():Promise<void>;
export function RemoveHost(arg1:string):Promise<void>; export function RemoveHost(arg1:string):Promise<void>;
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>;

View File

@ -74,6 +74,10 @@ export function PerformExport(arg1) {
return window['go']['app']['App']['PerformExport'](arg1); return window['go']['app']['App']['PerformExport'](arg1);
} }
export function PurgeLogDirectory() {
return window['go']['app']['App']['PurgeLogDirectory']();
}
export function RemoveHost(arg1) { export function RemoveHost(arg1) {
return window['go']['app']['App']['RemoveHost'](arg1); return window['go']['app']['App']['RemoveHost'](arg1);
} }

View File

@ -86,6 +86,34 @@ func (a *App) Environment() EnvironmentInfo {
return a.Env return a.Env
} }
func (a *App) PurgeLogDirectory() {
sure, _ := wailsRuntime.MessageDialog(a.ctx, wailsRuntime.MessageDialogOptions{
Title: "Are you sure you want to remove all logfiles?",
Buttons: []string{"Yes", "No"},
DefaultButton: "Yes",
CancelButton: "No",
Type: wailsRuntime.QuestionDialog,
})
if sure != "Yes" {
return
}
err := os.RemoveAll(a.Env.LogDirectory)
if err == nil {
wailsRuntime.MessageDialog(a.ctx, wailsRuntime.MessageDialogOptions{
Title: "Successfully purged log directory.",
Type: wailsRuntime.InfoDialog,
})
} else {
wailsRuntime.MessageDialog(a.ctx, wailsRuntime.MessageDialogOptions{
Title: "Encountered an error while purging log directory.",
Message: err.Error(),
Type: wailsRuntime.WarningDialog,
})
}
}
func menuEventEmitter(a *App, eventName string, data ...interface{}) func(cd *menu.CallbackData) { func menuEventEmitter(a *App, eventName string, data ...interface{}) func(cd *menu.CallbackData) {
return func(cd *menu.CallbackData) { return func(cd *menu.CallbackData) {
wailsRuntime.EventsEmit(a.ctx, eventName, data...) wailsRuntime.EventsEmit(a.ctx, eventName, data...)
@ -99,6 +127,9 @@ func (a *App) Menu() *menu.Menu {
aboutMenu.AddText("About…", nil, menuEventEmitter(a, "OpenAboutModal")) aboutMenu.AddText("About…", nil, menuEventEmitter(a, "OpenAboutModal"))
aboutMenu.AddText("Prefrences…", keys.CmdOrCtrl(","), menuEventEmitter(a, "OpenPrefrences")) aboutMenu.AddText("Prefrences…", keys.CmdOrCtrl(","), menuEventEmitter(a, "OpenPrefrences"))
aboutMenu.AddSeparator() aboutMenu.AddSeparator()
aboutMenu.AddText("Open log directory…", nil, func(cd *menu.CallbackData) { open_file.Reveal(a.Env.LogDirectory) })
aboutMenu.AddText("Purge logs", nil, func(cd *menu.CallbackData) { a.PurgeLogDirectory() })
aboutMenu.AddSeparator()
aboutMenu.AddText("Quit", keys.CmdOrCtrl("q"), func(cd *menu.CallbackData) { wailsRuntime.Quit(a.ctx) }) aboutMenu.AddText("Quit", keys.CmdOrCtrl("q"), func(cd *menu.CallbackData) { wailsRuntime.Quit(a.ctx) })
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
@ -123,8 +154,6 @@ func (a *App) Menu() *menu.Menu {
helpMenu := appMenu.AddSubmenu("Help") helpMenu := appMenu.AddSubmenu("Help")
helpMenu.AddText("User guide", nil, func(cd *menu.CallbackData) { wailsRuntime.BrowserOpenURL(a.ctx, "") }) helpMenu.AddText("User guide", nil, func(cd *menu.CallbackData) { wailsRuntime.BrowserOpenURL(a.ctx, "") })
helpMenu.AddSeparator()
helpMenu.AddText("Open log directory", nil, func(cd *menu.CallbackData) { open_file.Reveal(a.Env.LogDirectory) })
return appMenu return appMenu
} }

View File

@ -64,6 +64,7 @@ func (a *App) TruncateCollection(hostKey, dbKey, collKey string) bool {
Buttons: []string{"Yes", "No"}, Buttons: []string{"Yes", "No"},
DefaultButton: "Yes", DefaultButton: "Yes",
CancelButton: "No", CancelButton: "No",
Type: runtime.WarningDialog,
}) })
if sure != "Yes" { if sure != "Yes" {
return false return false
@ -97,6 +98,7 @@ func (a *App) DropCollection(hostKey, dbKey, collKey string) bool {
Buttons: []string{"Yes", "No"}, Buttons: []string{"Yes", "No"},
DefaultButton: "Yes", DefaultButton: "Yes",
CancelButton: "No", CancelButton: "No",
Type: runtime.WarningDialog,
}) })
if sure != "Yes" { if sure != "Yes" {
return false return false

View File

@ -20,6 +20,7 @@ func (a *App) RemoveItems(hostKey, dbKey, collKey, jsonData string, many bool) i
Buttons: []string{"Yes", "No"}, Buttons: []string{"Yes", "No"},
DefaultButton: "Yes", DefaultButton: "Yes",
CancelButton: "No", CancelButton: "No",
Type: runtime.WarningDialog,
}) })
if sure != "Yes" { if sure != "Yes" {
return 0 return 0

View File

@ -44,11 +44,12 @@ func (a *App) UpdateItems(hostKey, dbKey, collKey string, formJson string) int64
err = bson.UnmarshalExtJSON([]byte(form.Query), true, &query) err = bson.UnmarshalExtJSON([]byte(form.Query), true, &query)
if err != nil { if err != nil {
runtime.LogError(a.ctx, "Invalid update query:") runtime.LogWarning(a.ctx, "Invalid update query:")
runtime.LogError(a.ctx, err.Error()) runtime.LogWarning(a.ctx, form.Query)
runtime.LogWarning(a.ctx, err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{ runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog, Type: runtime.ErrorDialog,
Title: "Invalid query", Title: "Invalid update query",
Message: err.Error(), Message: err.Error(),
}) })
return 0 return 0
@ -60,11 +61,12 @@ func (a *App) UpdateItems(hostKey, dbKey, collKey string, formJson string) int64
if err == nil { if err == nil {
update[param.Type] = unmarshalled update[param.Type] = unmarshalled
} else { } else {
runtime.LogError(a.ctx, "Invalid update query:") runtime.LogWarning(a.ctx, "Invalid update parameter value:")
runtime.LogError(a.ctx, err.Error()) runtime.LogWarning(a.ctx, param.Value)
runtime.LogWarning(a.ctx, err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{ runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog, Type: runtime.ErrorDialog,
Title: "Invalid query", Title: "Invalid update query",
Message: err.Error(), Message: err.Error(),
}) })
return 0 return 0

View File

@ -34,6 +34,7 @@ func (a *App) DropDatabase(hostKey, dbKey string) bool {
Buttons: []string{"Yes", "No"}, Buttons: []string{"Yes", "No"},
DefaultButton: "Yes", DefaultButton: "Yes",
CancelButton: "No", CancelButton: "No",
Type: runtime.WarningDialog,
}) })
if sure != "Yes" { if sure != "Yes" {
return false return false

View File

@ -150,6 +150,7 @@ func (a *App) RemoveHost(key string) error {
Buttons: []string{"Yes", "No"}, Buttons: []string{"Yes", "No"},
DefaultButton: "Yes", DefaultButton: "Yes",
CancelButton: "No", CancelButton: "No",
Type: runtime.WarningDialog,
}) })
if sure != "Yes" { if sure != "Yes" {
return errors.New("operation aborted") return errors.New("operation aborted")

View File

@ -135,6 +135,7 @@ func (a *App) RemoveView(viewKey string) error {
Buttons: []string{"Yes", "No"}, Buttons: []string{"Yes", "No"},
DefaultButton: "Yes", DefaultButton: "Yes",
CancelButton: "No", CancelButton: "No",
Type: runtime.WarningDialog,
}) })
if sure != "Yes" { if sure != "Yes" {
return errors.New("operation aborted") return errors.New("operation aborted")