Groups: actions, normalizers, reducers

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
environments/review-mastodon-g-0qbqe2/deployments/1694
marcin mikołajczak 2022-12-11 21:37:00 +01:00
rodzic 5a2d082437
commit 12825f9350
29 zmienionych plików z 1542 dodań i 47 usunięć

Wyświetl plik

@ -286,6 +286,7 @@ const submitCompose = (composeId: string, routerHistory?: History, force = false
poll: compose.poll,
scheduled_at: compose.schedule,
to,
group_id: compose.privacy === 'group' ? compose.group_id : null,
};
dispatch(createStatus(params, idempotencyKey, statusId)).then(function(data) {

Wyświetl plik

@ -0,0 +1,931 @@
import { GroupRole } from 'soapbox/reducers/group-memberships';
import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts';
import { importFetchedGroups, importFetchedAccounts } from './importer';
import { deleteFromTimelines } from './timelines';
import type { AxiosError } from 'axios';
import type { AppDispatch, RootState } from 'soapbox/store';
import type { APIEntity } from 'soapbox/types/entities';
const GROUP_CREATE_REQUEST = 'GROUP_CREATE_REQUEST';
const GROUP_CREATE_SUCCESS = 'GROUP_CREATE_SUCCESS';
const GROUP_CREATE_FAIL = 'GROUP_CREATE_FAIL';
const GROUP_DELETE_REQUEST = 'GROUP_DELETE_REQUEST';
const GROUP_DELETE_SUCCESS = 'GROUP_DELETE_SUCCESS';
const GROUP_DELETE_FAIL = 'GROUP_DELETE_FAIL';
const GROUP_FETCH_REQUEST = 'GROUP_FETCH_REQUEST';
const GROUP_FETCH_SUCCESS = 'GROUP_FETCH_SUCCESS';
const GROUP_FETCH_FAIL = 'GROUP_FETCH_FAIL';
const GROUPS_FETCH_REQUEST = 'GROUPS_FETCH_REQUEST';
const GROUPS_FETCH_SUCCESS = 'GROUPS_FETCH_SUCCESS';
const GROUPS_FETCH_FAIL = 'GROUPS_FETCH_FAIL';
const GROUP_RELATIONSHIPS_FETCH_REQUEST = 'GROUP_RELATIONSHIPS_FETCH_REQUEST';
const GROUP_RELATIONSHIPS_FETCH_SUCCESS = 'GROUP_RELATIONSHIPS_FETCH_SUCCESS';
const GROUP_RELATIONSHIPS_FETCH_FAIL = 'GROUP_RELATIONSHIPS_FETCH_FAIL';
const GROUP_JOIN_REQUEST = 'GROUP_JOIN_REQUEST';
const GROUP_JOIN_SUCCESS = 'GROUP_JOIN_SUCCESS';
const GROUP_JOIN_FAIL = 'GROUP_JOIN_FAIL';
const GROUP_LEAVE_REQUEST = 'GROUP_LEAVE_REQUEST';
const GROUP_LEAVE_SUCCESS = 'GROUP_LEAVE_SUCCESS';
const GROUP_LEAVE_FAIL = 'GROUP_LEAVE_FAIL';
const GROUP_DELETE_STATUS_REQUEST = 'GROUP_DELETE_STATUS_REQUEST';
const GROUP_DELETE_STATUS_SUCCESS = 'GROUP_DELETE_STATUS_SUCCESS';
const GROUP_DELETE_STATUS_FAIL = 'GROUP_DELETE_STATUS_FAIL';
const GROUP_KICK_REQUEST = 'GROUP_KICK_REQUEST';
const GROUP_KICK_SUCCESS = 'GROUP_KICK_SUCCESS';
const GROUP_KICK_FAIL = 'GROUP_KICK_FAIL';
const GROUP_BLOCKS_FETCH_REQUEST = 'GROUP_BLOCKS_FETCH_REQUEST';
const GROUP_BLOCKS_FETCH_SUCCESS = 'GROUP_BLOCKS_FETCH_SUCCESS';
const GROUP_BLOCKS_FETCH_FAIL = 'GROUP_BLOCKS_FETCH_FAIL';
const GROUP_BLOCKS_EXPAND_REQUEST = 'GROUP_BLOCKS_EXPAND_REQUEST';
const GROUP_BLOCKS_EXPAND_SUCCESS = 'GROUP_BLOCKS_EXPAND_SUCCESS';
const GROUP_BLOCKS_EXPAND_FAIL = 'GROUP_BLOCKS_EXPAND_FAIL';
const GROUP_BLOCK_REQUEST = 'GROUP_BLOCK_REQUEST';
const GROUP_BLOCK_SUCCESS = 'GROUP_BLOCK_SUCCESS';
const GROUP_BLOCK_FAIL = 'GROUP_BLOCK_FAIL';
const GROUP_UNBLOCK_REQUEST = 'GROUP_UNBLOCK_REQUEST';
const GROUP_UNBLOCK_SUCCESS = 'GROUP_UNBLOCK_SUCCESS';
const GROUP_UNBLOCK_FAIL = 'GROUP_UNBLOCK_FAIL';
const GROUP_PROMOTE_REQUEST = 'GROUP_PROMOTE_REQUEST';
const GROUP_PROMOTE_SUCCESS = 'GROUP_PROMOTE_SUCCESS';
const GROUP_PROMOTE_FAIL = 'GROUP_PROMOTE_FAIL';
const GROUP_DEMOTE_REQUEST = 'GROUP_DEMOTE_REQUEST';
const GROUP_DEMOTE_SUCCESS = 'GROUP_DEMOTE_SUCCESS';
const GROUP_DEMOTE_FAIL = 'GROUP_DEMOTE_FAIL';
const GROUP_MEMBERSHIPS_FETCH_REQUEST = 'GROUP_MEMBERSHIPS_FETCH_REQUEST';
const GROUP_MEMBERSHIPS_FETCH_SUCCESS = 'GROUP_MEMBERSHIPS_FETCH_SUCCESS';
const GROUP_MEMBERSHIPS_FETCH_FAIL = 'GROUP_MEMBERSHIPS_FETCH_FAIL';
const GROUP_MEMBERSHIPS_EXPAND_REQUEST = 'GROUP_MEMBERSHIPS_EXPAND_REQUEST';
const GROUP_MEMBERSHIPS_EXPAND_SUCCESS = 'GROUP_MEMBERSHIPS_EXPAND_SUCCESS';
const GROUP_MEMBERSHIPS_EXPAND_FAIL = 'GROUP_MEMBERSHIPS_EXPAND_FAIL';
const GROUP_MEMBERSHIP_REQUESTS_FETCH_REQUEST = 'GROUP_MEMBERSHIP_REQUESTS_FETCH_REQUEST';
const GROUP_MEMBERSHIP_REQUESTS_FETCH_SUCCESS = 'GROUP_MEMBERSHIP_REQUESTS_FETCH_SUCCESS';
const GROUP_MEMBERSHIP_REQUESTS_FETCH_FAIL = 'GROUP_MEMBERSHIP_REQUESTS_FETCH_FAIL';
const GROUP_MEMBERSHIP_REQUESTS_EXPAND_REQUEST = 'GROUP_MEMBERSHIP_REQUESTS_EXPAND_REQUEST';
const GROUP_MEMBERSHIP_REQUESTS_EXPAND_SUCCESS = 'GROUP_MEMBERSHIP_REQUESTS_EXPAND_SUCCESS';
const GROUP_MEMBERSHIP_REQUESTS_EXPAND_FAIL = 'GROUP_MEMBERSHIP_REQUESTS_EXPAND_FAIL';
const GROUP_MEMBERSHIP_REQUEST_AUTHORIZE_REQUEST = 'GROUP_MEMBERSHIP_REQUEST_AUTHORIZE_REQUEST';
const GROUP_MEMBERSHIP_REQUEST_AUTHORIZE_SUCCESS = 'GROUP_MEMBERSHIP_REQUEST_AUTHORIZE_SUCCESS';
const GROUP_MEMBERSHIP_REQUEST_AUTHORIZE_FAIL = 'GROUP_MEMBERSHIP_REQUEST_AUTHORIZE_FAIL';
const GROUP_MEMBERSHIP_REQUEST_REJECT_REQUEST = 'GROUP_MEMBERSHIP_REQUEST_REJECT_REQUEST';
const GROUP_MEMBERSHIP_REQUEST_REJECT_SUCCESS = 'GROUP_MEMBERSHIP_REQUEST_REJECT_SUCCESS';
const GROUP_MEMBERSHIP_REQUEST_REJECT_FAIL = 'GROUP_MEMBERSHIP_REQUEST_REJECT_FAIL';
const GROUP_EDITOR_TITLE_CHANGE = 'GROUP_EDITOR_TITLE_CHANGE';
const GROUP_EDITOR_RESET = 'GROUP_EDITOR_RESET';
const createGroup = (displayName: string, shouldReset?: boolean) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(createGroupRequest());
api(getState).post('/api/v1/groups', { display_name: displayName })
.then(({ data }) => {
dispatch(importFetchedGroups([data]));
dispatch(createGroupSuccess(data));
if (shouldReset) {
dispatch(resetGroupEditor());
}
}).catch(err => dispatch(createGroupFail(err)));
};
const createGroupRequest = () => ({
type: GROUP_CREATE_REQUEST,
});
const createGroupSuccess = (group: APIEntity) => ({
type: GROUP_CREATE_SUCCESS,
group,
});
const createGroupFail = (error: AxiosError) => ({
type: GROUP_CREATE_FAIL,
error,
});
const deleteGroup = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => {
dispatch(deleteGroupRequest(id));
api(getState).delete(`/api/v1/groups/${id}`)
.then(() => dispatch(deleteGroupSuccess(id)))
.catch(err => dispatch(deleteGroupFail(id, err)));
};
const deleteGroupRequest = (id: string) => ({
type: GROUP_DELETE_REQUEST,
id,
});
const deleteGroupSuccess = (id: string) => ({
type: GROUP_DELETE_SUCCESS,
id,
});
const deleteGroupFail = (id: string, error: AxiosError) => ({
type: GROUP_DELETE_FAIL,
id,
error,
});
const fetchGroup = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => {
dispatch(fetchGroupRelationships([id]));
dispatch(fetchGroupRequest(id));
api(getState).get(`/api/v1/groups/${id}`)
.then(({ data }) => {
dispatch(importFetchedGroups([data]));
dispatch(fetchGroupSuccess(data));
})
.catch(err => dispatch(fetchGroupFail(id, err)));
};
const fetchGroupRequest = (id: string) => ({
type: GROUP_FETCH_REQUEST,
id,
});
const fetchGroupSuccess = (group: APIEntity) => ({
type: GROUP_FETCH_SUCCESS,
group,
});
const fetchGroupFail = (id: string, error: AxiosError) => ({
type: GROUP_FETCH_FAIL,
id,
error,
});
const fetchGroups = () => (dispatch: AppDispatch, getState: () => RootState) => {
dispatch(fetchGroupsRequest());
api(getState).get('/api/v1/groups')
.then(({ data }) => {
dispatch(importFetchedGroups(data));
dispatch(fetchGroupsSuccess(data));
dispatch(fetchGroupRelationships(data.map((item: APIEntity) => item.id)));
}).catch(err => dispatch(fetchGroupsFail(err)));
};
const fetchGroupsRequest = () => ({
type: GROUPS_FETCH_REQUEST,
});
const fetchGroupsSuccess = (groups: APIEntity[]) => ({
type: GROUPS_FETCH_SUCCESS,
groups,
});
const fetchGroupsFail = (error: AxiosError) => ({
type: GROUPS_FETCH_FAIL,
error,
});
const fetchGroupRelationships = (groupIds: string[]) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const loadedRelationships = state.group_relationships;
const newGroupIds = groupIds.filter(id => loadedRelationships.get(id, null) === null);
if (!state.me || newGroupIds.length === 0) {
return;
}
dispatch(fetchGroupRelationshipsRequest(newGroupIds));
api(getState).get(`/api/v1/groups/relationships?${newGroupIds.map(id => `id[]=${id}`).join('&')}`).then(response => {
dispatch(fetchGroupRelationshipsSuccess(response.data));
}).catch(error => {
dispatch(fetchGroupRelationshipsFail(error));
});
};
const fetchGroupRelationshipsRequest = (ids: string[]) => ({
type: GROUP_RELATIONSHIPS_FETCH_REQUEST,
ids,
skipLoading: true,
});
const fetchGroupRelationshipsSuccess = (relationships: APIEntity[]) => ({
type: GROUP_RELATIONSHIPS_FETCH_SUCCESS,
relationships,
skipLoading: true,
});
const fetchGroupRelationshipsFail = (error: AxiosError) => ({
type: GROUP_RELATIONSHIPS_FETCH_FAIL,
error,
skipLoading: true,
skipNotFound: true,
});
const joinGroup = (id: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const locked = (getState().groups.get(id) as any).locked || false;
dispatch(joinGroupRequest(id, locked));
api(getState).post(`/api/v1/groups/${id}/join`).then(response => {
dispatch(joinGroupSuccess(response.data));
}).catch(error => {
dispatch(joinGroupFail(error, locked));
});
};
const leaveGroup = (id: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(leaveGroupRequest(id));
api(getState).post(`/api/v1/groups/${id}/leave`).then(response => {
dispatch(leaveGroupSuccess(response.data));
}).catch(error => {
dispatch(leaveGroupFail(error));
});
};
const joinGroupRequest = (id: string, locked: boolean) => ({
type: GROUP_JOIN_REQUEST,
id,
locked,
skipLoading: true,
});
const joinGroupSuccess = (relationship: APIEntity) => ({
type: GROUP_JOIN_SUCCESS,
relationship,
skipLoading: true,
});
const joinGroupFail = (error: AxiosError, locked: boolean) => ({
type: GROUP_JOIN_FAIL,
error,
locked,
skipLoading: true,
});
const leaveGroupRequest = (id: string) => ({
type: GROUP_LEAVE_REQUEST,
id,
skipLoading: true,
});
const leaveGroupSuccess = (relationship: APIEntity) => ({
type: GROUP_LEAVE_SUCCESS,
relationship,
skipLoading: true,
});
const leaveGroupFail = (error: AxiosError) => ({
type: GROUP_LEAVE_FAIL,
error,
skipLoading: true,
});
const groupDeleteStatus = (groupId: string, statusId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(groupDeleteStatusRequest(groupId, statusId));
api(getState).delete(`/api/v1/groups/${groupId}/statuses/${statusId}`)
.then(() => {
dispatch(deleteFromTimelines(statusId));
dispatch(groupDeleteStatusSuccess(groupId, statusId));
}).catch(err => dispatch(groupDeleteStatusFail(groupId, statusId, err)));
};
const groupDeleteStatusRequest = (groupId: string, statusId: string) => ({
type: GROUP_DELETE_STATUS_REQUEST,
groupId,
statusId,
});
const groupDeleteStatusSuccess = (groupId: string, statusId: string) => ({
type: GROUP_DELETE_STATUS_SUCCESS,
groupId,
statusId,
});
const groupDeleteStatusFail = (groupId: string, statusId: string, error: AxiosError) => ({
type: GROUP_DELETE_STATUS_SUCCESS,
groupId,
statusId,
error,
});
const groupKick = (groupId: string, accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(groupKickRequest(groupId, accountId));
api(getState).post(`/api/v1/groups/${groupId}/kick`, { account_ids: [accountId] })
.then(() => dispatch(groupKickSuccess(groupId, accountId)))
.catch(err => dispatch(groupKickFail(groupId, accountId, err)));
};
const groupKickRequest = (groupId: string, accountId: string) => ({
type: GROUP_KICK_REQUEST,
groupId,
accountId,
});
const groupKickSuccess = (groupId: string, accountId: string) => ({
type: GROUP_KICK_SUCCESS,
groupId,
accountId,
});
const groupKickFail = (groupId: string, accountId: string, error: AxiosError) => ({
type: GROUP_KICK_SUCCESS,
groupId,
accountId,
error,
});
const fetchGroupBlocks = (id: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(fetchGroupBlocksRequest(id));
api(getState).get(`/api/v1/groups/${id}/blocks`).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data));
dispatch(fetchGroupBlocksSuccess(id, response.data, next ? next.uri : null));
}).catch(error => {
dispatch(fetchGroupBlocksFail(id, error));
});
};
const fetchGroupBlocksRequest = (id: string) => ({
type: GROUP_BLOCKS_FETCH_REQUEST,
id,
});
const fetchGroupBlocksSuccess = (id: string, accounts: APIEntity[], next: string | null) => ({
type: GROUP_BLOCKS_FETCH_SUCCESS,
id,
accounts,
next,
});
const fetchGroupBlocksFail = (id: string, error: AxiosError) => ({
type: GROUP_BLOCKS_FETCH_FAIL,
id,
error,
skipNotFound: true,
});
const expandGroupBlocks = (id: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const url = getState().user_lists.group_blocks.get(id)?.next || null;
if (url === null) {
return;
}
dispatch(expandGroupBlocksRequest(id));
api(getState).get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data));
dispatch(expandGroupBlocksSuccess(id, response.data, next ? next.uri : null));
dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id)));
}).catch(error => {
dispatch(expandGroupBlocksFail(id, error));
});
};
const expandGroupBlocksRequest = (id: string) => ({
type: GROUP_BLOCKS_EXPAND_REQUEST,
id,
});
const expandGroupBlocksSuccess = (id: string, accounts: APIEntity[], next: string | null) => ({
type: GROUP_BLOCKS_EXPAND_SUCCESS,
id,
accounts,
next,
});
const expandGroupBlocksFail = (id: string, error: AxiosError) => ({
type: GROUP_BLOCKS_EXPAND_FAIL,
id,
error,
});
const groupBlock = (groupId: string, accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(groupBlockRequest(groupId, accountId));
api(getState).post(`/api/v1/groups/${groupId}/blocks`, { account_ids: [accountId] })
.then(() => dispatch(groupBlockSuccess(groupId, accountId)))
.catch(err => dispatch(groupBlockFail(groupId, accountId, err)));
};
const groupBlockRequest = (groupId: string, accountId: string) => ({
type: GROUP_BLOCK_REQUEST,
groupId,
accountId,
});
const groupBlockSuccess = (groupId: string, accountId: string) => ({
type: GROUP_BLOCK_SUCCESS,
groupId,
accountId,
});
const groupBlockFail = (groupId: string, accountId: string, error: AxiosError) => ({
type: GROUP_BLOCK_FAIL,
groupId,
accountId,
error,
});
const groupUnblock = (groupId: string, accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(groupUnblockRequest(groupId, accountId));
api(getState).delete(`/api/v1/groups/${groupId}/blocks?account_ids[]=${accountId}`)
.then(() => dispatch(groupUnblockSuccess(groupId, accountId)))
.catch(err => dispatch(groupUnblockFail(groupId, accountId, err)));
};
const groupUnblockRequest = (groupId: string, accountId: string) => ({
type: GROUP_UNBLOCK_REQUEST,
groupId,
accountId,
});
const groupUnblockSuccess = (groupId: string, accountId: string) => ({
type: GROUP_UNBLOCK_SUCCESS,
groupId,
accountId,
});
const groupUnblockFail = (groupId: string, accountId: string, error: AxiosError) => ({
type: GROUP_UNBLOCK_FAIL,
groupId,
accountId,
error,
});
const groupPromoteAccount = (groupId: string, accountId: string, role: GroupRole) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(groupPromoteAccountRequest(groupId, accountId));
api(getState).post(`/api/v1/groups/${groupId}/promote`, { account_ids: [accountId], role: role })
.then((response) => dispatch(groupPromoteAccountSuccess(groupId, accountId, response.data)))
.catch(err => dispatch(groupPromoteAccountFail(groupId, accountId, err)));
};
const groupPromoteAccountRequest = (groupId: string, accountId: string) => ({
type: GROUP_PROMOTE_REQUEST,
groupId,
accountId,
});
const groupPromoteAccountSuccess = (groupId: string, accountId: string, memberships: APIEntity[]) => ({
type: GROUP_PROMOTE_SUCCESS,
groupId,
accountId,
memberships,
});
const groupPromoteAccountFail = (groupId: string, accountId: string, error: AxiosError) => ({
type: GROUP_PROMOTE_FAIL,
groupId,
accountId,
error,
});
const groupDemoteAccount = (groupId: string, accountId: string, role: GroupRole) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(groupDemoteAccountRequest(groupId, accountId));
api(getState).post(`/api/v1/groups/${groupId}/demote`, { account_ids: [accountId], role: role })
.then((response) => dispatch(groupDemoteAccountSuccess(groupId, accountId, response.data)))
.catch(err => dispatch(groupDemoteAccountFail(groupId, accountId, err)));
};
const groupDemoteAccountRequest = (groupId: string, accountId: string) => ({
type: GROUP_DEMOTE_REQUEST,
groupId,
accountId,
});
const groupDemoteAccountSuccess = (groupId: string, accountId: string, memberships: APIEntity[]) => ({
type: GROUP_DEMOTE_SUCCESS,
groupId,
accountId,
memberships,
});
const groupDemoteAccountFail = (groupId: string, accountId: string, error: AxiosError) => ({
type: GROUP_DEMOTE_FAIL,
groupId,
accountId,
error,
});
const fetchGroupMemberships = (id: string, role: GroupRole) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(fetchGroupMembershipsRequest(id, role));
api(getState).get(`/api/v1/groups/${id}/memberships`, { params: { role } }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data.map((membership: APIEntity) => membership.account)));
dispatch(fetchGroupMembershipsSuccess(id, role, response.data, next ? next.uri : null));
}).catch(error => {
dispatch(fetchGroupMembershipsFail(id, role, error));
});
};
const fetchGroupMembershipsRequest = (id: string, role: GroupRole) => ({
type: GROUP_MEMBERSHIPS_FETCH_REQUEST,
id,
role,
});
const fetchGroupMembershipsSuccess = (id: string, role: GroupRole, memberships: APIEntity[], next: string | null) => ({
type: GROUP_MEMBERSHIPS_FETCH_SUCCESS,
id,
role,
memberships,
next,
});
const fetchGroupMembershipsFail = (id: string, role: GroupRole, error: AxiosError) => ({
type: GROUP_MEMBERSHIPS_FETCH_FAIL,
id,
role,
error,
skipNotFound: true,
});
const expandGroupMemberships = (id: string, role: GroupRole) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const url = getState().group_memberships.get(role).get(id)?.next || null;
if (url === null) {
return;
}
dispatch(expandGroupMembershipsRequest(id, role));
api(getState).get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data.map((membership: APIEntity) => membership.account)));
dispatch(expandGroupMembershipsSuccess(id, role, response.data, next ? next.uri : null));
dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id)));
}).catch(error => {
dispatch(expandGroupMembershipsFail(id, role, error));
});
};
const expandGroupMembershipsRequest = (id: string, role: GroupRole) => ({
type: GROUP_MEMBERSHIPS_EXPAND_REQUEST,
id,
role,
});
const expandGroupMembershipsSuccess = (id: string, role: GroupRole, memberships: APIEntity[], next: string | null) => ({
type: GROUP_MEMBERSHIPS_EXPAND_SUCCESS,
id,
role,
memberships,
next,
});
const expandGroupMembershipsFail = (id: string, role: GroupRole, error: AxiosError) => ({
type: GROUP_MEMBERSHIPS_EXPAND_FAIL,
id,
role,
error,
});
const fetchGroupMembershipRequests = (id: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(fetchGroupMembershipRequestsRequest(id));
api(getState).get(`/api/v1/groups/${id}/membership_requests`).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data));
dispatch(fetchGroupMembershipRequestsSuccess(id, response.data, next ? next.uri : null));
}).catch(error => {
dispatch(fetchGroupMembershipRequestsFail(id, error));
});
};
const fetchGroupMembershipRequestsRequest = (id: string) => ({
type: GROUP_MEMBERSHIP_REQUESTS_FETCH_REQUEST,
id,
});
const fetchGroupMembershipRequestsSuccess = (id: string, accounts: APIEntity[], next: string | null) => ({
type: GROUP_MEMBERSHIP_REQUESTS_FETCH_SUCCESS,
id,
accounts,
next,
});
const fetchGroupMembershipRequestsFail = (id: string, error: AxiosError) => ({
type: GROUP_MEMBERSHIP_REQUESTS_FETCH_FAIL,
id,
error,
skipNotFound: true,
});
const expandGroupMembershipRequests = (id: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const url = getState().user_lists.membership_requests.get(id)?.next || null;
if (url === null) {
return;
}
dispatch(expandGroupMembershipRequestsRequest(id));
api(getState).get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data));
dispatch(expandGroupMembershipRequestsSuccess(id, response.data, next ? next.uri : null));
dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id)));
}).catch(error => {
dispatch(expandGroupMembershipRequestsFail(id, error));
});
};
const expandGroupMembershipRequestsRequest = (id: string) => ({
type: GROUP_MEMBERSHIP_REQUESTS_EXPAND_REQUEST,
id,
});
const expandGroupMembershipRequestsSuccess = (id: string, accounts: APIEntity[], next: string | null) => ({
type: GROUP_MEMBERSHIP_REQUESTS_EXPAND_SUCCESS,
id,
accounts,
next,
});
const expandGroupMembershipRequestsFail = (id: string, error: AxiosError) => ({
type: GROUP_MEMBERSHIP_REQUESTS_EXPAND_FAIL,
id,
error,
});
const authorizeGroupMembershipRequest = (groupId: string, accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(authorizeGroupMembershipRequestRequest(groupId, accountId));
api(getState)
.post(`/api/v1/groups/${groupId}/membership_requests/${accountId}/authorize`)
.then(() => dispatch(authorizeGroupMembershipRequestSuccess(groupId, accountId)))
.catch(error => dispatch(authorizeGroupMembershipRequestFail(groupId, accountId, error)));
};
const authorizeGroupMembershipRequestRequest = (groupId: string, accountId: string) => ({
type: GROUP_MEMBERSHIP_REQUEST_AUTHORIZE_REQUEST,
groupId,
accountId,
});
const authorizeGroupMembershipRequestSuccess = (groupId: string, accountId: string) => ({
type: GROUP_MEMBERSHIP_REQUEST_AUTHORIZE_SUCCESS,
groupId,
accountId,
});
const authorizeGroupMembershipRequestFail = (groupId: string, accountId: string, error: AxiosError) => ({
type: GROUP_MEMBERSHIP_REQUEST_AUTHORIZE_FAIL,
groupId,
accountId,
error,
});
const rejectGroupMembershipRequest = (groupId: string, accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(rejectGroupMembershipRequestRequest(groupId, accountId));
api(getState)
.post(`/api/v1/groups/${groupId}/membership_requests/${accountId}/reject`)
.then(() => dispatch(rejectGroupMembershipRequestSuccess(groupId, accountId)))
.catch(error => dispatch(rejectGroupMembershipRequestFail(groupId, accountId, error)));
};
const rejectGroupMembershipRequestRequest = (groupId: string, accountId: string) => ({
type: GROUP_MEMBERSHIP_REQUEST_REJECT_REQUEST,
groupId,
accountId,
});
const rejectGroupMembershipRequestSuccess = (groupId: string, accountId: string) => ({
type: GROUP_MEMBERSHIP_REQUEST_REJECT_SUCCESS,
groupId,
accountId,
});
const rejectGroupMembershipRequestFail = (groupId: string, accountId: string, error?: AxiosError) => ({
type: GROUP_MEMBERSHIP_REQUEST_REJECT_FAIL,
groupId,
accountId,
error,
});
const changeGroupEditorTitle = (value: string) => ({
type: GROUP_EDITOR_TITLE_CHANGE,
value,
});
const resetGroupEditor = () => ({
type: GROUP_EDITOR_RESET,
});
const submitGroupEditor = (shouldReset?: boolean) => (dispatch: AppDispatch, getState: () => RootState) => {
const groupId = getState().group_editor.groupId;
const displayName = getState().group_editor.displayName;
if (groupId === null) {
dispatch(createGroup(displayName, shouldReset));
} else {
// TODO: dispatch(updateList(listId, title, shouldReset));
}
};
export {
GROUP_CREATE_REQUEST,
GROUP_CREATE_SUCCESS,
GROUP_CREATE_FAIL,
GROUP_DELETE_REQUEST,
GROUP_DELETE_SUCCESS,
GROUP_DELETE_FAIL,
GROUP_FETCH_REQUEST,
GROUP_FETCH_SUCCESS,
GROUP_FETCH_FAIL,
GROUPS_FETCH_REQUEST,
GROUPS_FETCH_SUCCESS,
GROUPS_FETCH_FAIL,
GROUP_RELATIONSHIPS_FETCH_REQUEST,
GROUP_RELATIONSHIPS_FETCH_SUCCESS,
GROUP_RELATIONSHIPS_FETCH_FAIL,
GROUP_JOIN_REQUEST,
GROUP_JOIN_SUCCESS,
GROUP_JOIN_FAIL,
GROUP_LEAVE_REQUEST,
GROUP_LEAVE_SUCCESS,
GROUP_LEAVE_FAIL,
GROUP_DELETE_STATUS_REQUEST,
GROUP_DELETE_STATUS_SUCCESS,
GROUP_DELETE_STATUS_FAIL,
GROUP_KICK_REQUEST,
GROUP_KICK_SUCCESS,
GROUP_KICK_FAIL,
GROUP_BLOCKS_FETCH_REQUEST,
GROUP_BLOCKS_FETCH_SUCCESS,
GROUP_BLOCKS_FETCH_FAIL,
GROUP_BLOCKS_EXPAND_REQUEST,
GROUP_BLOCKS_EXPAND_SUCCESS,
GROUP_BLOCKS_EXPAND_FAIL,
GROUP_BLOCK_REQUEST,
GROUP_BLOCK_SUCCESS,
GROUP_BLOCK_FAIL,
GROUP_UNBLOCK_REQUEST,
GROUP_UNBLOCK_SUCCESS,
GROUP_UNBLOCK_FAIL,
GROUP_PROMOTE_REQUEST,
GROUP_PROMOTE_SUCCESS,
GROUP_PROMOTE_FAIL,
GROUP_DEMOTE_REQUEST,
GROUP_DEMOTE_SUCCESS,
GROUP_DEMOTE_FAIL,
GROUP_MEMBERSHIPS_FETCH_REQUEST,
GROUP_MEMBERSHIPS_FETCH_SUCCESS,
GROUP_MEMBERSHIPS_FETCH_FAIL,
GROUP_MEMBERSHIPS_EXPAND_REQUEST,
GROUP_MEMBERSHIPS_EXPAND_SUCCESS,
GROUP_MEMBERSHIPS_EXPAND_FAIL,
GROUP_MEMBERSHIP_REQUESTS_FETCH_REQUEST,
GROUP_MEMBERSHIP_REQUESTS_FETCH_SUCCESS,
GROUP_MEMBERSHIP_REQUESTS_FETCH_FAIL,
GROUP_MEMBERSHIP_REQUESTS_EXPAND_REQUEST,
GROUP_MEMBERSHIP_REQUESTS_EXPAND_SUCCESS,
GROUP_MEMBERSHIP_REQUESTS_EXPAND_FAIL,
GROUP_MEMBERSHIP_REQUEST_AUTHORIZE_REQUEST,
GROUP_MEMBERSHIP_REQUEST_AUTHORIZE_SUCCESS,
GROUP_MEMBERSHIP_REQUEST_AUTHORIZE_FAIL,
GROUP_MEMBERSHIP_REQUEST_REJECT_REQUEST,
GROUP_MEMBERSHIP_REQUEST_REJECT_SUCCESS,
GROUP_MEMBERSHIP_REQUEST_REJECT_FAIL,
GROUP_EDITOR_TITLE_CHANGE,
GROUP_EDITOR_RESET,
createGroup,
createGroupRequest,
createGroupSuccess,
createGroupFail,
deleteGroup,
deleteGroupRequest,
deleteGroupSuccess,
deleteGroupFail,
fetchGroup,
fetchGroupRequest,
fetchGroupSuccess,
fetchGroupFail,
fetchGroups,
fetchGroupsRequest,
fetchGroupsSuccess,
fetchGroupsFail,
fetchGroupRelationships,
fetchGroupRelationshipsRequest,
fetchGroupRelationshipsSuccess,
fetchGroupRelationshipsFail,
joinGroup,
leaveGroup,
joinGroupRequest,
joinGroupSuccess,
joinGroupFail,
leaveGroupRequest,
leaveGroupSuccess,
leaveGroupFail,
groupDeleteStatus,
groupDeleteStatusRequest,
groupDeleteStatusSuccess,
groupDeleteStatusFail,
groupKick,
groupKickRequest,
groupKickSuccess,
groupKickFail,
fetchGroupBlocks,
fetchGroupBlocksRequest,
fetchGroupBlocksSuccess,
fetchGroupBlocksFail,
expandGroupBlocks,
expandGroupBlocksRequest,
expandGroupBlocksSuccess,
expandGroupBlocksFail,
groupBlock,
groupBlockRequest,
groupBlockSuccess,
groupBlockFail,
groupUnblock,
groupUnblockRequest,
groupUnblockSuccess,
groupUnblockFail,
groupPromoteAccount,
groupPromoteAccountRequest,
groupPromoteAccountSuccess,
groupPromoteAccountFail,
groupDemoteAccount,
groupDemoteAccountRequest,
groupDemoteAccountSuccess,
groupDemoteAccountFail,
fetchGroupMemberships,
fetchGroupMembershipsRequest,
fetchGroupMembershipsSuccess,
fetchGroupMembershipsFail,
expandGroupMemberships,
expandGroupMembershipsRequest,
expandGroupMembershipsSuccess,
expandGroupMembershipsFail,
fetchGroupMembershipRequests,
fetchGroupMembershipRequestsRequest,
fetchGroupMembershipRequestsSuccess,
fetchGroupMembershipRequestsFail,
expandGroupMembershipRequests,
expandGroupMembershipRequestsRequest,
expandGroupMembershipRequestsSuccess,
expandGroupMembershipRequestsFail,
authorizeGroupMembershipRequest,
authorizeGroupMembershipRequestRequest,
authorizeGroupMembershipRequestSuccess,
authorizeGroupMembershipRequestFail,
rejectGroupMembershipRequest,
rejectGroupMembershipRequestRequest,
rejectGroupMembershipRequestSuccess,
rejectGroupMembershipRequestFail,
changeGroupEditorTitle,
resetGroupEditor,
submitGroupEditor,
};

