contacts-pane 2.4.8 → 2.4.12-beta4

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.
Files changed (51) hide show
  1. package/.eslintrc +3 -1
  2. package/.github/workflows/ci.yml +74 -0
  3. package/.nvmrc +1 -1
  4. package/LICENSE.md +0 -0
  5. package/Makefile +0 -0
  6. package/README.md +0 -0
  7. package/__tests__/unit/data-reformat-test.js +166 -0
  8. package/__tests__/unit/data-reformat-test.ts +185 -0
  9. package/__tests__/unit/setup.js +74 -0
  10. package/__tests__/unit/setup.ts +77 -0
  11. package/babel.config.js +13 -0
  12. package/card.ai +0 -0
  13. package/card.png +0 -0
  14. package/contactLogic.js +84 -7
  15. package/contactsPane.js +64 -20
  16. package/diff.txt +0 -0
  17. package/exampleOfOpenData/mit-wikidata-details.ttl +0 -0
  18. package/exampleOfOpenData/mit-wikidata-query.sparql +0 -0
  19. package/exampleOfOpenData/wikidata-get.json +0 -0
  20. package/groupMembershipControl.js +56 -13
  21. package/individual.js +3 -2
  22. package/individualForm.js +0 -0
  23. package/jest.config.js +11 -0
  24. package/jest.setup.ts +10 -0
  25. package/lib/autocompleteBar.js +99 -0
  26. package/lib/autocompleteBar.js.map +1 -0
  27. package/lib/autocompleteField.js +157 -0
  28. package/lib/autocompleteField.js.map +1 -0
  29. package/lib/autocompletePicker.js +240 -0
  30. package/lib/autocompletePicker.js.map +1 -0
  31. package/lib/forms.js +315 -0
  32. package/lib/instituteDetailsQuery.js +38 -0
  33. package/lib/publicData.js +387 -0
  34. package/lib/publicData.js.map +1 -0
  35. package/lib/vcard.js +916 -0
  36. package/mintNewAddressBook.js +5 -4
  37. package/mugshotGallery.js +16 -4
  38. package/organizationForm.js +0 -0
  39. package/organizationForm.ttl +0 -0
  40. package/package.json +26 -13
  41. package/shapes/contacts-shapes.ttl +0 -0
  42. package/src/autocompleteBar.ts +92 -0
  43. package/src/autocompleteField.ts +180 -0
  44. package/src/autocompletePicker.ts +253 -0
  45. package/src/forms.ttl +1 -1
  46. package/src/instituteDetailsQuery.sparql +0 -0
  47. package/src/publicData.ts +385 -0
  48. package/src/vcard.ttl +0 -0
  49. package/toolsPane.js +70 -19
  50. package/tsconfig.json +16 -0
  51. package/webidControl.js +49 -4
package/contactLogic.js CHANGED
@@ -1,11 +1,13 @@
1
1
  // Logic for solid contacts
2
2
 
3
3
  import * as UI from 'solid-ui'
4
+ import { store } from 'solid-logic'
5
+ import { getPersonas } from './webidControl'
4
6
 
5
7
  const ns = UI.ns
6
8
  const $rdf = UI.rdf
7
9
  const utils = UI.utils
8
- const kb = UI.store
10
+ const kb = store
9
11
  const updater = kb.updater
10
12
 
11
13
  /** Perform updates on more than one document @@ Move to rdflib!
@@ -28,6 +30,7 @@ export async function updateMany (deletions, insertions = []) {
28
30
  * @returns {NamedNode} the person
29
31
  */
