h1v3 0.3.0 → 0.4.1
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/dist/browser/event-store/modular.js +5 -6
- package/dist/browser/team/team.js +25 -0
- package/package.json +1 -1
- package/scripts/dist-client.js +2 -1
- package/src/client/index.js +3 -1
- package/src/client/modular.js +5 -6
- package/src/client/node.js +28 -0
- package/src/client/team-node.js +28 -0
- package/src/client/team.js +38 -0
- package/src/commands/generate-rules.js +14 -1
- package/src/commands/vendor.js +6 -0
- package/src/exec-eventstore.js +13 -5
- package/src/membership/index.js +3 -2
- package/src/membership/membership-store.js +57 -14
- package/src/membership/user-profile-store.js +5 -2
- package/src/membership/user-teams-store.js +43 -0
|
@@ -41,7 +41,6 @@ export function eventStore(firebase, path) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
return {
|
|
44
|
-
|
|
45
44
|
async record(eventType, payload) {
|
|
46
45
|
|
|
47
46
|
const eid = newEventId(eventType);
|
|
@@ -49,14 +48,12 @@ export function eventStore(firebase, path) {
|
|
|
49
48
|
await set(ref(db, `${eventsPath}/${eid}`), data);
|
|
50
49
|
|
|
51
50
|
},
|
|
52
|
-
|
|
53
51
|
async getProjection(name) {
|
|
54
52
|
|
|
55
53
|
const snap = await get(ref(db, `${projectionsPath}/${name}`));
|
|
56
54
|
return snap.val();
|
|
57
55
|
|
|
58
56
|
},
|
|
59
|
-
|
|
60
57
|
onProjectionValue(name, callback) {
|
|
61
58
|
|
|
62
59
|
const query = ref(db, `${projectionsPath}/${name}`);
|
|
@@ -64,19 +61,21 @@ export function eventStore(firebase, path) {
|
|
|
64
61
|
subscriptions[name] = onValue(query, callback);
|
|
65
62
|
|
|
66
63
|
},
|
|
67
|
-
|
|
68
64
|
async offProjection(name) {
|
|
69
65
|
|
|
70
66
|
await unsubscribe(name);
|
|
71
67
|
|
|
72
68
|
},
|
|
73
|
-
|
|
74
69
|
async dispose() {
|
|
75
70
|
|
|
76
71
|
await unsubscribeAll();
|
|
77
72
|
|
|
78
|
-
}
|
|
73
|
+
},
|
|
74
|
+
get path() {
|
|
79
75
|
|
|
76
|
+
return path;
|
|
77
|
+
|
|
78
|
+
}
|
|
80
79
|
};
|
|
81
80
|
|
|
82
81
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const BECAME_MEMBER = "BECAME_MEMBER";
|
|
2
|
+
|
|
3
|
+
export const ADDED_TO_TEAM = "ADDED_TO_TEAM";
|
|
4
|
+
|
|
5
|
+
export function team(teamMembershipStore, userTeamsStoreFactory) {
|
|
6
|
+
|
|
7
|
+
const teamPath = /\/teams\/([^\/]*)/g.exec(teamMembershipStore.path);
|
|
8
|
+
if (!teamPath?.[1]) throw new Error(`Invalid team path: ${teamMembershipStore.path}`);
|
|
9
|
+
const tid = teamPath[1];
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
|
|
13
|
+
async addAdmin({ uid, name, ...rest }) {
|
|
14
|
+
|
|
15
|
+
if (!uid) throw new Error("Missing uid");
|
|
16
|
+
if (!name) throw new Error("Missing name");
|
|
17
|
+
await teamMembershipStore.record(BECAME_MEMBER, { uid, name, ...rest });
|
|
18
|
+
const userTeamsStore = userTeamsStoreFactory({ uid });
|
|
19
|
+
await userTeamsStore.record(ADDED_TO_TEAM, { tid });
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
}
|
package/package.json
CHANGED
package/scripts/dist-client.js
CHANGED
package/src/client/index.js
CHANGED
package/src/client/modular.js
CHANGED
|
@@ -41,7 +41,6 @@ export function eventStore(firebase, path) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
return {
|
|
44
|
-
|
|
45
44
|
async record(eventType, payload) {
|
|
46
45
|
|
|
47
46
|
const eid = newEventId(eventType);
|
|
@@ -49,14 +48,12 @@ export function eventStore(firebase, path) {
|
|
|
49
48
|
await set(ref(db, `${eventsPath}/${eid}`), data);
|
|
50
49
|
|
|
51
50
|
},
|
|
52
|
-
|
|
53
51
|
async getProjection(name) {
|
|
54
52
|
|
|
55
53
|
const snap = await get(ref(db, `${projectionsPath}/${name}`));
|
|
56
54
|
return snap.val();
|
|
57
55
|
|
|
58
56
|
},
|
|
59
|
-
|
|
60
57
|
onProjectionValue(name, callback) {
|
|
61
58
|
|
|
62
59
|
const query = ref(db, `${projectionsPath}/${name}`);
|
|
@@ -64,19 +61,21 @@ export function eventStore(firebase, path) {
|
|
|
64
61
|
subscriptions[name] = onValue(query, callback);
|
|
65
62
|
|
|
66
63
|
},
|
|
67
|
-
|
|
68
64
|
async offProjection(name) {
|
|
69
65
|
|
|
70
66
|
await unsubscribe(name);
|
|
71
67
|
|
|
72
68
|
},
|
|
73
|
-
|
|
74
69
|
async dispose() {
|
|
75
70
|
|
|
76
71
|
await unsubscribeAll();
|
|
77
72
|
|
|
78
|
-
}
|
|
73
|
+
},
|
|
74
|
+
get path() {
|
|
79
75
|
|
|
76
|
+
return path;
|
|
77
|
+
|
|
78
|
+
}
|
|
80
79
|
};
|
|
81
80
|
|
|
82
81
|
}
|
package/src/client/node.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EVENT_STORE_META } from "../event-store/initialise.js";
|
|
1
2
|
import { eventStore as eventStoreModular } from "./modular.js";
|
|
2
3
|
|
|
3
4
|
function adapter(db) {
|
|
@@ -5,22 +6,49 @@ function adapter(db) {
|
|
|
5
6
|
return {
|
|
6
7
|
db,
|
|
7
8
|
ref(_db, path) {
|
|
9
|
+
|
|
8
10
|
return db.ref(path);
|
|
11
|
+
|
|
9
12
|
},
|
|
10
13
|
async onValue(query, callback) {
|
|
14
|
+
|
|
11
15
|
query.on("value", callback);
|
|
12
16
|
return () => query.off("value", callback);
|
|
17
|
+
|
|
13
18
|
},
|
|
14
19
|
async get(query) {
|
|
20
|
+
|
|
15
21
|
return await query.get();
|
|
22
|
+
|
|
16
23
|
},
|
|
17
24
|
async set(ref, data) {
|
|
25
|
+
|
|
18
26
|
return await ref.set(data);
|
|
27
|
+
|
|
28
|
+
},
|
|
29
|
+
get path() {
|
|
30
|
+
|
|
31
|
+
return path;
|
|
32
|
+
|
|
19
33
|
}
|
|
20
34
|
};
|
|
21
35
|
|
|
22
36
|
}
|
|
23
37
|
|
|
38
|
+
function resolvePath(pathTemplate, pathValues) {
|
|
39
|
+
|
|
40
|
+
return Object.entries(pathValues)
|
|
41
|
+
.reduce((working, [key, value]) => working.replaceAll(`$${key}`, value), pathTemplate);
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function eventStoreFromConfig(db, config, pathValues = {}) {
|
|
46
|
+
|
|
47
|
+
const meta = config[EVENT_STORE_META];
|
|
48
|
+
return eventStore(db, resolvePath(meta.ref, pathValues));
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
24
52
|
export function eventStore(db, path) {
|
|
25
53
|
|
|
26
54
|
const firebase = adapter(db);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { eventStoreFromConfig as defaultEventStoreFromConfig } from "./node.js";
|
|
2
|
+
import { team } from "./team.js";
|
|
3
|
+
|
|
4
|
+
export function teamsContext({ db, teamMembershipConfig, userTeamsConfig, eventStoreFromConfig = defaultEventStoreFromConfig }) {
|
|
5
|
+
|
|
6
|
+
if (!teamMembershipConfig) throw new Error("Missing: teamMembershipConfig");
|
|
7
|
+
if (!userTeamsConfig) throw new Error("Missing: userTeamsConfig");
|
|
8
|
+
if (!db) throw new Error("Missing: db");
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
|
|
12
|
+
team({ tid }) {
|
|
13
|
+
|
|
14
|
+
const membershipEventStore = eventStoreFromConfig(db, teamMembershipConfig, { tid });
|
|
15
|
+
return team(
|
|
16
|
+
membershipEventStore,
|
|
17
|
+
function({ uid }) {
|
|
18
|
+
|
|
19
|
+
return eventStoreFromConfig(db, userTeamsConfig, { uid });
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const BECAME_MEMBER = "BECAME_MEMBER";
|
|
2
|
+
export const BECAME_ADMIN = "BECAME_ADMIN";
|
|
3
|
+
export const ADDED_TO_TEAM = "ADDED_TO_TEAM";
|
|
4
|
+
|
|
5
|
+
export function team(teamMembershipStore, userTeamsStoreFactory) {
|
|
6
|
+
|
|
7
|
+
const teamPath = /\/teams\/([^\/]*)/g.exec(teamMembershipStore.path);
|
|
8
|
+
if (!teamPath?.[1]) throw new Error(`Invalid team path: ${teamMembershipStore.path}`);
|
|
9
|
+
const tid = teamPath[1];
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
|
|
13
|
+
async addAdmin({ uid, name, ...rest }) {
|
|
14
|
+
|
|
15
|
+
await record({ uid, name, eventType: BECAME_ADMIN, rest });
|
|
16
|
+
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
async addMember({ uid, name, ...rest }) {
|
|
20
|
+
|
|
21
|
+
await record({ uid, name, eventType: BECAME_MEMBER, rest });
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async function record({ uid, name, eventType, rest }) {
|
|
29
|
+
|
|
30
|
+
if (!uid) throw new Error("Missing uid");
|
|
31
|
+
if (!name) throw new Error("Missing name");
|
|
32
|
+
await teamMembershipStore.record(eventType, { uid, name, ...rest });
|
|
33
|
+
const userTeamsStore = userTeamsStoreFactory({ uid });
|
|
34
|
+
await userTeamsStore.record(ADDED_TO_TEAM, { tid });
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
function eventWriteConditions(config) {
|
|
2
2
|
|
|
3
|
+
if (config?.write === false)
|
|
4
|
+
return false;
|
|
3
5
|
let expr = "!data.exists()"
|
|
4
6
|
if (config?.write)
|
|
5
7
|
expr += " && (" + config.write + ")"
|
|
@@ -19,7 +21,7 @@ const eventStoreRules = config => ({
|
|
|
19
21
|
events: {
|
|
20
22
|
"$eid": {
|
|
21
23
|
".write": eventWriteConditions(config),
|
|
22
|
-
".validate":
|
|
24
|
+
".validate": eventValidation(config)
|
|
23
25
|
}
|
|
24
26
|
},
|
|
25
27
|
projections: {
|
|
@@ -62,6 +64,17 @@ const parseConfig = ([_name, config]) => [
|
|
|
62
64
|
config
|
|
63
65
|
];
|
|
64
66
|
|
|
67
|
+
function eventValidation(config) {
|
|
68
|
+
|
|
69
|
+
const requireType = "newData.child('type').isString()";
|
|
70
|
+
if (!Array.isArray(config?.eventTypes)) return requireType;
|
|
71
|
+
return [
|
|
72
|
+
requireType,
|
|
73
|
+
`newData.child('type').val().matches(/^(${config.eventTypes.join("|")})$/)`
|
|
74
|
+
].join(" && ");
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
65
78
|
export function generateRules(argv, stores) {
|
|
66
79
|
|
|
67
80
|
const json = JSON.stringify(
|
package/src/commands/vendor.js
CHANGED
package/src/exec-eventstore.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { listEventStores } from "./commands/list-event-stores.js";
|
|
4
4
|
import { generateRules } from "./commands/generate-rules.js";
|
|
5
5
|
import { main } from "./system/main.js";
|
|
6
|
-
import
|
|
6
|
+
import * as vendor from "./commands/vendor.js";
|
|
7
7
|
|
|
8
8
|
const configParameter = {
|
|
9
9
|
|
|
@@ -40,7 +40,7 @@ main({
|
|
|
40
40
|
description: "Copy event store client into your browser assets folder",
|
|
41
41
|
loadConfig: false,
|
|
42
42
|
parameters: {},
|
|
43
|
-
strategy: eventStore
|
|
43
|
+
strategy: vendor.eventStore
|
|
44
44
|
|
|
45
45
|
},
|
|
46
46
|
"vendor-web": {
|
|
@@ -48,7 +48,7 @@ main({
|
|
|
48
48
|
description: "Copy web logic platform into your browser assets folder",
|
|
49
49
|
loadConfig: false,
|
|
50
50
|
parameters: {},
|
|
51
|
-
strategy: webPlatform
|
|
51
|
+
strategy: vendor.webPlatform
|
|
52
52
|
|
|
53
53
|
},
|
|
54
54
|
"vendor-webui": {
|
|
@@ -56,7 +56,15 @@ main({
|
|
|
56
56
|
description: "Copy web UI platform into your browser assets folder (requires web)",
|
|
57
57
|
loadConfig: false,
|
|
58
58
|
parameters: {},
|
|
59
|
-
strategy: webPlatformUI
|
|
59
|
+
strategy: vendor.webPlatformUI
|
|
60
|
+
|
|
61
|
+
},
|
|
62
|
+
"vendor-membership": {
|
|
63
|
+
|
|
64
|
+
description: "Copy team management logic into your browser assets folder",
|
|
65
|
+
loadConfig: false,
|
|
66
|
+
parameters: {},
|
|
67
|
+
strategy: vendor.membership
|
|
60
68
|
|
|
61
69
|
}
|
|
62
70
|
|
package/src/membership/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export * as userProfile from "./user-profile-store.js";
|
|
2
|
+
export * as membership from "./membership-store.js";
|
|
3
|
+
export * as userTeams from "./user-teams-store.js";
|
|
@@ -8,49 +8,76 @@ export const ifMyTeam = rootPath =>
|
|
|
8
8
|
export const ifAdminInThisTeam = rootPath =>
|
|
9
9
|
ifUserIdExists(`${membershipStorePath(rootPath)}/projections/details/admins`);
|
|
10
10
|
|
|
11
|
+
export const BECAME_MEMBER = "BECAME_MEMBER";
|
|
12
|
+
export const BECAME_ADMIN = "BECAME_ADMIN";
|
|
13
|
+
export const LEFT_TEAM = "LEFT_TEAM";
|
|
14
|
+
export const INVITED_MEMBER = "INVITED_MEMBER";
|
|
15
|
+
export const INVITED_ADMIN = "INVITED_ADMIN";
|
|
16
|
+
|
|
17
|
+
const eventTypes = [
|
|
18
|
+
BECAME_MEMBER,
|
|
19
|
+
BECAME_ADMIN,
|
|
20
|
+
LEFT_TEAM,
|
|
21
|
+
INVITED_MEMBER,
|
|
22
|
+
INVITED_ADMIN
|
|
23
|
+
];
|
|
11
24
|
|
|
12
25
|
const membershipDetails = {
|
|
13
26
|
|
|
14
|
-
|
|
27
|
+
[BECAME_MEMBER]: (view, { payload }) => {
|
|
15
28
|
|
|
16
29
|
const found = purgeUser(view, payload);
|
|
17
|
-
view.members = { ...view.members, [payload.uid]:
|
|
30
|
+
view.members = { ...view.members, [payload.uid]: resolveUserView(payload, found)};
|
|
18
31
|
return view;
|
|
19
32
|
|
|
20
33
|
},
|
|
21
|
-
|
|
34
|
+
[BECAME_ADMIN]: (view, { payload }) => {
|
|
22
35
|
|
|
23
36
|
const found = purgeUser(view, payload);
|
|
24
|
-
view.admins = { ...view.admins, [payload.uid]:
|
|
37
|
+
view.admins = { ...view.admins, [payload.uid]: resolveUserView(payload, found)};
|
|
25
38
|
return view;
|
|
26
39
|
|
|
27
40
|
},
|
|
28
|
-
|
|
41
|
+
[LEFT_TEAM]: (view, { payload }) => {
|
|
29
42
|
|
|
30
43
|
purgeUser(view, payload);
|
|
31
44
|
return view;
|
|
32
45
|
|
|
46
|
+
},
|
|
47
|
+
[INVITED_MEMBER]: (view, { payload }) => {
|
|
48
|
+
|
|
49
|
+
const found = purgeInvited(view, payload);
|
|
50
|
+
view.invitedMembers = { ...view.invited, [payload.uid]: resolveUserView(payload, found)};
|
|
51
|
+
return view;
|
|
52
|
+
|
|
53
|
+
},
|
|
54
|
+
[INVITED_ADMIN]: (view, { payload }) => {
|
|
55
|
+
|
|
56
|
+
const found = purgeInvited(view, payload);
|
|
57
|
+
view.invitedAdmins = { ...view.invited, [payload.uid]: resolveUserView(payload, found)};
|
|
58
|
+
return view;
|
|
59
|
+
|
|
33
60
|
}
|
|
34
61
|
|
|
35
62
|
};
|
|
36
63
|
|
|
37
64
|
const members = {
|
|
38
65
|
|
|
39
|
-
|
|
66
|
+
[BECAME_MEMBER]: (view, { payload }) => {
|
|
40
67
|
|
|
41
68
|
if (!payload?.uid) return view;
|
|
42
69
|
view[payload.uid] = true;
|
|
43
70
|
return view;
|
|
44
71
|
|
|
45
72
|
},
|
|
46
|
-
|
|
73
|
+
[BECAME_ADMIN]: (view, { payload }) => {
|
|
47
74
|
|
|
48
75
|
if (!payload?.uid) return view;
|
|
49
76
|
view[payload.uid] = true;
|
|
50
77
|
return view;
|
|
51
78
|
|
|
52
79
|
},
|
|
53
|
-
|
|
80
|
+
[LEFT_TEAM]: (view, { payload }) => {
|
|
54
81
|
|
|
55
82
|
if (!payload?.uid) return view;
|
|
56
83
|
delete view[payload.uid];
|
|
@@ -60,15 +87,30 @@ const members = {
|
|
|
60
87
|
|
|
61
88
|
}
|
|
62
89
|
|
|
90
|
+
function resolveUserView(payload, found) {
|
|
91
|
+
|
|
92
|
+
return { name: payload.name || found?.name || "" };
|
|
93
|
+
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function purgeInvited(view, { uid }) {
|
|
97
|
+
|
|
98
|
+
const found = view.invitedMembers?.[uid] || view.invitedAdmins?.[uid];
|
|
99
|
+
if (view.invitedMembers && (uid in view.invitedMembers))
|
|
100
|
+
delete view.invitedMembers[uid];
|
|
101
|
+
if (view.invitedAdmins && (uid in view.invitedAdmins))
|
|
102
|
+
delete view.invitedAdmins[uid];
|
|
103
|
+
return found;
|
|
104
|
+
|
|
105
|
+
}
|
|
106
|
+
|
|
63
107
|
function purgeUser(view, { uid }) {
|
|
64
108
|
|
|
65
|
-
|
|
109
|
+
const found = view.members?.[uid] || view.admins?.[uid] || view.owners?.[uid];
|
|
66
110
|
if (view.members && (uid in view.members))
|
|
67
111
|
delete view.members[uid];
|
|
68
112
|
if (view.admins && (uid in view.admins))
|
|
69
113
|
delete view.admins[uid];
|
|
70
|
-
if (view.owners && (uid in view.owners))
|
|
71
|
-
delete view.owners[uid];
|
|
72
114
|
return found;
|
|
73
115
|
|
|
74
116
|
}
|
|
@@ -79,10 +121,11 @@ export function membershipStore(rootPath) {
|
|
|
79
121
|
ref: membershipStorePath(rootPath),
|
|
80
122
|
projections: {
|
|
81
123
|
"details": membershipDetails,
|
|
82
|
-
members
|
|
124
|
+
members,
|
|
83
125
|
},
|
|
84
|
-
write: ifAdminInThisTeam,
|
|
85
|
-
read:
|
|
126
|
+
write: ifAdminInThisTeam(rootPath),
|
|
127
|
+
read: ifMyTeam(rootPath),
|
|
128
|
+
eventTypes
|
|
86
129
|
};
|
|
87
130
|
|
|
88
131
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { deleteByPath, filterBySchema, schemaAllowsPath } from "h1v3/schema";
|
|
2
2
|
|
|
3
|
+
export const DETAILS_UPDATED = "DETAILS_UPDATED";
|
|
4
|
+
|
|
3
5
|
const current = schema => ({
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
[DETAILS_UPDATED]: (view, { payload }) => {
|
|
6
8
|
|
|
7
9
|
if (!payload || typeof payload !== "object") return view;
|
|
8
10
|
view = Object.assign(view, filterBySchema(schema, payload));
|
|
@@ -32,7 +34,8 @@ export function userProfileStore(rootPath, schema) {
|
|
|
32
34
|
current: current(schema)
|
|
33
35
|
},
|
|
34
36
|
write: ifMe,
|
|
35
|
-
read: ifMe
|
|
37
|
+
read: ifMe,
|
|
38
|
+
eventTypes: [DETAILS_UPDATED]
|
|
36
39
|
|
|
37
40
|
};
|
|
38
41
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const ADDED_TO_TEAM = "ADDED_TO_TEAM";
|
|
2
|
+
|
|
3
|
+
export const LEFT_TEAM = "LEFT_TEAM";
|
|
4
|
+
|
|
5
|
+
const current = {
|
|
6
|
+
|
|
7
|
+
[ADDED_TO_TEAM]: (view, { payload }) => {
|
|
8
|
+
|
|
9
|
+
if (!payload || typeof payload !== "object") return view;
|
|
10
|
+
if (!payload.tid) return view;
|
|
11
|
+
view.teams = Object.assign({}, view.teams, { [payload.tid]: Date.now() });
|
|
12
|
+
return view;
|
|
13
|
+
|
|
14
|
+
},
|
|
15
|
+
[LEFT_TEAM]: (view, { payload }) => {
|
|
16
|
+
|
|
17
|
+
if (!payload || typeof payload !== "object") return view;
|
|
18
|
+
if (!payload.tid) return view;
|
|
19
|
+
if (!view.teams) return view;
|
|
20
|
+
delete view.teams[payload.tid];
|
|
21
|
+
return view;
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const ifMe = "$uid == auth.uid";
|
|
28
|
+
|
|
29
|
+
export function userTeamsStore(rootPath) {
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
|
|
33
|
+
ref: `${rootPath}/users/$uid/teams`,
|
|
34
|
+
projections: {
|
|
35
|
+
current
|
|
36
|
+
},
|
|
37
|
+
eventTypes: [ADDED_TO_TEAM, LEFT_TEAM],
|
|
38
|
+
read: ifMe,
|
|
39
|
+
write: false
|
|
40
|
+
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
}
|