110 lines
4.3 KiB
TypeScript
110 lines
4.3 KiB
TypeScript
import { List as ImmutableList, Record as ImmutableRecord, Set as ImmutableSet } from 'immutable';
|
|
|
|
import {
|
|
ANNOUNCEMENTS_FETCH_REQUEST,
|
|
ANNOUNCEMENTS_FETCH_SUCCESS,
|
|
ANNOUNCEMENTS_FETCH_FAIL,
|
|
ANNOUNCEMENTS_UPDATE,
|
|
ANNOUNCEMENTS_REACTION_UPDATE,
|
|
ANNOUNCEMENTS_REACTION_ADD_REQUEST,
|
|
ANNOUNCEMENTS_REACTION_ADD_FAIL,
|
|
ANNOUNCEMENTS_REACTION_REMOVE_REQUEST,
|
|
ANNOUNCEMENTS_REACTION_REMOVE_FAIL,
|
|
ANNOUNCEMENTS_TOGGLE_SHOW,
|
|
ANNOUNCEMENTS_DELETE,
|
|
ANNOUNCEMENTS_DISMISS_SUCCESS,
|
|
} from 'soapbox/actions/announcements';
|
|
import { normalizeAnnouncement, normalizeAnnouncementReaction } from 'soapbox/normalizers';
|
|
|
|
import type { AnyAction } from 'redux';
|
|
import type { Announcement, AnnouncementReaction, APIEntity } from 'soapbox/types/entities';
|
|
|
|
const ReducerRecord = ImmutableRecord({
|
|
items: ImmutableList<Announcement>(),
|
|
isLoading: false,
|
|
show: false,
|
|
unread: ImmutableSet<string>(),
|
|
});
|
|
|
|
type State = ReturnType<typeof ReducerRecord>;
|
|
|
|
const updateReaction = (state: State, id: string, name: string, updater: (a: AnnouncementReaction) => AnnouncementReaction) => state.update('items', list => list.map(announcement => {
|
|
if (announcement.id === id) {
|
|
return announcement.update('reactions', reactions => {
|
|
const idx = reactions.findIndex(reaction => reaction.name === name);
|
|
|
|
if (idx > -1) {
|
|
return reactions.update(idx, reaction => updater(reaction!));
|
|
}
|
|
|
|
return reactions.push(updater(normalizeAnnouncementReaction({ name, count: 0 })));
|
|
});
|
|
}
|
|
|
|
return announcement;
|
|
}));
|
|
|
|
const updateReactionCount = (state: State, reaction: AnnouncementReaction) => updateReaction(state, reaction.announcement_id, reaction.name, x => x.set('count', reaction.count));
|
|
|
|
const addReaction = (state: State, id: string, name: string) => updateReaction(state, id, name, (x: AnnouncementReaction) => x.set('me', true).update('count', y => y + 1));
|
|
|
|
const removeReaction = (state: State, id: string, name: string) => updateReaction(state, id, name, (x: AnnouncementReaction) => x.set('me', false).update('count', y => y - 1));
|
|
|
|
const sortAnnouncements = (list: ImmutableList<Announcement>) => list.sortBy(x => x.starts_at || x.published_at);
|
|
|
|
const updateAnnouncement = (state: State, announcement: Announcement) => {
|
|
const idx = state.items.findIndex(x => x.id === announcement.id);
|
|
|
|
if (idx > -1) {
|
|
// Deep merge is used because announcements from the streaming API do not contain
|
|
// personalized data about which reactions have been selected by the given user,
|
|
// and that is information we want to preserve
|
|
return state.update('items', list => sortAnnouncements(list.update(idx, x => x!.mergeDeep(announcement))));
|
|
}
|
|
|
|
return state.update('items', list => sortAnnouncements(list.unshift(announcement)));
|
|
};
|
|
|
|
export default function announcementsReducer(state = ReducerRecord(), action: AnyAction) {
|
|
switch (action.type) {
|
|
case ANNOUNCEMENTS_TOGGLE_SHOW:
|
|
return state.withMutations(map => {
|
|
map.set('show', !map.show);
|
|
});
|
|
case ANNOUNCEMENTS_FETCH_REQUEST:
|
|
return state.set('isLoading', true);
|
|
case ANNOUNCEMENTS_FETCH_SUCCESS:
|
|
return state.withMutations(map => {
|
|
const items = ImmutableList<Announcement>((action.announcements).map((announcement: APIEntity) => normalizeAnnouncement(announcement)));
|
|
|
|
map.set('items', items);
|
|
map.set('isLoading', false);
|
|
});
|
|
case ANNOUNCEMENTS_FETCH_FAIL:
|
|
return state.set('isLoading', false);
|
|
case ANNOUNCEMENTS_UPDATE:
|
|
return updateAnnouncement(state, normalizeAnnouncement(action.announcement));
|
|
case ANNOUNCEMENTS_REACTION_UPDATE:
|
|
return updateReactionCount(state, action.reaction);
|
|
case ANNOUNCEMENTS_REACTION_ADD_REQUEST:
|
|
case ANNOUNCEMENTS_REACTION_REMOVE_FAIL:
|
|
return addReaction(state, action.id, action.name);
|
|
case ANNOUNCEMENTS_REACTION_REMOVE_REQUEST:
|
|
case ANNOUNCEMENTS_REACTION_ADD_FAIL:
|
|
return removeReaction(state, action.id, action.name);
|
|
case ANNOUNCEMENTS_DISMISS_SUCCESS:
|
|
return updateAnnouncement(state, normalizeAnnouncement({ id: action.id, read: true }));
|
|
case ANNOUNCEMENTS_DELETE:
|
|
return state.update('items', list => {
|
|
const idx = list.findIndex(x => x.id === action.id);
|
|
|
|
if (idx > -1) {
|
|
return list.delete(idx);
|
|
}
|
|
|
|
return list;
|
|
});
|
|
default:
|
|
return state;
|
|
}
|
|
}
|