30
32
  export async function saveNewContact (book, name, selectedGroups, klass) {
33
+ await kb.fetcher.load(book.doc())
31
34
  const nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex'))
32
35
 
33
36
  const uuid = utils.genUuid()
@@ -59,9 +62,9 @@ export async function saveNewContact (book, name, selectedGroups, klass) {
59
62
  }
60
63
 
61
64
  try {
62
- await updateMany([], agenda) // @@ in future, updater.updateMany
65
+ await updater.updateMany([], agenda) // @@ in future, updater.updateMany
63
66
  } catch (e) {
64
- console.log("Error: can't update " + person + ' as new contact:' + e)
67
+ console.error("Error: can't update " + person + ' as new contact:' + e)
65
68
  throw new Error('Updating new contact: ' + e)
66
69
  }
67
70
  return person
@@ -77,12 +80,13 @@ export function sanitizeToAlpha (name) { // https://mathiasbynens.be/notes/es6-u
77
80
  * @returns group
78
81
  */
79
82
  export async function saveNewGroup (book, name) {
83
+ await kb.fetcher.load(book.doc())
80
84
  const gix = kb.any(book, ns.vcard('groupIndex'))
81
85
 
82
86
  const gname = sanitizeToAlpha(name)
83
87
  const group = kb.sym(book.dir().uri + 'Group/' + gname + '.ttl#this')
84
88
  const doc = group.doc()
85
- console.log(' New group will be: ' + group + '\n')
89
+ // console.log(' New group will be: ' + group + '\n')
86
90
  try {
87
91
  await kb.fetcher.load(gix)
88
92
  } catch (err) {
@@ -125,7 +129,7 @@ export async function addPersonToGroup (thing, group) {
125
129
 
126
130
  const types = kb.findTypeURIs(thing)
127
131
  for (const ty in types) {
128
- console.log(' drop object type includes: ' + ty) // @@ Allow email addresses and phone numbers to be dropped?
132
+ // console.log(' drop object type includes: ' + ty) // @@ Allow email addresses and phone numbers to be dropped?
129
133
  }
130
134
  if (!(ns.vcard('Individual').uri in types ||
131
135
  ns.vcard('Organization').uri in types)) {
@@ -134,7 +138,7 @@ export async function addPersonToGroup (thing, group) {
134
138
  const pname = kb.any(thing, ns.vcard('fn'))
135
139
  const gname = kb.any(group, ns.vcard('fn'))
136
140
  if (!pname) { return alert('No vcard name known for ' + thing) }
137
- const already = kb.holds(group, ns.vcard('hasMember'), thing, group.doc())
141
+ const already = kb.holds(thing, ns.vcard('fn'), null, group.doc())
138
142
  if (already) {
139
143
  return alert(
140
144
  'ALREADY added ' + pname + ' to group ' + gname
@@ -143,13 +147,86 @@ export async function addPersonToGroup (thing, group) {
143
147
  const message = 'Add ' + pname + ' to group ' + gname + '?'
144
148
  if (!confirm(message)) return
145
149
  const ins = [
146
- $rdf.st(group, ns.vcard('hasMember'), thing, group.doc()),
147
150
  $rdf.st(thing, ns.vcard('fn'), pname, group.doc())
148
151
  ]
152
+ // find person webIDs and insert in vcard:hasMember
153
+ const webIDs = getPersonas(kb, thing).map(webid => webid.value)
154
+ if (webIDs.length) {
155
+ webIDs.forEach(webid => {
156
+ ins.push($rdf.st(kb.sym(webid), ns.owl('sameAs'), thing, group.doc()))
157
+ ins.push($rdf.st(group, ns.vcard('hasMember'), kb.sym(webid), group.doc()))
158
+ })
159
+ } else {
160
+ ins.push($rdf.st(group, ns.vcard('hasMember'), thing, group.doc()))
161
+ }
149
162
  try {
150
163
  await updater.update([], ins)
164
+ // to allow refresh of card groupList
165
+ kb.fetcher.unload(group.doc())
166
+ await kb.fetcher.load(group.doc())
151
167
  } catch (e) {
152
168
  throw new Error(`Error adding ${pname} to group ${gname}:` + e)
153
169
  }
154
170
  return thing
155
171
  }
172
+
173
+ /**
174
+ * Find persons member of a group
175
+ */
176
+
177
+ export function groupMembers (kb, group) {
178
+ const a = kb.each(group, ns.vcard('hasMember'), null, group.doc())
179
+ let b = []
180
+ a.forEach(item => {
181
+ /* const contacts = kb.each(item, ns.owl('sameAs'), null, group.doc())
182
+ if (contacts.length) {
183
+ if (!kb.any(contacts[0], ns.vard('fn'))) b = b.concat(item) // this is the old data model
184
+ else b = b.concat(contacts)
185
+ } else { b = b.concat(item) }
186
+ b = b.concat(item) */
187
+
188
+ // to keep compatibility with old data model
189
+ // check if item is a contact, else it is a WebID and parse 'sameAs' for contacts
190
+ b = kb.any(item, ns.vcard('fn'), null, group.doc()) ? b.concat(item) : b.concat(kb.each(item, ns.owl('sameAs'), null, group.doc()))
191
+ })
192
+ const strings = new Set(b.map(contact => contact.uri)) // remove dups
193
+ b = [...strings].map(uri => kb.sym(uri))
194
+ return b
195
+ }
196
+
197
+ export function isLocal(group, item) {
198
+ const tree = group.dir().dir().dir()
199
+ const local = item.uri && item.uri.startsWith(tree.uri)
200
+ // console.log(` isLocal ${local} for ${item.uri} in group ${group} tree ${tree.uri}`)
201
+ return local
202
+ }
203
+
204
+ export function getSameAs(kb, item, doc) {
205
+ return kb.each(item, ns.owl('sameAs'), null, doc).concat(
206
+ kb.each(null, ns.owl('sameAs'), item, doc))
207
+ }
208
+
209
+ export async function getDataModelIssues(groups) {
210
+ let del = []
211
+ let ins = []
212
+ groups.forEach(group => {
213
+ const members = kb.each(group, ns.vcard('hasMember'), null, group.doc())
214
+ members.forEach((member) => {
215
+ const others = getSameAs(kb, member, group.doc())
216
+ if (others.length && isLocal(group, member)) { // Problem: local ID used instead of webID
217
+ for (const other of others) {
218
+ if (!isLocal(group, other)) { // Let's use this one as the immediate member for CSS ACLs'
219
+ // console.warn(`getDataModelIssues: Need to swap ${member} to ${other}`)
220
+ del.push($rdf.st(group, ns.vcard('hasMember'), member, group.doc()))
221
+ ins.push($rdf.st(group, ns.vcard('hasMember'), other, group.doc()))
222
+ break
223
+ }
224
+ // console.log('getDataModelIssues: ??? expected id not to be local ' + other)
225
+ } // other
226
+ } // if
227
+ }) // member
228
+ }) // next group
229
+ return {del, ins }
230
+ } // getDataModelIssues
231
+
232
+ // Ends
package/contactsPane.js CHANGED
@@ -14,11 +14,14 @@ to change its state according to an ontology, comment on it, etc.
14
14
  */
15
15
  /* global alert, confirm */
16
16
 
17
+ import { authn } from 'solid-logic'
18
+ import { addPersonToGroup, saveNewContact, saveNewGroup, groupMembers } from './contactLogic'
17
19
  import * as UI from 'solid-ui'
18
- import { toolsPane } from './toolsPane'
19
20
  import { mintNewAddressBook } from './mintNewAddressBook'
20
21
  import { renderIndividual } from './individual'
21
- import { saveNewContact, saveNewGroup, addPersonToGroup } from './contactLogic'
22
+ import { toolsPane } from './toolsPane'
23
+ import { groupMembership } from './groupMembershipControl'
24
+ import { getDataModelIssues } from './contactLogic'
22
25
 
23
26
  // const $rdf = UI.rdf
24
27
  const ns = UI.ns
@@ -63,7 +66,7 @@ export default {
63
66
 
64
67
  // Reproduction: Spawn a new instance of this app
65
68
  function newAddressBookButton (thisAddressBook) {
66
- return UI.authn.newAppInstance(
69
+ return UI.login.newAppInstance(
67
70
  dom,
68
71
  { noun: 'address book', appPathSegment: 'contactorator.timbl.com' },
69
72
  function (ws, newBase) {
@@ -79,14 +82,14 @@ export default {
79
82
  const dom = dataBrowserContext.dom
80
83
  const kb = dataBrowserContext.session.store
81
84
  const div = dom.createElement('div')
82
- const me = UI.authn.currentUser() // If already logged on
85
+ const me = authn.currentUser() // If already logged on
83
86
 
84
87
  UI.aclControl.preventBrowserDropEvents(dom) // protect drag and drop
85
88
 
86
89
  div.setAttribute('class', 'contactPane')
87
90
 
88
91
  asyncRender().then(
89
- () => console.log('contctsPane Rendered ' + subject),
92
+ () => console.log('contactsPane Rendered ' + subject),
90
93
  err => complain('' + err))
91
94
  return div
92
95
 
@@ -96,7 +99,7 @@ export default {
96
99
 
97
100
  const t = kb.findTypeURIs(subject)
98
101
 
99
- let me = UI.authn.currentUser()
102
+ let me = authn.currentUser()
100
103
 
101
104
  const context = {
102
105
  target: subject,
@@ -212,9 +215,30 @@ export default {
212
215
  ) {
213
216
  console.log('Deleting a contact ' + pname)
214
217
  await loadAllGroups() // need to wait for all groups to be loaded in case they have a link to this person
215
- await deleteThingAndDoc(person)
218
+ // load people.ttl
219
+ const nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex'))
220
+ await kb.fetcher.load(nameEmailIndex)
221
+
222
+ // - delete person's WebID's in each Group
216
223
  // - delete the references to it in group files and save them back
217
- // - delete the reference in people.ttl and save it back
224
+ // - delete the reference in people.ttl and save it back
225
+
226
+ // find all Groups
227
+ const groups = groupMembership(person)
228
+ let removeFromGroups = []
229
+ // find person WebID's
230
+ groups.map( group => {
231
+ const webids = getSameAs(kb, person, group.doc())
232
+ // for each check in each Group that it is not used by an other person then delete
233
+ webids.map( webid => {
234
+ if (getSameAs(kb, webid, group.doc()).length = 1) {
235
+ removeFromGroups = removeFromGroups.concat(kb.statementsMatching(group, ns.vcard('hasMember'), webid, group.doc()))
236
+ }
237
+ })
238
+ })
239
+ // console.log(removeFromGroups)
240
+ await kb.updater.updateMany(removeFromGroups)
241
+ await deleteThingAndDoc(person)
218
242
  await deleteRecursive(kb, container)
219
243
  refreshNames() // "Doesn't work" -- maybe does now with waiting for async
220
244
  cardMain.innerHTML = 'Contact Data Deleted.'
@@ -289,8 +313,10 @@ export default {
289
313
  }
290
314
 
291
315
  async function loadAllGroups () {
316
+ await kb.fetcher.load(groupIndex)
292
317
  const gs = book ? kb.each(book, ns.vcard('includesGroup'), null, groupIndex) : []
293
318
  await kb.fetcher.load(gs)
319
+ return gs
294
320
  }
295
321
 
296
322
  function groupsInOrder () {
@@ -409,12 +435,12 @@ export default {
409
435
  }
410
436
 
411
437
  let cards = []
412
- for (const u in selectedGroups) {
413
- if (selectedGroups[u]) {
414
- const a = kb.each(kb.sym(u), ns.vcard('hasMember'))
415
- cards = cards.concat(a)
438
+ const groups = Object.keys(selectedGroups).map(groupURI => kb.sym(groupURI))
439
+ groups.forEach(group => {
440
+ if (selectedGroups[group.value]) {
441
+ cards = cards.concat(groupMembers(kb, group))
416
442
  }
417
- }
443
+ })
418
444
  cards.sort(compareForSort) // @@ sort by name not UID later
419
445
  for (let k = 0; k < cards.length - 1;) {
420
446
  if (cards[k].uri === cards[k + 1].uri) {
@@ -572,14 +598,28 @@ export default {
572
598
  const groups = groupsInOrder()
573
599
  utils.syncTableToArrayReOrdered(groupsMainTable, groups, renderGroupRow)
574
600
  refreshGroupsSelected()
601
+ // await checkDataModel(groups)
575
602
  } // syncGroupTable
576
603
 
604
+
605
+ async function checkDataModel () {
606
+ // await kb.fetcher.load(groups) // asssume loaded already
607
+ const groups = await loadAllGroups()
608
+
609
+ const { del, ins } = await getDataModelIssues(groups)
610
+
611
+ if (del.length && confirm(`Groups data model need to be updated? (${del.length})`)) {
612
+ await kb.updater.updateMany(del, ins)
613
+ alert('Update done')
614
+ }
615
+ } // checkDataModel
616
+
577
617
  // Click on New Group button
578
618
  async function newGroupClickHandler (_event) {
579
619
  cardMain.innerHTML = ''
580
620
  const groupIndex = kb.any(book, ns.vcard('groupIndex'))
581
621
  try {
582
- await fetch.load(groupIndex)
622
+ await kb.fetcher.load(groupIndex)
583
623
  } catch (e) {
584
624
  console.log('Error: Group index NOT loaded:' + e + '\n')
585
625
  }
@@ -623,7 +663,7 @@ export default {
623
663
  const nameEmailIndex = kb.any(ourBook, ns.vcard('nameEmailIndex'))
624
664
  if (!nameEmailIndex) throw new Error('Wot no nameEmailIndex?')
625
665
  await kb.fetcher.load(nameEmailIndex)
626
- console.log('Name index loaded async' + nameEmailIndex)
666
+ // console.log('Name index loaded async' + nameEmailIndex)
627
667
 
628
668
  const name = await UI.widgets
629
669
  .askName(dom, kb, cardMain, UI.ns.foaf('name'), klass) // @@ was, 'person'
@@ -772,7 +812,7 @@ export default {
772
812
  const container = dom.createElement('div')
773
813
  newContactButton.setAttribute('type', 'button')
774
814
  if (!me) newContactButton.setAttribute('disabled', 'true')
775
- UI.authn.checkUser().then(webId => {
815
+ authn.checkUser().then(webId => {
776
816
  if (webId) {
777
817
  me = webId
778
818
  newContactButton.removeAttribute('disabled')
@@ -788,7 +828,7 @@ export default {
788
828
  const container2 = dom.createElement('div')
789
829
  newOrganizationButton.setAttribute('type', 'button')
790
830
  if (!me) newOrganizationButton.setAttribute('disabled', 'true')
791
- UI.authn.checkUser().then(webId => {
831
+ authn.checkUser().then(webId => {
792
832
  if (webId) {
793
833
  me = webId
794
834
  newOrganizationButton.removeAttribute('disabled')
@@ -835,6 +875,10 @@ export default {
835
875
  // })
836
876
 
837
877
  div.appendChild(dom.createElement('hr'))
878
+
879
+ // const groups = await loadAllGroups() @@@
880
+ checkDataModel().then(()=> {console.log('async checkDataModel done.')})
881
+
838
882
  // div.appendChild(newAddressBookButton(book)) // later
839
883
  // end of AddressBook instance
840
884
  } // renderThreeColumnBrowser
@@ -855,7 +899,7 @@ export default {
855
899
  // Render a Group instance
856
900
  } else if (t[ns.vcard('Group').uri]) {
857
901
  // If we have a main address book, then render this group as a guest group within it
858
- UI.authn
902
+ UI.login
859
903
  .findAppInstances(context, ns.vcard('AddressBook'))
860
904
  .then(function (context) {
861
905
  const addressBooks = context.instances
@@ -883,12 +927,12 @@ export default {
883
927
  )
884
928
  }
885
929
 
886
- me = UI.authn.currentUser()
930
+ me = authn.currentUser()
887
931
  if (!me) {
888
932
  console.log(
889
933
  '(You do not have your Web Id set. Sign in or sign up to make changes.)'
890
934
  )
891
- UI.authn.logInLoadProfile(context).then(
935
+ UI.login.ensureLoadedProfile(context).then(
892
936
  context => {
893
937
  console.log('Logged in as ' + context.me)
894
938
  me = context.me
package/diff.txt CHANGED
File without changes
File without changes
File without changes
File without changes
@@ -1,23 +1,41 @@
1
1
 
2
2
  // Render a control to record the group memberships we have for this agent
3
3
  import * as UI from 'solid-ui'
4
+ import { store } from 'solid-logic'
4
5
 
5
- const $rdf = UI.rdf
6
+ // const $rdf = UI.rdf
6
7
  const ns = UI.ns
7
8
  // const buttons = UI.buttonsn no
8
9
  // const widgets = UI.widgets
9
10
  const utils = UI.utils
10
- const kb = UI.store
11
11
  // const style = UI.style
12
+ const kb = store
12
13
 
13
14
  // Groups the person is a member of
15
+ export function groupMembership (person) {
16
+ let groups = kb.statementsMatching(null, ns.owl('sameAs'), person).map(st => st.why)
17
+ .concat(kb.each(null, ns.vcard('hasMember'), person))
18
+ const strings = new Set(groups.map(group => group.uri)) // remove dups
19
+ groups = [...strings].map(uri => kb.sym(uri))
20
+ return groups
21
+ }
14
22
 
15
23
  export async function renderGroupMemberships (person, context) {
16
24
  // Remove a person from a group
17
- function removeFromGroup (thing, group) {
25
+ async function removeFromGroup (thing, group) {
18
26
  const pname = kb.any(thing, ns.vcard('fn'))
19
27
  const gname = kb.any(group, ns.vcard('fn'))
20
- const groups = kb.each(null, ns.vcard('hasMember'), thing)
28
+ // find all WebIDs of thing
29
+ const thingwebids = kb.each(null, ns.owl('sameAs'), thing, group.doc())
30
+ // WebID can be deleted only if not used in another thing
31
+ let webids = []
32
+ thingwebids.map(webid => {
33
+ if (kb.statementsMatching(webid, ns.owl('sameAs'), thing, group.doc())) webids = webids.concat(webid)
34
+ }
35
+ )
36
+ let thingOrWebid = thing
37
+ if (webids.length > 0) thingOrWebid = webids[0]
38
+ const groups = kb.each(null, ns.vcard('hasMember'), thingOrWebid) // in all groups a person has same structure
21
39
  if (groups.length < 2) {
22
40
  alert(
23
41
  'Must be a member of at least one group. Add to another group first.'
@@ -26,21 +44,28 @@ export async function renderGroupMemberships (person, context) {
26
44
  }
27
45
  const message = 'Remove ' + pname + ' from group ' + gname + '?'
28
46
  if (confirm(message)) {
29
- const del = [
30
- $rdf.st(group, ns.vcard('hasMember'), thing, group.doc()),
31
- $rdf.st(thing, ns.vcard('fn'), pname, group.doc())
32
- ]
47
+ let del = kb
48
+ .statementsMatching(person, undefined, undefined, group.doc())
49
+ .concat(kb.statementsMatching(undefined, undefined, person, group.doc()))
50
+ webids.map(webid => {
51
+ if (kb.statementsMatching(webid, ns.owl('sameAs'), undefined, group.doc()).length < 2) {
52
+ del = del.concat(kb.statementsMatching(undefined, undefined, webid, group.doc()))
53
+ }
54
+ })
33
55
  kb.updater.update(del, [], function (uri, ok, err) {
34
56
  if (!ok) {
35
57
  const message = 'Error removing member from group ' + group + ': ' + err
36
58
  groupList.parentNode.appendChild(UI.widgets.errorMessageBlock(dom, message, 'pink'))
37
- return
38
59
  }
39
- console.log('Removed ' + pname + ' from group ' + gname)
40
- syncGroupList()
41
60
  })
61
+ console.log('Removed ' + pname + ' from group ' + gname)
62
+ // to allow refresh of card groupList
63
+ kb.fetcher.unload(group.doc())
64
+ await kb.fetcher.load(group.doc())
65
+ syncGroupList()
42
66
  }
43
67
  }
68
+
44
69
  function newRowForGroup (group) {
45
70
  const options = {
46
71
  deleteFunction: function () {
@@ -52,13 +77,31 @@ export async function renderGroupMemberships (person, context) {
52
77
  return tr
53
78
  }
54
79
 
80
+ // find all groups where person has membership
55
81
  function syncGroupList () {
56
- const groups = kb.each(null, ns.vcard('hasMember'), person)
57
- utils.syncTableToArray(groupList, groups, newRowForGroup)
82
+ // person and/or WebIDs to be changed
83
+ utils.syncTableToArray(groupList, groupMembership(person), newRowForGroup)
84
+ }
85
+
86
+ async function loadGroupsFromBook (book = null) {
87
+ if (!book) {
88
+ book = kb.any(undefined, ns.vcard('includesGroup'))
89
+ if (!book) {
90
+ throw new Error('findBookFromGroups: Cant find address book which this group is part of')
91
+ }
92
+ }
93
+ const groupIndex = kb.any(book, ns.vcard('groupIndex'))
94
+ const gs = book ? kb.each(book, ns.vcard('includesGroup'), null, groupIndex) : []
95
+ await kb.fetcher.load(gs)
58
96
  }
59
97
 
60
98
  const { dom } = context
99
+ const kb = context.session.store
61
100
  const groupList = dom.createElement('table')
101
+
102
+ // find book any group and load all groups
103
+ await loadGroupsFromBook()
104
+
62
105
  groupList.refresh = syncGroupList
63
106
  syncGroupList()
64
107
  return groupList
package/individual.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as UI from 'solid-ui'
2
+ import { authn, store } from 'solid-logic'
2
3
  import { renderMugshotGallery } from './mugshotGallery'
3
4
  import { renderWebIdControl, renderPublicIdControl } from './webidControl'
4
5
  import { renderGroupMemberships } from './groupMembershipControl.js'
@@ -7,7 +8,7 @@ import VCARD_ONTOLOGY_TEXT from './lib/vcard.js'
7
8
 
8
9
  const $rdf = UI.rdf
9
10
  const ns = UI.ns
10
- const kb = UI.store
11
+ const kb = store
11
12
  const style = UI.style
12
13
 
13
14
  export function loadTurtleText (kb, thing, text) {
@@ -68,7 +69,7 @@ export async function renderIndividual (dom, div, subject, dataBrowserContext) {
68
69
 
69
70
  div.style = style.paneDivStyle || 'padding: 0.5em 1.5em 1em 1.5em;'
70
71
 
71
- UI.authn.checkUser() // kick off async operation @@@ use async version
72
+ authn.checkUser() // kick off async operation @@@ use async version
72
73
 
73
74
  div.appendChild(renderMugshotGallery(dom, subject))
74
75
 
package/individualForm.js CHANGED
File without changes
package/jest.config.js ADDED
@@ -0,0 +1,11 @@
1
+ module.exports = {
2
+ "verbose": true, // Turn on console.log
3
+
4
+ testEnvironment: 'jsdom',
5
+ setupFilesAfterEnv: ["./jest.setup.ts"],
6
+ transformIgnorePatterns: ["/node_modules/(?!lit-html).+\\.js"],
7
+
8
+ testEnvironmentOptions: {
9
+ customExportConditions: ['node']
10
+ }
11
+ };
package/jest.setup.ts ADDED
@@ -0,0 +1,10 @@
1
+ import "@testing-library/jest-dom";
2
+ import fetchMock from "jest-fetch-mock";
3
+
4
+ fetchMock.enableMocks();
5
+
6
+ // Polyfill for encoding which isn't present globally in jsdom
7
+ import { TextEncoder, TextDecoder } from 'util';
8
+
9
+ global.TextEncoder = TextEncoder;
10
+ global.TextDecoder = TextDecoder;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ // The Control with decorations
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.renderAutocompleteControl = void 0;
14
+ const solid_logic_1 = require("solid-logic");
15
+ const solid_ui_1 = require("solid-ui");
16
+ const autocompletePicker_1 = require("./autocompletePicker"); // dbpediaParameters
17
+ const publicData_1 = require("./publicData");
18
+ const WEBID_NOUN = 'Solid ID';
19
+ const kb = solid_logic_1.store;
20
+ const AUTOCOMPLETE_THRESHOLD = 4; // don't check until this many characters typed
21
+ const AUTOCOMPLETE_ROWS = 12; // 20?
22
+ const GREEN_PLUS = solid_ui_1.icons.iconBase + 'noun_34653_green.svg';
23
+ const SEARCH_ICON = solid_ui_1.icons.iconBase + 'noun_Search_875351.svg';
24
+ function renderAutocompleteControl(dom, person, options, addOneIdAndRefresh) {
25
+ return __awaiter(this, void 0, void 0, function* () {
26
+ function autoCompleteDone(object, _name) {
27
+ return __awaiter(this, void 0, void 0, function* () {
28
+ const webid = object.uri;
29
+ removeDecorated();
30
+ return addOneIdAndRefresh(person, webid);
31
+ });
32
+ }
33
+ function greenButtonHandler(_event) {
34
+ return __awaiter(this, void 0, void 0, function* () {
35
+ const webid = yield solid_ui_1.widgets.askName(dom, solid_logic_1.store, creationArea, solid_ui_1.ns.vcard('url'), null, WEBID_NOUN);
36
+ if (!webid) {
37
+ return; // cancelled by user
38
+ }
39
+ return addOneIdAndRefresh(person, webid);
40
+ });
41
+ }
42
+ function removeDecorated() {
43
+ creationArea.removeChild(decoratedAutocomplete);
44
+ decoratedAutocomplete = null;
45
+ }
46
+ function searchButtonHandler(_event) {
47
+ return __awaiter(this, void 0, void 0, function* () {
48
+ if (decoratedAutocomplete) {
49
+ creationArea.removeChild(decoratedAutocomplete);
50
+ decoratedAutocomplete = null;
51
+ }
52
+ else {
53
+ decoratedAutocomplete = dom.createElement('div');
54
+ decoratedAutocomplete.setAttribute('style', 'display: flex; flex-flow: wrap;');
55
+ decoratedAutocomplete.appendChild(yield (0, autocompletePicker_1.renderAutoComplete)(dom, acOptions, autoCompleteDone));
56
+ decoratedAutocomplete.appendChild(acceptButton);
57
+ decoratedAutocomplete.appendChild(cancelButton);
58
+ creationArea.appendChild(decoratedAutocomplete);
59
+ }
60
+ });
61
+ }
62
+ function droppedURIHandler(uris) {
63
+ return __awaiter(this, void 0, void 0, function* () {
64
+ for (const webid of uris) { // normally one but can be more than one
65
+ yield addOneIdAndRefresh(person, webid);
66
+ }
67
+ });
68
+ }
69
+ const queryParams = options.queryParameters || publicData_1.wikidataParameters;
70
+ const acceptButton = solid_ui_1.widgets.continueButton(dom);
71
+ const cancelButton = solid_ui_1.widgets.cancelButton(dom, removeDecorated);
72
+ const klass = options.class;
73
+ const acOptions = {
74
+ queryParams,
75
+ class: klass,
76
+ acceptButton,
77
+ cancelButton
78
+ };
79
+ var decoratedAutocomplete = null;
80
+ // const { dom } = dataBrowserContext
81
+ options = options || {};
82
+ options.editable = kb.updater.editable(person.doc().uri, kb);
83
+ const creationArea = dom.createElement('div');
84
+ creationArea.setAttribute('style', 'display: flex; flex-flow: wrap;');
85
+ if (options.editable) {
86
+ // creationArea.appendChild(await renderAutoComplete(dom, options, autoCompleteDone)) wait for searchButton
87
+ creationArea.style.width = '100%';
88
+ const plus = creationArea.appendChild(solid_ui_1.widgets.button(dom, GREEN_PLUS, options.idNoun, greenButtonHandler));
89
+ solid_ui_1.widgets.makeDropTarget(plus, droppedURIHandler, null);
90
+ if (options.dbLookup) {
91
+ creationArea.appendChild(solid_ui_1.widgets.button(dom, SEARCH_ICON, options.idNoun, searchButtonHandler));
92
+ }
93
+ }
94
+ return creationArea;
95
+ });
96
+ } // renderAutocompleteControl
97
+ exports.renderAutocompleteControl = renderAutocompleteControl;
98
+ // ends
99
+ //# sourceMappingURL=autocompleteBar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autocompleteBar.js","sourceRoot":"","sources":["../src/autocompleteBar.ts"],"names":[],"mappings":"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"}