contacts-pane 3.2.1-2 → 3.2.1-4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/addressBookPresenter.esm.js +270 -0
- package/dist/addressBookPresenter.esm.js.map +1 -0
- package/dist/contactLogic.esm.js +138 -0
- package/dist/contactLogic.esm.js.map +1 -0
- package/dist/contactsPane.esm.js +360 -0
- package/dist/contactsPane.esm.js.map +1 -0
- package/dist/debug.esm.js +14 -0
- package/dist/debug.esm.js.map +1 -0
- package/dist/groupMembershipControl.esm.js +73 -0
- package/dist/groupMembershipControl.esm.js.map +1 -0
- package/dist/index.cjs.js +1155 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.js +2 -0
- package/dist/individual.esm.js +37 -0
- package/dist/individual.esm.js.map +1 -0
- package/dist/localUtils.esm.js +159 -0
- package/dist/localUtils.esm.js.map +1 -0
- package/dist/mintNewAddressBook.esm.js +81 -0
- package/dist/mintNewAddressBook.esm.js.map +1 -0
- package/dist/mugshotGallery.esm.js +167 -0
- package/dist/mugshotGallery.esm.js.map +1 -0
- package/dist/ontology/individualAndOrganizationForm.esm.js +6 -0
- package/dist/ontology/individualAndOrganizationForm.esm.js.map +1 -0
- package/dist/ontology/vcard.esm.js +6 -0
- package/dist/ontology/vcard.esm.js.map +1 -0
- package/dist/rdfFormsHelper.esm.js +18 -0
- package/dist/rdfFormsHelper.esm.js.map +1 -0
- package/dist/toolsPane.esm.js +300 -0
- package/dist/toolsPane.esm.js.map +1 -0
- package/dist/webidControl.esm.js +175 -0
- package/dist/webidControl.esm.js.map +1 -0
- package/package.json +38 -50
- package/dist/contactsPane.js +0 -6697
- package/dist/contactsPane.js.map +0 -1
- package/dist/contactsPane.min.js +0 -2
- package/dist/contactsPane.min.js.map +0 -1
- package/dist/styles/contactsPane.css +0 -620
- package/dist/styles/contactsRDFFormsEnforced.css +0 -515
- package/dist/styles/groupMembership.css +0 -115
- package/dist/styles/individual.css +0 -12
- package/dist/styles/localUtils.css +0 -49
- package/dist/styles/mugshotGallery.css +0 -17
- package/dist/styles/toolsPane.css +0 -43
- package/dist/styles/utilities.css +0 -10
- package/dist/styles/webidControl.css +0 -252
package/README.md
CHANGED
|
@@ -31,14 +31,14 @@ Accessibility is verified via `axe-core`/`jest-axe` – new tests extend the set
|
|
|
31
31
|
|
|
32
32
|
### Dev Server
|
|
33
33
|
|
|
34
|
-
Start a
|
|
34
|
+
Start a dev server:
|
|
35
35
|
|
|
36
36
|
```shell script
|
|
37
37
|
npm start
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
Visit `http://localhost:
|
|
41
|
-
|
|
40
|
+
Visit `http://localhost:5173/` to render the pane in a local sandbox. Adjust the settings in `vite.config.ts` to provide different defaults.
|
|
41
|
+
|
|
42
42
|
### Build
|
|
43
43
|
|
|
44
44
|
```
|
|
@@ -46,9 +46,9 @@ npm run build
|
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
### Generative AI usage
|
|
49
|
-
The SolidOS team is using GitHub Copilot integrated in Visual Studio Code.
|
|
49
|
+
The SolidOS team is using GitHub Copilot integrated in Visual Studio Code.
|
|
50
50
|
We have added comments in the code to make it explicit which parts are 100% written by AI.
|
|
51
|
-
Example:
|
|
51
|
+
Example:
|
|
52
52
|
* Some code was generated by the Raptor mini and Claude Opus 4.6 model in GitHub Copilot based on the following prompt:
|
|
53
53
|
* Regarding accessibility, how should I replace alert()/confirm() with accessible modal dialogs?
|
|
54
54
|
* Let us implement the modals in the localUtils.js.
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { error as e, log as t, warn as n } from "./debug.esm.js";
|
|
2
|
+
import { alertDialog as r, compareForSort as i, complain as a, deleteRecursive as o, deleteThingAndDoc as s, getSameAs as c, nameFor as l } from "./localUtils.esm.js";
|
|
3
|
+
import { addPersonToGroup as u, getDataModelIssues as d, groupMembers as f } from "./contactLogic.esm.js";
|
|
4
|
+
import { groupMembership as p } from "./groupMembershipControl.esm.js";
|
|
5
|
+
import { authn as m, store as h } from "solid-logic";
|
|
6
|
+
import * as g from "solid-ui";
|
|
7
|
+
//#region src/addressBookPresenter.js
|
|
8
|
+
var _ = g.ns, v = g.utils, y = h, b, x = {}, S = {}, C = null, w = null, T = null, E = null, D = null, O = null, k = null;
|
|
9
|
+
function A(e, t) {
|
|
10
|
+
e.querySelectorAll("button").forEach((e) => {
|
|
11
|
+
e.classList.remove("btn-primary", "allGroupsButton--selected", "allGroupsButton--active", "allGroupsButton--loaded"), e.classList.add("btn-secondary");
|
|
12
|
+
}), t && (t.classList.remove("btn-secondary"), t.classList.add("btn-primary"));
|
|
13
|
+
}
|
|
14
|
+
function j(e, t, n, r, i, a, o, s, c, l) {
|
|
15
|
+
b = r, x = i || {}, a && (C = a), o && (T = o), s && (E = s), c && (O = c), l && (k = l), D = e, w = t;
|
|
16
|
+
let u = R(D, n);
|
|
17
|
+
v.syncTableToArrayReOrdered(w, u, P);
|
|
18
|
+
}
|
|
19
|
+
function M(e) {
|
|
20
|
+
let t = y.any(e, _.vcard("fn")), n = b.createElement("li");
|
|
21
|
+
n.setAttribute("role", "listitem"), n.setAttribute("aria-label", t ? t.value : "Some group"), n.subject = e, g.widgets.makeDraggable(n, e);
|
|
22
|
+
let r = n.appendChild(b.createElement("button"));
|
|
23
|
+
return r.setAttribute("type", "button"), r.innerHTML = t ? t.value : "Some group", r.classList.add("allGroupsButton", "actionButton", "btn-secondary", "action-button-focus"), {
|
|
24
|
+
groupLi: n,
|
|
25
|
+
groupButton: r,
|
|
26
|
+
name: t
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
async function N(e, t) {
|
|
30
|
+
for (let n of e) {
|
|
31
|
+
let e = y.sym(n);
|
|
32
|
+
try {
|
|
33
|
+
e = await u(e, t);
|
|
34
|
+
} catch {
|
|
35
|
+
r("Error adding to group. Make sure you are adding a contact URI.");
|
|
36
|
+
}
|
|
37
|
+
e && V(C);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function P(t) {
|
|
41
|
+
function n(n) {
|
|
42
|
+
if (n.preventDefault(), A(w, i), k && k(), !n.metaKey) for (let e in x) delete x[e];
|
|
43
|
+
x[t.uri] = !x[t.uri], I(w, x), y.fetcher.nowOrWhenFetched(t.doc(), void 0, function(n, r) {
|
|
44
|
+
n || e("Cannot load one group: " + t + ". Stack: " + r), V(C, null, !1);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
let { groupLi: r, groupButton: i } = M(t);
|
|
48
|
+
return i.addEventListener("click", n, !1), g.widgets.makeDropTarget(r, (e) => N(e, t)), r.addEventListener("click", n, !0), r;
|
|
49
|
+
}
|
|
50
|
+
function F(t, n, r) {
|
|
51
|
+
function i(i, a) {
|
|
52
|
+
return new Promise((o, s) => {
|
|
53
|
+
a.classList.add("group-loading"), a.setAttribute("aria-busy", "true"), y.fetcher.nowOrWhenFetched(i.doc(), void 0, function(c, l) {
|
|
54
|
+
if (!c) {
|
|
55
|
+
let t = "Cannot load group " + i + ". Stack: " + l;
|
|
56
|
+
e(t), r && r(!1, t), s(t);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
a.classList.remove("group-loading"), a.setAttribute("aria-busy", "false"), a.classList.add("selected"), t[i.uri] = !0, I(n, t), V(C, null), r && r(!0), o(!0);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
for (let e = 0; e < n.children.length; e++) {
|
|
64
|
+
let t = n.children[e], a = t.subject;
|
|
65
|
+
a && i(a, t).catch((e) => {
|
|
66
|
+
r && r(!1, e);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function I(e, t) {
|
|
71
|
+
for (let n = 0; n < e.children.length; n++) {
|
|
72
|
+
let r = e.children[n];
|
|
73
|
+
r.subject && r.classList.toggle("selected", !!t[r.subject.uri]);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function L(e, t, n, r, i, a, o) {
|
|
77
|
+
b = r, i && (x = i), a && (C = a), o && (T = o), w = n;
|
|
78
|
+
let s = R(e, t);
|
|
79
|
+
s.length > 0 && P(s[0]), v.syncTableToArrayReOrdered(n, s, P);
|
|
80
|
+
}
|
|
81
|
+
function R(e, t) {
|
|
82
|
+
let n = [];
|
|
83
|
+
if (t.foreignGroup && n.push([
|
|
84
|
+
"",
|
|
85
|
+
y.any(t.foreignGroup, _.vcard("fn")),
|
|
86
|
+
t.foreignGroup
|
|
87
|
+
]), e) {
|
|
88
|
+
let t = y.any(e, _.vcard("groupIndex")), r = (e ? y.each(e, _.vcard("includesGroup"), null, t) : []).map(function(t) {
|
|
89
|
+
return [
|
|
90
|
+
e,
|
|
91
|
+
y.any(t, _.vcard("fn")),
|
|
92
|
+
t
|
|
93
|
+
];
|
|
94
|
+
});
|
|
95
|
+
n = n.concat(r), n.sort();
|
|
96
|
+
}
|
|
97
|
+
return n.map((e) => e[2]);
|
|
98
|
+
}
|
|
99
|
+
async function z(e) {
|
|
100
|
+
let t = y.any(e, _.vcard("groupIndex"));
|
|
101
|
+
if (t) {
|
|
102
|
+
await y.fetcher.load(t);
|
|
103
|
+
let n = e ? y.each(e, _.vcard("includesGroup"), null, t) : [];
|
|
104
|
+
return await y.fetcher.load(n), n;
|
|
105
|
+
} else return [];
|
|
106
|
+
}
|
|
107
|
+
function B(e) {
|
|
108
|
+
if (e) return e;
|
|
109
|
+
let t;
|
|
110
|
+
for (let e in x) {
|
|
111
|
+
t = y.sym(e);
|
|
112
|
+
let n = y.any(void 0, _.vcard("includesGroup"), t);
|
|
113
|
+
if (n) return n;
|
|
114
|
+
}
|
|
115
|
+
throw Error("findBookFromGroups: Cant find address book which this group is part of");
|
|
116
|
+
}
|
|
117
|
+
function V(t, r, a = !0) {
|
|
118
|
+
let o = t || C;
|
|
119
|
+
if (!o || !o.children || typeof o.children.length != "number") {
|
|
120
|
+
n("refreshNames called with invalid ulPeople:", o);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
function s(e, t) {
|
|
124
|
+
function n(e) {
|
|
125
|
+
e.preventDefault(), H(o, t, E);
|
|
126
|
+
}
|
|
127
|
+
e.addEventListener("click", n), e.addEventListener("keydown", function(e) {
|
|
128
|
+
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), n(e));
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
let c = [];
|
|
132
|
+
Object.keys(x).map((e) => y.sym(e)).forEach((e) => {
|
|
133
|
+
x[e.value] && (c = c.concat(f(y, e)));
|
|
134
|
+
}), c.sort(i);
|
|
135
|
+
for (let e = 0; e < c.length - 1;) c[e].uri === c[e + 1].uri ? c.splice(e, 1) : e++;
|
|
136
|
+
function u(t, n) {
|
|
137
|
+
let r = b.createElement("li");
|
|
138
|
+
r.setAttribute("role", "listitem"), r.setAttribute("tabindex", "0"), r.classList.add("personLi"), r.subject = t, g.widgets.makeDraggable(r, t);
|
|
139
|
+
let i = b.createElement("div");
|
|
140
|
+
i.classList.add("personLi-row");
|
|
141
|
+
let a = b.createElement("div");
|
|
142
|
+
a.classList.add("personLi-avatar");
|
|
143
|
+
let o = b.createElement("div");
|
|
144
|
+
o.classList.add("avatar-placeholder"), o.innerHTML = "<svg aria-hidden=\"true\" width=\"36\" height=\"36\" viewBox=\"0 0 36 36\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"18\" cy=\"18\" r=\"18\" fill=\"#e0e0e0\"/><text x=\"50%\" y=\"58%\" text-anchor=\"middle\" fill=\"#595959\" font-size=\"16\" font-family=\"Arial\" dy=\".3em\">?</text></svg>", a.appendChild(o);
|
|
145
|
+
let c = l(t) || "Unknown Name";
|
|
146
|
+
function u() {
|
|
147
|
+
let e = y.any(t, _.vcard("hasPhoto"));
|
|
148
|
+
if (e && e.value) {
|
|
149
|
+
let t = b.createElement("img");
|
|
150
|
+
t.src = e.value, t.alt = c + " avatar", a.replaceChild(t, a.firstChild);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
u(), y.fetcher.nowOrWhenFetched(t.doc(), void 0, function(n, i) {
|
|
154
|
+
if (!n) {
|
|
155
|
+
e("Cannot load contact: " + t + ". Stack: " + i), r.classList.add("personLi--error");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
u();
|
|
159
|
+
});
|
|
160
|
+
let d = b.createElement("div");
|
|
161
|
+
d.classList.add("personLi-info"), r.setAttribute("aria-label", c);
|
|
162
|
+
let f = b.createElement("div");
|
|
163
|
+
f.classList.add("personLi-name"), f.textContent = c, d.appendChild(f);
|
|
164
|
+
let p = b.createElement("div");
|
|
165
|
+
return p.classList.add("personLi-arrow"), p.innerHTML = "<svg aria-hidden=\"true\" width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M6 4.5L11.25 9L6 13.5\" stroke=\"#595959\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>", i.appendChild(a), i.appendChild(d), i.appendChild(p), r.appendChild(i), s(r, t), r;
|
|
166
|
+
}
|
|
167
|
+
v.syncTableToArrayReOrdered(o, c, (e) => u(e, o)), W(o, a, r || E);
|
|
168
|
+
}
|
|
169
|
+
function H(n, r, i) {
|
|
170
|
+
if (!i) return;
|
|
171
|
+
i.parentNode && i.parentNode.classList.remove("hidden"), i.innerHTML = "Loading...", i.setAttribute("aria-busy", "true"), i.classList.add("detailsSectionContent--wide"), S = {}, S[r.uri] = !0, W(n, !1, i);
|
|
172
|
+
let l;
|
|
173
|
+
try {
|
|
174
|
+
l = D ? q(r) : r;
|
|
175
|
+
} catch (e) {
|
|
176
|
+
i.innerHTML = "", i.setAttribute("aria-busy", "false"), a(i, b, "Cannot load contact: " + e.message);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
y.fetcher.nowOrWhenFetched(l.doc(), void 0, function(u, d) {
|
|
180
|
+
if (i.innerHTML = "", i.setAttribute("aria-busy", "false"), !u) {
|
|
181
|
+
e("Failed to load contact card: " + l + ". Stack: " + d), a(i, b, "Failed to load contact. If it persists, contact your admin.");
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
let f = b.createElement("div");
|
|
185
|
+
f.classList.add("contact-toolbar");
|
|
186
|
+
let h = g.widgets.linkIcon(b, l);
|
|
187
|
+
h.setAttribute("title", "Uri of contact"), f.appendChild(h), m.currentUser() && g.widgets.deleteButtonWithCheck(b, f, "contact", async function() {
|
|
188
|
+
let e = r.dir();
|
|
189
|
+
t("We are about to delete the contact " + y.any(r, _.vcard("fn"))), await z();
|
|
190
|
+
let l = y.any(D, _.vcard("nameEmailIndex"));
|
|
191
|
+
await y.fetcher.load(l);
|
|
192
|
+
let u = p(r), d = [];
|
|
193
|
+
u.forEach((e) => {
|
|
194
|
+
c(y, r, e.doc()).forEach((t) => {
|
|
195
|
+
c(y, t, e.doc()).length === 1 && (d = d.concat(y.statementsMatching(e, _.vcard("hasMember"), t, e.doc())));
|
|
196
|
+
});
|
|
197
|
+
}), await y.updater.updateMany(d);
|
|
198
|
+
try {
|
|
199
|
+
await s(r);
|
|
200
|
+
} catch {
|
|
201
|
+
a(i, b, "Failed to delete contact. If it persists, contact your admin.");
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
await o(y, e, f, b);
|
|
206
|
+
} catch {
|
|
207
|
+
a(i, b, "Failed to delete contact. If it persists, contact your admin.");
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
V(n, i), i.innerHTML = "Contact data deleted.";
|
|
211
|
+
}).classList.add("deleteButton"), i.appendChild(f), i.classList.add("detailsSectionContent--wide"), i.appendChild(K(l, "contact"));
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
function U(e) {
|
|
215
|
+
if (S = {}, e) for (let t = 0; t < e.children.length; t++) e.children[t].classList.remove("selected");
|
|
216
|
+
}
|
|
217
|
+
function W(e, t, n) {
|
|
218
|
+
let r = 0, i = null;
|
|
219
|
+
for (let t = 0; t < e.children.length; t++) {
|
|
220
|
+
let n = e.children[t], a = G(l(n.subject));
|
|
221
|
+
a && (r++, i = n), n.classList.toggle("selected", a && !!S[n.subject.uri]), n.classList.toggle("hidden", !a);
|
|
222
|
+
}
|
|
223
|
+
if (r === 1 && t) {
|
|
224
|
+
let t = i.subject;
|
|
225
|
+
H(e, t, n);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function G(e) {
|
|
229
|
+
let t = T.value.trim().toLowerCase();
|
|
230
|
+
if (t.length === 0) return !0;
|
|
231
|
+
let n = t.split(" ");
|
|
232
|
+
for (let t = 0; t < n.length; t++) {
|
|
233
|
+
let r = n[t];
|
|
234
|
+
if (e.toLowerCase().indexOf(r) < 0) return !1;
|
|
235
|
+
}
|
|
236
|
+
return !0;
|
|
237
|
+
}
|
|
238
|
+
function K(e, t) {
|
|
239
|
+
let n = O.session.paneRegistry.byName(t).render(e, O);
|
|
240
|
+
return n.classList.add("renderPane"), n;
|
|
241
|
+
}
|
|
242
|
+
function q(e) {
|
|
243
|
+
let t = y.allAliases(e), n = D.dir().uri;
|
|
244
|
+
for (let e = 0; e < t.length; e++) if (t[e].uri.slice(0, n.length) === n) return t[e];
|
|
245
|
+
throw Error("No local URI for " + e);
|
|
246
|
+
}
|
|
247
|
+
async function J(e, n) {
|
|
248
|
+
let r = await z(e);
|
|
249
|
+
if (r && r.length > 0) {
|
|
250
|
+
let { del: e, ins: i } = await d(r);
|
|
251
|
+
m.currentUser() && e.length && g.widgets.deleteButtonWithCheck(b, n, "contact", async function() {
|
|
252
|
+
await y.updater.updateMany(e, i), t("Deleted " + e.length + " bad statements from groups");
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async function Y() {
|
|
257
|
+
let e = B(D);
|
|
258
|
+
try {
|
|
259
|
+
await y.fetcher.load(e);
|
|
260
|
+
} catch {
|
|
261
|
+
throw Error("Book won't load:" + e);
|
|
262
|
+
}
|
|
263
|
+
let t = y.any(e, _.vcard("nameEmailIndex"));
|
|
264
|
+
if (!t) throw Error("No nameEmailIndex");
|
|
265
|
+
await y.fetcher.load(t);
|
|
266
|
+
}
|
|
267
|
+
//#endregion
|
|
268
|
+
export { J as checkDataModel, M as createGroupLi, U as deselectAllPeople, Y as ensureBookLoaded, B as findBookFromGroups, N as handleURIsDroppedOnGroup, z as loadAllGroups, W as refreshFilteredPeople, V as refreshNames, I as refreshThingsSelected, j as renderGroupButtons, F as selectAllGroups, H as selectPerson, A as setActiveGroupButton, L as syncGroupUl };
|
|
269
|
+
|
|
270
|
+
//# sourceMappingURL=addressBookPresenter.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"addressBookPresenter.esm.js","names":[],"sources":["../src/addressBookPresenter.js"],"sourcesContent":["import { addPersonToGroup, groupMembers, getDataModelIssues } from './contactLogic'\nimport * as UI from 'solid-ui'\nimport { authn, store } from 'solid-logic'\nimport * as debug from './debug'\nimport { complain, alertDialog, getSameAs, deleteRecursive, deleteThingAndDoc, compareForSort, nameFor } from './localUtils'\nimport { groupMembership } from './groupMembershipControl'\n\nconst ns = UI.ns\nconst utils = UI.utils\nconst kb = store\nlet dom\nlet selectedGroups = {}\nlet selectedPeople = {}\nlet ulPeople = null\nlet ulGroups = null\nlet searchInput = null\nlet cardMain = null\nlet book = null\nlet dataBrowserContext = null\nlet onGroupButtonClick = null\n\n// ######## Group presenter\n\nexport function setActiveGroupButton (groupsUl, activeBtn) {\n groupsUl.querySelectorAll('button').forEach(btn => {\n btn.classList.remove('btn-primary', 'allGroupsButton--selected', 'allGroupsButton--active', 'allGroupsButton--loaded')\n btn.classList.add('btn-secondary')\n })\n if (activeBtn) {\n activeBtn.classList.remove('btn-secondary')\n activeBtn.classList.add('btn-primary')\n }\n}\n\nexport function renderGroupButtons (currentBook, groupsUl, options, domElement, groupsSelected, peopleUl, searchEl, cardMainEl, context, groupClickCallback) {\n dom = domElement\n selectedGroups = groupsSelected || {}\n if (peopleUl) ulPeople = peopleUl\n if (searchEl) searchInput = searchEl\n if (cardMainEl) cardMain = cardMainEl\n if (context) dataBrowserContext = context\n if (groupClickCallback) onGroupButtonClick = groupClickCallback\n book = currentBook\n ulGroups = groupsUl\n const groups = groupsInOrder(book, options)\n utils.syncTableToArrayReOrdered(ulGroups, groups, renderGroupLi)\n}\n\n/** Create the common DOM structure for a group list item (li + button).\n * Returns { groupLi, groupButton, name } so callers can attach their own handlers.\n */\nexport function createGroupLi (group) {\n const name = kb.any(group, ns.vcard('fn'))\n const groupLi = dom.createElement('li')\n groupLi.setAttribute('role', 'listitem')\n groupLi.setAttribute('aria-label', name ? name.value : 'Some group')\n groupLi.subject = group\n UI.widgets.makeDraggable(groupLi, group)\n\n const groupButton = groupLi.appendChild(dom.createElement('button'))\n groupButton.setAttribute('type', 'button')\n groupButton.innerHTML = name ? name.value : 'Some group'\n groupButton.classList.add('allGroupsButton', 'actionButton', 'btn-secondary', 'action-button-focus')\n\n return { groupLi, groupButton, name }\n}\n\nexport async function handleURIsDroppedOnGroup (uris, group) {\n for (const u of uris) {\n let thing = kb.sym(u)\n try {\n thing = await addPersonToGroup(thing, group)\n } catch (_e) {\n const msg = 'Error adding to group. Make sure you are adding a contact URI.'\n alertDialog(msg)\n }\n if (thing) refreshNames(ulPeople)\n }\n}\n\nfunction renderGroupLi (group) {\n function groupLiClickListener (event) {\n event.preventDefault()\n setActiveGroupButton(ulGroups, groupButton)\n if (onGroupButtonClick) onGroupButtonClick()\n if (!event.metaKey) {\n for (const key in selectedGroups) delete selectedGroups[key] // If Command key pressed, accumulate multiple\n }\n selectedGroups[group.uri] = !selectedGroups[group.uri]\n refreshThingsSelected(ulGroups, selectedGroups)\n // Load group members and refresh people list\n kb.fetcher.nowOrWhenFetched(group.doc(), undefined, function (ok, message) {\n if (!ok) {\n debug.error('Cannot load one group: ' + group + '. Stack: ' + message)\n }\n refreshNames(ulPeople, null, false)\n })\n }\n\n const { groupLi, groupButton } = createGroupLi(group)\n\n groupButton.addEventListener('click', groupLiClickListener, false)\n UI.widgets.makeDropTarget(groupLi, uris => handleURIsDroppedOnGroup(uris, group))\n groupLi.addEventListener('click', groupLiClickListener, true)\n return groupLi\n} // renderGroupLi\n\nexport function selectAllGroups (\n selectedGroups,\n ulGroups,\n callbackFunction\n) {\n function fetchGroupAndSelect (group, groupLi) {\n return new Promise((resolve, reject) => {\n groupLi.classList.add('group-loading')\n groupLi.setAttribute('aria-busy', 'true')\n kb.fetcher.nowOrWhenFetched(group.doc(), undefined, function (\n ok,\n message\n ) {\n if (!ok) {\n const msg = 'Cannot load group ' + group + '. Stack: ' + message\n debug.error(msg)\n if (callbackFunction) callbackFunction(false, msg)\n reject(msg)\n return\n }\n groupLi.classList.remove('group-loading')\n groupLi.setAttribute('aria-busy', 'false')\n groupLi.classList.add('selected')\n selectedGroups[group.uri] = true\n refreshThingsSelected(ulGroups, selectedGroups)\n refreshNames(ulPeople, null) // @@ every time??\n if (callbackFunction) callbackFunction(true)\n resolve(true)\n })\n })\n }\n\n for (let k = 0; k < ulGroups.children.length; k++) {\n const groupLi = ulGroups.children[k]\n const group = groupLi.subject\n if (!group) continue // Skip non-group items (e.g. All contacts, New group)\n fetchGroupAndSelect(group, groupLi)\n .catch(err => {\n if (callbackFunction) callbackFunction(false, err)\n })\n } // for each row\n}\n\nexport function refreshThingsSelected (ul, selectionArray) {\n for (let i = 0; i < ul.children.length; i++) {\n const li = ul.children[i]\n if (li.subject) {\n li.classList.toggle('selected', !!selectionArray[li.subject.uri])\n }\n }\n}\n\nexport function syncGroupUl (book, options, groupsUl, domElement, groupsSelected, peopleUl, searchEl) {\n dom = domElement\n if (groupsSelected) selectedGroups = groupsSelected\n if (peopleUl) ulPeople = peopleUl\n if (searchEl) searchInput = searchEl\n ulGroups = groupsUl\n const groups = groupsInOrder(book, options)\n if (groups.length > 0) {\n renderGroupLi(groups[0]) // pre-render one to get the style right, then throw it away\n }\n utils.syncTableToArrayReOrdered(groupsUl, groups, renderGroupLi)\n // refreshThingsSelected(groupsUl, selectedGroups)\n}\n\nfunction groupsInOrder (book, options) {\n let sortMe = []\n if (options.foreignGroup) {\n sortMe.push([\n '',\n kb.any(options.foreignGroup, ns.vcard('fn')),\n options.foreignGroup\n ])\n }\n if (book) {\n const groupIndex = kb.any(book, ns.vcard('groupIndex'))\n const gs = book ? kb.each(book, ns.vcard('includesGroup'), null, groupIndex) : []\n const gs2 = gs.map(function (g) {\n return [book, kb.any(g, ns.vcard('fn')), g]\n })\n sortMe = sortMe.concat(gs2)\n sortMe.sort()\n }\n return sortMe.map(tuple => tuple[2])\n}\n\nexport async function loadAllGroups (book) {\n const groupIndex = kb.any(book, ns.vcard('groupIndex'))\n if (groupIndex) {\n await kb.fetcher.load(groupIndex)\n const gs = book ? kb.each(book, ns.vcard('includesGroup'), null, groupIndex) : []\n await kb.fetcher.load(gs)\n return gs\n } else {\n return [] // no groups\n }\n}\n\n// The book could be the main subject, or linked from a group we are dealing with\nexport function findBookFromGroups (book) {\n if (book) {\n return book\n }\n let g\n for (const gu in selectedGroups) {\n g = kb.sym(gu)\n const b = kb.any(undefined, ns.vcard('includesGroup'), g)\n if (b) return b\n }\n throw new Error(\n 'findBookFromGroups: Cant find address book which this group is part of'\n )\n}\n// ######## Group presenter - END\n\n// ######## Person presenter\n/** Refresh the list of names */\nexport function refreshNames (ulPeopleArg, detailsView, autoSelect = true) {\n // If the caller did not explicitly pass a list element, fall back to the\n // global variable that other helpers (renderGroupButtons, syncGroupUl, etc.)\n // keep up to date. This allows callers that don't have easy access to the\n // element to simply invoke `refreshNames()` and get the behaviour they\n // expect when the address-book UI is present.\n const ul = ulPeopleArg || ulPeople\n\n // Guard: ul must be a DOM element with children. Callers sometimes pass the\n // wrong thing (e.g. a person object) which leads to the\n // \"Cannot read properties of undefined (reading 'length')\" error in\n // syncTableToArrayReOrdered. Bail out early if the value is not valid.\n if (!ul || !ul.children || typeof ul.children.length !== 'number') {\n debug.warn('refreshNames called with invalid ulPeople:', ul)\n return\n }\n\n function setPersonListener (personLi, person) {\n function handleSelect (event) {\n event.preventDefault()\n selectPerson(ul, person, cardMain)\n }\n personLi.addEventListener('click', handleSelect)\n personLi.addEventListener('keydown', function (event) {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleSelect(event)\n }\n })\n }\n\n let cards = []\n const groups = Object.keys(selectedGroups).map(groupURI => kb.sym(groupURI))\n groups.forEach(group => {\n if (selectedGroups[group.value]) {\n cards = cards.concat(groupMembers(kb, group))\n }\n })\n cards.sort(compareForSort) // @@ sort by name not UID later\n for (let k = 0; k < cards.length - 1;) {\n if (cards[k].uri === cards[k + 1].uri) {\n cards.splice(k, 1) // Eliminate duplicates from more than one group\n } else {\n k++\n }\n }\n\n function renderNameInGroupList (person, ul) {\n const personLi = dom.createElement('li')\n personLi.setAttribute('role', 'listitem')\n personLi.setAttribute('tabindex', '0')\n personLi.classList.add('personLi')\n personLi.subject = person\n UI.widgets.makeDraggable(personLi, person)\n\n // Container for the row\n const rowDiv = dom.createElement('div')\n rowDiv.classList.add('personLi-row')\n\n // Left: Avatar\n const avatarDiv = dom.createElement('div')\n avatarDiv.classList.add('personLi-avatar')\n // Placeholder avatar (shown initially while person doc loads)\n const placeholderEl = dom.createElement('div')\n placeholderEl.classList.add('avatar-placeholder')\n placeholderEl.innerHTML = '<svg aria-hidden=\"true\" width=\"36\" height=\"36\" viewBox=\"0 0 36 36\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"18\" cy=\"18\" r=\"18\" fill=\"#e0e0e0\"/><text x=\"50%\" y=\"58%\" text-anchor=\"middle\" fill=\"#595959\" font-size=\"16\" font-family=\"Arial\" dy=\".3em\">?</text></svg>'\n avatarDiv.appendChild(placeholderEl)\n\n // Get name early so it can be used in trySetAvatar\n const name = nameFor(person) || 'Unknown Name'\n\n // Try to set avatar from already-loaded data, or fetch the person's doc\n function trySetAvatar () {\n const avatarUrl = kb.any(person, ns.vcard('hasPhoto'))\n if (avatarUrl && avatarUrl.value) {\n const img = dom.createElement('img')\n img.src = avatarUrl.value\n img.alt = name + ' avatar'\n avatarDiv.replaceChild(img, avatarDiv.firstChild)\n }\n }\n trySetAvatar() // check if already in store\n\n // Load person's own document in background to get hasPhoto\n kb.fetcher.nowOrWhenFetched(person.doc(), undefined, function (ok, message) {\n if (!ok) {\n debug.error('Cannot load contact: ' + person + '. Stack: ' + message)\n personLi.classList.add('personLi--error')\n return // skip avatar – doc is unavailable\n }\n trySetAvatar()\n })\n\n // Center: Name\n const infoDiv = dom.createElement('div')\n infoDiv.classList.add('personLi-info')\n\n personLi.setAttribute('aria-label', name)\n const nameDiv = dom.createElement('div')\n nameDiv.classList.add('personLi-name')\n nameDiv.textContent = name\n\n infoDiv.appendChild(nameDiv)\n\n // Right: Arrow icon\n const arrowDiv = dom.createElement('div')\n arrowDiv.classList.add('personLi-arrow')\n arrowDiv.innerHTML = '<svg aria-hidden=\"true\" width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M6 4.5L11.25 9L6 13.5\" stroke=\"#595959\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>'\n\n // Assemble\n rowDiv.appendChild(avatarDiv)\n rowDiv.appendChild(infoDiv)\n rowDiv.appendChild(arrowDiv)\n personLi.appendChild(rowDiv)\n\n setPersonListener(personLi, person)\n return personLi\n }\n\n utils.syncTableToArrayReOrdered(ul, cards, person => renderNameInGroupList(person, ul))\n refreshFilteredPeople(ul, autoSelect, detailsView || cardMain)\n} // refreshNames\n\nexport function selectPerson (ulPeople, person, detailsView) {\n if (!detailsView) return\n if (detailsView.parentNode) detailsView.parentNode.classList.remove('hidden')\n detailsView.innerHTML = 'Loading...'\n detailsView.setAttribute('aria-busy', 'true')\n detailsView.classList.add('detailsSectionContent--wide')\n selectedPeople = {}\n selectedPeople[person.uri] = true\n refreshFilteredPeople(ulPeople, false, detailsView) // Color to remember which one you picked\n let local\n try {\n local = book ? localNode(person) : person\n } catch (err) {\n detailsView.innerHTML = ''\n detailsView.setAttribute('aria-busy', 'false')\n complain(detailsView, dom, 'Cannot load contact: ' + err.message)\n return\n }\n kb.fetcher.nowOrWhenFetched(local.doc(), undefined, function (\n ok,\n message\n ) {\n detailsView.innerHTML = ''\n detailsView.setAttribute('aria-busy', 'false')\n if (!ok) {\n debug.error('Failed to load contact card: ' + local + '. Stack: ' + message)\n complain(detailsView, dom, 'Failed to load contact. If it persists, contact your admin.')\n return\n }\n // debug.log(\"Loaded card \" + local + '\\n')\n\n // Top-right toolbar with link icon and delete button\n const toolbar = dom.createElement('div')\n toolbar.classList.add('contact-toolbar')\n const linkEl = UI.widgets.linkIcon(dom, local)\n linkEl.setAttribute('title', 'Uri of contact')\n toolbar.appendChild(linkEl)\n\n if (authn.currentUser()) {\n // Add in a delete button to delete from AB\n const deleteButton = UI.widgets.deleteButtonWithCheck(\n dom,\n toolbar, // appends it to toolbar.appendChild(deleteButton)\n 'contact',\n async function () {\n const container = person.dir() // ASSUMPTION THAT CARD IS IN ITS OWN DIRECTORY\n\n const pname = kb.any(person, ns.vcard('fn'))\n debug.log('We are about to delete the contact ' + pname)\n await loadAllGroups() // need to wait for all groups to be loaded in case they have a link to this person\n // load people.ttl\n const nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex'))\n await kb.fetcher.load(nameEmailIndex)\n\n // - delete person's WebID's in each Group\n // - delete the references to it in group files and save them back\n // - delete the reference in people.ttl and save it back\n\n // find all Groups\n const groups = groupMembership(person)\n let removeFromGroups = []\n // find person WebID's\n groups.forEach(group => {\n const webids = getSameAs(kb, person, group.doc())\n // for each check in each Group that it is not used by an other person then delete\n webids.forEach(webid => {\n if (getSameAs(kb, webid, group.doc()).length === 1) {\n removeFromGroups = removeFromGroups.concat(kb.statementsMatching(group, ns.vcard('hasMember'), webid, group.doc()))\n }\n })\n })\n\n // Only if folder deletion succeeds, proceed with person deletion\n await kb.updater.updateMany(removeFromGroups)\n\n try {\n await deleteThingAndDoc(person)\n } catch (err) {\n complain(detailsView, dom, 'Failed to delete contact. If it persists, contact your admin.')\n return\n }\n\n try {\n await deleteRecursive(kb, container, toolbar, dom)\n } catch (err) {\n const msg = 'Failed to delete contact. If it persists, contact your admin.'\n complain(detailsView, dom, msg)\n return\n }\n refreshNames(ulPeople, detailsView)\n detailsView.innerHTML = 'Contact data deleted.'\n }\n )\n deleteButton.classList.add('deleteButton')\n }\n detailsView.appendChild(toolbar)\n\n detailsView.classList.add('detailsSectionContent--wide')\n detailsView.appendChild(renderPane(local, 'contact'))\n })\n}\n\nexport function deselectAllPeople (ulPeople) {\n selectedPeople = {}\n if (ulPeople) {\n for (let i = 0; i < ulPeople.children.length; i++) {\n ulPeople.children[i].classList.remove('selected')\n }\n }\n}\n\nexport function refreshFilteredPeople (ulPeople, active, detailsView) {\n let count = 0\n let lastRow = null\n for (let i = 0; i < ulPeople.children.length; i++) {\n const liElement = ulPeople.children[i]\n const matches = filterName(nameFor(liElement.subject))\n if (matches) {\n count++\n lastRow = liElement\n }\n liElement.classList.toggle('selected', matches && !!selectedPeople[liElement.subject.uri])\n liElement.classList.toggle('hidden', !matches)\n }\n if (count === 1 && active) {\n const unique = lastRow.subject\n selectPerson(ulPeople, unique, detailsView)\n }\n}\n\nfunction filterName (name) {\n const filter = searchInput.value.trim().toLowerCase()\n if (filter.length === 0) return true\n const parts = filter.split(' ') // Each name part must be somewhere\n for (let j = 0; j < parts.length; j++) {\n const word = parts[j]\n if (name.toLowerCase().indexOf(word) < 0) return false\n }\n return true\n}\n\nfunction renderPane (subject, paneName) {\n const p = dataBrowserContext.session.paneRegistry.byName(paneName)\n const d = p.render(subject, dataBrowserContext)\n d.classList.add('renderPane')\n return d\n}\n\nfunction localNode (person) {\n const aliases = kb.allAliases(person)\n const prefix = book.dir().uri\n for (let i = 0; i < aliases.length; i++) {\n if (aliases[i].uri.slice(0, prefix.length) === prefix) {\n return aliases[i]\n }\n }\n throw new Error('No local URI for ' + person)\n}\n\n// Check every group is in the list and add it if not.\nexport async function checkDataModel (book, detailsSectionContent) {\n // await kb.fetcher.load(groups) // asssume loaded already\n const groups = await loadAllGroups(book)\n\n if (groups && groups.length > 0) {\n const { del, ins } = await getDataModelIssues(groups)\n\n if (authn.currentUser()) {\n if (del.length) {\n UI.widgets.deleteButtonWithCheck(\n dom,\n detailsSectionContent, // where it appends it to\n 'contact',\n async function () {\n await kb.updater.updateMany(del, ins)\n debug.log('Deleted ' + del.length + ' bad statements from groups')\n })\n }\n }\n }\n}\n\n// Prepare book data once so askName forms load instantly\nexport async function ensureBookLoaded () {\n const ourBook = findBookFromGroups(book)\n try {\n await kb.fetcher.load(ourBook)\n } catch (err) {\n throw new Error('Book won\\'t load:' + ourBook)\n }\n const nameEmailIndex = kb.any(ourBook, ns.vcard('nameEmailIndex'))\n if (!nameEmailIndex) throw new Error('No nameEmailIndex')\n await kb.fetcher.load(nameEmailIndex)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { error as e, warn as t } from "./debug.esm.js";
|
|
2
|
+
import { getPersonas as n } from "./webidControl.esm.js";
|
|
3
|
+
import { alertDialog as r, confirmDialog as i, getSameAs as a } from "./localUtils.esm.js";
|
|
4
|
+
import { store as o } from "solid-logic";
|
|
5
|
+
import * as s from "solid-ui";
|
|
6
|
+
import * as c from "rdflib";
|
|
7
|
+
//#region src/contactLogic.js
|
|
8
|
+
var l = s.ns, u = s.utils, d = o, f = d.updater;
|
|
9
|
+
async function p(e, t = []) {
|
|
10
|
+
let n = e.concat(t).map((e) => e.why), r = [];
|
|
11
|
+
n.forEach((e) => {
|
|
12
|
+
r.find((t) => t.equals(e)) || r.push(e);
|
|
13
|
+
});
|
|
14
|
+
let i = r.map((n) => d.updater.update(e.filter((e) => e.why.sameTerm(n)), t.filter((e) => e.why.sameTerm(n))));
|
|
15
|
+
return Promise.all(i);
|
|
16
|
+
}
|
|
17
|
+
async function m(t, n, i, a) {
|
|
18
|
+
await d.fetcher.load(t.doc());
|
|
19
|
+
let o = d.any(t, l.vcard("nameEmailIndex")), s = u.genUuid(), p = d.sym(t.dir().uri + "Person/" + s + "/index.ttl#this"), m = p.doc(), h = [
|
|
20
|
+
c.st(p, l.vcard("inAddressBook"), t, o),
|
|
21
|
+
c.st(p, l.vcard("fn"), n, o),
|
|
22
|
+
c.st(p, l.vcard("fn"), n, m),
|
|
23
|
+
c.st(p, l.rdf("type"), a, m),
|
|
24
|
+
c.st(m, l.dct("created"), /* @__PURE__ */ new Date(), m)
|
|
25
|
+
], g = Array.isArray(i) ? i : Object.keys(i || {});
|
|
26
|
+
if (g.length > 0) for (let e of g) {
|
|
27
|
+
let t = d.sym(e), r = t.doc();
|
|
28
|
+
h.push(c.st(t, l.vcard("hasMember"), p, r), c.st(p, l.vcard("fn"), n, r));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
r("Must be a member of at least one group. Please select or create a group.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
await f.updateMany([], h);
|
|
36
|
+
} catch (t) {
|
|
37
|
+
throw e("Cannot add group membership for " + p + ". Stack:" + t), Error("Save new contact");
|
|
38
|
+
}
|
|
39
|
+
return p;
|
|
40
|
+
}
|
|
41
|
+
function h(e) {
|
|
42
|
+
return e.replace(/\W/gu, "_").replace(/_+/g, "_");
|
|
43
|
+
}
|
|
44
|
+
async function g(e, t) {
|
|
45
|
+
await d.fetcher.load(e.doc());
|
|
46
|
+
let n = d.any(e, l.vcard("groupIndex")), r = h(t), i = d.sym(e.dir().uri + "Group/" + r + ".ttl#this"), a = i.doc();
|
|
47
|
+
try {
|
|
48
|
+
await d.fetcher.load(n);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
throw Error("Error loading group index!" + n.uri + ": " + e);
|
|
51
|
+
}
|
|
52
|
+
if (d.holds(e, l.vcard("includesGroup"), i, n)) return i;
|
|
53
|
+
let o = [
|
|
54
|
+
c.st(e, l.vcard("includesGroup"), i, n),
|
|
55
|
+
c.st(i, l.rdf("type"), l.vcard("Group"), n),
|
|
56
|
+
c.st(i, l.vcard("fn"), t, n)
|
|
57
|
+
];
|
|
58
|
+
try {
|
|
59
|
+
await f.update([], o);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
throw Error("Could not update group index " + e);
|
|
62
|
+
}
|
|
63
|
+
let s = [
|
|
64
|
+
c.st(e, l.vcard("includesGroup"), i, a),
|
|
65
|
+
c.st(i, l.rdf("type"), l.vcard("Group"), a),
|
|
66
|
+
c.st(i, l.vcard("fn"), t, a)
|
|
67
|
+
];
|
|
68
|
+
try {
|
|
69
|
+
await f.update([], s);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
throw Error("Could not update group file: " + e);
|
|
72
|
+
}
|
|
73
|
+
return i;
|
|
74
|
+
}
|
|
75
|
+
async function _(a, o) {
|
|
76
|
+
let s = [a.doc(), o.doc()];
|
|
77
|
+
try {
|
|
78
|
+
await d.fetcher.load(s);
|
|
79
|
+
} catch (t) {
|
|
80
|
+
throw e("Error adding " + a + " to group " + o + ". Stack: " + t), Error("Error adding to group.");
|
|
81
|
+
}
|
|
82
|
+
let u = d.findTypeURIs(a);
|
|
83
|
+
if (!(l.vcard("Individual").uri in u || l.vcard("Organization").uri in u)) {
|
|
84
|
+
t("Thing " + a + " is not an Individual or Organization, but has types: " + Object.keys(u)), r("You are trying to add something else than an individual or organization.");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
let p = d.any(a, l.vcard("fn")), m = d.any(o, l.vcard("fn"));
|
|
88
|
+
if (!p) {
|
|
89
|
+
t("Thing " + a + " has no vcard:fn"), r("What you are trying to add seems to have no full name.");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (d.holds(a, l.vcard("fn"), null, o.doc())) {
|
|
93
|
+
p === "" && (p = "Contact"), r(p + " already exists in group " + m + ".");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!await i("Add " + p + " to group " + m + "?")) return;
|
|
97
|
+
let h = [c.st(a, l.vcard("fn"), p, o.doc())], g = n(d, a).map((e) => e.value);
|
|
98
|
+
g.length ? g.forEach((e) => {
|
|
99
|
+
h.push(c.st(d.sym(e), l.owl("sameAs"), a, o.doc())), h.push(c.st(o, l.vcard("hasMember"), d.sym(e), o.doc()));
|
|
100
|
+
}) : h.push(c.st(o, l.vcard("hasMember"), a, o.doc()));
|
|
101
|
+
try {
|
|
102
|
+
await f.update([], h), d.fetcher.unload(o.doc()), await d.fetcher.load(o.doc());
|
|
103
|
+
} catch (t) {
|
|
104
|
+
throw e("Error adding " + a + " to group " + o + ". Stack: " + t), Error("Error adding to group.");
|
|
105
|
+
}
|
|
106
|
+
return a;
|
|
107
|
+
}
|
|
108
|
+
function v(e, t) {
|
|
109
|
+
let n = e.each(t, l.vcard("hasMember"), null, t.doc()), r = [];
|
|
110
|
+
return n.forEach((n) => {
|
|
111
|
+
r = e.any(n, l.vcard("fn"), null, t.doc()) ? r.concat(n) : r.concat(e.each(n, l.owl("sameAs"), null, t.doc()));
|
|
112
|
+
}), r = [...new Set(r.map((e) => e.uri))].map((t) => e.sym(t)), r;
|
|
113
|
+
}
|
|
114
|
+
function y(e, t) {
|
|
115
|
+
let n = e.dir().dir().dir();
|
|
116
|
+
return t.uri && t.uri.startsWith(n.uri);
|
|
117
|
+
}
|
|
118
|
+
async function b(e) {
|
|
119
|
+
let t = [], n = [];
|
|
120
|
+
return e.forEach((e) => {
|
|
121
|
+
d.each(e, l.vcard("hasMember"), null, e.doc()).forEach((r) => {
|
|
122
|
+
let i = a(d, r, e.doc());
|
|
123
|
+
if (i.length && y(e, r)) {
|
|
124
|
+
for (let a of i) if (!y(e, a)) {
|
|
125
|
+
t.push(c.st(e, l.vcard("hasMember"), r, e.doc())), n.push(c.st(e, l.vcard("hasMember"), a, e.doc()));
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}), {
|
|
131
|
+
del: t,
|
|
132
|
+
ins: n
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//#endregion
|
|
136
|
+
export { _ as addPersonToGroup, b as getDataModelIssues, v as groupMembers, y as isLocal, h as sanitizeToAlpha, m as saveNewContact, g as saveNewGroup, p as updateMany };
|
|
137
|
+
|
|
138
|
+
//# sourceMappingURL=contactLogic.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contactLogic.esm.js","names":[],"sources":["../src/contactLogic.js"],"sourcesContent":["import * as UI from 'solid-ui'\nimport * as $rdf from 'rdflib'\nimport { store } from 'solid-logic'\nimport { getPersonas } from './webidControl'\nimport * as debug from './debug'\nimport { getSameAs, confirmDialog, alertDialog } from './localUtils'\n\nconst ns = UI.ns\nconst utils = UI.utils\nconst kb = store\nconst updater = kb.updater\n\n/** Perform updates on more than one document @@ Move to rdflib!\n*/\nexport async function updateMany (deletions, insertions = []) {\n const docs = deletions.concat(insertions).map(st => st.why)\n const uniqueDocs = []\n docs.forEach(doc => {\n if (!uniqueDocs.find(uniqueDoc => uniqueDoc.equals(doc))) uniqueDocs.push(doc)\n })\n const updates = uniqueDocs.map(doc =>\n kb.updater.update(deletions.filter(st => st.why.sameTerm(doc)),\n insertions.filter(st => st.why.sameTerm(doc))))\n return Promise.all(updates)\n}\n\n/** Add a new person to the web data\n*\n* adds them to the given groups as well.\n* @returns {NamedNode} the person\n*/\nexport async function saveNewContact (book, name, selectedGroups, klass) {\n await kb.fetcher.load(book.doc())\n const nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex'))\n\n const uuid = utils.genUuid()\n const person = kb.sym(\n book.dir().uri + 'Person/' + uuid + '/index.ttl#this'\n )\n const doc = person.doc()\n\n // Set of statements to different files\n const agenda = [\n // Patch the main index to add the person\n $rdf.st(person, ns.vcard('inAddressBook'), book, nameEmailIndex), // The people index\n $rdf.st(person, ns.vcard('fn'), name, nameEmailIndex),\n // The new person file\n $rdf.st(person, ns.vcard('fn'), name, doc),\n $rdf.st(person, ns.rdf('type'), klass, doc),\n\n $rdf.st(doc, ns.dct('created'), new Date(), doc) // Note when created - useful for triaging later\n // Note this is propert of the file -- not when the person was created!\n ]\n\n // `selectedGroups` may be an array (older callers) or an object map\n // (contactsPane.js tracks it as `{ uri: true }`). Normalize and make sure\n // at least one group is selected before proceeding – otherwise we return\n // `undefined` and the caller must handle it.\n const groups = Array.isArray(selectedGroups)\n ? selectedGroups\n : Object.keys(selectedGroups || {})\n\n if (groups.length > 0) {\n for (const gu of groups) {\n const g = kb.sym(gu)\n const gd = g.doc()\n agenda.push(\n $rdf.st(g, ns.vcard('hasMember'), person, gd),\n $rdf.st(person, ns.vcard('fn'), name, gd)\n )\n }\n } else {\n alertDialog('Must be a member of at least one group. Please select or create a group.')\n return // caller should check for undefined result\n }\n\n try {\n await updater.updateMany([], agenda)\n } catch (e) {\n debug.error('Cannot add group membership for ' + person + '. Stack:' + e)\n throw new Error('Save new contact')\n }\n return person\n}\n\nexport function sanitizeToAlpha (name) { // https://mathiasbynens.be/notes/es6-unicode-regex\n const n2 = name.replace(/\\W/gu, '_') // Anything which is not a unicode word characeter\n return n2.replace(/_+/g, '_') // https://www.regular-expressions.info/shorthand.html\n}\n\n/** Write new group to web\n * Creates an empty new group file and adds it to the index\n * @returns group\n*/\nexport async function saveNewGroup (book, name) {\n await kb.fetcher.load(book.doc())\n const gix = kb.any(book, ns.vcard('groupIndex'))\n\n const gname = sanitizeToAlpha(name)\n const group = kb.sym(book.dir().uri + 'Group/' + gname + '.ttl#this')\n const doc = group.doc()\n // debug.log(' New group will be: ' + group + '\\n')\n try {\n await kb.fetcher.load(gix)\n } catch (err) {\n throw new Error('Error loading group index!' + gix.uri + ': ' + err)\n }\n if (kb.holds(book, ns.vcard('includesGroup'), group, gix)) {\n return group // Already exists\n }\n const insertTriples = [\n $rdf.st(book, ns.vcard('includesGroup'), group, gix),\n $rdf.st(group, ns.rdf('type'), ns.vcard('Group'), gix),\n $rdf.st(group, ns.vcard('fn'), name, gix)\n ]\n try {\n await updater.update([], insertTriples)\n } catch (e) {\n throw new Error('Could not update group index ' + e) // fail\n }\n\n const triples = [\n $rdf.st(book, ns.vcard('includesGroup'), group, doc), // Pointer back to book\n $rdf.st(group, ns.rdf('type'), ns.vcard('Group'), doc),\n $rdf.st(group, ns.vcard('fn'), name, doc)\n ]\n try {\n await updater.update([], triples)\n } catch (err) {\n throw new Error('Could not update group file: ' + err) // fail\n }\n return group\n}\n\nexport async function addPersonToGroup (thing, group) {\n const toBeFetched = [thing.doc(), group.doc()]\n try {\n await kb.fetcher.load(toBeFetched)\n } catch (e) {\n debug.error('Error adding ' + thing + ' to group ' + group + '. Stack: ' + e)\n throw new Error('Error adding to group.')\n }\n\n const types = kb.findTypeURIs(thing)\n\n if (!(ns.vcard('Individual').uri in types ||\n ns.vcard('Organization').uri in types)) {\n debug.warn('Thing ' + thing + ' is not an Individual or Organization, but has types: ' + Object.keys(types))\n alertDialog('You are trying to add something else than an individual or organization.')\n return\n }\n let pname = kb.any(thing, ns.vcard('fn'))\n const gname = kb.any(group, ns.vcard('fn'))\n if (!pname) {\n debug.warn('Thing ' + thing + ' has no vcard:fn')\n alertDialog('What you are trying to add seems to have no full name.')\n return\n }\n const already = kb.holds(thing, ns.vcard('fn'), null, group.doc())\n if (already) {\n if (pname === '') pname = 'Contact'\n alertDialog(pname + ' already exists in group ' + gname + '.')\n return\n }\n const message = 'Add ' + pname + ' to group ' + gname + '?'\n if (!await confirmDialog(message)) return\n const ins = [\n $rdf.st(thing, ns.vcard('fn'), pname, group.doc())\n ]\n // find person webIDs and insert in vcard:hasMember\n const webIDs = getPersonas(kb, thing).map(webid => webid.value)\n if (webIDs.length) {\n webIDs.forEach(webid => {\n ins.push($rdf.st(kb.sym(webid), ns.owl('sameAs'), thing, group.doc()))\n ins.push($rdf.st(group, ns.vcard('hasMember'), kb.sym(webid), group.doc()))\n })\n } else {\n ins.push($rdf.st(group, ns.vcard('hasMember'), thing, group.doc()))\n }\n try {\n await updater.update([], ins)\n // to allow refresh of card groupList\n kb.fetcher.unload(group.doc())\n await kb.fetcher.load(group.doc())\n } catch (e) {\n debug.error('Error adding ' + thing + ' to group ' + group + '. Stack: ' + e)\n throw new Error('Error adding to group.')\n }\n return thing\n}\n\n/**\n * Find persons member of a group\n */\n\nexport function groupMembers (kb, group) {\n const a = kb.each(group, ns.vcard('hasMember'), null, group.doc())\n let b = []\n a.forEach(item => {\n /* const contacts = kb.each(item, ns.owl('sameAs'), null, group.doc())\n if (contacts.length) {\n if (!kb.any(contacts[0], ns.vard('fn'))) b = b.concat(item) // this is the old data model\n else b = b.concat(contacts)\n } else { b = b.concat(item) }\n b = b.concat(item) */\n\n // to keep compatibility with old data model\n // check if item is a contact, else it is a WebID and parse 'sameAs' for contacts\n b = kb.any(item, ns.vcard('fn'), null, group.doc()) ? b.concat(item) : b.concat(kb.each(item, ns.owl('sameAs'), null, group.doc()))\n })\n const strings = new Set(b.map(contact => contact.uri)) // remove dups\n b = [...strings].map(uri => kb.sym(uri))\n return b\n}\n\nexport function isLocal (group, item) {\n const tree = group.dir().dir().dir()\n const local = item.uri && item.uri.startsWith(tree.uri)\n // debug.log(` isLocal ${local} for ${item.uri} in group ${group} tree ${tree.uri}`)\n return local\n}\n\nexport async function getDataModelIssues (groups) {\n const del = []\n const ins = []\n groups.forEach(group => {\n const members = kb.each(group, ns.vcard('hasMember'), null, group.doc())\n members.forEach((member) => {\n const others = getSameAs(kb, member, group.doc())\n if (others.length && isLocal(group, member)) { // Problem: local ID used instead of webID\n for (const other of others) {\n if (!isLocal(group, other)) { // Let's use this one as the immediate member for CSS ACLs'\n // console.warn(`getDataModelIssues: Need to swap ${member} to ${other}`)\n del.push($rdf.st(group, ns.vcard('hasMember'), member, group.doc()))\n ins.push($rdf.st(group, ns.vcard('hasMember'), other, group.doc()))\n break\n }\n // debug.log('getDataModelIssues: ??? expected id not to be local ' + other)\n } // other\n } // if\n }) // member\n }) // next group\n return { del, ins }\n} // getDataModelIssues\n\n// Ends\n"],"mappings":"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"}
|