solid-panes 4.2.5 → 4.4.0

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 (106) hide show
  1. package/README.md +42 -0
  2. package/dist/0314353e28ce6e5539af.svg +9 -0
  3. package/dist/04567ff683933c35c465.png +0 -0
  4. package/dist/10163fd9b5a0e00d63a0.png +0 -0
  5. package/dist/1234dcb2eec2e45f252b.png +0 -0
  6. package/dist/20899934157df4db56cb.png +0 -0
  7. package/dist/33760bf79f097f449da5.png +0 -0
  8. package/dist/4cceba29ab33b1ddd9bb.svg +6 -0
  9. package/dist/578d2b6ed32e7624164e.png +0 -0
  10. package/dist/5f62a5b2b7e99b9640c7.png +0 -0
  11. package/dist/6525766ecd288ec60129.png +0 -0
  12. package/dist/7800be6f6c4b5b0f4f20.png +0 -0
  13. package/dist/7b7538c6f6b317968009.svg +9 -0
  14. package/dist/92d03142abe6efc0b42d.svg +6 -0
  15. package/dist/976473cf5fe24d657d4b.png +0 -0
  16. package/dist/RDFXMLPane.js +1 -0
  17. package/dist/bda84f59e7216675a208.png +0 -0
  18. package/dist/cd68e8f3990ba8b2139e.png +0 -0
  19. package/dist/dashboard/basicPreferences.d.ts.map +1 -1
  20. package/dist/dashboard/basicPreferences.js +1 -0
  21. package/dist/dashboard/dashboardPane.d.ts.map +1 -1
  22. package/dist/dashboard/dashboardPane.js +9 -3
  23. package/dist/dashboard/homepage.d.ts +1 -1
  24. package/dist/dashboard/homepage.d.ts.map +1 -1
  25. package/dist/dashboard/homepage.js +5 -35
  26. package/dist/e7074a7e2cb69e51cfd3.png +0 -0
  27. package/dist/f3772696fb7ee53c23d8.png +0 -0
  28. package/dist/form/pane.js +1 -1
  29. package/dist/home/homePane.d.ts.map +1 -1
  30. package/dist/home/homePane.js +2 -0
  31. package/dist/humanReadablePane.js +34 -8
  32. package/dist/icons/clock.svg +7 -0
  33. package/dist/icons/comment.svg +6 -0
  34. package/dist/icons/dashboard.svg +9 -0
  35. package/dist/icons/downArrow.svg +6 -0
  36. package/dist/icons/folder.svg +6 -0
  37. package/dist/icons/friends.svg +9 -0
  38. package/dist/icons/help.svg +8 -0
  39. package/dist/icons/iconHelper.d.ts +2 -0
  40. package/dist/icons/iconHelper.d.ts.map +1 -0
  41. package/dist/icons/iconHelper.js +23 -0
  42. package/dist/icons/menu.svg +8 -0
  43. package/dist/icons/person.svg +6 -0
  44. package/dist/icons/personInCircle.svg +8 -0
  45. package/dist/icons/sharing.svg +10 -0
  46. package/dist/icons/signOut.svg +8 -0
  47. package/dist/icons/signup.png +0 -0
  48. package/dist/icons/star.svg +3 -0
  49. package/dist/index.d.ts +5 -4
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +19 -4
  52. package/dist/internal/internalPane.d.ts.map +1 -1
  53. package/dist/internal/internalPane.js +1 -0
  54. package/dist/mainPage/footer.d.ts +14 -2
  55. package/dist/mainPage/footer.d.ts.map +1 -1
  56. package/dist/mainPage/footer.js +21 -13
  57. package/dist/mainPage/header.d.ts +16 -1
  58. package/dist/mainPage/header.d.ts.map +1 -1
  59. package/dist/mainPage/header.js +179 -61
  60. package/dist/mainPage/index.d.ts +15 -1
  61. package/dist/mainPage/index.d.ts.map +1 -1
  62. package/dist/mainPage/index.js +38 -7
  63. package/dist/mainPage/menu.css +243 -0
  64. package/dist/mainPage/menu.d.ts +7 -0
  65. package/dist/mainPage/menu.d.ts.map +1 -0
  66. package/dist/mainPage/menu.js +409 -0
  67. package/dist/n3Pane.js +1 -0
  68. package/dist/outline/context.d.ts +2 -2
  69. package/dist/outline/context.d.ts.map +1 -1
  70. package/dist/outline/context.js +5 -2
  71. package/dist/outline/manager.css +12 -14
  72. package/dist/outline/manager.js +152 -81
  73. package/dist/outline/userInput.js +6 -3
  74. package/dist/pad/padPane.css +36 -0
  75. package/dist/pad/padPane.d.ts +1 -0
  76. package/dist/pad/padPane.d.ts.map +1 -1
  77. package/dist/pad/padPane.js +32 -21
  78. package/dist/playlist/playlistPane.js +2 -6
  79. package/dist/profileUtils/ownerProfile.d.ts +5 -0
  80. package/dist/profileUtils/ownerProfile.d.ts.map +1 -0
  81. package/dist/profileUtils/ownerProfile.js +84 -0
  82. package/dist/registerPanes.js +4 -4
  83. package/dist/slideshow/slideshowPane.js +1 -1
  84. package/dist/social/editProfileDetails.d.ts +19 -0
  85. package/dist/social/editProfileDetails.d.ts.map +1 -0
  86. package/dist/social/editProfileDetails.js +267 -0
  87. package/dist/social/icons.d.ts +5 -0
  88. package/dist/social/icons.d.ts.map +1 -0
  89. package/dist/social/icons.js +60 -0
  90. package/dist/social/socialPane.css +804 -0
  91. package/dist/social/socialPane.d.ts +30 -0
  92. package/dist/social/socialPane.d.ts.map +1 -0
  93. package/dist/social/socialPane.js +558 -0
  94. package/dist/social/socialSections.d.ts +66 -0
  95. package/dist/social/socialSections.d.ts.map +1 -0
  96. package/dist/social/socialSections.js +317 -0
  97. package/dist/solid-panes.js +28903 -13713
  98. package/dist/solid-panes.js.map +1 -1
  99. package/dist/solid-panes.min.js +2235 -247
  100. package/dist/solid-panes.min.js.map +1 -1
  101. package/dist/style/tabbedtab.css +0 -124
  102. package/dist/tabbed/tabbedPane.d.ts.map +1 -1
  103. package/dist/tabbed/tabbedPane.js +2 -0
  104. package/dist/versionInfo.js +7 -7
  105. package/package.json +15 -10
  106. package/dist/socialPane.js +0 -430
