1
0
mirror of https://github.com/garraflavatra/rolens.git synced 2025-04-20 01:01:03 +00:00

Find view: make changes to single documents from within the object editor

This commit is contained in:
Romein van Buren 2023-06-02 22:33:43 +02:00
parent 33d41064cc
commit 7eb7f97d21
Signed by: romein
GPG Key ID: 0EFF8478ADDF6C49
8 changed files with 92 additions and 40 deletions

View File

@ -2,6 +2,7 @@
* Added some external links related to Rolens to the application menu. * Added some external links related to Rolens to the application menu.
* Fix an infinite loop bug when a document in the find view has been double clicked. * Fix an infinite loop bug when a document in the find view has been double clicked.
* Find view: added the ability to make changes to single documents from within the object editor.
## [v0.1.0](https://github.com/garraflavatra/rolens/releases/tag/v0.1.0) ## [v0.1.0](https://github.com/garraflavatra/rolens/releases/tag/v0.1.0)

View File

@ -57,7 +57,7 @@
} }
} }
function select(itemKey) { function select(itemKey, index) {
if (!canSelect) { if (!canSelect) {
return false; return false;
} }
@ -72,7 +72,7 @@
else { else {
activePath = [ ...path, itemKey ]; activePath = [ ...path, itemKey ];
} }
dispatch('select', { level, itemKey }); dispatch('select', { level, itemKey, index });
} }
} }
@ -88,9 +88,9 @@
} }
} }
function doubleClick(itemKey) { function doubleClick(itemKey, index) {
// toggleChildren(itemKey, false); // toggleChildren(itemKey, false);
dispatch('trigger', { level, itemKey }); dispatch('trigger', { level, itemKey, index });
childrenOpen[itemKey] = true; childrenOpen[itemKey] = true;
} }
@ -132,8 +132,8 @@
{#each _items as item, index} {#each _items as item, index}
<tr <tr
on:click={() => select(item[key])} on:click={() => select(item[key], index)}
on:dblclick={() => doubleClick(item[key])} on:dblclick={() => doubleClick(item[key], index)}
on:contextmenu|preventDefault={evt => showContextMenu(evt, item)} on:contextmenu|preventDefault={evt => showContextMenu(evt, item)}
class:selectable={canSelect} class:selectable={canSelect}
class:selected={canSelect && !activePath[level + 1] && activePath.every(k => path.includes(k) || k === item[key]) && (activePath[level] === item[key])} class:selected={canSelect && !activePath[level + 1] && activePath.every(k => path.includes(k) || k === item[key]) && (activePath[level] === item[key])}

View File

@ -25,6 +25,7 @@
return; return;
} }
text = e.state.doc.toString(); text = e.state.doc.toString();
dispatch('updated', { text });
}), }),
], ],
}); });

View File

@ -1,28 +1,20 @@
<script> <script>
import { jsonLooseParse } from '$lib/strings'; import { jsonLooseParse, looseJsonIsValid } from '$lib/strings';
import { createEventDispatcher, onDestroy } from 'svelte'; import { createEventDispatcher, onDestroy } from 'svelte';
import Icon from './icon.svelte'; import Icon from './icon.svelte';
import Modal from './modal.svelte'; import Modal from './modal.svelte';
import ObjectEditor from './objecteditor.svelte'; import ObjectEditor from './objecteditor.svelte';
import { EJSON } from 'bson';
export let data; export let data;
export let saveable = false; export let saveable = false;
export let successMessage = '';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let copySucceeded = false; let copySucceeded = false;
let timeout; let timeout;
let text = JSON.stringify(data, undefined, '\t'); let text = EJSON.stringify(data, undefined, '\t');
let newData; $: invalid = !looseJsonIsValid(text);
let invalid = false;
$: {
try {
newData = jsonLooseParse(text);
}
catch {
invalid = true;
}
}
async function copy() { async function copy() {
await navigator.clipboard.writeText(text); await navigator.clipboard.writeText(text);
@ -36,7 +28,7 @@
} }
function save() { function save() {
dispatch('save', text); dispatch('save', { text, originalData: data });
} }
onDestroy(() => clearTimeout(timeout)); onDestroy(() => clearTimeout(timeout));
@ -45,7 +37,7 @@
{#if data} {#if data}
<Modal bind:show={data} contentPadding={false}> <Modal bind:show={data} contentPadding={false}>
<div class="objectviewer"> <div class="objectviewer">
<ObjectEditor {text} /> <ObjectEditor bind:text on:updated={() => successMessage = ''} />
</div> </div>
<svelte:fragment slot="footer"> <svelte:fragment slot="footer">
@ -62,6 +54,10 @@
<button class="btn secondary" on:click={copy}> <button class="btn secondary" on:click={copy}>
<Icon name={copySucceeded ? 'check' : 'clipboard'} /> Copy <Icon name={copySucceeded ? 'check' : 'clipboard'} /> Copy
</button> </button>
{#if successMessage}
<span class="flash-green">{successMessage}</span>
{/if}
</svelte:fragment> </svelte:fragment>
</Modal> </Modal>
{/if} {/if}
@ -74,4 +70,8 @@
align-items: stretch; align-items: stretch;
height: 100%; height: 100%;
} }
.flash-green {
margin-left: 0.5rem;
}
</style> </style>

