contacts-pane 2.4.9 → 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 +80 -12
  15. package/contactsPane.js +55 -16
  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 +33 -8
  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 +24 -12
  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 +28 -10
package/contactLogic.js CHANGED
@@ -1,12 +1,13 @@
1
1
  // Logic for solid contacts
2
2
 
3
3
  import * as UI from 'solid-ui'
4
+ import { store } from 'solid-logic'
4
5
  import { getPersonas } from './webidControl'
5
6
 
6
7
  const ns = UI.ns
7
8
  const $rdf = UI.rdf
8
9
  const utils = UI.utils
9
- const kb = UI.store
10
+ const kb = store
10
11
  const updater = kb.updater
11
12
 
12
13
  /** Perform updates on more than one document @@ Move to rdflib!
@@ -29,6 +30,7 @@ export async function updateMany (deletions, insertions = []) {
29
30
  * @returns {NamedNode} the person
30
31
  */
31
32
  export async function saveNewContact (book, name, selectedGroups, klass) {
33
+ await kb.fetcher.load(book.doc())
32
34
  const nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex'))
33
35
 
34
36
  const uuid = utils.genUuid()
@@ -60,9 +62,9 @@ export async function saveNewContact (book, name, selectedGroups, klass) {
60
62
  }
61
63
 
62
64
  try {
63
- await updateMany([], agenda) // @@ in future, updater.updateMany
65
+ await updater.updateMany([], agenda) // @@ in future, updater.updateMany
64
66
  } catch (e) {
65
- console.log("Error: can't update " + person + ' as new contact:' + e)
67
+ console.error("Error: can't update " + person + ' as new contact:' + e)
66
68
  throw new Error('Updating new contact: ' + e)
67
69
  }
68
70
  return person
@@ -78,12 +80,13 @@ export function sanitizeToAlpha (name) { // https://mathiasbynens.be/notes/es6-u
78
80
  * @returns group
79
81
  */
80
82
  export async function saveNewGroup (book, name) {
83
+ await kb.fetcher.load(book.doc())
81
84
  const gix = kb.any(book, ns.vcard('groupIndex'))
82
85
 
83
86
  const gname = sanitizeToAlpha(name)
84
87
  const group = kb.sym(book.dir().uri + 'Group/' + gname + '.ttl#this')
85
88
  const doc = group.doc()
86
- console.log(' New group will be: ' + group + '\n')
89
+ // console.log(' New group will be: ' + group + '\n')
87
90
  try {
88
91
  await kb.fetcher.load(gix)
89
92
  } catch (err) {
@@ -126,7 +129,7 @@ export async function addPersonToGroup (thing, group) {
126
129
 
127
130
  const types = kb.findTypeURIs(thing)
128
131
  for (const ty in types) {
129
- 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?
130
133
  }
131
134
  if (!(ns.vcard('Individual').uri in types ||
132
135
  ns.vcard('Organization').uri in types)) {
@@ -135,7 +138,7 @@ export async function addPersonToGroup (thing, group) {
135
138
  const pname = kb.any(thing, ns.vcard('fn'))
136
139
  const gname = kb.any(group, ns.vcard('fn'))
137
140
  if (!pname) { return alert('No vcard name known for ' + thing) }
138
- const already = kb.holds(group, ns.vcard('hasMember'), thing, group.doc())
141
+ const already = kb.holds(thing, ns.vcard('fn'), null, group.doc())
139
142
  if (already) {
140
143
  return alert(
141
144
  'ALREADY added ' + pname + ' to group ' + gname
@@ -144,21 +147,86 @@ export async function addPersonToGroup (thing, group) {
144
147
  const message = 'Add ' + pname + ' to group ' + gname + '?'
145
148
  if (!confirm(message)) return
146
149
  const ins = [
147
- $rdf.st(group, ns.vcard('hasMember'), thing, group.doc()),
148
150
  $rdf.st(thing, ns.vcard('fn'), pname, group.doc())
149
151
  ]
150
- // find person webIDs
152
+ // find person webIDs and insert in vcard:hasMember
151
153
  const webIDs = getPersonas(kb, thing).map(webid => webid.value)
152
- webIDs.forEach(webid => {
153
- ins.push($rdf.st(thing, ns.owl('sameAs'), kb.sym(webid), group.doc()))
154
- })
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
+ }
155
162
  try {
156
163
  await updater.update([], ins)
157
164
  // to allow refresh of card groupList
158
165
  kb.fetcher.unload(group.doc())
159
- kb.fetcher.load(group.doc())
166
+ await kb.fetcher.load(group.doc())
160
167
  } catch (e) {
161
168
  throw new Error(`Error adding ${pname} to group ${gname}:` + e)
162
169
  }
163
170
  return thing
164
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,
@@ -216,8 +219,25 @@ export default {
216
219
  const nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex'))
217
220
  await kb.fetcher.load(nameEmailIndex)
218
221
 
222
+ // - delete person's WebID's in each Group
219
223
  // - delete the references to it in group files and save them back
220
- // - 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)
221
241
  await deleteThingAndDoc(person)
222
242
  await deleteRecursive(kb, container)
223
243
  refreshNames() // "Doesn't work" -- maybe does now with waiting for async
@@ -293,8 +313,10 @@ export default {
293
313
  }
294
314
 
295
315
  async function loadAllGroups () {
316
+ await kb.fetcher.load(groupIndex)
296
317
  const gs = book ? kb.each(book, ns.vcard('includesGroup'), null, groupIndex) : []
297
318
  await kb.fetcher.load(gs)
319
+ return gs
298
320
  }
299
321
 
300
322
  function groupsInOrder () {
@@ -416,8 +438,7 @@ export default {
416
438
  const groups = Object.keys(selectedGroups).map(groupURI => kb.sym(groupURI))
417
439
  groups.forEach(group => {
418
440
  if (selectedGroups[group.value]) {
419
- const a = kb.each(group, ns.vcard('hasMember'), null, group.doc())
420
- cards = cards.concat(a)
441
+ cards = cards.concat(groupMembers(kb, group))
421
442
  }
422
443
  })
423
444
  cards.sort(compareForSort) // @@ sort by name not UID later
@@ -577,14 +598,28 @@ export default {
577
598
  const groups = groupsInOrder()
578
599
  utils.syncTableToArrayReOrdered(groupsMainTable, groups, renderGroupRow)
579
600
  refreshGroupsSelected()
601
+ // await checkDataModel(groups)
580
602
  } // syncGroupTable
581
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
+
582
617
  // Click on New Group button
583
618
  async function newGroupClickHandler (_event) {
584
619
  cardMain.innerHTML = ''
585
620
  const groupIndex = kb.any(book, ns.vcard('groupIndex'))
586
621
  try {
587
- await fetch.load(groupIndex)
622
+ await kb.fetcher.load(groupIndex)
588
623
  } catch (e) {
589
624
  console.log('Error: Group index NOT loaded:' + e + '\n')
590
625
  }
@@ -628,7 +663,7 @@ export default {
628
663
  const nameEmailIndex = kb.any(ourBook, ns.vcard('nameEmailIndex'))
629
664
  if (!nameEmailIndex) throw new Error('Wot no nameEmailIndex?')
630
665
  await kb.fetcher.load(nameEmailIndex)
631
- console.log('Name index loaded async' + nameEmailIndex)
666
+ // console.log('Name index loaded async' + nameEmailIndex)
632
667
 
633
668
  const name = await UI.widgets
634
669
  .askName(dom, kb, cardMain, UI.ns.foaf('name'), klass) // @@ was, 'person'
@@ -777,7 +812,7 @@ export default {
777
812
  const container = dom.createElement('div')
778
813
  newContactButton.setAttribute('type', 'button')
779
814
  if (!me) newContactButton.setAttribute('disabled', 'true')
780
- UI.authn.checkUser().then(webId => {
815
+ authn.checkUser().then(webId => {
781
816
  if (webId) {
782
817
  me = webId
783
818
  newContactButton.removeAttribute('disabled')
@@ -793,7 +828,7 @@ export default {
793
828
  const container2 = dom.createElement('div')
794
829
  newOrganizationButton.setAttribute('type', 'button')
795
830
  if (!me) newOrganizationButton.setAttribute('disabled', 'true')
796
- UI.authn.checkUser().then(webId => {
831
+ authn.checkUser().then(webId => {
797
832
  if (webId) {
798
833
  me = webId
799
834
  newOrganizationButton.removeAttribute('disabled')
@@ -840,6 +875,10 @@ export default {
840
875
  // })
841
876
 
842
877
  div.appendChild(dom.createElement('hr'))
878
+
879
+ // const groups = await loadAllGroups() @@@
880
+ checkDataModel().then(()=> {console.log('async checkDataModel done.')})
881
+
843
882
  // div.appendChild(newAddressBookButton(book)) // later
844
883
  // end of AddressBook instance
845
884
  } // renderThreeColumnBrowser
@@ -860,7 +899,7 @@ export default {
860
899
  // Render a Group instance
861
900
  } else if (t[ns.vcard('Group').uri]) {
862
901
  // If we have a main address book, then render this group as a guest group within it
863
- UI.authn
902
+ UI.login
864
903
  .findAppInstances(context, ns.vcard('AddressBook'))
865
904
  .then(function (context) {
866
905
  const addressBooks = context.instances
@@ -888,12 +927,12 @@ export default {
888
927
  )
889
928
  }
890
929
 
891
- me = UI.authn.currentUser()
930
+ me = authn.currentUser()
892
931
  if (!me) {
893
932
  console.log(
894
933
  '(You do not have your Web Id set. Sign in or sign up to make changes.)'
895
934
  )
896
- UI.authn.logInLoadProfile(context).then(
935
+ UI.login.ensureLoadedProfile(context).then(
897
936
  context => {
898
937
  console.log('Logged in as ' + context.me)
899
938
  me = context.me
package/diff.txt CHANGED
File without changes
File without changes
File without changes
File without changes
@@ -1,22 +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
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
+ }
22
+
14
23
  export async function renderGroupMemberships (person, context) {
15
24
  // Remove a person from a group
16
25
  async function removeFromGroup (thing, group) {
17
26
  const pname = kb.any(thing, ns.vcard('fn'))
18
27
  const gname = kb.any(group, ns.vcard('fn'))
19
- 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
20
39
  if (groups.length < 2) {
21
40
  alert(
22
41
  'Must be a member of at least one group. Add to another group first.'
@@ -25,9 +44,14 @@ export async function renderGroupMemberships (person, context) {
25
44
  }
26
45
  const message = 'Remove ' + pname + ' from group ' + gname + '?'
27
46
  if (confirm(message)) {
28
- const del = kb
47
+ let del = kb
29
48
  .statementsMatching(person, undefined, undefined, group.doc())
30
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
+ })
31
55
  kb.updater.update(del, [], function (uri, ok, err) {
32
56
  if (!ok) {
33
57
  const message = 'Error removing member from group ' + group + ': ' + err
@@ -37,7 +61,7 @@ export async function renderGroupMemberships (person, context) {
37
61
  console.log('Removed ' + pname + ' from group ' + gname)
38
62
  // to allow refresh of card groupList
39
63
  kb.fetcher.unload(group.doc())
40
- kb.fetcher.load(group.doc())
64
+ await kb.fetcher.load(group.doc())
41
65
  syncGroupList()
42
66
  }
43
67
  }
@@ -53,13 +77,13 @@ export async function renderGroupMemberships (person, context) {
53
77
  return tr
54
78
  }
55
79
 
80
+ // find all groups where person has membership
56
81
  function syncGroupList () {
57
- const groups = kb.each(null, ns.vcard('hasMember'), person)
58
-
59
- utils.syncTableToArray(groupList, groups, newRowForGroup)
82
+ // person and/or WebIDs to be changed
83
+ utils.syncTableToArray(groupList, groupMembership(person), newRowForGroup)
60
84
  }
61
85
 
62
- async function loadGroupsFromBook (book = undefined) {
86
+ async function loadGroupsFromBook (book = null) {
63
87
  if (!book) {
64
88
  book = kb.any(undefined, ns.vcard('includesGroup'))
65
89
  if (!book) {
@@ -72,6 +96,7 @@ export async function renderGroupMemberships (person, context) {
72
96
  }
73
97
 
74
98
  const { dom } = context
99
+ const kb = context.session.store
75
100
  const groupList = dom.createElement('table')
76
101
 
77
102
  // find book any group and load all groups
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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"}