@@ -0,0 +1,30 @@
1
+ import './socialPane.css';
2
+ import { LiveStore, NamedNode } from 'rdflib';
3
+ import { DataBrowserContext } from 'pane-registry';
4
+ export declare const socialPane: {
5
+ icon: string;
6
+ name: string;
7
+ label: (subject: any, context: any) => "Friends" | null;
8
+ global: boolean;
9
+ render: (s: any, context: any) => any;
10
+ };
11
+ export interface ProfileDetails {
12
+ url: string;
13
+ imageUrl?: string;
14
+ name?: string;
15
+ nickname?: string;
16
+ jobTitle?: string;
17
+ organization?: string;
18
+ location?: string | null;
19
+ pronouns?: string;
20
+ birthdate?: string;
21
+ }
22
+ export interface FriendDetails extends ProfileDetails {
23
+ subjectNode: NamedNode;
24
+ }
25
+ export declare function pronounsAsText(store: LiveStore, subject: NamedNode): string;
26
+ export declare function streamFriends(context: DataBrowserContext, subject: NamedNode, batchSize?: number): AsyncGenerator<FriendDetails[], void, void>;
27
+ export declare function extractFriends(context: DataBrowserContext, subject: NamedNode): Promise<FriendDetails[] | null>;
28
+ export declare function selectProfileData(context: DataBrowserContext, subject: NamedNode): ProfileDetails | null;
29
+ export type { ViewerMode } from './socialSections';
30
+ //# sourceMappingURL=socialPane.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socialPane.d.ts","sourceRoot":"","sources":["../../src/social/socialPane.ts"],"names":[],"mappings":"AAUA,OAAO,kBAAkB,CAAA;AAGzB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAa,MAAM,QAAQ,CAAA;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAYlD,eAAO,MAAM,UAAU;;;;;;CAggBtB,CAAA;AAQD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,aAAc,SAAQ,cAAc;IACnD,WAAW,EAAE,SAAS,CAAA;CACvB;AAGD,wBAAgB,cAAc,CAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,CAS5E;AAiDD,wBAAwB,aAAa,CACnC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,SAAS,EAClB,SAAS,SAAoB,GAC5B,cAAc,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CA4C7C;AAED,wBAAsB,cAAc,CAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,GAAG,OAAO,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,CAQtH;AAED,wBAAgB,iBAAiB,CAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,GAAG,cAAc,GAAG,IAAI,CAuCzG;AAED,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA"}
@@ -0,0 +1,558 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.extractFriends = extractFriends;
7
+ exports.pronounsAsText = pronounsAsText;
8
+ exports.selectProfileData = selectProfileData;
9
+ exports.socialPane = void 0;
10
+ exports.streamFriends = streamFriends;
11
+ require("./socialPane.css");
12
+ var _solidUi = require("solid-ui");
13
+ var _solidLogic = require("solid-logic");
14
+ var _icons = require("./icons");
15
+ var _socialSections = require("./socialSections");
16
+ /* Social Pane
17
+ **
18
+ ** This outline pane provides social network functions
19
+ ** Using for example the FOAF ontology.
20
+ ** Goal: A *distributed* version of facebook, advogato, etc etc
21
+ ** - Similarly easy user interface, but data storage distributed
22
+ ** - Read and write both user-private (address book) and public data clearly
23
+ ** -- todo: use common code to get username and load profile and set 'me'
24
+ */
25
+
26
+ const socialPane = exports.socialPane = {
27
+ icon: _solidUi.icons.originalIconBase + 'foaf/foafTiny.gif',
28
+ name: 'social',
29
+ label: function (subject, context) {
30
+ const kb = context.session.store;
31
+ const types = kb.findTypeURIs(subject);
32
+ if (types[_solidUi.ns.foaf('Person').uri] || types[_solidUi.ns.vcard('Individual').uri]) {
33
+ return 'Friends';
34
+ }
35
+ return null;
36
+ },
37
+ global: false,
38
+ render: function (s, context) {
39
+ const dom = context.dom;
40
+ const common = function (x, y) {
41
+ // Find common members of two lists
42
+ const both = [];
43
+ for (let i = 0; i < x.length; i++) {
44
+ for (let j = 0; j < y.length; j++) {
45
+ if (y[j].sameTerm(x[i])) {
46
+ both.push(y[j]);
47
+ break;
48
+ }
49
+ }
50
+ }
51
+ return both;
52
+ };
53
+ const uniqueNodes = function (nodes) {
54
+ const seen = new Set();
55
+ const unique = [];
56
+ for (const node of nodes) {
57
+ if (!node?.value || seen.has(node.value)) continue;
58
+ seen.add(node.value);
59
+ unique.push(node);
60
+ }
61
+ return unique;
62
+ };
63
+ const link = function (contents, uri) {
64
+ if (!uri) return contents;
65
+ const a = dom.createElement('a');
66
+ a.setAttribute('href', uri);
67
+ a.appendChild(contents);
68
+ return a;
69
+ };
70
+ const text = function (str) {
71
+ return dom.createTextNode(str);
72
+ };
73
+ const buildCheckboxForm = function (lab, statement, state, options = {}) {
74
+ const f = dom.createElement('form');
75
+ const label = dom.createElement('label');
76
+ const input = dom.createElement('input');
77
+ const tx = dom.createElement('span');
78
+ tx.className = 'question';
79
+ if (typeof lab === 'string') {
80
+ tx.textContent = lab;
81
+ } else {
82
+ tx.appendChild(lab);
83
+ }
84
+ input.setAttribute('type', 'checkbox');
85
+ if (options.disabled) {
86
+ input.disabled = true;
87
+ if (options.disabledTitle) {
88
+ input.title = options.disabledTitle;
89
+ input.setAttribute('aria-label', options.disabledTitle);
90
+ }
91
+ }
92
+ label.appendChild(tx);
93
+ label.appendChild(input);
94
+ f.appendChild(label);
95
+ const boxHandler = function (_e) {
96
+ if (this.checked) {
97
+ try {
98
+ outliner.UserInput.sparqler.insert_statement(statement, function (uri, success, errorBody) {
99
+ tx.className = 'question';
100
+ if (!success) {
101
+ _solidUi.log.alert('Error occurs while inserting ' + statement + '\n\n' + errorBody);
102
+ input.checked = false; // rollback UI
103
+ return;
104
+ }
105
+ kb.add(statement.subject, statement.predicate, statement.object, statement.why);
106
+ });
107
+ } catch (e) {
108
+ _solidUi.log.error('Data write fails:' + e);
109
+ _solidUi.log.alert('Data write fails:' + e);
110
+ input.checked = false; // rollback UI
111
+ tx.className = 'question';
112
+ }
113
+ } else {
114
+ try {
115
+ outliner.UserInput.sparqler.delete_statement(statement, function (uri, success, errorBody) {
116
+ tx.className = 'question';
117
+ if (!success) {
118
+ _solidUi.log.alert('Error occurs while deleting ' + statement + '\n\n' + errorBody);
119
+ input.checked = true; // Rollback UI
120
+ } else {
121
+ kb.removeMany(statement.subject, statement.predicate, statement.object, statement.why);
122
+ }
123
+ });
124
+ } catch (e) {
125
+ _solidUi.log.alert('Delete fails:' + e);
126
+ input.checked = true; // Rollback UI
127
+ // return
128
+ }
129
+ }
130
+ };
131
+ input.checked = state;
132
+ input.addEventListener('click', boxHandler, false);
133
+ return f;
134
+ };
135
+
136
+ // ////////// Body of render():
137
+
138
+ const outliner = context.getOutliner(dom);
139
+ const kb = context.session.store;
140
+ const socialPane = dom.createElement('div');
141
+ socialPane.classList.add('social-pane', 'flex-column', 'gap-xxs', 'p-lg');
142
+ const foaf = _solidUi.ns.foaf;
143
+ const vcard = _solidUi.ns.vcard;
144
+ const me = _solidLogic.authn.currentUser();
145
+ const meUri = me ? me.uri : null;
146
+ const thisIsYou = me && kb.sameThings(me, s);
147
+ const knows = foaf('knows');
148
+ // var givenName = kb.sym('http://www.w3.org/2000/10/swap/pim/contact#givenName')
149
+ const familiar = kb.anyValue(s, foaf('givenname')) || kb.anyValue(s, foaf('firstName')) || kb.anyValue(s, foaf('nick')) || kb.anyValue(s, foaf('name')) || kb.anyValue(s, vcard('fn'));
150
+ const friends = kb.each(s, knows);
151
+ const uniqueFriends = uniqueNodes(friends);
152
+ const myFriends = me ? uniqueNodes(kb.each(me, foaf('knows'))) : [];
153
+ const mutualConnections = me && !thisIsYou ? uniqueNodes(common(uniqueFriends, myFriends)).filter(friend => !kb.sameThings(friend, me)) : [];
154
+ const mutualFriendCount = me && !thisIsYou ? mutualConnections.length : null;
155
+ const viewerMode = getViewerMode(s, me);
156
+
157
+ // Do I have a public profile document?
158
+ let profile = null; // This could be SPARQL { ?me foaf:primaryTopic [ a foaf:PersonalProfileDocument ] }
159
+ let editable = false;
160
+ let incoming = false;
161
+ let outgoing = false;
162
+ const structure = socialPane.appendChild(dom.createElement('div'));
163
+ structure.className = 'social-layout';
164
+ const primary = structure.appendChild(dom.createElement('div'));
165
+ primary.className = 'social-primary';
166
+ const tabs = primary.appendChild(dom.createElement('div'));
167
+ tabs.classList.add('social-primary__tabs', 'flex-center');
168
+ tabs.setAttribute('role', 'tablist');
169
+ tabs.setAttribute('aria-label', 'Social sections');
170
+ const allFriendsTab = tabs.appendChild(dom.createElement('button'));
171
+ allFriendsTab.className = 'social-primary__tab';
172
+ allFriendsTab.type = 'button';
173
+ allFriendsTab.id = 'social-tab-all-friends';
174
+ allFriendsTab.textContent = 'All Friends';
175
+ allFriendsTab.setAttribute('role', 'tab');
176
+ allFriendsTab.setAttribute('aria-controls', 'social-panel-all-friends');
177
+ allFriendsTab.setAttribute('aria-selected', 'true');
178
+ allFriendsTab.tabIndex = 0;
179
+ const mutualTab = tabs.appendChild(dom.createElement('button'));
180
+ mutualTab.className = 'social-primary__tab';
181
+ mutualTab.type = 'button';
182
+ mutualTab.id = 'social-tab-mutual';
183
+ mutualTab.textContent = 'Mutual';
184
+ mutualTab.setAttribute('role', 'tab');
185
+ mutualTab.setAttribute('aria-controls', 'social-panel-mutual');
186
+ mutualTab.setAttribute('aria-selected', 'false');
187
+ mutualTab.tabIndex = -1;
188
+ if (me) {
189
+ // The definition of FOAF personal profile document is ..
190
+ const works = kb.each(undefined, foaf('primaryTopic'), me); // having me as primary topic
191
+ let message = '';
192
+ for (let i = 0; i < works.length; i++) {
193
+ if (kb.whether(works[i], _solidUi.ns.rdf('type'), foaf('PersonalProfileDocument'))) {
194
+ const doc = works[i];
195
+ editable = outliner.UserInput.sparqler.editable(doc.uri, kb);
196
+ if (!editable) {
197
+ message += 'Your profile <' + _solidUi.utils.escapeForXML(doc.uri) + '> is not remotely editable.';
198
+ } else {
199
+ profile = doc;
200
+ break;
201
+ }
202
+ }
203
+ }
204
+
205
+ /*
206
+ if (!profile) {
207
+ say(
208
+ message + '\nI couldn\'t find your editable personal profile document.'
209
+ )
210
+ } else {
211
+ say('Editing your profile ' + profile + '.')
212
+ editable = outliner.UserInput.sparqler.editable(profile.uri, kb)
213
+ }
214
+ */
215
+
216
+ if (thisIsYou) {
217
+ // This is about me
218
+ // pass... @@
219
+ } else {
220
+ // This is about someone else
221
+ // My relationship with this person
222
+
223
+ const cme = kb.canon(me);
224
+ incoming = kb.whether(s, knows, cme);
225
+ outgoing = false;
226
+ const outgoingSt = kb.statementsMatching(cme, knows, s);
227
+ if (outgoingSt.length) {
228
+ outgoing = true;
229
+ if (!profile) profile = outgoingSt[0].why;
230
+ }
231
+ } // About someone else
232
+ } // me is defined
233
+ // End of you and s
234
+
235
+ let headerControls = {
236
+ canEdit: viewerMode === 'owner',
237
+ viewerMode
238
+ };
239
+ const header = (0, _socialSections.createHeaderSection)(context, s, headerControls, {
240
+ friendCount: uniqueFriends.length,
241
+ mutualFriendCount,
242
+ onSelectFriends: function () {
243
+ setActivePanel('all-friends');
244
+ },
245
+ onSelectMutual: typeof mutualFriendCount === 'number' ? function () {
246
+ setActivePanel('mutual');
247
+ } : undefined
248
+ }, function () {
249
+ return selectProfileData(context, s);
250
+ });
251
+ header.classList.add('social-pane__header-section', 'flex-column');
252
+ socialPane.prepend(header);
253
+
254
+ // div.appendChild(dom.createTextNode(plural(friends.length, 'acquaintance') +'. '))
255
+
256
+ // ///////////////////////////////////////////// Main block
257
+ //
258
+ // Should: Find the intersection and difference sets
259
+
260
+ const friendDetailsByUri = new Map();
261
+ const hydrateFriendDetailsCache = function (friendNodes) {
262
+ const nextCache = new Map();
263
+ friendNodes.forEach(friendNode => {
264
+ if (!friendNode?.value || friendNode.value === s.value) return;
265
+ nextCache.set(friendNode.value, toFriendDetails(kb, friendNode));
266
+ });
267
+ friendDetailsByUri.clear();
268
+ nextCache.forEach((value, key) => {
269
+ friendDetailsByUri.set(key, value);
270
+ });
271
+ };
272
+ hydrateFriendDetailsCache(uniqueFriends);
273
+ const renderSupportingInfo = function (target, renderDom) {
274
+ const friend = friendDetailsByUri.get(target.value);
275
+ if (!friend) return null;
276
+ const container = renderDom.createElement('div');
277
+ const jobAndOrganization = [friend.jobTitle, friend.organization].filter(Boolean).join(' | ');
278
+ if (jobAndOrganization) {
279
+ const jobLine = container.appendChild(renderDom.createElement('div'));
280
+ jobLine.className = 'social-friend-job-org';
281
+ jobLine.textContent = jobAndOrganization;
282
+ }
283
+ if (friend.location) {
284
+ const locationLine = container.appendChild(renderDom.createElement('div'));
285
+ locationLine.className = 'social-friend-location';
286
+ locationLine.innerHTML = `${_icons.locationIcon} ${friend.location}`;
287
+ }
288
+ if (!container.childNodes.length) return null;
289
+ return container;
290
+ };
291
+ const renderNameSuffix = function (target, renderDom) {
292
+ const pronouns = friendDetailsByUri.get(target.value)?.pronouns;
293
+ if (!pronouns) return null;
294
+ const suffix = renderDom.createElement('span');
295
+ suffix.className = 'social-friend-pronouns';
296
+ suffix.textContent = `(${pronouns})`;
297
+ return suffix;
298
+ };
299
+ const sEditable = outliner.UserInput.sparqler.editable(s.uri, kb);
300
+ const mutualSection = me && !thisIsYou ? (0, _socialSections.createMutualSection)({
301
+ dom,
302
+ subject: s,
303
+ familiar,
304
+ me,
305
+ meUri,
306
+ incoming,
307
+ outgoing,
308
+ editable: !!sEditable,
309
+ profile,
310
+ knows,
311
+ mutualConnections,
312
+ link,
313
+ text,
314
+ buildCheckboxForm,
315
+ renderSupportingInfo,
316
+ renderNameSuffix
317
+ }) : {
318
+ section: dom.createElement('section'),
319
+ content: dom.createElement('div'),
320
+ refreshMutualFriends: function () {}
321
+ };
322
+ const mutualFriends = mutualSection.section;
323
+ const mutualContent = mutualSection.content;
324
+ if (me && !thisIsYou) {
325
+ mutualFriends.setAttribute('style', 'display: none');
326
+ } else {
327
+ mutualFriends.setAttribute('style', 'display: block');
328
+ }
329
+ if (!mutualFriends.className) {
330
+ mutualFriends.className = 'social-pane__mutual-friends social-primary__panel';
331
+ mutualFriends.id = 'social-panel-mutual';
332
+ mutualFriends.setAttribute('role', 'tabpanel');
333
+ mutualFriends.setAttribute('aria-labelledby', 'social-tab-mutual');
334
+ mutualContent.className = 'social-main social-main--mutual';
335
+ mutualFriends.appendChild(mutualContent);
336
+ }
337
+ primary.appendChild(mutualFriends);
338
+ const allFriendsSection = (0, _socialSections.createAllFriendsSection)({
339
+ dom,
340
+ subject: s,
341
+ profile,
342
+ editable: !!sEditable,
343
+ renderSupportingInfo,
344
+ renderNameSuffix
345
+ });
346
+ const allFriends = allFriendsSection.section;
347
+ const friendsList = allFriendsSection.friendsList;
348
+ primary.appendChild(allFriends);
349
+ const setActivePanel = function (panel) {
350
+ const showMutual = panel === 'mutual';
351
+ mutualTab.classList.toggle('social-primary__tab--active', showMutual);
352
+ mutualTab.setAttribute('aria-selected', String(showMutual));
353
+ mutualTab.tabIndex = showMutual ? 0 : -1;
354
+ allFriendsTab.classList.toggle('social-primary__tab--active', !showMutual);
355
+ allFriendsTab.setAttribute('aria-selected', String(!showMutual));
356
+ allFriendsTab.tabIndex = showMutual ? -1 : 0;
357
+ mutualFriends.classList.toggle('social-primary__panel--active', showMutual);
358
+ mutualFriends.setAttribute('aria-hidden', String(!showMutual));
359
+ allFriends.classList.toggle('social-primary__panel--active', !showMutual);
360
+ allFriends.setAttribute('aria-hidden', String(showMutual));
361
+ };
362
+ setActivePanel('all-friends');
363
+ const applyViewerMode = function (mode) {
364
+ const showMutualTab = mode === 'authenticated';
365
+ mutualTab.hidden = !showMutualTab;
366
+ setActivePanel('all-friends');
367
+ };
368
+ mutualTab.addEventListener('click', function () {
369
+ setActivePanel('mutual');
370
+ });
371
+ allFriendsTab.addEventListener('click', function () {
372
+ setActivePanel('all-friends');
373
+ });
374
+ const refreshFriendsList = function () {
375
+ const refresh = friendsList.refresh;
376
+ if (typeof refresh !== 'function') return;
377
+ refresh.call(friendsList);
378
+ };
379
+ const refreshMutualFriends = function () {
380
+ mutualSection.refreshMutualFriends();
381
+ };
382
+ (async () => {
383
+ try {
384
+ for await (const streamedFriends of streamFriends(context, s)) {
385
+ friendDetailsByUri.clear();
386
+ streamedFriends.forEach(friend => {
387
+ friendDetailsByUri.set(friend.url, friend);
388
+ });
389
+ refreshFriendsList();
390
+ refreshMutualFriends();
391
+ }
392
+ } catch {
393
+ // Keep the initial snapshot if async friend loading fails.
394
+ }
395
+ })();
396
+
397
+ /* if ($rdf.keepThisCodeForLaterButDisableFerossConstantConditionPolice) {
398
+ triageFriends(s)
399
+ } */
400
+ // //////////////////////////////////// Basic info on left
401
+
402
+ const preds2 = [_solidUi.ns.foaf('openid'), _solidUi.ns.foaf('nick')];
403
+ for (let i2 = 0; i2 < preds2.length; i2++) {
404
+ const pred = preds2[i2];
405
+ const sts2 = kb.statementsMatching(s, pred);
406
+ if (sts2.length === 0) {
407
+ // if (editable) say("No home page set. Use the blue + icon at the bottom of the main view to add information.")
408
+ } else {
409
+ outliner.appendPropertyTRs(mutualContent, sts2, false, function (_pred) {
410
+ return true;
411
+ });
412
+ }
413
+ }
414
+ applyViewerMode('anonymous');
415
+ _solidLogic.authn.checkUser().then(webId => {
416
+ const confirmedViewerMode = getViewerMode(s, webId);
417
+ applyViewerMode(confirmedViewerMode);
418
+ headerControls = {
419
+ ...headerControls,
420
+ canEdit: confirmedViewerMode === 'owner',
421
+ viewerMode: confirmedViewerMode
422
+ };
423
+ header.refreshSocialHeader?.(headerControls);
424
+ }).catch(() => {
425
+ applyViewerMode('anonymous');
426
+ headerControls = {
427
+ ...headerControls,
428
+ canEdit: false,
429
+ viewerMode: 'anonymous'
430
+ };
431
+ header.refreshSocialHeader?.(headerControls);
432
+ });
433
+ return socialPane;
434
+ } // render()
435
+ }; //
436
+ // ends
437
+ // ***************** Social Pane Selectors **********/
438
+ /* Should move to another file, but will leave for now */
439
+ /* Will create a social pane folder or maybe repo later */
440
+
441
+ const FRIEND_BATCH_SIZE = 3;
442
+ /* pronounsAsText and formatLocation were copied from HeadingSection selectors */
443
+ function pronounsAsText(store, subject) {
444
+ let pronouns = store.anyJS(subject, _solidUi.ns.solid('preferredSubjectPronoun')) || '';
445
+ if (pronouns) {
446
+ const them = store.anyJS(subject, _solidUi.ns.solid('preferredObjectPronoun'));
447
+ if (them) {
448
+ pronouns += '/' + them;
449
+ }
450
+ }
451
+ return pronouns || '';
452
+ }
453
+ function formatLocation(countryName, locality) {
454
+ return countryName && locality ? `${locality}, ${countryName}` : countryName || locality || null;
455
+ }
456
+ function toFriendDetails(store, friendNode) {
457
+ const name = store.anyValue(friendNode, _solidUi.ns.vcard('fn')) || store.anyValue(friendNode, _solidUi.ns.foaf('name')) || null;
458
+ const nickname = store.anyValue(friendNode, _solidUi.ns.vcard('nickname')) || store.anyValue(friendNode, _solidUi.ns.foaf('nick')) || null;
459
+ const dateOfBirth = store.anyValue(friendNode, _solidUi.ns.vcard('bday')) || null;
460
+ const imageSrc = _solidUi.widgets.findImage(friendNode);
461
+ const jobTitle = store.anyValue(friendNode, _solidUi.ns.vcard('role')) || null;
462
+ const orgName = store.anyValue(friendNode, _solidUi.ns.vcard('organization-name')) || null;
463
+ const primaryAddressEntryNode = store.any(friendNode, _solidUi.ns.vcard('hasAddress'));
464
+ const address = primaryAddressEntryNode || null;
465
+ const countryName = address != null ? store.anyValue(address, _solidUi.ns.vcard('country-name')) : null;
466
+ const locality = address != null ? store.anyValue(address, _solidUi.ns.vcard('locality')) : null;
467
+ const location = formatLocation(countryName, locality);
468
+ const pronouns = pronounsAsText(store, friendNode);
469
+ return {
470
+ url: friendNode.value,
471
+ imageUrl: imageSrc,
472
+ name,
473
+ nickname,
474
+ jobTitle,
475
+ organization: orgName,
476
+ birthdate: dateOfBirth,
477
+ location,
478
+ pronouns,
479
+ subjectNode: friendNode
480
+ };
481
+ }
482
+ async function* streamFriends(context, subject, batchSize = FRIEND_BATCH_SIZE) {
483
+ const store = context.session.store;
484
+ const fetcher = store?.fetcher;
485
+ if (fetcher && typeof fetcher.load === 'function') {
486
+ try {
487
+ await fetcher.load(subject.doc());
488
+ } catch {
489
+ // Continue with whatever is already in the store.
490
+ }
491
+ }
492
+ const seen = new Set();
493
+ const friendNodes = store.each(subject, _solidUi.ns.foaf('knows'), null, subject.doc());
494
+ const uniqueFriendNodes = [];
495
+ for (const friendNode of friendNodes) {
496
+ const key = friendNode?.value;
497
+ if (!key || seen.has(key) || subject.value === key) continue;
498
+ seen.add(key);
499
+ uniqueFriendNodes.push(friendNode);
500
+ }
501
+ const friends = [];
502
+ for (const friendNode of uniqueFriendNodes) {
503
+ if (fetcher && typeof fetcher.load === 'function') {
504
+ try {
505
+ await fetcher.load(friendNode.doc());
506
+ } catch {
507
+ // Keep partial friend data when one linked document fails to load.
508
+ }
509
+ }
510
+ friends.push(toFriendDetails(store, friendNode));
511
+ if (friends.length % batchSize === 0) {
512
+ yield [...friends];
513
+ }
514
+ }
515
+ if (friends.length > 0 && friends.length % batchSize !== 0) {
516
+ yield [...friends];
517
+ }
518
+ }
519
+ async function extractFriends(context, subject) {
520
+ let latestFriends = null;
521
+ for await (const friends of streamFriends(context, subject)) {
522
+ latestFriends = friends;
523
+ }
524
+ return latestFriends;
525
+ }
526
+ function selectProfileData(context, subject) {
527
+ const store = context.session.store;
528
+ const name = store.anyValue(subject, _solidUi.ns.vcard('fn')) || store.anyValue(subject, _solidUi.ns.foaf('name')) || undefined;
529
+ const nickname = store.anyValue(subject, _solidUi.ns.vcard('nickname')) || store.anyValue(subject, _solidUi.ns.foaf('nick')) || undefined;
530
+ const dateOfBirth = store.anyValue(subject, _solidUi.ns.vcard('bday')) || undefined;
531
+ const imageSrc = _solidUi.widgets.findImage(subject);
532
+ const jobTitle = store.anyValue(subject, _solidUi.ns.vcard('role')) || undefined;
533
+ const orgName = store.anyValue(subject, _solidUi.ns.vcard('organization-name')) || undefined;
534
+ const primaryAddressEntryNode = store.any(subject, _solidUi.ns.vcard('hasAddress'));
535
+ const address = primaryAddressEntryNode || null;
536
+ const countryName = address != null ? store.anyValue(address, _solidUi.ns.vcard('country-name')) : undefined;
537
+ const locality = address != null ? store.anyValue(address, _solidUi.ns.vcard('locality')) : undefined;
538
+ const location = formatLocation(countryName, locality);
539
+ const pronouns = pronounsAsText(store, subject);
540
+ return {
541
+ url: subject.value,
542
+ imageUrl: imageSrc,
543
+ name,
544
+ nickname,
545
+ jobTitle,
546
+ organization: orgName,
547
+ birthdate: dateOfBirth,
548
+ location,
549
+ pronouns
550
+ };
551
+ }
552
+ function getViewerMode(subject, currentUser = _solidLogic.authn.currentUser()) {
553
+ const currentUserUri = typeof currentUser === 'string' ? currentUser : typeof currentUser === 'object' && currentUser !== null ? currentUser.value || currentUser.uri || null : null;
554
+ let mode = 'anonymous';
555
+ if (currentUserUri === subject.value) mode = 'owner';
556
+ if (currentUserUri && currentUserUri !== subject.value) mode = 'authenticated';
557
+ return mode;
558
+ }
@@ -0,0 +1,66 @@
1
+ import { DataBrowserContext } from 'pane-registry';
2
+ import { Statement, NamedNode } from 'rdflib';
3
+ export type ViewerMode = 'owner' | 'authenticated' | 'anonymous';
4
+ export type HeaderControls = {
5
+ canEdit: boolean;
6
+ viewerMode: ViewerMode;
7
+ };
8
+ export type SocialHeaderElement = HTMLElement & {
9
+ refreshSocialHeader?: (controls: HeaderControls) => void;
10
+ };
11
+ export type HeaderStats = {
12
+ friendCount: number;
13
+ mutualFriendCount: number | null;
14
+ onSelectFriends?: () => void;
15
+ onSelectMutual?: () => void;
16
+ };
17
+ export type HeaderProfileData = {
18
+ imageUrl?: string;
19
+ name?: string;
20
+ jobTitle?: string;
21
+ organization?: string;
22
+ location?: string | null;
23
+ } | null;
24
+ export type FriendRowRenderers = {
25
+ renderSupportingInfo: (target: NamedNode, renderDom: HTMLDocument) => HTMLElement | null;
26
+ renderNameSuffix: (target: NamedNode, renderDom: HTMLDocument) => HTMLElement | null;
27
+ };
28
+ export declare function createHeaderSection(context: DataBrowserContext, subject: NamedNode, controls: HeaderControls, stats: HeaderStats, getProfileData: () => HeaderProfileData): SocialHeaderElement;
29
+ export declare function createMutualSection(options: {
30
+ dom: HTMLDocument;
31
+ subject: NamedNode;
32
+ familiar: string;
33
+ me: NamedNode;
34
+ meUri: string | null;
35
+ incoming: boolean | NamedNode[] | undefined;
36
+ outgoing: boolean | NamedNode[] | undefined;
37
+ editable: boolean;
38
+ profile: NamedNode | null;
39
+ knows: NamedNode;
40
+ mutualConnections: NamedNode[];
41
+ link: (contents: Node, uri: string | null | undefined) => Node;
42
+ text: (value: string) => Text;
43
+ buildCheckboxForm: (label: string | Node, statement: Statement, state: boolean, options?: {
44
+ disabled?: boolean;
45
+ disabledTitle?: string;
46
+ }) => HTMLElement;
47
+ renderSupportingInfo: FriendRowRenderers['renderSupportingInfo'];
48
+ renderNameSuffix: FriendRowRenderers['renderNameSuffix'];
49
+ }): {
50
+ section: HTMLElement;
51
+ content: HTMLElement;
52
+ refreshMutualFriends: () => void;
53
+ };
54
+ export declare function createAllFriendsSection(options: {
55
+ dom: HTMLDocument;
56
+ subject: NamedNode;
57
+ profile: NamedNode | null;
58
+ editable: boolean;
59
+ renderSupportingInfo: (target: NamedNode, renderDom: HTMLDocument) => HTMLElement | null;
60
+ renderNameSuffix: (target: NamedNode, renderDom: HTMLDocument) => HTMLElement | null;
61
+ }): {
62
+ section: HTMLElement;
63
+ mainTable: HTMLTableElement;
64
+ friendsList: HTMLElement;
65
+ };
66
+ //# sourceMappingURL=socialSections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socialSections.d.ts","sourceRoot":"","sources":["../../src/social/socialSections.ts"],"names":[],"mappings":"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"}