aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/modules/sbbuilder/Store.qml
blob: b4a1ba1ab17a0be569b15cadb54d8fd2f48aae69 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

import QtQuick
import "Toml.js" as T

// One config.toml's draft, the store qtmaildir's Setting and BoolSetting
// edit. Fields hand it text; it writes the type of the key's default, the
// type sb-builder checks on load: a list is words split on spaces, a number
// is a number, a switch is true or false.
QtObject {
    id: st
    // The file as last read or written, the draft's start, and the draft.
    property string text: ""
    property string base: ""
    property string draft: ""
    readonly property bool dirty: st.draft !== st.base
    // {section: {key: value}}, top-level keys under "".
    property var defaults: ({})
    property var invalid: ({})
    readonly property bool valid: Object.keys(st.invalid).length === 0
    property string error: ""
    // Assigned only when the set changes, as Qtm does: a Repeater bound to a
    // fresh array per keystroke rebuilds the row being typed in.
    property var manifests: []
    onDraftChanged: {
        const k = T.entries(st.draft, "manifests").map(e => e.key);
        if (JSON.stringify(k) !== JSON.stringify(st.manifests)) st.manifests = k;
    }

    function fallback(section, key) { return (st.defaults[section] || {})[key]; }
    function show(v) { return v === undefined ? undefined : Array.isArray(v) ? v.join(" ") : String(v); }
    function placeholder(section, key) { return st.show(st.fallback(section, key)) ?? ""; }

    function get(section, key) { return st.show(T.get(st.draft, section, key)); }
    // The effective value, typed: the file's, else the default.
    function value(section, key) { return T.get(st.draft, section, key) ?? st.fallback(section, key); }
    function entries(section) { return T.entries(st.draft, section); }
    function put(section, key, v) {
        try { st.draft = T.set(st.draft, section, key, v); } catch (e) { st.error = e.message; }
    }
    // A field's text; null or "" unsets.
    function set(section, key, t) {
        if (t === null || t === "") return st.put(section, key, null);
        const d = st.fallback(section, key);
        st.put(section, key, Array.isArray(d) ? t.split(/\s+/).filter(x => x !== "")
                           : typeof d === "number" ? Number(t)
                           : typeof d === "boolean" ? t === "true" : t);
    }
    function remove(section, key) { st.draft = T.remove(st.draft, section, key); }

    function markInvalid(id, bad) {
        if (!!st.invalid[id] === bad) return;
        const next = Object.assign({}, st.invalid);
        if (bad) next[id] = true; else delete next[id];
        st.invalid = next;
    }

    // The file as read: a clean draft follows it, a dirty one is kept and
    // its save is refused.
    function loaded(t) {
        st.text = t;
        if (!st.dirty) { st.base = t; st.draft = t; }
    }
    function discard() {
        st.error = "";
        st.invalid = ({});
        st.base = st.text;
        st.draft = st.text;
    }
}