View File

@ -9,7 +9,7 @@
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';
import { FindItems, RemoveItemById } from '$wails/go/app/App'; import { FindItems, RemoveItemById, UpdateFoundDocument } from '$wails/go/app/App';
import { EJSON } from 'bson'; import { EJSON } from 'bson';
import { createEventDispatcher, onMount } from 'svelte'; import { createEventDispatcher, onMount } from 'svelte';
import ExportInfo from './components/export.svelte'; import ExportInfo from './components/export.svelte';
@ -36,6 +36,7 @@
let showQueryChooser = false; let showQueryChooser = false;
let exportInfo; let exportInfo;
let querying = false; let querying = false;
let objectViewerSuccessMessage = '';
$: viewsForCollection = views.forCollection(collection.hostKey, collection.dbKey, collection.key); $: viewsForCollection = views.forCollection(collection.hostKey, collection.dbKey, collection.key);
$: 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})` : ''};`;
@ -124,8 +125,8 @@
queryField?.select(); queryField?.select();
} }
function openJson(itemId) { function openJson(index) {
const item = result?.results?.find(i => i._id == itemId); const item = result?.results?.[index];
objectViewerData = item; objectViewerData = item;
} }
@ -134,6 +135,24 @@
submitQuery(); submitQuery();
} }
async function saveDocument(event) {
const progress = startProgress('Performing update…');
const success = await UpdateFoundDocument(
collection.hostKey,
collection.dbKey,
collection.key,
EJSON.stringify({ _id: event.detail.originalData._id }),
event.detail.text
);
if (success) {
objectViewerSuccessMessage = 'Document has been saved!';
submitQuery();
}
progress.end();
}
$: collection && refresh(); $: collection && refresh();
onMount(refresh); onMount(refresh);
</script> </script>
@ -195,7 +214,7 @@
data={result.results} data={result.results}
hideObjectIndicators={$views[collection.viewKey]?.hideObjectIndicators} hideObjectIndicators={$views[collection.viewKey]?.hideObjectIndicators}
bind:activePath bind:activePath
on:trigger={e => openJson(e.detail?.itemKey)} on:trigger={e => openJson(e.detail?.index)}
/> />
{:else} {:else}
<Grid <Grid
@ -204,7 +223,7 @@
showHeaders={true} showHeaders={true}
items={result.results ? result.results.map(r => EJSON.deserialize(r)) : []} items={result.results ? result.results.map(r => EJSON.deserialize(r)) : []}
bind:activePath bind:activePath
on:trigger={e => openJson(e.detail?.itemKey)} on:trigger={e => openJson(e.detail?.index)}
/> />
{/if} {/if}
{/key} {/key}
@ -258,7 +277,7 @@
{#if objectViewerData} {#if objectViewerData}
<!-- @todo Implement save --> <!-- @todo Implement save -->
<ObjectViewer bind:data={objectViewerData} saveable /> <ObjectViewer bind:data={objectViewerData} saveable on:save={saveDocument} bind:successMessage={objectViewerSuccessMessage} />
{/if} {/if}
<datalist id="limits"> <datalist id="limits">

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

@ -67,6 +67,8 @@ 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 UpdateFoundDocument(arg1:string,arg2:string,arg3:string,arg4:string,arg5:string):Promise<boolean>;
export function UpdateHost(arg1:string,arg2:string):Promise<boolean>; 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

@ -122,6 +122,10 @@ export function TruncateCollection(arg1, arg2, arg3) {
return window['go']['app']['App']['TruncateCollection'](arg1, arg2, arg3); return window['go']['app']['App']['TruncateCollection'](arg1, arg2, arg3);
} }
export function UpdateFoundDocument(arg1, arg2, arg3, arg4, arg5) {
return window['go']['app']['App']['UpdateFoundDocument'](arg1, arg2, arg3, arg4, arg5);
}
export function UpdateHost(arg1, arg2) { export function UpdateHost(arg1, arg2) {
return window['go']['app']['App']['UpdateHost'](arg1, arg2); return window['go']['app']['App']['UpdateHost'](arg1, arg2);
} }

View File

