Skip to content

Commit 07dfc53

Browse files
committed
Switch partners to createSlice
1 parent 91dc1bb commit 07dfc53

File tree

12 files changed

+182
-123
lines changed

12 files changed

+182
-123
lines changed

firestore.indexes.json

+22
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,28 @@
2323
}
2424
]
2525
},
26+
{
27+
"collectionGroup": "items",
28+
"fieldPath": "order",
29+
"indexes": [
30+
{
31+
"order": "ASCENDING",
32+
"queryScope": "COLLECTION"
33+
},
34+
{
35+
"order": "DESCENDING",
36+
"queryScope": "COLLECTION"
37+
},
38+
{
39+
"arrayConfig": "CONTAINS",
40+
"queryScope": "COLLECTION"
41+
},
42+
{
43+
"order": "ASCENDING",
44+
"queryScope": "COLLECTION_GROUP"
45+
}
46+
]
47+
},
2648
{
2749
"collectionGroup": "members",
2850
"fieldPath": "name",

firestore.rules

+6
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ service cloud.firestore {
6161
// Disallow writes
6262
allow write: if false;
6363

64+
// TODO: Remove
6465
match /items/{itemId} {
6566
// Applies to single document read requests
6667
allow get;
@@ -73,6 +74,11 @@ service cloud.firestore {
7374
}
7475
}
7576

77+
match /{path=**}/items/{itemId} {
78+
allow read;
79+
allow write: if false;
80+
}
81+
7682
match /potentialPartners/{partnerId} {
7783
allow delete: if false;
7884

src/elements/partners-block.ts

+3-11
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import '@power-elements/lazy-image';
77
import { RootState, store } from '../store';
88
import { closeDialog, openSubscribeDialog } from '../store/dialogs/actions';
99
import { ReduxMixin } from '../store/mixin';
10-
import { fetchPartners } from '../store/partners/actions';
11-
import { initialPartnersState } from '../store/partners/state';
10+
import { PartnerGroupsState, selectPartnerGroups } from '../store/partners';
1211
import { addPotentialPartner } from '../store/potential-partners/actions';
1312
import {
1413
initialPotentialPartnersState,
@@ -116,7 +115,7 @@ export class PartnersBlock extends ReduxMixin(PolymerElement) {
116115
@property({ type: Object })
117116
potentialPartners = initialPotentialPartnersState;
118117
@property({ type: Object })
119-
partners = initialPartnersState;
118+
partners: PartnerGroupsState = new Initialized();
120119

121120
@computed('partners')
122121
get pending() {
@@ -129,17 +128,10 @@ export class PartnersBlock extends ReduxMixin(PolymerElement) {
129128
}
130129

131130
override stateChanged(state: RootState) {
132-
this.partners = state.partners;
131+
this.partners = selectPartnerGroups(state);
133132
this.potentialPartners = state.potentialPartners;
134133
}
135134

136-
override connectedCallback() {
137-
super.connectedCallback();
138-
if (this.partners instanceof Initialized) {
139-
store.dispatch(fetchPartners);
140-
}
141-
}
142-
143135
private addPotentialPartner() {
144136
openSubscribeDialog({
145137
title: this.partnersBlock.form.title,

src/models/partner-group.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { Partner, PartnerData } from './partner';
22
import { Id } from './types';
33

4+
export interface PartnerGroupWithoutItems {
5+
id: string;
6+
order: number;
7+
title: string;
8+
}
9+
410
export interface PartnerGroupData {
511
items: PartnerData[];
612
order: number;

src/models/partner.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Id } from './types';
1+
import { Id, ParentId } from './types';
22

33
export interface PartnerData {
44
logoUrl: string;
@@ -7,4 +7,4 @@ export interface PartnerData {
77
url: string;
88
}
99

10-
export type Partner = Id & PartnerData;
10+
export type Partner = Id & ParentId & PartnerData;

src/store/feedback/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ export const selectFeedbackSubscription = (state: RootState) => state.feedback.s
126126
export const selectFeedback = (state: RootState) => state.feedback.data;
127127
export const selectFeedbackDelete = (state: RootState) => state.feedback.delete;
128128

129-
export const selectSubscription = createSelector(
129+
const selectSubscription = createSelector(
130130
selectUser,
131131
selectFeedbackSubscription,
132132
(user: UserState, subscription: FeedbackState['subscription']): FeedbackState['subscription'] => {

src/store/index.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@ import { authReducer } from './auth/reducers';
33
import { blogReducer } from './blog/reducers';
44
import { dialogsReducer } from './dialogs/reducers';
55
import { featuredSessionsReducer } from './featured-sessions/reducers';
6+
import feedback from './feedback';
67
import { filtersReducer } from './filters/reducers';
78
import { galleryReducer } from './gallery/reducers';
89
import { membersReducer } from './members/reducers';
910
import notificationPermission from './notification-permission';
1011
import { notificationsSubscribersReducer } from './notifications-subscribers/reducers';
1112
import { notificationsUsersReducer } from './notifications-users/reducers';
12-
import { partnersReducer } from './partners/reducers';
13+
import partners from './partners';
1314
import { potentialPartnersReducer } from './potential-partners/reducers';
1415
import { previousSpeakersReducer } from './previous-speakers/reducers';
1516
import { scheduleReducer } from './schedule/reducers';
1617
import { sessionsReducer } from './sessions/reducers';
1718
import snackbars from './snackbars';
18-
import feedback from './feedback';
1919
import { speakersReducer } from './speakers/reducers';
2020
import { subscribeReducer } from './subscribe/reducers';
2121
import { teamsReducer } from './teams/reducers';
@@ -40,15 +40,15 @@ export const store = configureStore({
4040
filters: filtersReducer,
4141
gallery: galleryReducer,
4242
members: membersReducer,
43-
snackbars,
4443
notificationPermission,
4544
notificationsSubscribers: notificationsSubscribersReducer,
4645
notificationsUsers: notificationsUsersReducer,
47-
partners: partnersReducer,
46+
partners,
4847
potentialPartners: potentialPartnersReducer,
4948
previousSpeakers: previousSpeakersReducer,
5049
schedule: scheduleReducer,
5150
sessions: sessionsReducer,
51+
snackbars,
5252
speakers: speakersReducer,
5353
subscribed: subscribeReducer,
5454
teams: teamsReducer,

src/store/partners/actions.ts

-49
This file was deleted.

src/store/partners/index.ts

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { Failure, Initialized, Pending, RemoteData, Success } from '@abraham/remotedata';
2+
import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit';
3+
import {
4+
collection,
5+
collectionGroup,
6+
onSnapshot,
7+
orderBy,
8+
query,
9+
Unsubscribe,
10+
} from 'firebase/firestore';
11+
import { RootState, store } from '..';
12+
import { db } from '../../firebase';
13+
import { Partner } from '../../models/partner';
14+
import { PartnerGroup, PartnerGroupWithoutItems } from '../../models/partner-group';
15+
import { dataWithParentId, mergeDataAndId } from '../../utils/firestore';
16+
17+
export type PartnerGroupsState = RemoteData<Error, PartnerGroup[]>;
18+
19+
export interface PartnersState {
20+
groups: RemoteData<Error, PartnerGroupWithoutItems[]>;
21+
partners: RemoteData<Error, Partner[]>;
22+
partnersSubscription: RemoteData<Error, Unsubscribe>;
23+
groupsSubscription: RemoteData<Error, Unsubscribe>;
24+
}
25+
26+
export const initialState = {
27+
groups: new Initialized(),
28+
partners: new Initialized(),
29+
partnersSubscription: new Initialized(),
30+
groupsSubscription: new Initialized(),
31+
} as PartnersState;
32+
33+
const subscribeToPartners = () => {
34+
return onSnapshot(
35+
query(collectionGroup(db, 'items'), orderBy('order')),
36+
(snapshot) => {
37+
store.dispatch(setPartnersSuccess(snapshot.docs.map<Partner>(dataWithParentId)));
38+
},
39+
(error) => store.dispatch(setPartnersFailure(error as Error))
40+
);
41+
};
42+
43+
const subscribeToGroups = () => {
44+
return onSnapshot(
45+
query(collection(db, 'partners'), orderBy('order')),
46+
(snapshot) => {
47+
store.dispatch(setGroupsSuccess(snapshot.docs.map<PartnerGroupWithoutItems>(mergeDataAndId)));
48+
},
49+
(error) => store.dispatch(setGroupsFailure(error as Error))
50+
);
51+
};
52+
53+
const partnersSlice = createSlice({
54+
name: 'partners',
55+
initialState,
56+
reducers: {
57+
subscribe(state) {
58+
if (state.partnersSubscription instanceof Initialized) {
59+
state.partnersSubscription = new Success(subscribeToPartners());
60+
state.partners = new Pending();
61+
}
62+
if (state.groupsSubscription instanceof Initialized) {
63+
state.groupsSubscription = new Success(subscribeToGroups());
64+
state.groups = new Pending();
65+
}
66+
},
67+
unsubscribe(state) {
68+
if (state.partnersSubscription instanceof Success) {
69+
state.partnersSubscription.data();
70+
}
71+
if (state.groupsSubscription instanceof Success) {
72+
state.groupsSubscription.data();
73+
}
74+
state.partnersSubscription = new Initialized();
75+
state.partners = new Initialized();
76+
state.groupsSubscription = new Initialized();
77+
state.groups = new Initialized();
78+
},
79+
setPartnersSuccess(state, action: PayloadAction<Partner[]>) {
80+
state.partners = new Success(action.payload);
81+
},
82+
setPartnersFailure(state, action: PayloadAction<Error>) {
83+
state.partners = new Failure(action.payload);
84+
},
85+
setGroupsSuccess(state, action: PayloadAction<PartnerGroupWithoutItems[]>) {
86+
state.groups = new Success(action.payload);
87+
},
88+
setGroupsFailure(state, action: PayloadAction<Error>) {
89+
state.groups = new Failure(action.payload);
90+
},
91+
},
92+
});
93+
94+
const selectPartners = (state: RootState) => state.partners.partners;
95+
const selectGroups = (state: RootState) => state.partners.groups;
96+
97+
export const selectPartnerGroups = createSelector(
98+
selectGroups,
99+
selectPartners,
100+
(groups: PartnersState['groups'], partners: PartnersState['partners']): PartnerGroupsState => {
101+
if (groups instanceof Initialized || partners instanceof Initialized) {
102+
store.dispatch(subscribe());
103+
return new Pending();
104+
}
105+
106+
if (groups instanceof Success && partners instanceof Success) {
107+
const partnerGroups = groups.data.map((group) => {
108+
return {
109+
...group,
110+
items: partners.data.filter((partner: Partner) => partner.parentId === group.id),
111+
};
112+
});
113+
return new Success(partnerGroups);
114+
}
115+
116+
if (groups instanceof Failure) {
117+
return groups;
118+
}
119+
120+
if (partners instanceof Failure) {
121+
return partners;
122+
}
123+
124+
return new Pending();
125+
}
126+
);
127+
128+
const {
129+
subscribe,
130+
unsubscribe,
131+
setPartnersSuccess,
132+
setPartnersFailure,
133+
setGroupsSuccess,
134+
setGroupsFailure,
135+
} = partnersSlice.actions;
136+
137+
export { subscribe, unsubscribe };
138+
export default partnersSlice.reducer;

src/store/partners/reducers.ts

-27
This file was deleted.

src/store/partners/state.ts

-5
This file was deleted.

0 commit comments

Comments
 (0)