Wyświetl plik

@ -5,42 +5,44 @@ import type { APIEntity } from 'soapbox/types/entities';
const ACCOUNT_IMPORT = 'ACCOUNT_IMPORT';
const ACCOUNTS_IMPORT = 'ACCOUNTS_IMPORT';
const GROUP_IMPORT = 'GROUP_IMPORT';
const GROUPS_IMPORT = 'GROUPS_IMPORT';
const STATUS_IMPORT = 'STATUS_IMPORT';
const STATUSES_IMPORT = 'STATUSES_IMPORT';
const POLLS_IMPORT = 'POLLS_IMPORT';
const ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP = 'ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP';
export function importAccount(account: APIEntity) {
return { type: ACCOUNT_IMPORT, account };
}
const importAccount = (account: APIEntity) =>
({ type: ACCOUNT_IMPORT, account });
export function importAccounts(accounts: APIEntity[]) {
return { type: ACCOUNTS_IMPORT, accounts };
}
const importAccounts = (accounts: APIEntity[]) =>
({ type: ACCOUNTS_IMPORT, accounts });
export function importStatus(status: APIEntity, idempotencyKey?: string) {
return (dispatch: AppDispatch, getState: () => RootState) => {
const importGroup = (group: APIEntity) =>
({ type: GROUP_IMPORT, group });
const importGroups = (groups: APIEntity[]) =>
({ type: GROUPS_IMPORT, groups });
const importStatus = (status: APIEntity, idempotencyKey?: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const expandSpoilers = getSettings(getState()).get('expandSpoilers');
return dispatch({ type: STATUS_IMPORT, status, idempotencyKey, expandSpoilers });
};
}
export function importStatuses(statuses: APIEntity[]) {
return (dispatch: AppDispatch, getState: () => RootState) => {
const importStatuses = (statuses: APIEntity[]) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const expandSpoilers = getSettings(getState()).get('expandSpoilers');
return dispatch({ type: STATUSES_IMPORT, statuses, expandSpoilers });
};
}
export function importPolls(polls: APIEntity[]) {
return { type: POLLS_IMPORT, polls };
}
const importPolls = (polls: APIEntity[]) =>
({ type: POLLS_IMPORT, polls });
export function importFetchedAccount(account: APIEntity) {
return importFetchedAccounts([account]);
}
const importFetchedAccount = (account: APIEntity) =>
importFetchedAccounts([account]);
export function importFetchedAccounts(accounts: APIEntity[], args = { should_refetch: false }) {
const importFetchedAccounts = (accounts: APIEntity[], args = { should_refetch: false }) => {
const { should_refetch } = args;
const normalAccounts: APIEntity[] = [];
@ -61,10 +63,24 @@ export function importFetchedAccounts(accounts: APIEntity[], args = { should_ref
accounts.forEach(processAccount);
return importAccounts(normalAccounts);
}
};
export function importFetchedStatus(status: APIEntity, idempotencyKey?: string) {
return (dispatch: AppDispatch) => {
const importFetchedGroups = (groups: APIEntity[]) => {
const normalGroups: APIEntity[] = [];
const processGroup = (group: APIEntity) => {
if (!group.id) return;
normalGroups.push(group);
};
groups.forEach(processGroup);
return importGroups(normalGroups);
};
const importFetchedStatus = (status: APIEntity, idempotencyKey?: string) =>
(dispatch: AppDispatch) => {
// Skip broken statuses
if (isBroken(status)) return;
@ -99,7 +115,6 @@ export function importFetchedStatus(status: APIEntity, idempotencyKey?: string)
dispatch(importFetchedAccount(status.account));
dispatch(importStatus(status, idempotencyKey));
};
}
// Sometimes Pleroma can return an empty account,
// or a repost can appear of a deleted account. Skip these statuses.
@ -117,8 +132,8 @@ const isBroken = (status: APIEntity) => {
}
};
export function importFetchedStatuses(statuses: APIEntity[]) {
return (dispatch: AppDispatch, getState: () => RootState) => {
const importFetchedStatuses = (statuses: APIEntity[]) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const accounts: APIEntity[] = [];
const normalStatuses: APIEntity[] = [];
const polls: APIEntity[] = [];
@ -154,23 +169,36 @@ export function importFetchedStatuses(statuses: APIEntity[]) {
dispatch(importFetchedAccounts(accounts));
dispatch(importStatuses(normalStatuses));
};
}
export function importFetchedPoll(poll: APIEntity) {
return (dispatch: AppDispatch) => {
const importFetchedPoll = (poll: APIEntity) =>
(dispatch: AppDispatch) => {
dispatch(importPolls([poll]));
};
}
export function importErrorWhileFetchingAccountByUsername(username: string) {
return { type: ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP, username };
}
const importErrorWhileFetchingAccountByUsername = (username: string) =>
({ type: ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP, username });
export {
ACCOUNT_IMPORT,
ACCOUNTS_IMPORT,
GROUP_IMPORT,
GROUPS_IMPORT,
STATUS_IMPORT,
STATUSES_IMPORT,
POLLS_IMPORT,
ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP,
importAccount,
importAccounts,
importGroup,
importGroups,
importStatus,
importStatuses,
importPolls,
importFetchedAccount,
importFetchedAccounts,
importFetchedGroups,
importFetchedStatus,
importFetchedStatuses,
importFetchedPoll,
importErrorWhileFetchingAccountByUsername,
};

Wyświetl plik

@ -1,7 +1,7 @@
import api from '../api';
import { fetchRelationships } from './accounts';
import { importFetchedAccounts, importFetchedStatuses } from './importer';
import { importFetchedAccounts, importFetchedGroups, importFetchedStatuses } from './importer';
import type { AxiosError } from 'axios';
import type { SearchFilter } from 'soapbox/reducers/search';
@ -83,6 +83,10 @@ const submitSearch = (filter?: SearchFilter) =>
dispatch(importFetchedStatuses(response.data.statuses));
}
if (response.data.groups) {
dispatch(importFetchedGroups(response.data.groups));
}
dispatch(fetchSearchSuccess(response.data, value, type));
dispatch(fetchRelationships(response.data.accounts.map((item: APIEntity) => item.id)));
}).catch(error => {
@ -139,6 +143,10 @@ const expandSearch = (type: SearchFilter) => (dispatch: AppDispatch, getState: (
dispatch(importFetchedStatuses(data.statuses));
}
if (data.groups) {
dispatch(importFetchedGroups(data.groups));
}
dispatch(expandSearchSuccess(data, value, type));
dispatch(fetchRelationships(data.accounts.map((item: APIEntity) => item.id)));
}).catch(error => {

Wyświetl plik

@ -159,6 +159,8 @@ const defaultSettings = ImmutableMap({
}),
}),
groups: ImmutableMap({}),
trends: ImmutableMap({
show: true,
}),

Wyświetl plik

@ -22,6 +22,7 @@ import type { SearchFilter } from 'soapbox/reducers/search';
const messages = defineMessages({
accounts: { id: 'search_results.accounts', defaultMessage: 'People' },
statuses: { id: 'search_results.statuses', defaultMessage: 'Posts' },
groups: { id: 'search_results.groups', defaultMessage: 'Groups' },
hashtags: { id: 'search_results.hashtags', defaultMessage: 'Hashtags' },
});
@ -59,6 +60,11 @@ const SearchResults = () => {
action: () => selectFilter('statuses'),
name: 'statuses',
},
{
text: intl.formatMessage(messages.groups),
action: () => selectFilter('groups'),
name: 'groups',
},
{
text: intl.formatMessage(messages.hashtags),
action: () => selectFilter('hashtags'),
@ -170,6 +176,39 @@ const SearchResults = () => {
}
}
if (selectedFilter === 'groups') {
hasMore = results.groupsHasMore;
loaded = results.groupsLoaded;
if (results.groups && results.groups.size > 0) {
searchResults = results.groups.map((groupId: string) => (
<></>
));
resultsIds = results.groups;
} else if (!submitted && trendingStatuses && !trendingStatuses.isEmpty()) {
// searchResults = trendingStatuses.map((statusId: string) => (
// // @ts-ignore
// <StatusContainer
// key={statusId}
// id={statusId}
// onMoveUp={handleMoveUp}
// onMoveDown={handleMoveDown}
// />
// ));
// resultsIds = trendingStatuses;
} else if (loaded) {
noResultsMessage = (
<div className='empty-column-indicator'>
<FormattedMessage
id='empty_column.search.groups'
defaultMessage='There are no groups results for "{term}"'
values={{ term: value }}
/>
</div>
);
}
}
if (selectedFilter === 'hashtags') {
hasMore = results.hashtagsHasMore;
loaded = results.hashtagsLoaded;

Wyświetl plik

@ -0,0 +1,22 @@
/**
* Group relationship normalizer:
* Converts API group relationships into our internal format.
*/
import {
Map as ImmutableMap,
Record as ImmutableRecord,
fromJS,
} from 'immutable';
export const GroupRelationshipRecord = ImmutableRecord({
id: '',
member: false,
requested: false,
role: null as 'admin' | 'moderator' | 'user' | null,
});
export const normalizeGroupRelationship = (relationship: Record<string, any>) => {
return GroupRelationshipRecord(
ImmutableMap(fromJS(relationship)),
);
};

Wyświetl plik

@ -0,0 +1,152 @@
/**
* Group normalizer:
* Converts API groups into our internal format.
*/
import escapeTextContentForBrowser from 'escape-html';
import {
Map as ImmutableMap,
List as ImmutableList,
Record as ImmutableRecord,
fromJS,
} from 'immutable';
import emojify from 'soapbox/features/emoji/emoji';
import { normalizeEmoji } from 'soapbox/normalizers/emoji';
import { unescapeHTML } from 'soapbox/utils/html';
import { makeEmojiMap } from 'soapbox/utils/normalizers';
import type { Emoji, GroupRelationship } from 'soapbox/types/entities';
export const GroupRecord = ImmutableRecord({
avatar: '',
avatar_static: '',
created_at: '',
display_name: '',
domain: '',
emojis: ImmutableList<Emoji>(),
header: '',
header_static: '',
id: '',
locked: false,
membership_required: false,
note: '',
statuses_visibility: 'public',
uri: '',
url: '',
// Internal fields
display_name_html: '',
note_emojified: '',
note_plain: '',
relationship: null as GroupRelationship | null,
});
/** Add avatar, if missing */
const normalizeAvatar = (group: ImmutableMap<string, any>) => {
const avatar = group.get('avatar');
const avatarStatic = group.get('avatar_static');
const missing = require('assets/images/avatar-missing.png');
return group.withMutations(group => {
group.set('avatar', avatar || avatarStatic || missing);
group.set('avatar_static', avatarStatic || avatar || missing);
});
};
/** Add header, if missing */
const normalizeHeader = (group: ImmutableMap<string, any>) => {
const header = group.get('header');
const headerStatic = group.get('header_static');
const missing = require('assets/images/header-missing.png');
return group.withMutations(group => {
group.set('header', header || headerStatic || missing);
group.set('header_static', headerStatic || header || missing);
});
};
/** Normalize emojis */
const normalizeEmojis = (entity: ImmutableMap<string, any>) => {
const emojis = entity.get('emojis', ImmutableList()).map(normalizeEmoji);
return entity.set('emojis', emojis);
};
/** Set display name from username, if applicable */
const fixDisplayName = (group: ImmutableMap<string, any>) => {
const displayName = group.get('display_name') || '';
return group.set('display_name', displayName.trim().length === 0 ? group.get('username') : displayName);
};
/** Emojification, etc */
const addInternalFields = (group: ImmutableMap<string, any>) => {
const emojiMap = makeEmojiMap(group.get('emojis'));
return group.withMutations((group: ImmutableMap<string, any>) => {
// Emojify group properties
group.merge({
display_name_html: emojify(escapeTextContentForBrowser(group.get('display_name')), emojiMap),
note_emojified: emojify(group.get('note', ''), emojiMap),
note_plain: unescapeHTML(group.get('note', '')),
});
// Emojify fields
group.update('fields', ImmutableList(), fields => {
return fields.map((field: ImmutableMap<string, any>) => {
return field.merge({
name_emojified: emojify(escapeTextContentForBrowser(field.get('name')), emojiMap),
value_emojified: emojify(field.get('value'), emojiMap),
value_plain: unescapeHTML(field.get('value')),
});
});
});
});
};
const getDomainFromURL = (group: ImmutableMap<string, any>): string => {
try {
const url = group.get('url');
return new URL(url).host;
} catch {
return '';
}
};
export const guessFqn = (group: ImmutableMap<string, any>): string => {
const acct = group.get('acct', '');
const [user, domain] = acct.split('@');
if (domain) {
return acct;
} else {
return [user, getDomainFromURL(group)].join('@');
}
};
const normalizeFqn = (group: ImmutableMap<string, any>) => {
const fqn = group.get('fqn') || guessFqn(group);
return group.set('fqn', fqn);
};
/** Rewrite `<p></p>` to empty string. */
const fixNote = (group: ImmutableMap<string, any>) => {
if (group.get('note') === '<p></p>') {
return group.set('note', '');
} else {
return group;
}
};
export const normalizeGroup = (group: Record<string, any>) => {
return GroupRecord(
ImmutableMap(fromJS(group)).withMutations(group => {
normalizeEmojis(group);
normalizeAvatar(group);
normalizeHeader(group);
normalizeFqn(group);
fixDisplayName(group);
fixNote(group);
addInternalFields(group);
}),
);
};

Wyświetl plik

@ -9,6 +9,8 @@ export { ChatRecord, normalizeChat } from './chat';
export { ChatMessageRecord, normalizeChatMessage } from './chat-message';
export { EmojiRecord, normalizeEmoji } from './emoji';
export { FilterRecord, normalizeFilter } from './filter';
export { GroupRecord, normalizeGroup } from './group';
export { GroupRelationshipRecord, normalizeGroupRelationship } from './group-relationship';
export { HistoryRecord, normalizeHistory } from './history';
export { InstanceRecord, normalizeInstance } from './instance';
export { ListRecord, normalizeList } from './list';

Wyświetl plik

@ -17,7 +17,7 @@ import { normalizeMention } from 'soapbox/normalizers/mention';
import { normalizePoll } from 'soapbox/normalizers/poll';
import type { ReducerAccount } from 'soapbox/reducers/accounts';
import type { Account, Attachment, Card, Emoji, Mention, Poll, EmbeddedEntity } from 'soapbox/types/entities';
import type { Account, Attachment, Card, Emoji, Group, Mention, Poll, EmbeddedEntity } from 'soapbox/types/entities';
export type StatusVisibility = 'public' | 'unlisted' | 'private' | 'direct' | 'self';
@ -48,7 +48,7 @@ export const StatusRecord = ImmutableRecord({
emojis: ImmutableList<Emoji>(),
favourited: false,
favourites_count: 0,
group: null as EmbeddedEntity<any>,
group: null as EmbeddedEntity<Group>,
in_reply_to_account_id: null as string | null,
in_reply_to_id: null as string | null,
id: '',

Wyświetl plik

@ -8,6 +8,7 @@ import { ACCOUNT_IMPORT, ACCOUNTS_IMPORT } from 'soapbox/actions/importer';
import { STREAMING_FOLLOW_RELATIONSHIPS_UPDATE } from 'soapbox/actions/streaming';
import type { AnyAction } from 'redux';
import type { APIEntity } from 'soapbox/types/entities';
const CounterRecord = ImmutableRecord({
followers_count: 0,
@ -17,7 +18,6 @@ const CounterRecord = ImmutableRecord({
type Counter = ReturnType<typeof CounterRecord>;
type State = ImmutableMap<string, Counter>;
type APIEntity = Record<string, any>;
type APIEntities = Array<APIEntity>;
const normalizeAccount = (state: State, account: APIEntity) => state.set(account.id, CounterRecord({

Wyświetl plik

@ -39,10 +39,10 @@ import { normalizeAccount } from 'soapbox/normalizers/account';
import { normalizeId } from 'soapbox/utils/normalizers';
import type { AnyAction } from 'redux';
import type { APIEntity } from 'soapbox/types/entities';
type AccountRecord = ReturnType<typeof normalizeAccount>;
type AccountMap = ImmutableMap<string, any>;
type APIEntity = Record<string, any>;
type APIEntities = Array<APIEntity>;
export interface ReducerAccount extends AccountRecord {

Wyświetl plik

@ -7,6 +7,7 @@ import {
import { ADMIN_LOG_FETCH_SUCCESS } from 'soapbox/actions/admin';
import type { AnyAction } from 'redux';
import type { APIEntity } from 'soapbox/types/entities';
const LogEntryRecord = ImmutableRecord({
data: ImmutableMap<string, any>(),
@ -23,7 +24,6 @@ const ReducerRecord = ImmutableRecord({
type LogEntry = ReturnType<typeof LogEntryRecord>;
type State = ReturnType<typeof ReducerRecord>;
type APIEntity = Record<string, any>;
type APIEntities = Array<APIEntity>;
const parseItems = (items: APIEntities) => {

Wyświetl plik

@ -11,8 +11,8 @@ import {
import { STREAMING_CHAT_UPDATE } from 'soapbox/actions/streaming';
import type { AnyAction } from 'redux';
import type { APIEntity } from 'soapbox/types/entities';
type APIEntity = Record<string, any>;
type APIEntities = Array<APIEntity>;
type State = ImmutableMap<string, ImmutableOrderedSet<string>>;

Wyświetl plik

@ -13,9 +13,9 @@ import { STREAMING_CHAT_UPDATE } from 'soapbox/actions/streaming';
import { normalizeChatMessage } from 'soapbox/normalizers';
import type { AnyAction } from 'redux';
import type { APIEntity } from 'soapbox/types/entities';
type ChatMessageRecord = ReturnType<typeof normalizeChatMessage>;
type APIEntity = Record<string, any>;
type APIEntities = Array<APIEntity>;
type State = ImmutableMap<string, ChatMessageRecord>;

Wyświetl plik

@ -14,9 +14,9 @@ import { normalizeChat } from 'soapbox/normalizers';
import { normalizeId } from 'soapbox/utils/normalizers';
import type { AnyAction } from 'redux';
import type { APIEntity } from 'soapbox/types/entities';
type ChatRecord = ReturnType<typeof normalizeChat>;
type APIEntity = Record<string, any>;
type APIEntities = Array<APIEntity>;
export interface ReducerChat extends ChatRecord {

Wyświetl plik

@ -78,6 +78,7 @@ export const ReducerCompose = ImmutableRecord({
caretPosition: null as number | null,
content_type: 'text/plain',
focusDate: null as Date | null,
group_id: null as string | null,
idempotencyKey: '',
id: null as string | null,
in_reply_to: null as string | null,
@ -202,6 +203,9 @@ const insertEmoji = (compose: Compose, position: number, emojiData: Emoji, needs
const privacyPreference = (a: string, b: string) => {
const order = ['public', 'unlisted', 'private', 'direct'];
if (a === 'group') return a;
return order[Math.max(order.indexOf(a), order.indexOf(b), 0)];
};
@ -309,6 +313,7 @@ export default function compose(state = initialState, action: AnyAction) {
return updateCompose(state, action.id, compose => compose.withMutations(map => {
const defaultCompose = state.get('default')!;
map.set('group_id', action.status.getIn(['group', 'id']) || action.status.get('group'));
map.set('in_reply_to', action.status.get('id'));
map.set('to', action.explicitAddressing ? statusToMentionsArray(action.status, action.account) : ImmutableOrderedSet<string>());
map.set('text', !action.explicitAddressing ? statusToTextMentions(action.status, action.account) : '');
@ -427,6 +432,7 @@ export default function compose(state = initialState, action: AnyAction) {
map.set('idempotencyKey', uuid());
map.set('content_type', action.contentType || 'text/plain');
map.set('quote', action.status.get('quote'));
map.set('group_id', action.status.get('group'));
if (action.v?.software === PLEROMA && action.withRedraft && hasIntegerMediaIds(action.status)) {
map.set('media_attachments', ImmutableList());

Wyświetl plik

@ -0,0 +1,49 @@
import { Record as ImmutableRecord } from 'immutable';
import {
GROUP_EDITOR_RESET,
GROUP_EDITOR_TITLE_CHANGE,
GROUP_CREATE_REQUEST,
GROUP_CREATE_FAIL,
GROUP_CREATE_SUCCESS,
} from 'soapbox/actions/groups';
import type { AnyAction } from 'redux';
const ReducerRecord = ImmutableRecord({
groupId: null as string | null,
isSubmitting: false,
isChanged: false,
displayName: '',
note: '',
avatar: null,
header: null,
});
type State = ReturnType<typeof ReducerRecord>;
export default function groupEditor(state: State = ReducerRecord(), action: AnyAction) {
switch (action.type) {
case GROUP_EDITOR_RESET:
return ReducerRecord();
case GROUP_EDITOR_TITLE_CHANGE:
return state.withMutations(map => {
map.set('displayName', action.value);
map.set('isChanged', true);
});
case GROUP_CREATE_REQUEST:
return state.withMutations(map => {
map.set('isSubmitting', true);
map.set('isChanged', false);
});
case GROUP_CREATE_FAIL:
return state.set('isSubmitting', false);
case GROUP_CREATE_SUCCESS:
return state.withMutations(map => {
map.set('isSubmitting', false);
map.set('groupId', action.group.id);
});
default:
return state;
}
}

Wyświetl plik

@ -0,0 +1,100 @@
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet, Record as ImmutableRecord } from 'immutable';
import {
GROUP_DELETE_SUCCESS,
GROUP_MEMBERSHIPS_FETCH_REQUEST,
GROUP_MEMBERSHIPS_FETCH_FAIL,
GROUP_MEMBERSHIPS_FETCH_SUCCESS,
GROUP_MEMBERSHIPS_EXPAND_REQUEST,
GROUP_MEMBERSHIPS_EXPAND_FAIL,
GROUP_MEMBERSHIPS_EXPAND_SUCCESS,
GROUP_PROMOTE_SUCCESS,
GROUP_DEMOTE_SUCCESS,
GROUP_KICK_SUCCESS,
GROUP_BLOCK_SUCCESS,
} from 'soapbox/actions/groups';
import type { AnyAction } from 'redux';
import type { APIEntity } from 'soapbox/types/entities';
const ListRecord = ImmutableRecord({
next: null as string | null,
isLoading: false,
items: ImmutableOrderedSet<string>(),
});
const ReducerRecord = ImmutableRecord({
admin: ImmutableMap<string, List>({}),
moderator: ImmutableMap<string, List>({}),
user: ImmutableMap<string, List>({}),
});
export type GroupRole = 'admin' | 'moderator' | 'user';
type List = ReturnType<typeof ListRecord>;
type State = ReturnType<typeof ReducerRecord>;
const normalizeList = (state: State, path: string[], memberships: APIEntity[], next: string | null) => {
return state.setIn(path, ImmutableMap({
next,
items: ImmutableOrderedSet(memberships.map(item => item.account.id)),
isLoading: false,
}));
};
const appendToList = (state: State, path: string[], memberships: APIEntity[], next: string | null) => {
return state.updateIn(path, map => {
return (map as List).set('next', next).set('isLoading', false).update('items', list => list.concat(memberships.map(item => item.account.id)));
});
};
const updateLists = (state: State, groupId: string, memberships: APIEntity[]) => {
const updateList = (state: State, role: string, membership: APIEntity) => {
if (role === membership.role) {
return state.updateIn([role, groupId], map => (map as List).update('items', set => set.add(membership.account.id)));
} else {
return state.updateIn([role, groupId], map => (map as List).update('items', set => set.delete(membership.account.id)));
}
};
memberships.forEach(membership => {
state = updateList(state, 'admin', membership);
state = updateList(state, 'moderator', membership);
state = updateList(state, 'user', membership);
});
return state;
};
const removeFromList = (state: State, path: string[], accountId: string) => {
return state.updateIn(path, map => {
return (map as List).update('items', set => set.delete(accountId));
});
};
export default function groupMemberships(state: State = ReducerRecord(), action: AnyAction) {
switch (action.type) {
case GROUP_DELETE_SUCCESS:
return state.deleteIn(['admin', action.id]).deleteIn(['moderator', action.id]).deleteIn(['user', action.id]);
case GROUP_MEMBERSHIPS_FETCH_REQUEST:
case GROUP_MEMBERSHIPS_EXPAND_REQUEST:
return state.updateIn([action.role, action.id], map => (map as List || ListRecord()).set('isLoading', true));
case GROUP_MEMBERSHIPS_FETCH_FAIL:
case GROUP_MEMBERSHIPS_EXPAND_FAIL:
return state.updateIn([action.role, action.id], map => (map as List || ListRecord()).set('isLoading', false));
case GROUP_MEMBERSHIPS_FETCH_SUCCESS:
return normalizeList(state, [action.role, action.id], action.memberships, action.next);
case GROUP_MEMBERSHIPS_EXPAND_SUCCESS:
return appendToList(state, [action.role, action.id], action.memberships, action.next);
case GROUP_PROMOTE_SUCCESS:
case GROUP_DEMOTE_SUCCESS:
return updateLists(state, action.groupId, action.memberships);
case GROUP_KICK_SUCCESS:
case GROUP_BLOCK_SUCCESS:
state = removeFromList(state, ['admin', action.groupId], action.accountId);
state = removeFromList(state, ['moderator', action.groupId], action.accountId);
state = removeFromList(state, ['user', action.groupId], action.accountId);
return state;
default:
return state;
}
}

Wyświetl plik

@ -0,0 +1,54 @@
import { Map as ImmutableMap } from 'immutable';
import {
GROUP_CREATE_SUCCESS,
GROUP_DELETE_SUCCESS,
GROUP_RELATIONSHIPS_FETCH_SUCCESS,
GROUP_JOIN_REQUEST,
GROUP_JOIN_SUCCESS,
GROUP_JOIN_FAIL,
GROUP_LEAVE_REQUEST,
GROUP_LEAVE_SUCCESS,
GROUP_LEAVE_FAIL,
} from 'soapbox/actions/groups';
import { normalizeGroupRelationship } from 'soapbox/normalizers';
import type { AnyAction } from 'redux';
import type { APIEntity } from 'soapbox/types/entities';
type GroupRelationshipRecord = ReturnType<typeof normalizeGroupRelationship>;
type APIEntities = Array<APIEntity>;
type State = ImmutableMap<string, GroupRelationshipRecord>;
const normalizeRelationships = (state: State, relationships: APIEntities) => {
relationships.forEach(relationship => {
state = state.set(relationship.id, normalizeGroupRelationship(relationship));
});
return state;
};
export default function groupRelationships(state: State = ImmutableMap(), action: AnyAction) {
switch (action.type) {
case GROUP_CREATE_SUCCESS:
return state.set(action.group.id, normalizeGroupRelationship({ id: action.group.id, member: true, requested: false, role: 'admin' }));
case GROUP_DELETE_SUCCESS:
return state.delete(action.id);
case GROUP_JOIN_REQUEST:
return state.getIn([action.id, 'member']) ? state : state.setIn([action.id, action.locked ? 'requested' : 'member'], true);
case GROUP_JOIN_FAIL:
return state.setIn([action.id, action.locked ? 'requested' : 'member'], false);
case GROUP_LEAVE_REQUEST:
return state.setIn([action.id, 'member'], false);
case GROUP_LEAVE_FAIL:
return state.setIn([action.id, 'member'], true);
case GROUP_JOIN_SUCCESS:
case GROUP_LEAVE_SUCCESS:
return normalizeRelationships(state, [action.relationship]);
case GROUP_RELATIONSHIPS_FETCH_SUCCESS:
return normalizeRelationships(state, action.relationships);
default:
return state;
}
}

Wyświetl plik

@ -0,0 +1,33 @@
import { Map as ImmutableMap } from 'immutable';
import { GROUP_FETCH_FAIL, GROUP_DELETE_SUCCESS } from 'soapbox/actions/groups';
import { GROUPS_IMPORT } from 'soapbox/actions/importer';
import { normalizeGroup } from 'soapbox/normalizers';
import type { AnyAction } from 'redux';
import type { APIEntity } from 'soapbox/types/entities';
type GroupRecord = ReturnType<typeof normalizeGroup>;
type APIEntities = Array<APIEntity>;
type State = ImmutableMap<string, GroupRecord | false>;
const normalizeGroups = (state: State, relationships: APIEntities) => {
relationships.forEach(relationship => {
state = state.set(relationship.id, normalizeGroup(relationship));
});
return state;
};
export default function groups(state: State = ImmutableMap(), action: AnyAction) {
switch (action.type) {
case GROUPS_IMPORT:
return normalizeGroups(state, action.groups);
case GROUP_DELETE_SUCCESS:
case GROUP_FETCH_FAIL:
return state.set(action.id, false);
default:
return state;
}
}

Wyświetl plik

@ -26,6 +26,10 @@ import custom_emojis from './custom-emojis';
import domain_lists from './domain-lists';
import dropdown_menu from './dropdown-menu';
import filters from './filters';
import group_editor from './group-editor';
import group_memberships from './group-memberships';
import group_relationships from './group-relationships';
import groups from './groups';
import history from './history';
import instance from './instance';
import listAdder from './list-adder';
@ -120,6 +124,10 @@ const reducers = {
history,
announcements,
compose_event,
groups,
group_relationships,
group_memberships,
group_editor,
};
// Build a default state from all reducers: it has the key and `undefined`

Wyświetl plik

@ -11,9 +11,9 @@ import {
import { normalizeList } from 'soapbox/normalizers';
import type { AnyAction } from 'redux';
import type { APIEntity } from 'soapbox/types/entities';
type ListRecord = ReturnType<typeof normalizeList>;
type APIEntity = Record<string, any>;
type APIEntities = Array<APIEntity>;
type State = ImmutableMap<string, ListRecord | false>;

Wyświetl plik

@ -33,10 +33,10 @@ import {
} from '../actions/importer';
import type { AnyAction } from 'redux';
import type { APIEntity } from 'soapbox/types/entities';
type Relationship = ReturnType<typeof normalizeRelationship>;
type State = ImmutableMap<string, Relationship>;
type APIEntity = Record<string, any>;
type APIEntities = Array<APIEntity>;
const normalizeRelationships = (state: State, relationships: APIEntities) => {

Wyświetl plik

@ -27,12 +27,15 @@ import type { APIEntity, Tag } from 'soapbox/types/entities';
const ResultsRecord = ImmutableRecord({
accounts: ImmutableOrderedSet<string>(),
statuses: ImmutableOrderedSet<string>(),
groups: ImmutableOrderedSet<string>(),
hashtags: ImmutableOrderedSet<Tag>(), // it's a list of maps
accountsHasMore: false,
statusesHasMore: false,
groupsHasMore: false,
hashtagsHasMore: false,
accountsLoaded: false,
statusesLoaded: false,
groupsLoaded: false,
hashtagsLoaded: false,
});
@ -48,7 +51,7 @@ const ReducerRecord = ImmutableRecord({
type State = ReturnType<typeof ReducerRecord>;
type APIEntities = Array<APIEntity>;
export type SearchFilter = 'accounts' | 'statuses' | 'hashtags';
export type SearchFilter = 'accounts' | 'statuses' | 'groups' | 'hashtags';
const toIds = (items: APIEntities) => {
return ImmutableOrderedSet(items.map(item => item.id));
@ -60,12 +63,15 @@ const importResults = (state: State, results: APIEntity, searchTerm: string, sea
state.set('results', ResultsRecord({
accounts: toIds(results.accounts),
statuses: toIds(results.statuses),
groups: toIds(results.groups),
hashtags: ImmutableOrderedSet(results.hashtags.map(normalizeTag)), // it's a list of records
accountsHasMore: results.accounts.length >= 20,
statusesHasMore: results.statuses.length >= 20,
groupsHasMore: results.groups.length >= 20,
hashtagsHasMore: results.hashtags.length >= 20,
accountsLoaded: true,
statusesLoaded: true,
groupsLoaded: true,
hashtagsLoaded: true,
}));

Wyświetl plik

@ -42,11 +42,11 @@ import {
import { TIMELINE_DELETE } from '../actions/timelines';
import type { AnyAction } from 'redux';
import type { APIEntity } from 'soapbox/types/entities';
const domParser = new DOMParser();
type StatusRecord = ReturnType<typeof normalizeStatus>;
type APIEntity = Record<string, any>;
type APIEntities = Array<APIEntity>;
type State = ImmutableMap<string, ReducerStatus>;
@ -56,6 +56,7 @@ export interface ReducerStatus extends StatusRecord {
reblog: string | null,
poll: string | null,
quote: string | null,
group: string | null,
}
const minifyStatus = (status: StatusRecord): ReducerStatus => {

Wyświetl plik

@ -40,6 +40,23 @@ import {
import {
FAMILIAR_FOLLOWERS_FETCH_SUCCESS,
} from 'soapbox/actions/familiar-followers';
import {
GROUP_MEMBERSHIP_REQUESTS_FETCH_SUCCESS,
GROUP_MEMBERSHIP_REQUESTS_EXPAND_SUCCESS,
GROUP_MEMBERSHIP_REQUESTS_FETCH_REQUEST,
GROUP_MEMBERSHIP_REQUESTS_EXPAND_REQUEST,
GROUP_MEMBERSHIP_REQUESTS_FETCH_FAIL,
GROUP_MEMBERSHIP_REQUESTS_EXPAND_FAIL,
GROUP_MEMBERSHIP_REQUEST_AUTHORIZE_SUCCESS,
GROUP_MEMBERSHIP_REQUEST_REJECT_SUCCESS,
GROUP_BLOCKS_FETCH_REQUEST,
GROUP_BLOCKS_FETCH_SUCCESS,
GROUP_BLOCKS_FETCH_FAIL,
GROUP_BLOCKS_EXPAND_REQUEST,
GROUP_BLOCKS_EXPAND_SUCCESS,
GROUP_BLOCKS_EXPAND_FAIL,
GROUP_UNBLOCK_SUCCESS,
} from 'soapbox/actions/groups';
import {
REBLOGS_FETCH_SUCCESS,
FAVOURITES_FETCH_SUCCESS,
@ -99,6 +116,8 @@ export const ReducerRecord = ImmutableRecord({
familiar_followers: ImmutableMap<string, List>(),
event_participations: ImmutableMap<string, List>(),
event_participation_requests: ImmutableMap<string, ParticipationRequestList>(),
membership_requests: ImmutableMap<string, List>(),
group_blocks: ImmutableMap<string, List>(),
});
type State = ReturnType<typeof ReducerRecord>;
@ -108,7 +127,7 @@ type ReactionList = ReturnType<typeof ReactionListRecord>;
type ParticipationRequest = ReturnType<typeof ParticipationRequestRecord>;
type ParticipationRequestList = ReturnType<typeof ParticipationRequestListRecord>;
type Items = ImmutableOrderedSet<string>;
type NestedListPath = ['followers' | 'following' | 'reblogged_by' | 'favourited_by' | 'reactions' | 'pinned' | 'birthday_reminders' | 'familiar_followers' | 'event_participations' | 'event_participation_requests', string];
type NestedListPath = ['followers' | 'following' | 'reblogged_by' | 'favourited_by' | 'reactions' | 'pinned' | 'birthday_reminders' | 'familiar_followers' | 'event_participations' | 'event_participation_requests' | 'membership_requests' | 'group_blocks', string];
type ListPath = ['follow_requests' | 'blocks' | 'mutes' | 'directory'];
const normalizeList = (state: State, path: NestedListPath | ListPath, accounts: APIEntity[], next?: string | null) => {
@ -220,6 +239,31 @@ export default function userLists(state = ReducerRecord(), action: AnyAction) {
['event_participation_requests', action.id, 'items'],
items => (items as ImmutableOrderedSet<ParticipationRequest>).filter(({ account }) => account !== action.accountId),
);
case GROUP_MEMBERSHIP_REQUESTS_FETCH_SUCCESS:
return normalizeList(state, ['membership_requests', action.id], action.accounts, action.next);
case GROUP_MEMBERSHIP_REQUESTS_EXPAND_SUCCESS:
return appendToList(state, ['membership_requests', action.id], action.accounts, action.next);
case GROUP_MEMBERSHIP_REQUESTS_FETCH_REQUEST:
case GROUP_MEMBERSHIP_REQUESTS_EXPAND_REQUEST:
return state.setIn(['membership_requests', action.id, 'isLoading'], true);
case GROUP_MEMBERSHIP_REQUESTS_FETCH_FAIL:
case GROUP_MEMBERSHIP_REQUESTS_EXPAND_FAIL:
return state.setIn(['membership_requests', action.id, 'isLoading'], false);
case GROUP_MEMBERSHIP_REQUEST_AUTHORIZE_SUCCESS:
case GROUP_MEMBERSHIP_REQUEST_REJECT_SUCCESS:
return state.updateIn(['membership_requests', action.groupId, 'items'], list => (list as ImmutableOrderedSet<string>).filterNot(item => item === action.accountId));
case GROUP_BLOCKS_FETCH_SUCCESS:
return normalizeList(state, ['group_blocks', action.id], action.accounts, action.next);
case GROUP_BLOCKS_EXPAND_SUCCESS:
return appendToList(state, ['group_blocks', action.id], action.accounts, action.next);
case GROUP_BLOCKS_FETCH_REQUEST:
case GROUP_BLOCKS_EXPAND_REQUEST:
return state.setIn(['group_blocks', action.id, 'isLoading'], true);
case GROUP_BLOCKS_FETCH_FAIL:
case GROUP_BLOCKS_EXPAND_FAIL:
return state.setIn(['group_blocks', action.id, 'isLoading'], false);
case GROUP_UNBLOCK_SUCCESS:
return state.updateIn(['group_blocks', action.groupId, 'items'], list => (list as ImmutableOrderedSet<string>).filterNot(item => item === action.accountId));
default:
return state;
}

Wyświetl plik

@ -144,12 +144,13 @@ export const makeGetStatus = () => {
(state: RootState, { id }: APIStatus) => state.statuses.get(state.statuses.get(id)?.reblog || ''),
(state: RootState, { id }: APIStatus) => state.accounts.get(state.statuses.get(id)?.account || ''),
(state: RootState, { id }: APIStatus) => state.accounts.get(state.statuses.get(state.statuses.get(id)?.reblog || '')?.account || ''),
(state: RootState, { id }: APIStatus) => state.groups.get(state.statuses.get(id)?.group || ''),
(_state: RootState, { username }: APIStatus) => username,
getFilters,
(state: RootState) => state.me,
],
(statusBase, statusReblog, accountBase, accountReblog, username, filters, me) => {
(statusBase, statusReblog, accountBase, accountReblog, group, username, filters, me) => {
if (!statusBase || !accountBase) return null;
const accountUsername = accountBase.acct;
@ -172,6 +173,8 @@ export const makeGetStatus = () => {
map.set('reblog', statusReblog || null);
// @ts-ignore :(
map.set('account', accountBase || null);
// @ts-ignore
map.set('group', group || null);
map.set('filtered', Boolean(filtered));
});
},

Wyświetl plik

@ -11,6 +11,8 @@ import {
EmojiRecord,
FieldRecord,
FilterRecord,
GroupRecord,
GroupRelationshipRecord,
HistoryRecord,
InstanceRecord,
ListRecord,
@ -38,6 +40,8 @@ type ChatMessage = ReturnType<typeof ChatMessageRecord>;
type Emoji = ReturnType<typeof EmojiRecord>;
type Field = ReturnType<typeof FieldRecord>;
type Filter = ReturnType<typeof FilterRecord>;
type Group = ReturnType<typeof GroupRecord>;
type GroupRelationship = ReturnType<typeof GroupRelationshipRecord>;
type History = ReturnType<typeof HistoryRecord>;
type Instance = ReturnType<typeof InstanceRecord>;
type List = ReturnType<typeof ListRecord>;
@ -79,6 +83,8 @@ export {
Emoji,
Field,
Filter,
Group,
GroupRelationship,
History,
Instance,
List,