@ -46,24 +46,21 @@ func (a *App) FindItems(hostKey, dbKey, collKey, formJson string) QueryResult {
err = bson.UnmarshalExtJSON([]byte(form.Query), true, &query) err = bson.UnmarshalExtJSON([]byte(form.Query), true, &query)
if err != nil { if err != nil {
runtime.LogInfo(a.ctx, "Invalid find query:") runtime.LogInfof(a.ctx, "Invalid find query: %s", err.Error())
runtime.LogInfo(a.ctx, err.Error())
zenity.Error(err.Error(), zenity.Title("Invalid query"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Invalid query"), zenity.ErrorIcon)
return out return out
} }
err = json.Unmarshal([]byte(form.Fields), &projection) err = json.Unmarshal([]byte(form.Fields), &projection)
if err != nil { if err != nil {
runtime.LogInfo(a.ctx, "Invalid find projection:") runtime.LogInfof(a.ctx, "Invalid find projection: %s", err.Error())
runtime.LogInfo(a.ctx, err.Error())
zenity.Error(err.Error(), zenity.Title("Invalid projection"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Invalid projection"), zenity.ErrorIcon)
return out return out
} }
err = json.Unmarshal([]byte(form.Sort), &sort) err = json.Unmarshal([]byte(form.Sort), &sort)
if err != nil { if err != nil {
runtime.LogInfo(a.ctx, "Invalid find sort:") runtime.LogInfof(a.ctx, "Invalid find sort: %s", err.Error())
runtime.LogInfo(a.ctx, err.Error())
zenity.Error(err.Error(), zenity.Title("Invalid sort"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Invalid sort"), zenity.ErrorIcon)
return out return out
} }
@ -77,16 +74,14 @@ func (a *App) FindItems(hostKey, dbKey, collKey, formJson string) QueryResult {
total, err := client.Database(dbKey).Collection(collKey).CountDocuments(ctx, query, nil) total, err := client.Database(dbKey).Collection(collKey).CountDocuments(ctx, query, nil)
if err != nil { if err != nil {
runtime.LogWarning(a.ctx, "Encountered an error while counting documents:") runtime.LogWarningf(a.ctx, "Encountered an error while counting documents: %s", err.Error())
runtime.LogWarning(a.ctx, err.Error())
zenity.Error(err.Error(), zenity.Title("Error while counting docs"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Error while counting docs"), zenity.ErrorIcon)
return out return out
} }
cur, err := client.Database(dbKey).Collection(collKey).Find(ctx, query, &opt) cur, err := client.Database(dbKey).Collection(collKey).Find(ctx, query, &opt)
if err != nil { if err != nil {
runtime.LogWarning(a.ctx, "Encountered an error while performing query:") runtime.LogWarningf(a.ctx, "Encountered an error while performing query: %s", err.Error())
runtime.LogWarning(a.ctx, err.Error())
zenity.Error(err.Error(), zenity.Title("Error while querying"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Error while querying"), zenity.ErrorIcon)
return out return out
} }
@ -96,14 +91,14 @@ func (a *App) FindItems(hostKey, dbKey, collKey, formJson string) QueryResult {
err = cur.All(ctx, &results) err = cur.All(ctx, &results)
if err != nil { if err != nil {
runtime.LogWarning(a.ctx, "Encountered an error while performing query:") runtime.LogWarningf(a.ctx, "Encountered an error while performing query: %s", err.Error())
runtime.LogWarning(a.ctx, err.Error())
zenity.Error(err.Error(), zenity.Title("Error while querying"), zenity.ErrorIcon) zenity.Error(err.Error(), zenity.Title("Error while querying"), zenity.ErrorIcon)
return out return out
} }
out.Results = make([]string, 0)
out.Total = total out.Total = total
out.Results = make([]string, 0)
for _, r := range results { for _, r := range results {
marshalled, err := bson.MarshalExtJSON(r, true, true) marshalled, err := bson.MarshalExtJSON(r, true, true)
if err != nil { if err != nil {
@ -117,3 +112,33 @@ func (a *App) FindItems(hostKey, dbKey, collKey, formJson string) QueryResult {
return out return out
} }
func (a *App) UpdateFoundDocument(hostKey, dbKey, collKey, idJson, newDocJson string) bool {
var id bson.M
if err := bson.UnmarshalExtJSON([]byte(idJson), true, &id); err != nil {
runtime.LogWarningf(a.ctx, "Could not parse find/update query: %s", err.Error())
zenity.Error(err.Error(), zenity.Title("Couldn't parse update query"), zenity.ErrorIcon)
return false
}
var newDoc bson.M
if err := bson.UnmarshalExtJSON([]byte(newDocJson), true, &newDoc); err != nil {
runtime.LogWarningf(a.ctx, "Could not parse new find/update document: %s", err.Error())
zenity.Error(err.Error(), zenity.Title("Couldn't parse document"), zenity.ErrorIcon)
return false
}
client, ctx, close, err := a.connectToHost(hostKey)
if err != nil {
return false
}
defer close()
if _, err := client.Database(dbKey).Collection(collKey).UpdateOne(ctx, id, bson.M{"$set": newDoc}); err != nil {
runtime.LogInfof(a.ctx, "Error while performing find/update: %s", err.Error())
zenity.Error(err.Error(), zenity.Title("Unable to perform update"), zenity.ErrorIcon)
return false
}
return true
}