Skip to content

Commit

Permalink
Fix "Import" button in Flags editor
Browse files Browse the repository at this point in the history
  • Loading branch information
OrigamingWasTaken committed Nov 2, 2024
1 parent 917c69b commit 59998eb
Show file tree
Hide file tree
Showing 5 changed files with 355 additions and 79 deletions.
67 changes: 47 additions & 20 deletions frontend/src/windows/main/components/flag-editor/flag-table.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@
import * as Card from '$lib/components/ui/card';
import { Checkbox } from '$lib/components/ui/checkbox';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import { Switch } from '$lib/components/ui/switch';
import * as Table from '$lib/components/ui/table';
import { clipboard } from '@neutralinojs/lib';
import beautify from 'json-beautify';
import { Braces, Clipboard, Delete, Plus, Search, Ellipsis, Edit, Trash2, Copy } from 'lucide-svelte';
import { Braces, Clipboard, Delete, Ellipsis, Plus, Search } from 'lucide-svelte';
import { createEventDispatcher } from 'svelte';
import { toast } from 'svelte-sonner';
import { correctAndParseJSON } from '../../ts/utils/';
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
import { correctAndParseJSON } from '../../ts/utils/json';
import Textarea from '$lib/components/ui/textarea/textarea.svelte';
type FastFlag = string | boolean | null | number;
interface EditorFlag {
Expand Down Expand Up @@ -103,24 +104,15 @@
let willBeReplacedFlags: string[] = [];
let newEditorFlags: EditorFlag[] = [];
async function importFlags(): Promise<void> {
let clipboardContent: string = '';
try {
clipboardContent = await clipboard.readText();
} catch (err) {
toast.error("Couldn't read clipboard");
console.error("Couldn't read clipboard:", err);
return;
}
let openImportFlagsDialog = false;
let importFlagsDialogValue: string;
function showImportFlagsDialog() {
openImportFlagsDialog = true;
}
async function importFlags(flagsString: string): Promise<void> {
let flagsJson: Record<string, FastFlag> = {};
try {
flagsJson = correctAndParseJSON(clipboardContent).parsedJSON;
} catch (err) {
console.error(err)
toast.error("Couldn't import flags, your JSON is probably invalid (malformatted)");
return;
}
flagsJson = correctAndParseJSON(flagsString, { friendly: true }); // Throws error if invalid
newEditorFlags = [];
for (const [flag, value] of Object.entries(flagsJson)) {
Expand Down Expand Up @@ -201,7 +193,7 @@
<Card.Root class="p-4 w-full mr-[5px]">
<div class="flex gap-2 mb-4">
<Button on:click={addFlag} variant="outline"><Plus class="h-5 w-5 mr-2" />Add Flag</Button>
<Button on:click={importFlags} variant="outline"><Braces class="h-5 w-5 mr-2" />Import</Button>
<Button on:click={showImportFlagsDialog} variant="outline"><Braces class="h-5 w-5 mr-2" />Import</Button>
<Button
on:click={copyFlags}
variant="outline"
Expand Down Expand Up @@ -395,3 +387,38 @@
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>

<Dialog.Root bind:open={openImportFlagsDialog}>
<Dialog.Content class="sm:max-w-[550px]">
<Dialog.Header>
<Dialog.Title>Import flags</Dialog.Title>
<Dialog.Description>Paste your fast flags JSON here.</Dialog.Description>
</Dialog.Header>
<div class="grid gap-4 py-4">
<Textarea
bind:value={importFlagsDialogValue}
placeholder={'{\n "DFFlagUseAppleBlox": true,\n "FFlagEnableUltraBurgers": false\n}'}
class="col-span-3 min-h-48 text-start align-top"
/>
</div>
<Dialog.Footer>
<Button
on:click={async () => {
const imported = await importFlags(importFlagsDialogValue || '')
.then(() => {
return true;
})
.catch((err) => {
console.error(err);
toast.error(err.toString());
return false;
});
if (imported) {
openImportFlagsDialog = false;
importFlagsDialogValue = '';
}
}}>Import</Button
>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>
2 changes: 1 addition & 1 deletion frontend/src/windows/main/pages/Info.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,4 @@
.build();
</script>

<Panel {panel} on:button={onButtonClicked} {render} />
<Panel {panel} on:button={onButtonClicked} {render} autosave={false} />
57 changes: 0 additions & 57 deletions frontend/src/windows/main/ts/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,63 +30,6 @@ interface JSONParseResult {
corrections: string[];
}

/**
* Attempts to correct and parse potentially invalid JSON input with support for nested JSON objects.
* Specifically handles the case of double-escaped nested JSON strings.
*
* @param {string} input - The JSON string to correct and parse
* @returns {JSONParseResult} Object containing parsed JSON and correction records
* @throws {Error} If the JSON cannot be parsed after attempted corrections
*/
export function correctAndParseJSON(input: string): JSONParseResult {
const corrections: string[] = [];
let jsonString = input.trim();

// Step 1: First try to parse as-is
try {
const parsed = JSON.parse(jsonString);
return { parsedJSON: parsed, corrections };
} catch (e) {
// Continue with corrections if direct parsing fails
}

// Step 2: Handle escaped string values that should be objects
try {
// Replace escaped quotes with temporary markers
jsonString = jsonString.replace(/\\"/g, '__QUOTE__');

// Parse the JSON with temporary markers
let parsed = JSON.parse(jsonString.replace(/__QUOTE__/g, '"'));

// Process any string values that look like they should be objects
for (const key in parsed) {
if (typeof parsed[key] === 'string') {
try {
// If the string value looks like JSON, try to parse it
if (parsed[key].includes('{') && parsed[key].includes('}')) {
const unescaped = parsed[key].replace(/\\\\"/g, '"').replace(/\\"/g, '"').replace(/\\\\/g, '\\');
parsed[key] = JSON.parse(unescaped);
corrections.push(`Parsed nested JSON in key: ${key}`);
}
} catch (innerError) {
// If parsing the nested JSON fails, leave it as a string
corrections.push(`Failed to parse nested JSON in key: ${key}`);
}
}
}

return { parsedJSON: parsed, corrections };
} catch (error) {
// If all parsing attempts fail, throw an error with details
throw new Error(
`Failed to parse JSON: ${(error as Error).message}\nNear: ${jsonString.substring(
Math.max(0, (error as any).pos - 50),
Math.min(jsonString.length, (error as any).pos + 50)
)}`
);
}
}

/**
* Returns the current date in a format compatible with POSIX path names.
* The format is: YYYY-MM-DD_HH-mm-ss
Expand Down
Loading

0 comments on commit 59998eb

Please sign in to comment.