Reducers: TypeScrpt + fixes
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
877cae1bf4
commit
af695e3812
|
@ -1,3 +1,5 @@
|
||||||
|
import { fromJS } from 'immutable';
|
||||||
|
|
||||||
import { mockStore } from 'soapbox/jest/test-helpers';
|
import { mockStore } from 'soapbox/jest/test-helpers';
|
||||||
import { InstanceRecord } from 'soapbox/normalizers';
|
import { InstanceRecord } from 'soapbox/normalizers';
|
||||||
import rootReducer from 'soapbox/reducers';
|
import rootReducer from 'soapbox/reducers';
|
||||||
|
@ -10,14 +12,14 @@ describe('uploadCompose()', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const instance = InstanceRecord({
|
const instance = InstanceRecord({
|
||||||
configuration: {
|
configuration: fromJS({
|
||||||
statuses: {
|
statuses: {
|
||||||
max_media_attachments: 4,
|
max_media_attachments: 4,
|
||||||
},
|
},
|
||||||
media_attachments: {
|
media_attachments: {
|
||||||
image_size_limit: 10,
|
image_size_limit: 10,
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const state = rootReducer(undefined, {})
|
const state = rootReducer(undefined, {})
|
||||||
|
@ -62,14 +64,14 @@ describe('uploadCompose()', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const instance = InstanceRecord({
|
const instance = InstanceRecord({
|
||||||
configuration: {
|
configuration: fromJS({
|
||||||
statuses: {
|
statuses: {
|
||||||
max_media_attachments: 4,
|
max_media_attachments: 4,
|
||||||
},
|
},
|
||||||
media_attachments: {
|
media_attachments: {
|
||||||
video_size_limit: 10,
|
video_size_limit: 10,
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const state = rootReducer(undefined, {})
|
const state = rootReducer(undefined, {})
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -4,78 +4,82 @@ import { getFeatures } from 'soapbox/utils/features';
|
||||||
|
|
||||||
import api, { getLinks } from '../api';
|
import api, { getLinks } from '../api';
|
||||||
|
|
||||||
export const ADMIN_CONFIG_FETCH_REQUEST = 'ADMIN_CONFIG_FETCH_REQUEST';
|
import type { AxiosResponse } from 'axios';
|
||||||
export const ADMIN_CONFIG_FETCH_SUCCESS = 'ADMIN_CONFIG_FETCH_SUCCESS';
|
import type { AppDispatch, RootState } from 'soapbox/store';
|
||||||
export const ADMIN_CONFIG_FETCH_FAIL = 'ADMIN_CONFIG_FETCH_FAIL';
|
import type { APIEntity } from 'soapbox/types/entities';
|
||||||
|
|
||||||
export const ADMIN_CONFIG_UPDATE_REQUEST = 'ADMIN_CONFIG_UPDATE_REQUEST';
|
const ADMIN_CONFIG_FETCH_REQUEST = 'ADMIN_CONFIG_FETCH_REQUEST';
|
||||||
export const ADMIN_CONFIG_UPDATE_SUCCESS = 'ADMIN_CONFIG_UPDATE_SUCCESS';
|
const ADMIN_CONFIG_FETCH_SUCCESS = 'ADMIN_CONFIG_FETCH_SUCCESS';
|
||||||
export const ADMIN_CONFIG_UPDATE_FAIL = 'ADMIN_CONFIG_UPDATE_FAIL';
|
const ADMIN_CONFIG_FETCH_FAIL = 'ADMIN_CONFIG_FETCH_FAIL';
|
||||||
|
|
||||||
export const ADMIN_REPORTS_FETCH_REQUEST = 'ADMIN_REPORTS_FETCH_REQUEST';
|
const ADMIN_CONFIG_UPDATE_REQUEST = 'ADMIN_CONFIG_UPDATE_REQUEST';
|
||||||
export const ADMIN_REPORTS_FETCH_SUCCESS = 'ADMIN_REPORTS_FETCH_SUCCESS';
|
const ADMIN_CONFIG_UPDATE_SUCCESS = 'ADMIN_CONFIG_UPDATE_SUCCESS';
|
||||||
export const ADMIN_REPORTS_FETCH_FAIL = 'ADMIN_REPORTS_FETCH_FAIL';
|
const ADMIN_CONFIG_UPDATE_FAIL = 'ADMIN_CONFIG_UPDATE_FAIL';
|
||||||
|
|
||||||
export const ADMIN_REPORTS_PATCH_REQUEST = 'ADMIN_REPORTS_PATCH_REQUEST';
|
const ADMIN_REPORTS_FETCH_REQUEST = 'ADMIN_REPORTS_FETCH_REQUEST';
|
||||||
export const ADMIN_REPORTS_PATCH_SUCCESS = 'ADMIN_REPORTS_PATCH_SUCCESS';
|
const ADMIN_REPORTS_FETCH_SUCCESS = 'ADMIN_REPORTS_FETCH_SUCCESS';
|
||||||
export const ADMIN_REPORTS_PATCH_FAIL = 'ADMIN_REPORTS_PATCH_FAIL';
|
const ADMIN_REPORTS_FETCH_FAIL = 'ADMIN_REPORTS_FETCH_FAIL';
|
||||||
|
|
||||||
export const ADMIN_USERS_FETCH_REQUEST = 'ADMIN_USERS_FETCH_REQUEST';
|
const ADMIN_REPORTS_PATCH_REQUEST = 'ADMIN_REPORTS_PATCH_REQUEST';
|
||||||
export const ADMIN_USERS_FETCH_SUCCESS = 'ADMIN_USERS_FETCH_SUCCESS';
|
const ADMIN_REPORTS_PATCH_SUCCESS = 'ADMIN_REPORTS_PATCH_SUCCESS';
|
||||||
export const ADMIN_USERS_FETCH_FAIL = 'ADMIN_USERS_FETCH_FAIL';
|
const ADMIN_REPORTS_PATCH_FAIL = 'ADMIN_REPORTS_PATCH_FAIL';
|
||||||
|
|
||||||
export const ADMIN_USERS_DELETE_REQUEST = 'ADMIN_USERS_DELETE_REQUEST';
|
const ADMIN_USERS_FETCH_REQUEST = 'ADMIN_USERS_FETCH_REQUEST';
|
||||||
export const ADMIN_USERS_DELETE_SUCCESS = 'ADMIN_USERS_DELETE_SUCCESS';
|
const ADMIN_USERS_FETCH_SUCCESS = 'ADMIN_USERS_FETCH_SUCCESS';
|
||||||
export const ADMIN_USERS_DELETE_FAIL = 'ADMIN_USERS_DELETE_FAIL';
|
const ADMIN_USERS_FETCH_FAIL = 'ADMIN_USERS_FETCH_FAIL';
|
||||||
|
|
||||||
export const ADMIN_USERS_APPROVE_REQUEST = 'ADMIN_USERS_APPROVE_REQUEST';
|
const ADMIN_USERS_DELETE_REQUEST = 'ADMIN_USERS_DELETE_REQUEST';
|
||||||
export const ADMIN_USERS_APPROVE_SUCCESS = 'ADMIN_USERS_APPROVE_SUCCESS';
|
const ADMIN_USERS_DELETE_SUCCESS = 'ADMIN_USERS_DELETE_SUCCESS';
|
||||||
export const ADMIN_USERS_APPROVE_FAIL = 'ADMIN_USERS_APPROVE_FAIL';
|
const ADMIN_USERS_DELETE_FAIL = 'ADMIN_USERS_DELETE_FAIL';
|
||||||
|
|
||||||
export const ADMIN_USERS_DEACTIVATE_REQUEST = 'ADMIN_USERS_DEACTIVATE_REQUEST';
|
const ADMIN_USERS_APPROVE_REQUEST = 'ADMIN_USERS_APPROVE_REQUEST';
|
||||||
export const ADMIN_USERS_DEACTIVATE_SUCCESS = 'ADMIN_USERS_DEACTIVATE_SUCCESS';
|
const ADMIN_USERS_APPROVE_SUCCESS = 'ADMIN_USERS_APPROVE_SUCCESS';
|
||||||
export const ADMIN_USERS_DEACTIVATE_FAIL = 'ADMIN_USERS_DEACTIVATE_FAIL';
|
const ADMIN_USERS_APPROVE_FAIL = 'ADMIN_USERS_APPROVE_FAIL';
|
||||||
|
|
||||||
export const ADMIN_STATUS_DELETE_REQUEST = 'ADMIN_STATUS_DELETE_REQUEST';
|
const ADMIN_USERS_DEACTIVATE_REQUEST = 'ADMIN_USERS_DEACTIVATE_REQUEST';
|
||||||
export const ADMIN_STATUS_DELETE_SUCCESS = 'ADMIN_STATUS_DELETE_SUCCESS';
|
const ADMIN_USERS_DEACTIVATE_SUCCESS = 'ADMIN_USERS_DEACTIVATE_SUCCESS';
|
||||||
export const ADMIN_STATUS_DELETE_FAIL = 'ADMIN_STATUS_DELETE_FAIL';
|
const ADMIN_USERS_DEACTIVATE_FAIL = 'ADMIN_USERS_DEACTIVATE_FAIL';
|
||||||
|
|
||||||
export const ADMIN_STATUS_TOGGLE_SENSITIVITY_REQUEST = 'ADMIN_STATUS_TOGGLE_SENSITIVITY_REQUEST';
|
const ADMIN_STATUS_DELETE_REQUEST = 'ADMIN_STATUS_DELETE_REQUEST';
|
||||||
export const ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS = 'ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS';
|
const ADMIN_STATUS_DELETE_SUCCESS = 'ADMIN_STATUS_DELETE_SUCCESS';
|
||||||
export const ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL = 'ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL';
|
const ADMIN_STATUS_DELETE_FAIL = 'ADMIN_STATUS_DELETE_FAIL';
|
||||||
|
|
||||||
export const ADMIN_LOG_FETCH_REQUEST = 'ADMIN_LOG_FETCH_REQUEST';
|
const ADMIN_STATUS_TOGGLE_SENSITIVITY_REQUEST = 'ADMIN_STATUS_TOGGLE_SENSITIVITY_REQUEST';
|
||||||
export const ADMIN_LOG_FETCH_SUCCESS = 'ADMIN_LOG_FETCH_SUCCESS';
|
const ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS = 'ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS';
|
||||||
export const ADMIN_LOG_FETCH_FAIL = 'ADMIN_LOG_FETCH_FAIL';
|
const ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL = 'ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL';
|
||||||
|
|
||||||
export const ADMIN_USERS_TAG_REQUEST = 'ADMIN_USERS_TAG_REQUEST';
|
const ADMIN_LOG_FETCH_REQUEST = 'ADMIN_LOG_FETCH_REQUEST';
|
||||||
export const ADMIN_USERS_TAG_SUCCESS = 'ADMIN_USERS_TAG_SUCCESS';
|
const ADMIN_LOG_FETCH_SUCCESS = 'ADMIN_LOG_FETCH_SUCCESS';
|
||||||
export const ADMIN_USERS_TAG_FAIL = 'ADMIN_USERS_TAG_FAIL';
|
const ADMIN_LOG_FETCH_FAIL = 'ADMIN_LOG_FETCH_FAIL';
|
||||||
|
|
||||||
export const ADMIN_USERS_UNTAG_REQUEST = 'ADMIN_USERS_UNTAG_REQUEST';
|
const ADMIN_USERS_TAG_REQUEST = 'ADMIN_USERS_TAG_REQUEST';
|
||||||
export const ADMIN_USERS_UNTAG_SUCCESS = 'ADMIN_USERS_UNTAG_SUCCESS';
|
const ADMIN_USERS_TAG_SUCCESS = 'ADMIN_USERS_TAG_SUCCESS';
|
||||||
export const ADMIN_USERS_UNTAG_FAIL = 'ADMIN_USERS_UNTAG_FAIL';
|
const ADMIN_USERS_TAG_FAIL = 'ADMIN_USERS_TAG_FAIL';
|
||||||
|
|
||||||
export const ADMIN_ADD_PERMISSION_GROUP_REQUEST = 'ADMIN_ADD_PERMISSION_GROUP_REQUEST';
|
const ADMIN_USERS_UNTAG_REQUEST = 'ADMIN_USERS_UNTAG_REQUEST';
|
||||||
export const ADMIN_ADD_PERMISSION_GROUP_SUCCESS = 'ADMIN_ADD_PERMISSION_GROUP_SUCCESS';
|
const ADMIN_USERS_UNTAG_SUCCESS = 'ADMIN_USERS_UNTAG_SUCCESS';
|
||||||
export const ADMIN_ADD_PERMISSION_GROUP_FAIL = 'ADMIN_ADD_PERMISSION_GROUP_FAIL';
|
const ADMIN_USERS_UNTAG_FAIL = 'ADMIN_USERS_UNTAG_FAIL';
|
||||||
|
|
||||||
export const ADMIN_REMOVE_PERMISSION_GROUP_REQUEST = 'ADMIN_REMOVE_PERMISSION_GROUP_REQUEST';
|
const ADMIN_ADD_PERMISSION_GROUP_REQUEST = 'ADMIN_ADD_PERMISSION_GROUP_REQUEST';
|
||||||
export const ADMIN_REMOVE_PERMISSION_GROUP_SUCCESS = 'ADMIN_REMOVE_PERMISSION_GROUP_SUCCESS';
|
const ADMIN_ADD_PERMISSION_GROUP_SUCCESS = 'ADMIN_ADD_PERMISSION_GROUP_SUCCESS';
|
||||||
export const ADMIN_REMOVE_PERMISSION_GROUP_FAIL = 'ADMIN_REMOVE_PERMISSION_GROUP_FAIL';
|
const ADMIN_ADD_PERMISSION_GROUP_FAIL = 'ADMIN_ADD_PERMISSION_GROUP_FAIL';
|
||||||
|
|
||||||
export const ADMIN_USERS_SUGGEST_REQUEST = 'ADMIN_USERS_SUGGEST_REQUEST';
|
const ADMIN_REMOVE_PERMISSION_GROUP_REQUEST = 'ADMIN_REMOVE_PERMISSION_GROUP_REQUEST';
|
||||||
export const ADMIN_USERS_SUGGEST_SUCCESS = 'ADMIN_USERS_SUGGEST_SUCCESS';
|
const ADMIN_REMOVE_PERMISSION_GROUP_SUCCESS = 'ADMIN_REMOVE_PERMISSION_GROUP_SUCCESS';
|
||||||
export const ADMIN_USERS_SUGGEST_FAIL = 'ADMIN_USERS_SUGGEST_FAIL';
|
const ADMIN_REMOVE_PERMISSION_GROUP_FAIL = 'ADMIN_REMOVE_PERMISSION_GROUP_FAIL';
|
||||||
|
|
||||||
export const ADMIN_USERS_UNSUGGEST_REQUEST = 'ADMIN_USERS_UNSUGGEST_REQUEST';
|
const ADMIN_USERS_SUGGEST_REQUEST = 'ADMIN_USERS_SUGGEST_REQUEST';
|
||||||
export const ADMIN_USERS_UNSUGGEST_SUCCESS = 'ADMIN_USERS_UNSUGGEST_SUCCESS';
|
const ADMIN_USERS_SUGGEST_SUCCESS = 'ADMIN_USERS_SUGGEST_SUCCESS';
|
||||||
export const ADMIN_USERS_UNSUGGEST_FAIL = 'ADMIN_USERS_UNSUGGEST_FAIL';
|
const ADMIN_USERS_SUGGEST_FAIL = 'ADMIN_USERS_SUGGEST_FAIL';
|
||||||
|
|
||||||
const nicknamesFromIds = (getState, ids) => ids.map(id => getState().getIn(['accounts', id, 'acct']));
|
const ADMIN_USERS_UNSUGGEST_REQUEST = 'ADMIN_USERS_UNSUGGEST_REQUEST';
|
||||||
|
const ADMIN_USERS_UNSUGGEST_SUCCESS = 'ADMIN_USERS_UNSUGGEST_SUCCESS';
|
||||||
|
const ADMIN_USERS_UNSUGGEST_FAIL = 'ADMIN_USERS_UNSUGGEST_FAIL';
|
||||||
|
|
||||||
export function fetchConfig() {
|
const nicknamesFromIds = (getState: () => RootState, ids: string[]) => ids.map(id => getState().accounts.get(id)!.acct);
|
||||||
return (dispatch, getState) => {
|
|
||||||
|
const fetchConfig = () =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: ADMIN_CONFIG_FETCH_REQUEST });
|
dispatch({ type: ADMIN_CONFIG_FETCH_REQUEST });
|
||||||
return api(getState)
|
return api(getState)
|
||||||
.get('/api/pleroma/admin/config')
|
.get('/api/pleroma/admin/config')
|
||||||
|
@ -85,10 +89,9 @@ export function fetchConfig() {
|
||||||
dispatch({ type: ADMIN_CONFIG_FETCH_FAIL, error });
|
dispatch({ type: ADMIN_CONFIG_FETCH_FAIL, error });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function updateConfig(configs) {
|
const updateConfig = (configs: Record<string, any>[]) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: ADMIN_CONFIG_UPDATE_REQUEST, configs });
|
dispatch({ type: ADMIN_CONFIG_UPDATE_REQUEST, configs });
|
||||||
return api(getState)
|
return api(getState)
|
||||||
.post('/api/pleroma/admin/config', { configs })
|
.post('/api/pleroma/admin/config', { configs })
|
||||||
|
@ -98,14 +101,13 @@ export function updateConfig(configs) {
|
||||||
dispatch({ type: ADMIN_CONFIG_UPDATE_FAIL, error, configs });
|
dispatch({ type: ADMIN_CONFIG_UPDATE_FAIL, error, configs });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
function fetchMastodonReports(params) {
|
const fetchMastodonReports = (params: Record<string, any>) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) =>
|
||||||
return api(getState)
|
api(getState)
|
||||||
.get('/api/v1/admin/reports', { params })
|
.get('/api/v1/admin/reports', { params })
|
||||||
.then(({ data: reports }) => {
|
.then(({ data: reports }) => {
|
||||||
reports.forEach(report => {
|
reports.forEach((report: APIEntity) => {
|
||||||
dispatch(importFetchedAccount(report.account?.account));
|
dispatch(importFetchedAccount(report.account?.account));
|
||||||
dispatch(importFetchedAccount(report.target_account?.account));
|
dispatch(importFetchedAccount(report.target_account?.account));
|
||||||
dispatch(importFetchedStatuses(report.statuses));
|
dispatch(importFetchedStatuses(report.statuses));
|
||||||
|
@ -114,15 +116,13 @@ function fetchMastodonReports(params) {
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch({ type: ADMIN_REPORTS_FETCH_FAIL, error, params });
|
dispatch({ type: ADMIN_REPORTS_FETCH_FAIL, error, params });
|
||||||
});
|
});
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchPleromaReports(params) {
|
const fetchPleromaReports = (params: Record<string, any>) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) =>
|
||||||
return api(getState)
|
api(getState)
|
||||||
.get('/api/pleroma/admin/reports', { params })
|
.get('/api/pleroma/admin/reports', { params })
|
||||||
.then(({ data: { reports } }) => {
|
.then(({ data: { reports } }) => {
|
||||||
reports.forEach(report => {
|
reports.forEach((report: APIEntity) => {
|
||||||
dispatch(importFetchedAccount(report.account));
|
dispatch(importFetchedAccount(report.account));
|
||||||
dispatch(importFetchedAccount(report.actor));
|
dispatch(importFetchedAccount(report.actor));
|
||||||
dispatch(importFetchedStatuses(report.statuses));
|
dispatch(importFetchedStatuses(report.statuses));
|
||||||
|
@ -131,19 +131,17 @@ function fetchPleromaReports(params) {
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch({ type: ADMIN_REPORTS_FETCH_FAIL, error, params });
|
dispatch({ type: ADMIN_REPORTS_FETCH_FAIL, error, params });
|
||||||
});
|
});
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchReports(params = {}) {
|
const fetchReports = (params: Record<string, any> = {}) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
const instance = state.get('instance');
|
const instance = state.instance;
|
||||||
const features = getFeatures(instance);
|
const features = getFeatures(instance);
|
||||||
|
|
||||||
dispatch({ type: ADMIN_REPORTS_FETCH_REQUEST, params });
|
dispatch({ type: ADMIN_REPORTS_FETCH_REQUEST, params });
|
||||||
|
|
||||||
if (features.mastodonAdmi) {
|
if (features.mastodonAdmin) {
|
||||||
return dispatch(fetchMastodonReports(params));
|
return dispatch(fetchMastodonReports(params));
|
||||||
} else {
|
} else {
|
||||||
const { resolved } = params;
|
const { resolved } = params;
|
||||||
|
@ -153,11 +151,10 @@ export function fetchReports(params = {}) {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
function patchMastodonReports(reports) {
|
const patchMastodonReports = (reports: { id: string, state: string }[]) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) =>
|
||||||
return Promise.all(reports.map(({ id, state }) => api(getState)
|
Promise.all(reports.map(({ id, state }) => api(getState)
|
||||||
.post(`/api/v1/admin/reports/${id}/${state === 'resolved' ? 'reopen' : 'resolve'}`)
|
.post(`/api/v1/admin/reports/${id}/${state === 'resolved' ? 'reopen' : 'resolve'}`)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
dispatch({ type: ADMIN_REPORTS_PATCH_SUCCESS, reports });
|
dispatch({ type: ADMIN_REPORTS_PATCH_SUCCESS, reports });
|
||||||
|
@ -165,26 +162,22 @@ function patchMastodonReports(reports) {
|
||||||
dispatch({ type: ADMIN_REPORTS_PATCH_FAIL, error, reports });
|
dispatch({ type: ADMIN_REPORTS_PATCH_FAIL, error, reports });
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function patchPleromaReports(reports) {
|
const patchPleromaReports = (reports: { id: string, state: string }[]) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) =>
|
||||||
return api(getState)
|
api(getState)
|
||||||
.patch('/api/pleroma/admin/reports', { reports })
|
.patch('/api/pleroma/admin/reports', { reports })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
dispatch({ type: ADMIN_REPORTS_PATCH_SUCCESS, reports });
|
dispatch({ type: ADMIN_REPORTS_PATCH_SUCCESS, reports });
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch({ type: ADMIN_REPORTS_PATCH_FAIL, error, reports });
|
dispatch({ type: ADMIN_REPORTS_PATCH_FAIL, error, reports });
|
||||||
});
|
});
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function patchReports(ids, reportState) {
|
const patchReports = (ids: string[], reportState: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
const instance = state.get('instance');
|
const instance = state.instance;
|
||||||
const features = getFeatures(instance);
|
const features = getFeatures(instance);
|
||||||
|
|
||||||
const reports = ids.map(id => ({ id, state: reportState }));
|
const reports = ids.map(id => ({ id, state: reportState }));
|
||||||
|
@ -197,15 +190,13 @@ function patchReports(ids, reportState) {
|
||||||
return dispatch(patchPleromaReports(reports));
|
return dispatch(patchPleromaReports(reports));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function closeReports(ids) {
|
const closeReports = (ids: string[]) =>
|
||||||
return patchReports(ids, 'closed');
|
patchReports(ids, 'closed');
|
||||||
}
|
|
||||||
|
|
||||||
function fetchMastodonUsers(filters, page, query, pageSize, next) {
|
const fetchMastodonUsers = (filters: string[], page: number, query: string | null | undefined, pageSize: number, next?: string | null) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const params = {
|
const params: Record<string, any> = {
|
||||||
username: query,
|
username: query,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -216,59 +207,56 @@ function fetchMastodonUsers(filters, page, query, pageSize, next) {
|
||||||
return api(getState)
|
return api(getState)
|
||||||
.get(next || '/api/v1/admin/accounts', { params })
|
.get(next || '/api/v1/admin/accounts', { params })
|
||||||
.then(({ data: accounts, ...response }) => {
|
.then(({ data: accounts, ...response }) => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response as AxiosResponse<any, any>).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
const count = next
|
const count = next
|
||||||
? page * pageSize + 1
|
? page * pageSize + 1
|
||||||
: (page - 1) * pageSize + accounts.length;
|
: (page - 1) * pageSize + accounts.length;
|
||||||
|
|
||||||
dispatch(importFetchedAccounts(accounts.map(({ account }) => account)));
|
dispatch(importFetchedAccounts(accounts.map(({ account }: APIEntity) => account)));
|
||||||
dispatch(fetchRelationships(accounts.map(account => account.id)));
|
dispatch(fetchRelationships(accounts.map((account: APIEntity) => account.id)));
|
||||||
dispatch({ type: ADMIN_USERS_FETCH_SUCCESS, users: accounts, count, pageSize, filters, page, next: next?.uri || false });
|
dispatch({ type: ADMIN_USERS_FETCH_SUCCESS, users: accounts, count, pageSize, filters, page, next: next?.uri || false });
|
||||||
return { users: accounts, count, pageSize, next: next?.uri || false };
|
return { users: accounts, count, pageSize, next: next?.uri || false };
|
||||||
}).catch(error => {
|
}).catch(error =>
|
||||||
dispatch({ type: ADMIN_USERS_FETCH_FAIL, error, filters, page, pageSize });
|
dispatch({ type: ADMIN_USERS_FETCH_FAIL, error, filters, page, pageSize }),
|
||||||
});
|
);
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
function fetchPleromaUsers(filters, page, query, pageSize) {
|
const fetchPleromaUsers = (filters: string[], page: number, query?: string | null, pageSize?: number) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const params = { filters: filters.join(), page, page_size: pageSize };
|
const params: Record<string, any> = { filters: filters.join(), page, page_size: pageSize };
|
||||||
if (query) params.query = query;
|
if (query) params.query = query;
|
||||||
|
|
||||||
return api(getState)
|
return api(getState)
|
||||||
.get('/api/pleroma/admin/users', { params })
|
.get('/api/pleroma/admin/users', { params })
|
||||||
.then(({ data: { users, count, page_size: pageSize } }) => {
|
.then(({ data: { users, count, page_size: pageSize } }) => {
|
||||||
dispatch(fetchRelationships(users.map(user => user.id)));
|
dispatch(fetchRelationships(users.map((user: APIEntity) => user.id)));
|
||||||
dispatch({ type: ADMIN_USERS_FETCH_SUCCESS, users, count, pageSize, filters, page });
|
dispatch({ type: ADMIN_USERS_FETCH_SUCCESS, users, count, pageSize, filters, page });
|
||||||
return { users, count, pageSize };
|
return { users, count, pageSize };
|
||||||
}).catch(error => {
|
}).catch(error =>
|
||||||
dispatch({ type: ADMIN_USERS_FETCH_FAIL, error, filters, page, pageSize });
|
dispatch({ type: ADMIN_USERS_FETCH_FAIL, error, filters, page, pageSize }),
|
||||||
});
|
);
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchUsers(filters = [], page = 1, query, pageSize = 50, next) {
|
const fetchUsers = (filters: string[] = [], page = 1, query?: string | null, pageSize = 50, next?: string | null) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
const instance = state.get('instance');
|
const instance = state.instance;
|
||||||
const features = getFeatures(instance);
|
const features = getFeatures(instance);
|
||||||
|
|
||||||
dispatch({ type: ADMIN_USERS_FETCH_REQUEST, filters, page, pageSize });
|
dispatch({ type: ADMIN_USERS_FETCH_REQUEST, filters, page, pageSize });
|
||||||
|
|
||||||
if (features.mastodonAdmi) {
|
if (features.mastodonAdmin) {
|
||||||
return dispatch(fetchMastodonUsers(filters, page, query, pageSize, next));
|
return dispatch(fetchMastodonUsers(filters, page, query, pageSize, next));
|
||||||
} else {
|
} else {
|
||||||
return dispatch(fetchPleromaUsers(filters, page, query, pageSize));
|
return dispatch(fetchPleromaUsers(filters, page, query, pageSize));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
function deactivateMastodonUsers(accountIds, reportId) {
|
const deactivateMastodonUsers = (accountIds: string[], reportId?: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) =>
|
||||||
return Promise.all(accountIds.map(accountId => {
|
Promise.all(accountIds.map(accountId => {
|
||||||
api(getState)
|
api(getState)
|
||||||
.post(`/api/v1/admin/accounts/${accountId}/action`, {
|
.post(`/api/v1/admin/accounts/${accountId}/action`, {
|
||||||
type: 'disable',
|
type: 'disable',
|
||||||
|
@ -280,11 +268,9 @@ function deactivateMastodonUsers(accountIds, reportId) {
|
||||||
dispatch({ type: ADMIN_USERS_DEACTIVATE_FAIL, error, accountIds: [accountId] });
|
dispatch({ type: ADMIN_USERS_DEACTIVATE_FAIL, error, accountIds: [accountId] });
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function deactivatePleromaUsers(accountIds) {
|
const deactivatePleromaUsers = (accountIds: string[]) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const nicknames = nicknamesFromIds(getState, accountIds);
|
const nicknames = nicknamesFromIds(getState, accountIds);
|
||||||
return api(getState)
|
return api(getState)
|
||||||
.patch('/api/pleroma/admin/users/deactivate', { nicknames })
|
.patch('/api/pleroma/admin/users/deactivate', { nicknames })
|
||||||
|
@ -294,27 +280,25 @@ function deactivatePleromaUsers(accountIds) {
|
||||||
dispatch({ type: ADMIN_USERS_DEACTIVATE_FAIL, error, accountIds });
|
dispatch({ type: ADMIN_USERS_DEACTIVATE_FAIL, error, accountIds });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function deactivateUsers(accountIds, reportId) {
|
const deactivateUsers = (accountIds: string[], reportId?: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
const instance = state.get('instance');
|
const instance = state.instance;
|
||||||
const features = getFeatures(instance);
|
const features = getFeatures(instance);
|
||||||
|
|
||||||
dispatch({ type: ADMIN_USERS_DEACTIVATE_REQUEST, accountIds });
|
dispatch({ type: ADMIN_USERS_DEACTIVATE_REQUEST, accountIds });
|
||||||
|
|
||||||
if (features.mastodonAdmi) {
|
if (features.mastodonAdmin) {
|
||||||
return dispatch(deactivateMastodonUsers(accountIds, reportId));
|
return dispatch(deactivateMastodonUsers(accountIds, reportId));
|
||||||
} else {
|
} else {
|
||||||
return dispatch(deactivatePleromaUsers(accountIds));
|
return dispatch(deactivatePleromaUsers(accountIds));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteUsers(accountIds) {
|
const deleteUsers = (accountIds: string[]) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const nicknames = nicknamesFromIds(getState, accountIds);
|
const nicknames = nicknamesFromIds(getState, accountIds);
|
||||||
dispatch({ type: ADMIN_USERS_DELETE_REQUEST, accountIds });
|
dispatch({ type: ADMIN_USERS_DELETE_REQUEST, accountIds });
|
||||||
return api(getState)
|
return api(getState)
|
||||||
|
@ -325,11 +309,10 @@ export function deleteUsers(accountIds) {
|
||||||
dispatch({ type: ADMIN_USERS_DELETE_FAIL, error, accountIds });
|
dispatch({ type: ADMIN_USERS_DELETE_FAIL, error, accountIds });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
function approveMastodonUsers(accountIds) {
|
const approveMastodonUsers = (accountIds: string[]) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) =>
|
||||||
return Promise.all(accountIds.map(accountId => {
|
Promise.all(accountIds.map(accountId => {
|
||||||
api(getState)
|
api(getState)
|
||||||
.post(`/api/v1/admin/accounts/${accountId}/approve`)
|
.post(`/api/v1/admin/accounts/${accountId}/approve`)
|
||||||
.then(({ data: user }) => {
|
.then(({ data: user }) => {
|
||||||
|
@ -338,11 +321,9 @@ function approveMastodonUsers(accountIds) {
|
||||||
dispatch({ type: ADMIN_USERS_APPROVE_FAIL, error, accountIds: [accountId] });
|
dispatch({ type: ADMIN_USERS_APPROVE_FAIL, error, accountIds: [accountId] });
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function approvePleromaUsers(accountIds) {
|
const approvePleromaUsers = (accountIds: string[]) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const nicknames = nicknamesFromIds(getState, accountIds);
|
const nicknames = nicknamesFromIds(getState, accountIds);
|
||||||
return api(getState)
|
return api(getState)
|
||||||
.patch('/api/pleroma/admin/users/approve', { nicknames })
|
.patch('/api/pleroma/admin/users/approve', { nicknames })
|
||||||
|
@ -352,27 +333,25 @@ function approvePleromaUsers(accountIds) {
|
||||||
dispatch({ type: ADMIN_USERS_APPROVE_FAIL, error, accountIds });
|
dispatch({ type: ADMIN_USERS_APPROVE_FAIL, error, accountIds });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function approveUsers(accountIds) {
|
const approveUsers = (accountIds: string[]) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
const instance = state.get('instance');
|
const instance = state.instance;
|
||||||
const features = getFeatures(instance);
|
const features = getFeatures(instance);
|
||||||
|
|
||||||
dispatch({ type: ADMIN_USERS_APPROVE_REQUEST, accountIds });
|
dispatch({ type: ADMIN_USERS_APPROVE_REQUEST, accountIds });
|
||||||
|
|
||||||
if (features.mastodonAdmi) {
|
if (features.mastodonAdmin) {
|
||||||
return dispatch(approveMastodonUsers(accountIds));
|
return dispatch(approveMastodonUsers(accountIds));
|
||||||
} else {
|
} else {
|
||||||
return dispatch(approvePleromaUsers(accountIds));
|
return dispatch(approvePleromaUsers(accountIds));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteStatus(id) {
|
const deleteStatus = (id: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: ADMIN_STATUS_DELETE_REQUEST, id });
|
dispatch({ type: ADMIN_STATUS_DELETE_REQUEST, id });
|
||||||
return api(getState)
|
return api(getState)
|
||||||
.delete(`/api/pleroma/admin/statuses/${id}`)
|
.delete(`/api/pleroma/admin/statuses/${id}`)
|
||||||
|
@ -382,10 +361,9 @@ export function deleteStatus(id) {
|
||||||
dispatch({ type: ADMIN_STATUS_DELETE_FAIL, error, id });
|
dispatch({ type: ADMIN_STATUS_DELETE_FAIL, error, id });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function toggleStatusSensitivity(id, sensitive) {
|
const toggleStatusSensitivity = (id: string, sensitive: boolean) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_REQUEST, id });
|
dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_REQUEST, id });
|
||||||
return api(getState)
|
return api(getState)
|
||||||
.put(`/api/pleroma/admin/statuses/${id}`, { sensitive: !sensitive })
|
.put(`/api/pleroma/admin/statuses/${id}`, { sensitive: !sensitive })
|
||||||
|
@ -395,10 +373,9 @@ export function toggleStatusSensitivity(id, sensitive) {
|
||||||
dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL, error, id });
|
dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL, error, id });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchModerationLog(params) {
|
const fetchModerationLog = (params?: Record<string, any>) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: ADMIN_LOG_FETCH_REQUEST });
|
dispatch({ type: ADMIN_LOG_FETCH_REQUEST });
|
||||||
return api(getState)
|
return api(getState)
|
||||||
.get('/api/pleroma/admin/moderation_log', { params })
|
.get('/api/pleroma/admin/moderation_log', { params })
|
||||||
|
@ -409,10 +386,9 @@ export function fetchModerationLog(params) {
|
||||||
dispatch({ type: ADMIN_LOG_FETCH_FAIL, error });
|
dispatch({ type: ADMIN_LOG_FETCH_FAIL, error });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function tagUsers(accountIds, tags) {
|
const tagUsers = (accountIds: string[], tags: string[]) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const nicknames = nicknamesFromIds(getState, accountIds);
|
const nicknames = nicknamesFromIds(getState, accountIds);
|
||||||
dispatch({ type: ADMIN_USERS_TAG_REQUEST, accountIds, tags });
|
dispatch({ type: ADMIN_USERS_TAG_REQUEST, accountIds, tags });
|
||||||
return api(getState)
|
return api(getState)
|
||||||
|
@ -423,10 +399,9 @@ export function tagUsers(accountIds, tags) {
|
||||||
dispatch({ type: ADMIN_USERS_TAG_FAIL, error, accountIds, tags });
|
dispatch({ type: ADMIN_USERS_TAG_FAIL, error, accountIds, tags });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function untagUsers(accountIds, tags) {
|
const untagUsers = (accountIds: string[], tags: string[]) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const nicknames = nicknamesFromIds(getState, accountIds);
|
const nicknames = nicknamesFromIds(getState, accountIds);
|
||||||
dispatch({ type: ADMIN_USERS_UNTAG_REQUEST, accountIds, tags });
|
dispatch({ type: ADMIN_USERS_UNTAG_REQUEST, accountIds, tags });
|
||||||
return api(getState)
|
return api(getState)
|
||||||
|
@ -437,34 +412,25 @@ export function untagUsers(accountIds, tags) {
|
||||||
dispatch({ type: ADMIN_USERS_UNTAG_FAIL, error, accountIds, tags });
|
dispatch({ type: ADMIN_USERS_UNTAG_FAIL, error, accountIds, tags });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function verifyUser(accountId) {
|
const verifyUser = (accountId: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch) =>
|
||||||
return dispatch(tagUsers([accountId], ['verified']));
|
dispatch(tagUsers([accountId], ['verified']));
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unverifyUser(accountId) {
|
const unverifyUser = (accountId: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch) =>
|
||||||
return dispatch(untagUsers([accountId], ['verified']));
|
dispatch(untagUsers([accountId], ['verified']));
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setDonor(accountId) {
|
const setDonor = (accountId: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch) =>
|
||||||
return dispatch(tagUsers([accountId], ['donor']));
|
dispatch(tagUsers([accountId], ['donor']));
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeDonor(accountId) {
|
const removeDonor = (accountId: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch) =>
|
||||||
return dispatch(untagUsers([accountId], ['donor']));
|
dispatch(untagUsers([accountId], ['donor']));
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addPermission(accountIds, permissionGroup) {
|
const addPermission = (accountIds: string[], permissionGroup: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const nicknames = nicknamesFromIds(getState, accountIds);
|
const nicknames = nicknamesFromIds(getState, accountIds);
|
||||||
dispatch({ type: ADMIN_ADD_PERMISSION_GROUP_REQUEST, accountIds, permissionGroup });
|
dispatch({ type: ADMIN_ADD_PERMISSION_GROUP_REQUEST, accountIds, permissionGroup });
|
||||||
return api(getState)
|
return api(getState)
|
||||||
|
@ -475,10 +441,9 @@ export function addPermission(accountIds, permissionGroup) {
|
||||||
dispatch({ type: ADMIN_ADD_PERMISSION_GROUP_FAIL, error, accountIds, permissionGroup });
|
dispatch({ type: ADMIN_ADD_PERMISSION_GROUP_FAIL, error, accountIds, permissionGroup });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function removePermission(accountIds, permissionGroup) {
|
const removePermission = (accountIds: string[], permissionGroup: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const nicknames = nicknamesFromIds(getState, accountIds);
|
const nicknames = nicknamesFromIds(getState, accountIds);
|
||||||
dispatch({ type: ADMIN_REMOVE_PERMISSION_GROUP_REQUEST, accountIds, permissionGroup });
|
dispatch({ type: ADMIN_REMOVE_PERMISSION_GROUP_REQUEST, accountIds, permissionGroup });
|
||||||
return api(getState)
|
return api(getState)
|
||||||
|
@ -489,37 +454,30 @@ export function removePermission(accountIds, permissionGroup) {
|
||||||
dispatch({ type: ADMIN_REMOVE_PERMISSION_GROUP_FAIL, error, accountIds, permissionGroup });
|
dispatch({ type: ADMIN_REMOVE_PERMISSION_GROUP_FAIL, error, accountIds, permissionGroup });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function promoteToAdmin(accountId) {
|
const promoteToAdmin = (accountId: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch) =>
|
||||||
return Promise.all([
|
Promise.all([
|
||||||
dispatch(addPermission([accountId], 'admin')),
|
dispatch(addPermission([accountId], 'admin')),
|
||||||
dispatch(removePermission([accountId], 'moderator')),
|
dispatch(removePermission([accountId], 'moderator')),
|
||||||
]);
|
]);
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function promoteToModerator(accountId) {
|
const promoteToModerator = (accountId: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch) =>
|
||||||
return Promise.all([
|
Promise.all([
|
||||||
dispatch(removePermission([accountId], 'admin')),
|
dispatch(removePermission([accountId], 'admin')),
|
||||||
dispatch(addPermission([accountId], 'moderator')),
|
dispatch(addPermission([accountId], 'moderator')),
|
||||||
]);
|
]);
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function demoteToUser(accountId) {
|
const demoteToUser = (accountId: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch) =>
|
||||||
return Promise.all([
|
Promise.all([
|
||||||
dispatch(removePermission([accountId], 'admin')),
|
dispatch(removePermission([accountId], 'admin')),
|
||||||
dispatch(removePermission([accountId], 'moderator')),
|
dispatch(removePermission([accountId], 'moderator')),
|
||||||
]);
|
]);
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function suggestUsers(accountIds) {
|
const suggestUsers = (accountIds: string[]) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const nicknames = nicknamesFromIds(getState, accountIds);
|
const nicknames = nicknamesFromIds(getState, accountIds);
|
||||||
dispatch({ type: ADMIN_USERS_SUGGEST_REQUEST, accountIds });
|
dispatch({ type: ADMIN_USERS_SUGGEST_REQUEST, accountIds });
|
||||||
return api(getState)
|
return api(getState)
|
||||||
|
@ -530,10 +488,9 @@ export function suggestUsers(accountIds) {
|
||||||
dispatch({ type: ADMIN_USERS_SUGGEST_FAIL, error, accountIds });
|
dispatch({ type: ADMIN_USERS_SUGGEST_FAIL, error, accountIds });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function unsuggestUsers(accountIds) {
|
const unsuggestUsers = (accountIds: string[]) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const nicknames = nicknamesFromIds(getState, accountIds);
|
const nicknames = nicknamesFromIds(getState, accountIds);
|
||||||
dispatch({ type: ADMIN_USERS_UNSUGGEST_REQUEST, accountIds });
|
dispatch({ type: ADMIN_USERS_UNSUGGEST_REQUEST, accountIds });
|
||||||
return api(getState)
|
return api(getState)
|
||||||
|
@ -544,4 +501,81 @@ export function unsuggestUsers(accountIds) {
|
||||||
dispatch({ type: ADMIN_USERS_UNSUGGEST_FAIL, error, accountIds });
|
dispatch({ type: ADMIN_USERS_UNSUGGEST_FAIL, error, accountIds });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
export {
|
||||||
|
ADMIN_CONFIG_FETCH_REQUEST,
|
||||||
|
ADMIN_CONFIG_FETCH_SUCCESS,
|
||||||
|
ADMIN_CONFIG_FETCH_FAIL,
|
||||||
|
ADMIN_CONFIG_UPDATE_REQUEST,
|
||||||
|
ADMIN_CONFIG_UPDATE_SUCCESS,
|
||||||
|
ADMIN_CONFIG_UPDATE_FAIL,
|
||||||
|
ADMIN_REPORTS_FETCH_REQUEST,
|
||||||
|
ADMIN_REPORTS_FETCH_SUCCESS,
|
||||||
|
ADMIN_REPORTS_FETCH_FAIL,
|
||||||
|
ADMIN_REPORTS_PATCH_REQUEST,
|
||||||
|
ADMIN_REPORTS_PATCH_SUCCESS,
|
||||||
|
ADMIN_REPORTS_PATCH_FAIL,
|
||||||
|
ADMIN_USERS_FETCH_REQUEST,
|
||||||
|
ADMIN_USERS_FETCH_SUCCESS,
|
||||||
|
ADMIN_USERS_FETCH_FAIL,
|
||||||
|
ADMIN_USERS_DELETE_REQUEST,
|
||||||
|
ADMIN_USERS_DELETE_SUCCESS,
|
||||||
|
ADMIN_USERS_DELETE_FAIL,
|
||||||
|
ADMIN_USERS_APPROVE_REQUEST,
|
||||||
|
ADMIN_USERS_APPROVE_SUCCESS,
|
||||||
|
ADMIN_USERS_APPROVE_FAIL,
|
||||||
|
ADMIN_USERS_DEACTIVATE_REQUEST,
|
||||||
|
ADMIN_USERS_DEACTIVATE_SUCCESS,
|
||||||
|
ADMIN_USERS_DEACTIVATE_FAIL,
|
||||||
|
ADMIN_STATUS_DELETE_REQUEST,
|
||||||
|
ADMIN_STATUS_DELETE_SUCCESS,
|
||||||
|
ADMIN_STATUS_DELETE_FAIL,
|
||||||
|
ADMIN_STATUS_TOGGLE_SENSITIVITY_REQUEST,
|
||||||
|
ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS,
|
||||||
|
ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL,
|
||||||
|
ADMIN_LOG_FETCH_REQUEST,
|
||||||
|
ADMIN_LOG_FETCH_SUCCESS,
|
||||||
|
ADMIN_LOG_FETCH_FAIL,
|
||||||
|
ADMIN_USERS_TAG_REQUEST,
|
||||||
|
ADMIN_USERS_TAG_SUCCESS,
|
||||||
|
ADMIN_USERS_TAG_FAIL,
|
||||||
|
ADMIN_USERS_UNTAG_REQUEST,
|
||||||
|
ADMIN_USERS_UNTAG_SUCCESS,
|
||||||
|
ADMIN_USERS_UNTAG_FAIL,
|
||||||
|
ADMIN_ADD_PERMISSION_GROUP_REQUEST,
|
||||||
|
ADMIN_ADD_PERMISSION_GROUP_SUCCESS,
|
||||||
|
ADMIN_ADD_PERMISSION_GROUP_FAIL,
|
||||||
|
ADMIN_REMOVE_PERMISSION_GROUP_REQUEST,
|
||||||
|
ADMIN_REMOVE_PERMISSION_GROUP_SUCCESS,
|
||||||
|
ADMIN_REMOVE_PERMISSION_GROUP_FAIL,
|
||||||
|
ADMIN_USERS_SUGGEST_REQUEST,
|
||||||
|
ADMIN_USERS_SUGGEST_SUCCESS,
|
||||||
|
ADMIN_USERS_SUGGEST_FAIL,
|
||||||
|
ADMIN_USERS_UNSUGGEST_REQUEST,
|
||||||
|
ADMIN_USERS_UNSUGGEST_SUCCESS,
|
||||||
|
ADMIN_USERS_UNSUGGEST_FAIL,
|
||||||
|
fetchConfig,
|
||||||
|
updateConfig,
|
||||||
|
fetchReports,
|
||||||
|
closeReports,
|
||||||
|
fetchUsers,
|
||||||
|
deactivateUsers,
|
||||||
|
deleteUsers,
|
||||||
|
approveUsers,
|
||||||
|
deleteStatus,
|
||||||
|
toggleStatusSensitivity,
|
||||||
|
fetchModerationLog,
|
||||||
|
tagUsers,
|
||||||
|
untagUsers,
|
||||||
|
verifyUser,
|
||||||
|
unverifyUser,
|
||||||
|
setDonor,
|
||||||
|
removeDonor,
|
||||||
|
addPermission,
|
||||||
|
removePermission,
|
||||||
|
promoteToAdmin,
|
||||||
|
promoteToModerator,
|
||||||
|
demoteToUser,
|
||||||
|
suggestUsers,
|
||||||
|
unsuggestUsers,
|
||||||
|
};
|
|
@ -1,760 +0,0 @@
|
||||||
import { CancelToken, isCancel } from 'axios';
|
|
||||||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
|
||||||
import throttle from 'lodash/throttle';
|
|
||||||
import { defineMessages } from 'react-intl';
|
|
||||||
|
|
||||||
import snackbar from 'soapbox/actions/snackbar';
|
|
||||||
import { isLoggedIn } from 'soapbox/utils/auth';
|
|
||||||
import { getFeatures, parseVersion } from 'soapbox/utils/features';
|
|
||||||
import { formatBytes } from 'soapbox/utils/media';
|
|
||||||
|
|
||||||
import api from '../api';
|
|
||||||
import { search as emojiSearch } from '../features/emoji/emoji_mart_search_light';
|
|
||||||
import { tagHistory } from '../settings';
|
|
||||||
import resizeImage from '../utils/resize_image';
|
|
||||||
|
|
||||||
import { showAlert, showAlertForError } from './alerts';
|
|
||||||
import { useEmoji } from './emojis';
|
|
||||||
import { importFetchedAccounts } from './importer';
|
|
||||||
import { uploadMedia, fetchMedia, updateMedia } from './media';
|
|
||||||
import { openModal, closeModal } from './modals';
|
|
||||||
import { getSettings } from './settings';
|
|
||||||
import { createStatus } from './statuses';
|
|
||||||
|
|
||||||
let cancelFetchComposeSuggestionsAccounts;
|
|
||||||
|
|
||||||
export const COMPOSE_CHANGE = 'COMPOSE_CHANGE';
|
|
||||||
export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST';
|
|
||||||
export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS';
|
|
||||||
export const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL';
|
|
||||||
export const COMPOSE_REPLY = 'COMPOSE_REPLY';
|
|
||||||
export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL';
|
|
||||||
export const COMPOSE_QUOTE = 'COMPOSE_QUOTE';
|
|
||||||
export const COMPOSE_QUOTE_CANCEL = 'COMPOSE_QUOTE_CANCEL';
|
|
||||||
export const COMPOSE_DIRECT = 'COMPOSE_DIRECT';
|
|
||||||
export const COMPOSE_MENTION = 'COMPOSE_MENTION';
|
|
||||||
export const COMPOSE_RESET = 'COMPOSE_RESET';
|
|
||||||
export const COMPOSE_UPLOAD_REQUEST = 'COMPOSE_UPLOAD_REQUEST';
|
|
||||||
export const COMPOSE_UPLOAD_SUCCESS = 'COMPOSE_UPLOAD_SUCCESS';
|
|
||||||
export const COMPOSE_UPLOAD_FAIL = 'COMPOSE_UPLOAD_FAIL';
|
|
||||||
export const COMPOSE_UPLOAD_PROGRESS = 'COMPOSE_UPLOAD_PROGRESS';
|
|
||||||
export const COMPOSE_UPLOAD_UNDO = 'COMPOSE_UPLOAD_UNDO';
|
|
||||||
|
|
||||||
export const COMPOSE_SUGGESTIONS_CLEAR = 'COMPOSE_SUGGESTIONS_CLEAR';
|
|
||||||
export const COMPOSE_SUGGESTIONS_READY = 'COMPOSE_SUGGESTIONS_READY';
|
|
||||||
export const COMPOSE_SUGGESTION_SELECT = 'COMPOSE_SUGGESTION_SELECT';
|
|
||||||
export const COMPOSE_SUGGESTION_TAGS_UPDATE = 'COMPOSE_SUGGESTION_TAGS_UPDATE';
|
|
||||||
|
|
||||||
export const COMPOSE_TAG_HISTORY_UPDATE = 'COMPOSE_TAG_HISTORY_UPDATE';
|
|
||||||
|
|
||||||
export const COMPOSE_MOUNT = 'COMPOSE_MOUNT';
|
|
||||||
export const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT';
|
|
||||||
|
|
||||||
export const COMPOSE_SENSITIVITY_CHANGE = 'COMPOSE_SENSITIVITY_CHANGE';
|
|
||||||
export const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE';
|
|
||||||
export const COMPOSE_TYPE_CHANGE = 'COMPOSE_TYPE_CHANGE';
|
|
||||||
export const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE';
|
|
||||||
export const COMPOSE_VISIBILITY_CHANGE = 'COMPOSE_VISIBILITY_CHANGE';
|
|
||||||
export const COMPOSE_LISTABILITY_CHANGE = 'COMPOSE_LISTABILITY_CHANGE';
|
|
||||||
export const COMPOSE_COMPOSING_CHANGE = 'COMPOSE_COMPOSING_CHANGE';
|
|
||||||
|
|
||||||
export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
|
|
||||||
|
|
||||||
export const COMPOSE_UPLOAD_CHANGE_REQUEST = 'COMPOSE_UPLOAD_UPDATE_REQUEST';
|
|
||||||
export const COMPOSE_UPLOAD_CHANGE_SUCCESS = 'COMPOSE_UPLOAD_UPDATE_SUCCESS';
|
|
||||||
export const COMPOSE_UPLOAD_CHANGE_FAIL = 'COMPOSE_UPLOAD_UPDATE_FAIL';
|
|
||||||
|
|
||||||
export const COMPOSE_POLL_ADD = 'COMPOSE_POLL_ADD';
|
|
||||||
export const COMPOSE_POLL_REMOVE = 'COMPOSE_POLL_REMOVE';
|
|
||||||
export const COMPOSE_POLL_OPTION_ADD = 'COMPOSE_POLL_OPTION_ADD';
|
|
||||||
export const COMPOSE_POLL_OPTION_CHANGE = 'COMPOSE_POLL_OPTION_CHANGE';
|
|
||||||
export const COMPOSE_POLL_OPTION_REMOVE = 'COMPOSE_POLL_OPTION_REMOVE';
|
|
||||||
export const COMPOSE_POLL_SETTINGS_CHANGE = 'COMPOSE_POLL_SETTINGS_CHANGE';
|
|
||||||
|
|
||||||
export const COMPOSE_SCHEDULE_ADD = 'COMPOSE_SCHEDULE_ADD';
|
|
||||||
export const COMPOSE_SCHEDULE_SET = 'COMPOSE_SCHEDULE_SET';
|
|
||||||
export const COMPOSE_SCHEDULE_REMOVE = 'COMPOSE_SCHEDULE_REMOVE';
|
|
||||||
|
|
||||||
export const COMPOSE_ADD_TO_MENTIONS = 'COMPOSE_ADD_TO_MENTIONS';
|
|
||||||
export const COMPOSE_REMOVE_FROM_MENTIONS = 'COMPOSE_REMOVE_FROM_MENTIONS';
|
|
||||||
|
|
||||||
export const COMPOSE_SET_STATUS = 'COMPOSE_SET_STATUS';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
exceededImageSizeLimit: { id: 'upload_error.image_size_limit', defaultMessage: 'Image exceeds the current file size limit ({limit})' },
|
|
||||||
exceededVideoSizeLimit: { id: 'upload_error.video_size_limit', defaultMessage: 'Video exceeds the current file size limit ({limit})' },
|
|
||||||
scheduleError: { id: 'compose.invalid_schedule', defaultMessage: 'You must schedule a post at least 5 minutes out.' },
|
|
||||||
success: { id: 'compose.submit_success', defaultMessage: 'Your post was sent' },
|
|
||||||
uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' },
|
|
||||||
uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' },
|
|
||||||
view: { id: 'snackbar.view', defaultMessage: 'View' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const COMPOSE_PANEL_BREAKPOINT = 600 + (285 * 1) + (10 * 1);
|
|
||||||
|
|
||||||
export const ensureComposeIsVisible = (getState, routerHistory) => {
|
|
||||||
if (!getState().getIn(['compose', 'mounted']) && window.innerWidth < COMPOSE_PANEL_BREAKPOINT) {
|
|
||||||
routerHistory.push('/posts/new');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export function setComposeToStatus(status, rawText, spoilerText, contentType, withRedraft) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
const { instance } = getState();
|
|
||||||
const { explicitAddressing } = getFeatures(instance);
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: COMPOSE_SET_STATUS,
|
|
||||||
status,
|
|
||||||
rawText,
|
|
||||||
explicitAddressing,
|
|
||||||
spoilerText,
|
|
||||||
contentType,
|
|
||||||
v: parseVersion(instance.version),
|
|
||||||
withRedraft,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changeCompose(text) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_CHANGE,
|
|
||||||
text: text,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function replyCompose(status, routerHistory) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
const state = getState();
|
|
||||||
const instance = state.get('instance');
|
|
||||||
const { explicitAddressing } = getFeatures(instance);
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: COMPOSE_REPLY,
|
|
||||||
status: status,
|
|
||||||
account: state.getIn(['accounts', state.get('me')]),
|
|
||||||
explicitAddressing,
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch(openModal('COMPOSE'));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function cancelReplyCompose() {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_REPLY_CANCEL,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function quoteCompose(status, routerHistory) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
const state = getState();
|
|
||||||
const instance = state.get('instance');
|
|
||||||
const { explicitAddressing } = getFeatures(instance);
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: COMPOSE_QUOTE,
|
|
||||||
status: status,
|
|
||||||
account: state.getIn(['accounts', state.get('me')]),
|
|
||||||
explicitAddressing,
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch(openModal('COMPOSE'));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function cancelQuoteCompose() {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_QUOTE_CANCEL,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resetCompose() {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_RESET,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mentionCompose(account, routerHistory) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
dispatch({
|
|
||||||
type: COMPOSE_MENTION,
|
|
||||||
account: account,
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch(openModal('COMPOSE'));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function directCompose(account, routerHistory) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
dispatch({
|
|
||||||
type: COMPOSE_DIRECT,
|
|
||||||
account: account,
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch(openModal('COMPOSE'));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function directComposeById(accountId) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
const account = getState().getIn(['accounts', accountId]);
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: COMPOSE_DIRECT,
|
|
||||||
account: account,
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch(openModal('COMPOSE'));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function handleComposeSubmit(dispatch, getState, data, status) {
|
|
||||||
if (!dispatch || !getState) return;
|
|
||||||
|
|
||||||
dispatch(insertIntoTagHistory(data.tags || [], status));
|
|
||||||
dispatch(submitComposeSuccess({ ...data }));
|
|
||||||
dispatch(snackbar.success(messages.success, messages.view, `/@${data.account.acct}/posts/${data.id}`));
|
|
||||||
}
|
|
||||||
|
|
||||||
const needsDescriptions = state => {
|
|
||||||
const media = state.getIn(['compose', 'media_attachments']);
|
|
||||||
const missingDescriptionModal = getSettings(state).get('missingDescriptionModal');
|
|
||||||
|
|
||||||
const hasMissing = media.filter(item => !item.get('description')).size > 0;
|
|
||||||
|
|
||||||
return missingDescriptionModal && hasMissing;
|
|
||||||
};
|
|
||||||
|
|
||||||
const validateSchedule = state => {
|
|
||||||
const schedule = state.getIn(['compose', 'schedule']);
|
|
||||||
if (!schedule) return true;
|
|
||||||
|
|
||||||
const fiveMinutesFromNow = new Date(new Date().getTime() + 300000);
|
|
||||||
|
|
||||||
return schedule.getTime() > fiveMinutesFromNow.getTime();
|
|
||||||
};
|
|
||||||
|
|
||||||
export function submitCompose(routerHistory, force = false) {
|
|
||||||
return function(dispatch, getState) {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
const state = getState();
|
|
||||||
|
|
||||||
const status = state.getIn(['compose', 'text'], '');
|
|
||||||
const media = state.getIn(['compose', 'media_attachments']);
|
|
||||||
const statusId = state.getIn(['compose', 'id'], null);
|
|
||||||
let to = state.getIn(['compose', 'to'], ImmutableOrderedSet());
|
|
||||||
|
|
||||||
if (!validateSchedule(state)) {
|
|
||||||
dispatch(snackbar.error(messages.scheduleError));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!status || !status.length) && media.size === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!force && needsDescriptions(state)) {
|
|
||||||
dispatch(openModal('MISSING_DESCRIPTION', {
|
|
||||||
onContinue: () => {
|
|
||||||
dispatch(closeModal('MISSING_DESCRIPTION'));
|
|
||||||
dispatch(submitCompose(routerHistory, true));
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (to && status) {
|
|
||||||
const mentions = status.match(/(?:^|\s|\.)@([a-z0-9_]+(?:@[a-z0-9\.\-]+)?)/gi); // not a perfect regex
|
|
||||||
|
|
||||||
if (mentions)
|
|
||||||
to = to.union(mentions.map(mention => mention.trim().slice(1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(submitComposeRequest());
|
|
||||||
dispatch(closeModal());
|
|
||||||
|
|
||||||
const idempotencyKey = state.getIn(['compose', 'idempotencyKey']);
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
status,
|
|
||||||
in_reply_to_id: state.getIn(['compose', 'in_reply_to'], null),
|
|
||||||
quote_id: state.getIn(['compose', 'quote'], null),
|
|
||||||
media_ids: media.map(item => item.get('id')),
|
|
||||||
sensitive: state.getIn(['compose', 'sensitive']),
|
|
||||||
spoiler_text: state.getIn(['compose', 'spoiler_text'], ''),
|
|
||||||
visibility: state.getIn(['compose', 'privacy']),
|
|
||||||
content_type: state.getIn(['compose', 'content_type']),
|
|
||||||
poll: state.getIn(['compose', 'poll'], null),
|
|
||||||
scheduled_at: state.getIn(['compose', 'schedule'], null),
|
|
||||||
to,
|
|
||||||
};
|
|
||||||
|
|
||||||
dispatch(createStatus(params, idempotencyKey, statusId)).then(function(data) {
|
|
||||||
if (!statusId && data.visibility === 'direct' && getState().getIn(['conversations', 'mounted']) <= 0 && routerHistory) {
|
|
||||||
routerHistory.push('/messages');
|
|
||||||
}
|
|
||||||
handleComposeSubmit(dispatch, getState, data, status);
|
|
||||||
}).catch(function(error) {
|
|
||||||
dispatch(submitComposeFail(error));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function submitComposeRequest() {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_SUBMIT_REQUEST,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function submitComposeSuccess(status) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_SUBMIT_SUCCESS,
|
|
||||||
status: status,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function submitComposeFail(error) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_SUBMIT_FAIL,
|
|
||||||
error: error,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function uploadCompose(files, intl) {
|
|
||||||
return function(dispatch, getState) {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
const attachmentLimit = getState().getIn(['instance', 'configuration', 'statuses', 'max_media_attachments']);
|
|
||||||
const maxImageSize = getState().getIn(['instance', 'configuration', 'media_attachments', 'image_size_limit']);
|
|
||||||
const maxVideoSize = getState().getIn(['instance', 'configuration', 'media_attachments', 'video_size_limit']);
|
|
||||||
|
|
||||||
const media = getState().getIn(['compose', 'media_attachments']);
|
|
||||||
const progress = new Array(files.length).fill(0);
|
|
||||||
let total = Array.from(files).reduce((a, v) => a + v.size, 0);
|
|
||||||
|
|
||||||
if (files.length + media.size > attachmentLimit) {
|
|
||||||
dispatch(showAlert(undefined, messages.uploadErrorLimit, 'error'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(uploadComposeRequest());
|
|
||||||
|
|
||||||
Array.from(files).forEach((f, i) => {
|
|
||||||
if (media.size + i > attachmentLimit - 1) return;
|
|
||||||
|
|
||||||
const isImage = f.type.match(/image.*/);
|
|
||||||
const isVideo = f.type.match(/video.*/);
|
|
||||||
if (isImage && maxImageSize && (f.size > maxImageSize)) {
|
|
||||||
const limit = formatBytes(maxImageSize);
|
|
||||||
const message = intl.formatMessage(messages.exceededImageSizeLimit, { limit });
|
|
||||||
dispatch(snackbar.error(message));
|
|
||||||
dispatch(uploadComposeFail(true));
|
|
||||||
return;
|
|
||||||
} else if (isVideo && maxVideoSize && (f.size > maxVideoSize)) {
|
|
||||||
const limit = formatBytes(maxVideoSize);
|
|
||||||
const message = intl.formatMessage(messages.exceededVideoSizeLimit, { limit });
|
|
||||||
dispatch(snackbar.error(message));
|
|
||||||
dispatch(uploadComposeFail(true));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Don't define function in loop
|
|
||||||
/* eslint-disable no-loop-func */
|
|
||||||
resizeImage(f).then(file => {
|
|
||||||
const data = new FormData();
|
|
||||||
data.append('file', file);
|
|
||||||
// Account for disparity in size of original image and resized data
|
|
||||||
total += file.size - f.size;
|
|
||||||
|
|
||||||
const onUploadProgress = function({ loaded }) {
|
|
||||||
progress[i] = loaded;
|
|
||||||
dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
|
|
||||||
};
|
|
||||||
|
|
||||||
return dispatch(uploadMedia(data, onUploadProgress))
|
|
||||||
.then(({ status, data }) => {
|
|
||||||
// If server-side processing of the media attachment has not completed yet,
|
|
||||||
// poll the server until it is, before showing the media attachment as uploaded
|
|
||||||
if (status === 200) {
|
|
||||||
dispatch(uploadComposeSuccess(data, f));
|
|
||||||
} else if (status === 202) {
|
|
||||||
const poll = () => {
|
|
||||||
dispatch(fetchMedia(data.id)).then(({ status, data }) => {
|
|
||||||
if (status === 200) {
|
|
||||||
dispatch(uploadComposeSuccess(data, f));
|
|
||||||
} else if (status === 206) {
|
|
||||||
setTimeout(() => poll(), 1000);
|
|
||||||
}
|
|
||||||
}).catch(error => dispatch(uploadComposeFail(error)));
|
|
||||||
};
|
|
||||||
|
|
||||||
poll();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).catch(error => dispatch(uploadComposeFail(error)));
|
|
||||||
/* eslint-enable no-loop-func */
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changeUploadCompose(id, params) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
dispatch(changeUploadComposeRequest());
|
|
||||||
|
|
||||||
dispatch(updateMedia(id, params)).then(response => {
|
|
||||||
dispatch(changeUploadComposeSuccess(response.data));
|
|
||||||
}).catch(error => {
|
|
||||||
dispatch(changeUploadComposeFail(id, error));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changeUploadComposeRequest() {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_UPLOAD_CHANGE_REQUEST,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export function changeUploadComposeSuccess(media) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_UPLOAD_CHANGE_SUCCESS,
|
|
||||||
media: media,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changeUploadComposeFail(error) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_UPLOAD_CHANGE_FAIL,
|
|
||||||
error: error,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function uploadComposeRequest() {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_UPLOAD_REQUEST,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function uploadComposeProgress(loaded, total) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_UPLOAD_PROGRESS,
|
|
||||||
loaded: loaded,
|
|
||||||
total: total,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function uploadComposeSuccess(media) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_UPLOAD_SUCCESS,
|
|
||||||
media: media,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function uploadComposeFail(error) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_UPLOAD_FAIL,
|
|
||||||
error: error,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function undoUploadCompose(media_id) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_UPLOAD_UNDO,
|
|
||||||
media_id: media_id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function clearComposeSuggestions() {
|
|
||||||
if (cancelFetchComposeSuggestionsAccounts) {
|
|
||||||
cancelFetchComposeSuggestionsAccounts();
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
type: COMPOSE_SUGGESTIONS_CLEAR,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
|
|
||||||
if (cancelFetchComposeSuggestionsAccounts) {
|
|
||||||
cancelFetchComposeSuggestionsAccounts();
|
|
||||||
}
|
|
||||||
api(getState).get('/api/v1/accounts/search', {
|
|
||||||
cancelToken: new CancelToken(cancel => {
|
|
||||||
cancelFetchComposeSuggestionsAccounts = cancel;
|
|
||||||
}),
|
|
||||||
params: {
|
|
||||||
q: token.slice(1),
|
|
||||||
resolve: false,
|
|
||||||
limit: 4,
|
|
||||||
},
|
|
||||||
}).then(response => {
|
|
||||||
dispatch(importFetchedAccounts(response.data));
|
|
||||||
dispatch(readyComposeSuggestionsAccounts(token, response.data));
|
|
||||||
}).catch(error => {
|
|
||||||
if (!isCancel(error)) {
|
|
||||||
dispatch(showAlertForError(error));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 200, { leading: true, trailing: true });
|
|
||||||
|
|
||||||
const fetchComposeSuggestionsEmojis = (dispatch, getState, token) => {
|
|
||||||
const results = emojiSearch(token.replace(':', ''), { maxResults: 5 });
|
|
||||||
dispatch(readyComposeSuggestionsEmojis(token, results));
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchComposeSuggestionsTags = (dispatch, getState, token) => {
|
|
||||||
dispatch(updateSuggestionTags(token));
|
|
||||||
};
|
|
||||||
|
|
||||||
export function fetchComposeSuggestions(token) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
switch (token[0]) {
|
|
||||||
case ':':
|
|
||||||
fetchComposeSuggestionsEmojis(dispatch, getState, token);
|
|
||||||
break;
|
|
||||||
case '#':
|
|
||||||
fetchComposeSuggestionsTags(dispatch, getState, token);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fetchComposeSuggestionsAccounts(dispatch, getState, token);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readyComposeSuggestionsEmojis(token, emojis) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_SUGGESTIONS_READY,
|
|
||||||
token,
|
|
||||||
emojis,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readyComposeSuggestionsAccounts(token, accounts) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_SUGGESTIONS_READY,
|
|
||||||
token,
|
|
||||||
accounts,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function selectComposeSuggestion(position, token, suggestion, path) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
let completion, startPosition;
|
|
||||||
|
|
||||||
if (typeof suggestion === 'object' && suggestion.id) {
|
|
||||||
completion = suggestion.native || suggestion.colons;
|
|
||||||
startPosition = position - 1;
|
|
||||||
|
|
||||||
dispatch(useEmoji(suggestion));
|
|
||||||
} else if (suggestion[0] === '#') {
|
|
||||||
completion = suggestion;
|
|
||||||
startPosition = position - 1;
|
|
||||||
} else {
|
|
||||||
completion = getState().getIn(['accounts', suggestion, 'acct']);
|
|
||||||
startPosition = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: COMPOSE_SUGGESTION_SELECT,
|
|
||||||
position: startPosition,
|
|
||||||
token,
|
|
||||||
completion,
|
|
||||||
path,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSuggestionTags(token) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_SUGGESTION_TAGS_UPDATE,
|
|
||||||
token,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateTagHistory(tags) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_TAG_HISTORY_UPDATE,
|
|
||||||
tags,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function insertIntoTagHistory(recognizedTags, text) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
const state = getState();
|
|
||||||
const oldHistory = state.getIn(['compose', 'tagHistory']);
|
|
||||||
const me = state.get('me');
|
|
||||||
const names = recognizedTags
|
|
||||||
.filter(tag => text.match(new RegExp(`#${tag.name}`, 'i')))
|
|
||||||
.map(tag => tag.name);
|
|
||||||
const intersectedOldHistory = oldHistory.filter(name => names.findIndex(newName => newName.toLowerCase() === name.toLowerCase()) === -1);
|
|
||||||
|
|
||||||
names.push(...intersectedOldHistory.toJS());
|
|
||||||
|
|
||||||
const newHistory = names.slice(0, 1000);
|
|
||||||
|
|
||||||
tagHistory.set(me, newHistory);
|
|
||||||
dispatch(updateTagHistory(newHistory));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mountCompose() {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_MOUNT,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unmountCompose() {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_UNMOUNT,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changeComposeSensitivity() {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_SENSITIVITY_CHANGE,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changeComposeSpoilerness() {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_SPOILERNESS_CHANGE,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changeComposeContentType(value) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_TYPE_CHANGE,
|
|
||||||
value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changeComposeSpoilerText(text) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_SPOILER_TEXT_CHANGE,
|
|
||||||
text,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changeComposeVisibility(value) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_VISIBILITY_CHANGE,
|
|
||||||
value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function insertEmojiCompose(position, emoji, needsSpace) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_EMOJI_INSERT,
|
|
||||||
position,
|
|
||||||
emoji,
|
|
||||||
needsSpace,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changeComposing(value) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_COMPOSING_CHANGE,
|
|
||||||
value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addPoll() {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_POLL_ADD,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removePoll() {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_POLL_REMOVE,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addSchedule() {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_SCHEDULE_ADD,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setSchedule(date) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_SCHEDULE_SET,
|
|
||||||
date: date,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeSchedule() {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_SCHEDULE_REMOVE,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addPollOption(title) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_POLL_OPTION_ADD,
|
|
||||||
title,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changePollOption(index, title) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_POLL_OPTION_CHANGE,
|
|
||||||
index,
|
|
||||||
title,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removePollOption(index) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_POLL_OPTION_REMOVE,
|
|
||||||
index,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changePollSettings(expiresIn, isMultiple) {
|
|
||||||
return {
|
|
||||||
type: COMPOSE_POLL_SETTINGS_CHANGE,
|
|
||||||
expiresIn,
|
|
||||||
isMultiple,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function openComposeWithText(text = '') {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
dispatch(resetCompose());
|
|
||||||
dispatch(openModal('COMPOSE'));
|
|
||||||
dispatch(changeCompose(text));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addToMentions(accountId) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
const state = getState();
|
|
||||||
const acct = state.getIn(['accounts', accountId, 'acct']);
|
|
||||||
|
|
||||||
return dispatch({
|
|
||||||
type: COMPOSE_ADD_TO_MENTIONS,
|
|
||||||
account: acct,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeFromMentions(accountId) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
const state = getState();
|
|
||||||
const acct = state.getIn(['accounts', accountId, 'acct']);
|
|
||||||
|
|
||||||
return dispatch({
|
|
||||||
type: COMPOSE_REMOVE_FROM_MENTIONS,
|
|
||||||
account: acct,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,784 @@
|
||||||
|
import axios, { AxiosError, Canceler } from 'axios';
|
||||||
|
import { List as ImmutableList, Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||||
|
import throttle from 'lodash/throttle';
|
||||||
|
import { defineMessages, IntlShape } from 'react-intl';
|
||||||
|
|
||||||
|
import snackbar from 'soapbox/actions/snackbar';
|
||||||
|
import api from 'soapbox/api';
|
||||||
|
import { search as emojiSearch } from 'soapbox/features/emoji/emoji_mart_search_light';
|
||||||
|
import { tagHistory } from 'soapbox/settings';
|
||||||
|
import { isLoggedIn } from 'soapbox/utils/auth';
|
||||||
|
import { getFeatures, parseVersion } from 'soapbox/utils/features';
|
||||||
|
import { formatBytes } from 'soapbox/utils/media';
|
||||||
|
import resizeImage from 'soapbox/utils/resize_image';
|
||||||
|
|
||||||
|
import { showAlert, showAlertForError } from './alerts';
|
||||||
|
import { useEmoji } from './emojis';
|
||||||
|
import { importFetchedAccounts } from './importer';
|
||||||
|
import { uploadMedia, fetchMedia, updateMedia } from './media';
|
||||||
|
import { openModal, closeModal } from './modals';
|
||||||
|
import { getSettings } from './settings';
|
||||||
|
import { createStatus } from './statuses';
|
||||||
|
|
||||||
|
import type { History } from 'history';
|
||||||
|
import type { Emoji } from 'soapbox/components/autosuggest_emoji';
|
||||||
|
import type { AutoSuggestion } from 'soapbox/components/autosuggest_input';
|
||||||
|
import type { AppDispatch, RootState } from 'soapbox/store';
|
||||||
|
import type { Account, APIEntity, Status } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
const { CancelToken, isCancel } = axios;
|
||||||
|
|
||||||
|
let cancelFetchComposeSuggestionsAccounts: Canceler;
|
||||||
|
|
||||||
|
const COMPOSE_CHANGE = 'COMPOSE_CHANGE';
|
||||||
|
const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST';
|
||||||
|
const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS';
|
||||||
|
const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL';
|
||||||
|
const COMPOSE_REPLY = 'COMPOSE_REPLY';
|
||||||
|
const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL';
|
||||||
|
const COMPOSE_QUOTE = 'COMPOSE_QUOTE';
|
||||||
|
const COMPOSE_QUOTE_CANCEL = 'COMPOSE_QUOTE_CANCEL';
|
||||||
|
const COMPOSE_DIRECT = 'COMPOSE_DIRECT';
|
||||||
|
const COMPOSE_MENTION = 'COMPOSE_MENTION';
|
||||||
|
const COMPOSE_RESET = 'COMPOSE_RESET';
|
||||||
|
const COMPOSE_UPLOAD_REQUEST = 'COMPOSE_UPLOAD_REQUEST';
|
||||||
|
const COMPOSE_UPLOAD_SUCCESS = 'COMPOSE_UPLOAD_SUCCESS';
|
||||||
|
const COMPOSE_UPLOAD_FAIL = 'COMPOSE_UPLOAD_FAIL';
|
||||||
|
const COMPOSE_UPLOAD_PROGRESS = 'COMPOSE_UPLOAD_PROGRESS';
|
||||||
|
const COMPOSE_UPLOAD_UNDO = 'COMPOSE_UPLOAD_UNDO';
|
||||||
|
|
||||||
|
const COMPOSE_SUGGESTIONS_CLEAR = 'COMPOSE_SUGGESTIONS_CLEAR';
|
||||||
|
const COMPOSE_SUGGESTIONS_READY = 'COMPOSE_SUGGESTIONS_READY';
|
||||||
|
const COMPOSE_SUGGESTION_SELECT = 'COMPOSE_SUGGESTION_SELECT';
|
||||||
|
const COMPOSE_SUGGESTION_TAGS_UPDATE = 'COMPOSE_SUGGESTION_TAGS_UPDATE';
|
||||||
|
|
||||||
|
const COMPOSE_TAG_HISTORY_UPDATE = 'COMPOSE_TAG_HISTORY_UPDATE';
|
||||||
|
|
||||||
|
const COMPOSE_MOUNT = 'COMPOSE_MOUNT';
|
||||||
|
const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT';
|
||||||
|
|
||||||
|
const COMPOSE_SENSITIVITY_CHANGE = 'COMPOSE_SENSITIVITY_CHANGE';
|
||||||
|
const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE';
|
||||||
|
const COMPOSE_TYPE_CHANGE = 'COMPOSE_TYPE_CHANGE';
|
||||||
|
const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE';
|
||||||
|
const COMPOSE_VISIBILITY_CHANGE = 'COMPOSE_VISIBILITY_CHANGE';
|
||||||
|
const COMPOSE_LISTABILITY_CHANGE = 'COMPOSE_LISTABILITY_CHANGE';
|
||||||
|
const COMPOSE_COMPOSING_CHANGE = 'COMPOSE_COMPOSING_CHANGE';
|
||||||
|
|
||||||
|
const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
|
||||||
|
|
||||||
|
const COMPOSE_UPLOAD_CHANGE_REQUEST = 'COMPOSE_UPLOAD_UPDATE_REQUEST';
|
||||||
|
const COMPOSE_UPLOAD_CHANGE_SUCCESS = 'COMPOSE_UPLOAD_UPDATE_SUCCESS';
|
||||||
|
const COMPOSE_UPLOAD_CHANGE_FAIL = 'COMPOSE_UPLOAD_UPDATE_FAIL';
|
||||||
|
|
||||||
|
const COMPOSE_POLL_ADD = 'COMPOSE_POLL_ADD';
|
||||||
|
const COMPOSE_POLL_REMOVE = 'COMPOSE_POLL_REMOVE';
|
||||||
|
const COMPOSE_POLL_OPTION_ADD = 'COMPOSE_POLL_OPTION_ADD';
|
||||||
|
const COMPOSE_POLL_OPTION_CHANGE = 'COMPOSE_POLL_OPTION_CHANGE';
|
||||||
|
const COMPOSE_POLL_OPTION_REMOVE = 'COMPOSE_POLL_OPTION_REMOVE';
|
||||||
|
const COMPOSE_POLL_SETTINGS_CHANGE = 'COMPOSE_POLL_SETTINGS_CHANGE';
|
||||||
|
|
||||||
|
const COMPOSE_SCHEDULE_ADD = 'COMPOSE_SCHEDULE_ADD';
|
||||||
|
const COMPOSE_SCHEDULE_SET = 'COMPOSE_SCHEDULE_SET';
|
||||||
|
const COMPOSE_SCHEDULE_REMOVE = 'COMPOSE_SCHEDULE_REMOVE';
|
||||||
|
|
||||||
|
const COMPOSE_ADD_TO_MENTIONS = 'COMPOSE_ADD_TO_MENTIONS';
|
||||||
|
const COMPOSE_REMOVE_FROM_MENTIONS = 'COMPOSE_REMOVE_FROM_MENTIONS';
|
||||||
|
|
||||||
|
const COMPOSE_SET_STATUS = 'COMPOSE_SET_STATUS';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
exceededImageSizeLimit: { id: 'upload_error.image_size_limit', defaultMessage: 'Image exceeds the current file size limit ({limit})' },
|
||||||
|
exceededVideoSizeLimit: { id: 'upload_error.video_size_limit', defaultMessage: 'Video exceeds the current file size limit ({limit})' },
|
||||||
|
scheduleError: { id: 'compose.invalid_schedule', defaultMessage: 'You must schedule a post at least 5 minutes out.' },
|
||||||
|
success: { id: 'compose.submit_success', defaultMessage: 'Your post was sent' },
|
||||||
|
uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' },
|
||||||
|
uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' },
|
||||||
|
view: { id: 'snackbar.view', defaultMessage: 'View' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const COMPOSE_PANEL_BREAKPOINT = 600 + (285 * 1) + (10 * 1);
|
||||||
|
|
||||||
|
const ensureComposeIsVisible = (getState: () => RootState, routerHistory: History) => {
|
||||||
|
if (!getState().compose.get('mounted') && window.innerWidth < COMPOSE_PANEL_BREAKPOINT) {
|
||||||
|
routerHistory.push('/posts/new');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setComposeToStatus = (status: Status, rawText: string, spoilerText?: string, contentType?: string | false, withRedraft?: boolean) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
const { instance } = getState();
|
||||||
|
const { explicitAddressing } = getFeatures(instance);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: COMPOSE_SET_STATUS,
|
||||||
|
status,
|
||||||
|
rawText,
|
||||||
|
explicitAddressing,
|
||||||
|
spoilerText,
|
||||||
|
contentType,
|
||||||
|
v: parseVersion(instance.version),
|
||||||
|
withRedraft,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeCompose = (text: string) => ({
|
||||||
|
type: COMPOSE_CHANGE,
|
||||||
|
text: text,
|
||||||
|
});
|
||||||
|
|
||||||
|
const replyCompose = (status: Status) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
const state = getState();
|
||||||
|
const instance = state.instance;
|
||||||
|
const { explicitAddressing } = getFeatures(instance);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: COMPOSE_REPLY,
|
||||||
|
status: status,
|
||||||
|
account: state.accounts.get(state.me),
|
||||||
|
explicitAddressing,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch(openModal('COMPOSE'));
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancelReplyCompose = () => ({
|
||||||
|
type: COMPOSE_REPLY_CANCEL,
|
||||||
|
});
|
||||||
|
|
||||||
|
const quoteCompose = (status: Status) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
const state = getState();
|
||||||
|
const instance = state.instance;
|
||||||
|
const { explicitAddressing } = getFeatures(instance);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: COMPOSE_QUOTE,
|
||||||
|
status: status,
|
||||||
|
account: state.accounts.get(state.me),
|
||||||
|
explicitAddressing,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch(openModal('COMPOSE'));
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancelQuoteCompose = () => ({
|
||||||
|
type: COMPOSE_QUOTE_CANCEL,
|
||||||
|
});
|
||||||
|
|
||||||
|
const resetCompose = () => ({
|
||||||
|
type: COMPOSE_RESET,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mentionCompose = (account: Account) =>
|
||||||
|
(dispatch: AppDispatch) => {
|
||||||
|
dispatch({
|
||||||
|
type: COMPOSE_MENTION,
|
||||||
|
account: account,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch(openModal('COMPOSE'));
|
||||||
|
};
|
||||||
|
|
||||||
|
const directCompose = (account: Account) =>
|
||||||
|
(dispatch: AppDispatch) => {
|
||||||
|
dispatch({
|
||||||
|
type: COMPOSE_DIRECT,
|
||||||
|
account: account,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch(openModal('COMPOSE'));
|
||||||
|
};
|
||||||
|
|
||||||
|
const directComposeById = (accountId: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
const account = getState().accounts.get(accountId);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: COMPOSE_DIRECT,
|
||||||
|
account: account,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch(openModal('COMPOSE'));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleComposeSubmit = (dispatch: AppDispatch, getState: () => RootState, data: APIEntity, status: string) => {
|
||||||
|
if (!dispatch || !getState) return;
|
||||||
|
|
||||||
|
dispatch(insertIntoTagHistory(data.tags || [], status));
|
||||||
|
dispatch(submitComposeSuccess({ ...data }));
|
||||||
|
dispatch(snackbar.success(messages.success, messages.view, `/@${data.account.acct}/posts/${data.id}`));
|
||||||
|
};
|
||||||
|
|
||||||
|
const needsDescriptions = (state: RootState) => {
|
||||||
|
const media = state.compose.get('media_attachments') as ImmutableList<ImmutableMap<string, any>>;
|
||||||
|
const missingDescriptionModal = getSettings(state).get('missingDescriptionModal');
|
||||||
|
|
||||||
|
const hasMissing = media.filter(item => !item.get('description')).size > 0;
|
||||||
|
|
||||||
|
return missingDescriptionModal && hasMissing;
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateSchedule = (state: RootState) => {
|
||||||
|
const schedule = state.compose.get('schedule');
|
||||||
|
if (!schedule) return true;
|
||||||
|
|
||||||
|
const fiveMinutesFromNow = new Date(new Date().getTime() + 300000);
|
||||||
|
|
||||||
|
return schedule.getTime() > fiveMinutesFromNow.getTime();
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitCompose = (routerHistory: History, force = false) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
const state = getState();
|
||||||
|
|
||||||
|
const status = state.compose.get('text') || '';
|
||||||
|
const media = state.compose.get('media_attachments') as ImmutableList<ImmutableMap<string, any>>;
|
||||||
|
const statusId = state.compose.get('id') || null;
|
||||||
|
let to = state.compose.get('to') || ImmutableOrderedSet();
|
||||||
|
|
||||||
|
if (!validateSchedule(state)) {
|
||||||
|
dispatch(snackbar.error(messages.scheduleError));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!status || !status.length) && media.size === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!force && needsDescriptions(state)) {
|
||||||
|
dispatch(openModal('MISSING_DESCRIPTION', {
|
||||||
|
onContinue: () => {
|
||||||
|
dispatch(closeModal('MISSING_DESCRIPTION'));
|
||||||
|
dispatch(submitCompose(routerHistory, true));
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to && status) {
|
||||||
|
const mentions: string[] = status.match(/(?:^|\s|\.)@([a-z0-9_]+(?:@[a-z0-9\.\-]+)?)/gi); // not a perfect regex
|
||||||
|
|
||||||
|
if (mentions)
|
||||||
|
to = to.union(mentions.map(mention => mention.trim().slice(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(submitComposeRequest());
|
||||||
|
dispatch(closeModal());
|
||||||
|
|
||||||
|
const idempotencyKey = state.compose.get('idempotencyKey');
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
status,
|
||||||
|
in_reply_to_id: state.compose.get('in_reply_to') || null,
|
||||||
|
quote_id: state.compose.get('quote') || null,
|
||||||
|
media_ids: media.map(item => item.get('id')),
|
||||||
|
sensitive: state.compose.get('sensitive'),
|
||||||
|
spoiler_text: state.compose.get('spoiler_text') || '',
|
||||||
|
visibility: state.compose.get('privacy'),
|
||||||
|
content_type: state.compose.get('content_type'),
|
||||||
|
poll: state.compose.get('poll') || null,
|
||||||
|
scheduled_at: state.compose.get('schedule') || null,
|
||||||
|
to,
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch(createStatus(params, idempotencyKey, statusId)).then(function(data) {
|
||||||
|
if (!statusId && data.visibility === 'direct' && getState().conversations.get('mounted') <= 0 && routerHistory) {
|
||||||
|
routerHistory.push('/messages');
|
||||||
|
}
|
||||||
|
handleComposeSubmit(dispatch, getState, data, status);
|
||||||
|
}).catch(function(error) {
|
||||||
|
dispatch(submitComposeFail(error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitComposeRequest = () => ({
|
||||||
|
type: COMPOSE_SUBMIT_REQUEST,
|
||||||
|
});
|
||||||
|
|
||||||
|
const submitComposeSuccess = (status: APIEntity) => ({
|
||||||
|
type: COMPOSE_SUBMIT_SUCCESS,
|
||||||
|
status: status,
|
||||||
|
});
|
||||||
|
|
||||||
|
const submitComposeFail = (error: AxiosError) => ({
|
||||||
|
type: COMPOSE_SUBMIT_FAIL,
|
||||||
|
error: error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const uploadCompose = (files: FileList, intl: IntlShape) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
const attachmentLimit = getState().instance.configuration.getIn(['statuses', 'max_media_attachments']) as number;
|
||||||
|
const maxImageSize = getState().instance.configuration.getIn(['media_attachments', 'image_size_limit']) as number | undefined;
|
||||||
|
const maxVideoSize = getState().instance.configuration.getIn(['media_attachments', 'video_size_limit']) as number | undefined;
|
||||||
|
|
||||||
|
const media = getState().compose.get('media_attachments');
|
||||||
|
const progress = new Array(files.length).fill(0);
|
||||||
|
let total = Array.from(files).reduce((a, v) => a + v.size, 0);
|
||||||
|
|
||||||
|
if (files.length + media.size > attachmentLimit) {
|
||||||
|
dispatch(showAlert(undefined, messages.uploadErrorLimit, 'error'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(uploadComposeRequest());
|
||||||
|
|
||||||
|
Array.from(files).forEach((f, i) => {
|
||||||
|
if (media.size + i > attachmentLimit - 1) return;
|
||||||
|
|
||||||
|
const isImage = f.type.match(/image.*/);
|
||||||
|
const isVideo = f.type.match(/video.*/);
|
||||||
|
if (isImage && maxImageSize && (f.size > maxImageSize)) {
|
||||||
|
const limit = formatBytes(maxImageSize);
|
||||||
|
const message = intl.formatMessage(messages.exceededImageSizeLimit, { limit });
|
||||||
|
dispatch(snackbar.error(message));
|
||||||
|
dispatch(uploadComposeFail(true));
|
||||||
|
return;
|
||||||
|
} else if (isVideo && maxVideoSize && (f.size > maxVideoSize)) {
|
||||||
|
const limit = formatBytes(maxVideoSize);
|
||||||
|
const message = intl.formatMessage(messages.exceededVideoSizeLimit, { limit });
|
||||||
|
dispatch(snackbar.error(message));
|
||||||
|
dispatch(uploadComposeFail(true));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Don't define const in loop
|
||||||
|
/* eslint-disable no-loop-func */
|
||||||
|
resizeImage(f).then(file => {
|
||||||
|
const data = new FormData();
|
||||||
|
data.append('file', file);
|
||||||
|
// Account for disparity in size of original image and resized data
|
||||||
|
total += file.size - f.size;
|
||||||
|
|
||||||
|
const onUploadProgress = ({ loaded }: any) => {
|
||||||
|
progress[i] = loaded;
|
||||||
|
dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
|
||||||
|
};
|
||||||
|
|
||||||
|
return dispatch(uploadMedia(data, onUploadProgress))
|
||||||
|
.then(({ status, data }) => {
|
||||||
|
// If server-side processing of the media attachment has not completed yet,
|
||||||
|
// poll the server until it is, before showing the media attachment as uploaded
|
||||||
|
if (status === 200) {
|
||||||
|
dispatch(uploadComposeSuccess(data, f));
|
||||||
|
} else if (status === 202) {
|
||||||
|
const poll = () => {
|
||||||
|
dispatch(fetchMedia(data.id)).then(({ status, data }) => {
|
||||||
|
if (status === 200) {
|
||||||
|
dispatch(uploadComposeSuccess(data, f));
|
||||||
|
} else if (status === 206) {
|
||||||
|
setTimeout(() => poll(), 1000);
|
||||||
|
}
|
||||||
|
}).catch(error => dispatch(uploadComposeFail(error)));
|
||||||
|
};
|
||||||
|
|
||||||
|
poll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).catch(error => dispatch(uploadComposeFail(error)));
|
||||||
|
/* eslint-enable no-loop-func */
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeUploadCompose = (id: string, params: Record<string, any>) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
dispatch(changeUploadComposeRequest());
|
||||||
|
|
||||||
|
dispatch(updateMedia(id, params)).then(response => {
|
||||||
|
dispatch(changeUploadComposeSuccess(response.data));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(changeUploadComposeFail(id, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeUploadComposeRequest = () => ({
|
||||||
|
type: COMPOSE_UPLOAD_CHANGE_REQUEST,
|
||||||
|
skipLoading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const changeUploadComposeSuccess = (media: APIEntity) => ({
|
||||||
|
type: COMPOSE_UPLOAD_CHANGE_SUCCESS,
|
||||||
|
media: media,
|
||||||
|
skipLoading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const changeUploadComposeFail = (id: string, error: AxiosError) => ({
|
||||||
|
type: COMPOSE_UPLOAD_CHANGE_FAIL,
|
||||||
|
id,
|
||||||
|
error: error,
|
||||||
|
skipLoading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const uploadComposeRequest = () => ({
|
||||||
|
type: COMPOSE_UPLOAD_REQUEST,
|
||||||
|
skipLoading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const uploadComposeProgress = (loaded: number, total: number) => ({
|
||||||
|
type: COMPOSE_UPLOAD_PROGRESS,
|
||||||
|
loaded: loaded,
|
||||||
|
total: total,
|
||||||
|
});
|
||||||
|
|
||||||
|
const uploadComposeSuccess = (media: APIEntity, file: File) => ({
|
||||||
|
type: COMPOSE_UPLOAD_SUCCESS,
|
||||||
|
media: media,
|
||||||
|
file,
|
||||||
|
skipLoading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const uploadComposeFail = (error: AxiosError | true) => ({
|
||||||
|
type: COMPOSE_UPLOAD_FAIL,
|
||||||
|
error: error,
|
||||||
|
skipLoading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const undoUploadCompose = (media_id: string) => ({
|
||||||
|
type: COMPOSE_UPLOAD_UNDO,
|
||||||
|
media_id: media_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const clearComposeSuggestions = () => {
|
||||||
|
if (cancelFetchComposeSuggestionsAccounts) {
|
||||||
|
cancelFetchComposeSuggestionsAccounts();
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: COMPOSE_SUGGESTIONS_CLEAR,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
|
||||||
|
if (cancelFetchComposeSuggestionsAccounts) {
|
||||||
|
cancelFetchComposeSuggestionsAccounts();
|
||||||
|
}
|
||||||
|
api(getState).get('/api/v1/accounts/search', {
|
||||||
|
cancelToken: new CancelToken(cancel => {
|
||||||
|
cancelFetchComposeSuggestionsAccounts = cancel;
|
||||||
|
}),
|
||||||
|
params: {
|
||||||
|
q: token.slice(1),
|
||||||
|
resolve: false,
|
||||||
|
limit: 4,
|
||||||
|
},
|
||||||
|
}).then(response => {
|
||||||
|
dispatch(importFetchedAccounts(response.data));
|
||||||
|
dispatch(readyComposeSuggestionsAccounts(token, response.data));
|
||||||
|
}).catch(error => {
|
||||||
|
if (!isCancel(error)) {
|
||||||
|
dispatch(showAlertForError(error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 200, { leading: true, trailing: true });
|
||||||
|
|
||||||
|
const fetchComposeSuggestionsEmojis = (dispatch: AppDispatch, getState: () => RootState, token: string) => {
|
||||||
|
const results = emojiSearch(token.replace(':', ''), { maxResults: 5 } as any);
|
||||||
|
dispatch(readyComposeSuggestionsEmojis(token, results));
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchComposeSuggestionsTags = (dispatch: AppDispatch, getState: () => RootState, token: string) => {
|
||||||
|
dispatch(updateSuggestionTags(token));
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchComposeSuggestions = (token: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
switch (token[0]) {
|
||||||
|
case ':':
|
||||||
|
fetchComposeSuggestionsEmojis(dispatch, getState, token);
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
fetchComposeSuggestionsTags(dispatch, getState, token);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fetchComposeSuggestionsAccounts(dispatch, getState, token);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const readyComposeSuggestionsEmojis = (token: string, emojis: Emoji[]) => ({
|
||||||
|
type: COMPOSE_SUGGESTIONS_READY,
|
||||||
|
token,
|
||||||
|
emojis,
|
||||||
|
});
|
||||||
|
|
||||||
|
const readyComposeSuggestionsAccounts = (token: string, accounts: APIEntity[]) => ({
|
||||||
|
type: COMPOSE_SUGGESTIONS_READY,
|
||||||
|
token,
|
||||||
|
accounts,
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectComposeSuggestion = (position: number, token: string | null, suggestion: AutoSuggestion, path: Array<string | number>) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
let completion, startPosition;
|
||||||
|
|
||||||
|
if (typeof suggestion === 'object' && suggestion.id) {
|
||||||
|
completion = suggestion.native || suggestion.colons;
|
||||||
|
startPosition = position - 1;
|
||||||
|
|
||||||
|
dispatch(useEmoji(suggestion));
|
||||||
|
} else if (typeof suggestion === 'string' && suggestion[0] === '#') {
|
||||||
|
completion = suggestion;
|
||||||
|
startPosition = position - 1;
|
||||||
|
} else {
|
||||||
|
completion = getState().accounts.get(suggestion)!.acct;
|
||||||
|
startPosition = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: COMPOSE_SUGGESTION_SELECT,
|
||||||
|
position: startPosition,
|
||||||
|
token,
|
||||||
|
completion,
|
||||||
|
path,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSuggestionTags = (token: string) => ({
|
||||||
|
type: COMPOSE_SUGGESTION_TAGS_UPDATE,
|
||||||
|
token,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateTagHistory = (tags: string[]) => ({
|
||||||
|
type: COMPOSE_TAG_HISTORY_UPDATE,
|
||||||
|
tags,
|
||||||
|
});
|
||||||
|
|
||||||
|
const insertIntoTagHistory = (recognizedTags: APIEntity[], text: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
const state = getState();
|
||||||
|
const oldHistory = state.compose.get('tagHistory') as ImmutableList<string>;
|
||||||
|
const me = state.me;
|
||||||
|
const names = recognizedTags
|
||||||
|
.filter(tag => text.match(new RegExp(`#${tag.name}`, 'i')))
|
||||||
|
.map(tag => tag.name);
|
||||||
|
const intersectedOldHistory = oldHistory.filter(name => names.findIndex(newName => newName.toLowerCase() === name.toLowerCase()) === -1);
|
||||||
|
|
||||||
|
names.push(...intersectedOldHistory.toJS());
|
||||||
|
|
||||||
|
const newHistory = names.slice(0, 1000);
|
||||||
|
|
||||||
|
tagHistory.set(me as string, newHistory);
|
||||||
|
dispatch(updateTagHistory(newHistory));
|
||||||
|
};
|
||||||
|
|
||||||
|
const mountCompose = () => ({
|
||||||
|
type: COMPOSE_MOUNT,
|
||||||
|
});
|
||||||
|
|
||||||
|
const unmountCompose = () => ({
|
||||||
|
type: COMPOSE_UNMOUNT,
|
||||||
|
});
|
||||||
|
|
||||||
|
const changeComposeSensitivity = () => ({
|
||||||
|
type: COMPOSE_SENSITIVITY_CHANGE,
|
||||||
|
});
|
||||||
|
|
||||||
|
const changeComposeSpoilerness = () => ({
|
||||||
|
type: COMPOSE_SPOILERNESS_CHANGE,
|
||||||
|
});
|
||||||
|
|
||||||
|
const changeComposeContentType = (value: string) => ({
|
||||||
|
type: COMPOSE_TYPE_CHANGE,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
|
||||||
|
const changeComposeSpoilerText = (text: string) => ({
|
||||||
|
type: COMPOSE_SPOILER_TEXT_CHANGE,
|
||||||
|
text,
|
||||||
|
});
|
||||||
|
|
||||||
|
const changeComposeVisibility = (value: string) => ({
|
||||||
|
type: COMPOSE_VISIBILITY_CHANGE,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
|
||||||
|
const insertEmojiCompose = (position: number, emoji: string, needsSpace: boolean) => ({
|
||||||
|
type: COMPOSE_EMOJI_INSERT,
|
||||||
|
position,
|
||||||
|
emoji,
|
||||||
|
needsSpace,
|
||||||
|
});
|
||||||
|
|
||||||
|
const changeComposing = (value: string) => ({
|
||||||
|
type: COMPOSE_COMPOSING_CHANGE,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
|
||||||
|
const addPoll = () => ({
|
||||||
|
type: COMPOSE_POLL_ADD,
|
||||||
|
});
|
||||||
|
|
||||||
|
const removePoll = () => ({
|
||||||
|
type: COMPOSE_POLL_REMOVE,
|
||||||
|
});
|
||||||
|
|
||||||
|
const addSchedule = () => ({
|
||||||
|
type: COMPOSE_SCHEDULE_ADD,
|
||||||
|
});
|
||||||
|
|
||||||
|
const setSchedule = (date: Date) => ({
|
||||||
|
type: COMPOSE_SCHEDULE_SET,
|
||||||
|
date: date,
|
||||||
|
});
|
||||||
|
|
||||||
|
const removeSchedule = () => ({
|
||||||
|
type: COMPOSE_SCHEDULE_REMOVE,
|
||||||
|
});
|
||||||
|
|
||||||
|
const addPollOption = (title: string) => ({
|
||||||
|
type: COMPOSE_POLL_OPTION_ADD,
|
||||||
|
title,
|
||||||
|
});
|
||||||
|
|
||||||
|
const changePollOption = (index: number, title: string) => ({
|
||||||
|
type: COMPOSE_POLL_OPTION_CHANGE,
|
||||||
|
index,
|
||||||
|
title,
|
||||||
|
});
|
||||||
|
|
||||||
|
const removePollOption = (index: number) => ({
|
||||||
|
type: COMPOSE_POLL_OPTION_REMOVE,
|
||||||
|
index,
|
||||||
|
});
|
||||||
|
|
||||||
|
const changePollSettings = (expiresIn?: string | number, isMultiple?: boolean) => ({
|
||||||
|
type: COMPOSE_POLL_SETTINGS_CHANGE,
|
||||||
|
expiresIn,
|
||||||
|
isMultiple,
|
||||||
|
});
|
||||||
|
|
||||||
|
const openComposeWithText = (text = '') =>
|
||||||
|
(dispatch: AppDispatch) => {
|
||||||
|
dispatch(resetCompose());
|
||||||
|
dispatch(openModal('COMPOSE'));
|
||||||
|
dispatch(changeCompose(text));
|
||||||
|
};
|
||||||
|
|
||||||
|
const addToMentions = (accountId: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
const state = getState();
|
||||||
|
const acct = state.accounts.get(accountId)!.acct;
|
||||||
|
|
||||||
|
return dispatch({
|
||||||
|
type: COMPOSE_ADD_TO_MENTIONS,
|
||||||
|
account: acct,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeFromMentions = (accountId: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
const state = getState();
|
||||||
|
const acct = state.accounts.get(accountId)!.acct;
|
||||||
|
|
||||||
|
return dispatch({
|
||||||
|
type: COMPOSE_REMOVE_FROM_MENTIONS,
|
||||||
|
account: acct,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
COMPOSE_CHANGE,
|
||||||
|
COMPOSE_SUBMIT_REQUEST,
|
||||||
|
COMPOSE_SUBMIT_SUCCESS,
|
||||||
|
COMPOSE_SUBMIT_FAIL,
|
||||||
|
COMPOSE_REPLY,
|
||||||
|
COMPOSE_REPLY_CANCEL,
|
||||||
|
COMPOSE_QUOTE,
|
||||||
|
COMPOSE_QUOTE_CANCEL,
|
||||||
|
COMPOSE_DIRECT,
|
||||||
|
COMPOSE_MENTION,
|
||||||
|
COMPOSE_RESET,
|
||||||
|
COMPOSE_UPLOAD_REQUEST,
|
||||||
|
COMPOSE_UPLOAD_SUCCESS,
|
||||||
|
COMPOSE_UPLOAD_FAIL,
|
||||||
|
COMPOSE_UPLOAD_PROGRESS,
|
||||||
|
COMPOSE_UPLOAD_UNDO,
|
||||||
|
COMPOSE_SUGGESTIONS_CLEAR,
|
||||||
|
COMPOSE_SUGGESTIONS_READY,
|
||||||
|
COMPOSE_SUGGESTION_SELECT,
|
||||||
|
COMPOSE_SUGGESTION_TAGS_UPDATE,
|
||||||
|
COMPOSE_TAG_HISTORY_UPDATE,
|
||||||
|
COMPOSE_MOUNT,
|
||||||
|
COMPOSE_UNMOUNT,
|
||||||
|
COMPOSE_SENSITIVITY_CHANGE,
|
||||||
|
COMPOSE_SPOILERNESS_CHANGE,
|
||||||
|
COMPOSE_TYPE_CHANGE,
|
||||||
|
COMPOSE_SPOILER_TEXT_CHANGE,
|
||||||
|
COMPOSE_VISIBILITY_CHANGE,
|
||||||
|
COMPOSE_LISTABILITY_CHANGE,
|
||||||
|
COMPOSE_COMPOSING_CHANGE,
|
||||||
|
COMPOSE_EMOJI_INSERT,
|
||||||
|
COMPOSE_UPLOAD_CHANGE_REQUEST,
|
||||||
|
COMPOSE_UPLOAD_CHANGE_SUCCESS,
|
||||||
|
COMPOSE_UPLOAD_CHANGE_FAIL,
|
||||||
|
COMPOSE_POLL_ADD,
|
||||||
|
COMPOSE_POLL_REMOVE,
|
||||||
|
COMPOSE_POLL_OPTION_ADD,
|
||||||
|
COMPOSE_POLL_OPTION_CHANGE,
|
||||||
|
COMPOSE_POLL_OPTION_REMOVE,
|
||||||
|
COMPOSE_POLL_SETTINGS_CHANGE,
|
||||||
|
COMPOSE_SCHEDULE_ADD,
|
||||||
|
COMPOSE_SCHEDULE_SET,
|
||||||
|
COMPOSE_SCHEDULE_REMOVE,
|
||||||
|
COMPOSE_ADD_TO_MENTIONS,
|
||||||
|
COMPOSE_REMOVE_FROM_MENTIONS,
|
||||||
|
COMPOSE_SET_STATUS,
|
||||||
|
ensureComposeIsVisible,
|
||||||
|
setComposeToStatus,
|
||||||
|
changeCompose,
|
||||||
|
replyCompose,
|
||||||
|
cancelReplyCompose,
|
||||||
|
quoteCompose,
|
||||||
|
cancelQuoteCompose,
|
||||||
|
resetCompose,
|
||||||
|
mentionCompose,
|
||||||
|
directCompose,
|
||||||
|
directComposeById,
|
||||||
|
handleComposeSubmit,
|
||||||
|
submitCompose,
|
||||||
|
submitComposeRequest,
|
||||||
|
submitComposeSuccess,
|
||||||
|
submitComposeFail,
|
||||||
|
uploadCompose,
|
||||||
|
changeUploadCompose,
|
||||||
|
changeUploadComposeRequest,
|
||||||
|
changeUploadComposeSuccess,
|
||||||
|
changeUploadComposeFail,
|
||||||
|
uploadComposeRequest,
|
||||||
|
uploadComposeProgress,
|
||||||
|
uploadComposeSuccess,
|
||||||
|
uploadComposeFail,
|
||||||
|
undoUploadCompose,
|
||||||
|
clearComposeSuggestions,
|
||||||
|
fetchComposeSuggestions,
|
||||||
|
readyComposeSuggestionsEmojis,
|
||||||
|
readyComposeSuggestionsAccounts,
|
||||||
|
selectComposeSuggestion,
|
||||||
|
updateSuggestionTags,
|
||||||
|
updateTagHistory,
|
||||||
|
mountCompose,
|
||||||
|
unmountCompose,
|
||||||
|
changeComposeSensitivity,
|
||||||
|
changeComposeSpoilerness,
|
||||||
|
changeComposeContentType,
|
||||||
|
changeComposeSpoilerText,
|
||||||
|
changeComposeVisibility,
|
||||||
|
insertEmojiCompose,
|
||||||
|
changeComposing,
|
||||||
|
addPoll,
|
||||||
|
removePoll,
|
||||||
|
addSchedule,
|
||||||
|
setSchedule,
|
||||||
|
removeSchedule,
|
||||||
|
addPollOption,
|
||||||
|
changePollOption,
|
||||||
|
removePollOption,
|
||||||
|
changePollSettings,
|
||||||
|
openComposeWithText,
|
||||||
|
addToMentions,
|
||||||
|
removeFromMentions,
|
||||||
|
};
|
|
@ -1,10 +1,11 @@
|
||||||
import { saveSettings } from './settings';
|
import { saveSettings } from './settings';
|
||||||
|
|
||||||
|
import type { Emoji } from 'soapbox/components/autosuggest_emoji';
|
||||||
import type { AppDispatch } from 'soapbox/store';
|
import type { AppDispatch } from 'soapbox/store';
|
||||||
|
|
||||||
const EMOJI_USE = 'EMOJI_USE';
|
const EMOJI_USE = 'EMOJI_USE';
|
||||||
|
|
||||||
const useEmoji = (emoji: string) =>
|
const useEmoji = (emoji: Emoji) =>
|
||||||
(dispatch: AppDispatch) => {
|
(dispatch: AppDispatch) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: EMOJI_USE,
|
type: EMOJI_USE,
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
import { isLoggedIn } from 'soapbox/utils/auth';
|
|
||||||
|
|
||||||
import api from '../api';
|
|
||||||
|
|
||||||
export const GROUP_CREATE_REQUEST = 'GROUP_CREATE_REQUEST';
|
|
||||||
export const GROUP_CREATE_SUCCESS = 'GROUP_CREATE_SUCCESS';
|
|
||||||
export const GROUP_CREATE_FAIL = 'GROUP_CREATE_FAIL';
|
|
||||||
|
|
||||||
export const GROUP_UPDATE_REQUEST = 'GROUP_UPDATE_REQUEST';
|
|
||||||
export const GROUP_UPDATE_SUCCESS = 'GROUP_UPDATE_SUCCESS';
|
|
||||||
export const GROUP_UPDATE_FAIL = 'GROUP_UPDATE_FAIL';
|
|
||||||
|
|
||||||
export const GROUP_EDITOR_VALUE_CHANGE = 'GROUP_EDITOR_VALUE_CHANGE';
|
|
||||||
export const GROUP_EDITOR_RESET = 'GROUP_EDITOR_RESET';
|
|
||||||
export const GROUP_EDITOR_SETUP = 'GROUP_EDITOR_SETUP';
|
|
||||||
|
|
||||||
export const submit = (routerHistory) => (dispatch, getState) => {
|
|
||||||
const groupId = getState().getIn(['group_editor', 'groupId']);
|
|
||||||
const title = getState().getIn(['group_editor', 'title']);
|
|
||||||
const description = getState().getIn(['group_editor', 'description']);
|
|
||||||
const coverImage = getState().getIn(['group_editor', 'coverImage']);
|
|
||||||
|
|
||||||
if (groupId === null) {
|
|
||||||
dispatch(create(title, description, coverImage, routerHistory));
|
|
||||||
} else {
|
|
||||||
dispatch(update(groupId, title, description, coverImage, routerHistory));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const create = (title, description, coverImage, routerHistory) => (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
dispatch(createRequest());
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('title', title);
|
|
||||||
formData.append('description', description);
|
|
||||||
|
|
||||||
if (coverImage !== null) {
|
|
||||||
formData.append('cover_image', coverImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
api(getState).post('/api/v1/groups', formData, { headers: { 'Content-Type': 'multipart/form-data' } }).then(({ data }) => {
|
|
||||||
dispatch(createSuccess(data));
|
|
||||||
routerHistory.push(`/groups/${data.id}`);
|
|
||||||
}).catch(err => dispatch(createFail(err)));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createRequest = id => ({
|
|
||||||
type: GROUP_CREATE_REQUEST,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const createSuccess = group => ({
|
|
||||||
type: GROUP_CREATE_SUCCESS,
|
|
||||||
group,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const createFail = error => ({
|
|
||||||
type: GROUP_CREATE_FAIL,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const update = (groupId, title, description, coverImage, routerHistory) => (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
dispatch(updateRequest());
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('title', title);
|
|
||||||
formData.append('description', description);
|
|
||||||
|
|
||||||
if (coverImage !== null) {
|
|
||||||
formData.append('cover_image', coverImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
api(getState).put(`/api/v1/groups/${groupId}`, formData, { headers: { 'Content-Type': 'multipart/form-data' } }).then(({ data }) => {
|
|
||||||
dispatch(updateSuccess(data));
|
|
||||||
routerHistory.push(`/groups/${data.id}`);
|
|
||||||
}).catch(err => dispatch(updateFail(err)));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateRequest = id => ({
|
|
||||||
type: GROUP_UPDATE_REQUEST,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const updateSuccess = group => ({
|
|
||||||
type: GROUP_UPDATE_SUCCESS,
|
|
||||||
group,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const updateFail = error => ({
|
|
||||||
type: GROUP_UPDATE_FAIL,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const changeValue = (field, value) => ({
|
|
||||||
type: GROUP_EDITOR_VALUE_CHANGE,
|
|
||||||
field,
|
|
||||||
value,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const reset = () => ({
|
|
||||||
type: GROUP_EDITOR_RESET,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const setUp = (group) => ({
|
|
||||||
type: GROUP_EDITOR_SETUP,
|
|
||||||
group,
|
|
||||||
});
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
import { isLoggedIn } from 'soapbox/utils/auth';
|
||||||
|
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
import type { AxiosError } from 'axios';
|
||||||
|
import type { History } from 'history';
|
||||||
|
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_UPDATE_REQUEST = 'GROUP_UPDATE_REQUEST';
|
||||||
|
const GROUP_UPDATE_SUCCESS = 'GROUP_UPDATE_SUCCESS';
|
||||||
|
const GROUP_UPDATE_FAIL = 'GROUP_UPDATE_FAIL';
|
||||||
|
|
||||||
|
const GROUP_EDITOR_VALUE_CHANGE = 'GROUP_EDITOR_VALUE_CHANGE';
|
||||||
|
const GROUP_EDITOR_RESET = 'GROUP_EDITOR_RESET';
|
||||||
|
const GROUP_EDITOR_SETUP = 'GROUP_EDITOR_SETUP';
|
||||||
|
|
||||||
|
const submit = (routerHistory: History) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
const groupId = getState().group_editor.get('groupId') as string;
|
||||||
|
const title = getState().group_editor.get('title') as string;
|
||||||
|
const description = getState().group_editor.get('description') as string;
|
||||||
|
const coverImage = getState().group_editor.get('coverImage') as any;
|
||||||
|
|
||||||
|
if (groupId === null) {
|
||||||
|
dispatch(create(title, description, coverImage, routerHistory));
|
||||||
|
} else {
|
||||||
|
dispatch(update(groupId, title, description, coverImage, routerHistory));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const create = (title: string, description: string, coverImage: File, routerHistory: History) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
dispatch(createRequest());
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('title', title);
|
||||||
|
formData.append('description', description);
|
||||||
|
|
||||||
|
if (coverImage !== null) {
|
||||||
|
formData.append('cover_image', coverImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
api(getState).post('/api/v1/groups', formData, { headers: { 'Content-Type': 'multipart/form-data' } }).then(({ data }) => {
|
||||||
|
dispatch(createSuccess(data));
|
||||||
|
routerHistory.push(`/groups/${data.id}`);
|
||||||
|
}).catch(err => dispatch(createFail(err)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const createRequest = (id?: string) => ({
|
||||||
|
type: GROUP_CREATE_REQUEST,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createSuccess = (group: APIEntity) => ({
|
||||||
|
type: GROUP_CREATE_SUCCESS,
|
||||||
|
group,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createFail = (error: AxiosError) => ({
|
||||||
|
type: GROUP_CREATE_FAIL,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const update = (groupId: string, title: string, description: string, coverImage: File, routerHistory: History) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
dispatch(updateRequest(groupId));
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('title', title);
|
||||||
|
formData.append('description', description);
|
||||||
|
|
||||||
|
if (coverImage !== null) {
|
||||||
|
formData.append('cover_image', coverImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
api(getState).put(`/api/v1/groups/${groupId}`, formData, { headers: { 'Content-Type': 'multipart/form-data' } }).then(({ data }) => {
|
||||||
|
dispatch(updateSuccess(data));
|
||||||
|
routerHistory.push(`/groups/${data.id}`);
|
||||||
|
}).catch(err => dispatch(updateFail(err)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateRequest = (id: string) => ({
|
||||||
|
type: GROUP_UPDATE_REQUEST,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateSuccess = (group: APIEntity) => ({
|
||||||
|
type: GROUP_UPDATE_SUCCESS,
|
||||||
|
group,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateFail = (error: AxiosError) => ({
|
||||||
|
type: GROUP_UPDATE_FAIL,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const changeValue = (field: string, value: string | File) => ({
|
||||||
|
type: GROUP_EDITOR_VALUE_CHANGE,
|
||||||
|
field,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
|
||||||
|
const reset = () => ({
|
||||||
|
type: GROUP_EDITOR_RESET,
|
||||||
|
});
|
||||||
|
|
||||||
|
const setUp = (group: string) => ({
|
||||||
|
type: GROUP_EDITOR_SETUP,
|
||||||
|
group,
|
||||||
|
});
|
||||||
|
|
||||||
|
export {
|
||||||
|
GROUP_CREATE_REQUEST,
|
||||||
|
GROUP_CREATE_SUCCESS,
|
||||||
|
GROUP_CREATE_FAIL,
|
||||||
|
GROUP_UPDATE_REQUEST,
|
||||||
|
GROUP_UPDATE_SUCCESS,
|
||||||
|
GROUP_UPDATE_FAIL,
|
||||||
|
GROUP_EDITOR_VALUE_CHANGE,
|
||||||
|
GROUP_EDITOR_RESET,
|
||||||
|
GROUP_EDITOR_SETUP,
|
||||||
|
submit,
|
||||||
|
create,
|
||||||
|
createRequest,
|
||||||
|
createSuccess,
|
||||||
|
createFail,
|
||||||
|
update,
|
||||||
|
updateRequest,
|
||||||
|
updateSuccess,
|
||||||
|
updateFail,
|
||||||
|
changeValue,
|
||||||
|
reset,
|
||||||
|
setUp,
|
||||||
|
};
|
|
@ -1,526 +0,0 @@
|
||||||
import { isLoggedIn } from 'soapbox/utils/auth';
|
|
||||||
|
|
||||||
import api, { getLinks } from '../api';
|
|
||||||
|
|
||||||
import { fetchRelationships } from './accounts';
|
|
||||||
import { importFetchedAccounts } from './importer';
|
|
||||||
|
|
||||||
export const GROUP_FETCH_REQUEST = 'GROUP_FETCH_REQUEST';
|
|
||||||
export const GROUP_FETCH_SUCCESS = 'GROUP_FETCH_SUCCESS';
|
|
||||||
export const GROUP_FETCH_FAIL = 'GROUP_FETCH_FAIL';
|
|
||||||
|
|
||||||
export const GROUP_RELATIONSHIPS_FETCH_REQUEST = 'GROUP_RELATIONSHIPS_FETCH_REQUEST';
|
|
||||||
export const GROUP_RELATIONSHIPS_FETCH_SUCCESS = 'GROUP_RELATIONSHIPS_FETCH_SUCCESS';
|
|
||||||
export const GROUP_RELATIONSHIPS_FETCH_FAIL = 'GROUP_RELATIONSHIPS_FETCH_FAIL';
|
|
||||||
|
|
||||||
export const GROUPS_FETCH_REQUEST = 'GROUPS_FETCH_REQUEST';
|
|
||||||
export const GROUPS_FETCH_SUCCESS = 'GROUPS_FETCH_SUCCESS';
|
|
||||||
export const GROUPS_FETCH_FAIL = 'GROUPS_FETCH_FAIL';
|
|
||||||
|
|
||||||
export const GROUP_JOIN_REQUEST = 'GROUP_JOIN_REQUEST';
|
|
||||||
export const GROUP_JOIN_SUCCESS = 'GROUP_JOIN_SUCCESS';
|
|
||||||
export const GROUP_JOIN_FAIL = 'GROUP_JOIN_FAIL';
|
|
||||||
|
|
||||||
export const GROUP_LEAVE_REQUEST = 'GROUP_LEAVE_REQUEST';
|
|
||||||
export const GROUP_LEAVE_SUCCESS = 'GROUP_LEAVE_SUCCESS';
|
|
||||||
export const GROUP_LEAVE_FAIL = 'GROUP_LEAVE_FAIL';
|
|
||||||
|
|
||||||
export const GROUP_MEMBERS_FETCH_REQUEST = 'GROUP_MEMBERS_FETCH_REQUEST';
|
|
||||||
export const GROUP_MEMBERS_FETCH_SUCCESS = 'GROUP_MEMBERS_FETCH_SUCCESS';
|
|
||||||
export const GROUP_MEMBERS_FETCH_FAIL = 'GROUP_MEMBERS_FETCH_FAIL';
|
|
||||||
|
|
||||||
export const GROUP_MEMBERS_EXPAND_REQUEST = 'GROUP_MEMBERS_EXPAND_REQUEST';
|
|
||||||
export const GROUP_MEMBERS_EXPAND_SUCCESS = 'GROUP_MEMBERS_EXPAND_SUCCESS';
|
|
||||||
export const GROUP_MEMBERS_EXPAND_FAIL = 'GROUP_MEMBERS_EXPAND_FAIL';
|
|
||||||
|
|
||||||
export const GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST = 'GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST';
|
|
||||||
export const GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS';
|
|
||||||
export const GROUP_REMOVED_ACCOUNTS_FETCH_FAIL = 'GROUP_REMOVED_ACCOUNTS_FETCH_FAIL';
|
|
||||||
|
|
||||||
export const GROUP_REMOVED_ACCOUNTS_EXPAND_REQUEST = 'GROUP_REMOVED_ACCOUNTS_EXPAND_REQUEST';
|
|
||||||
export const GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS';
|
|
||||||
export const GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL = 'GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL';
|
|
||||||
|
|
||||||
export const GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST = 'GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST';
|
|
||||||
export const GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS';
|
|
||||||
export const GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL = 'GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL';
|
|
||||||
|
|
||||||
export const GROUP_REMOVED_ACCOUNTS_CREATE_REQUEST = 'GROUP_REMOVED_ACCOUNTS_CREATE_REQUEST';
|
|
||||||
export const GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS';
|
|
||||||
export const GROUP_REMOVED_ACCOUNTS_CREATE_FAIL = 'GROUP_REMOVED_ACCOUNTS_CREATE_FAIL';
|
|
||||||
|
|
||||||
export const GROUP_REMOVE_STATUS_REQUEST = 'GROUP_REMOVE_STATUS_REQUEST';
|
|
||||||
export const GROUP_REMOVE_STATUS_SUCCESS = 'GROUP_REMOVE_STATUS_SUCCESS';
|
|
||||||
export const GROUP_REMOVE_STATUS_FAIL = 'GROUP_REMOVE_STATUS_FAIL';
|
|
||||||
|
|
||||||
export const fetchGroup = id => (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
dispatch(fetchGroupRelationships([id]));
|
|
||||||
|
|
||||||
if (getState().getIn(['groups', id])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(fetchGroupRequest(id));
|
|
||||||
|
|
||||||
api(getState).get(`/api/v1/groups/${id}`)
|
|
||||||
.then(({ data }) => dispatch(fetchGroupSuccess(data)))
|
|
||||||
.catch(err => dispatch(fetchGroupFail(id, err)));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchGroupRequest = id => ({
|
|
||||||
type: GROUP_FETCH_REQUEST,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const fetchGroupSuccess = group => ({
|
|
||||||
type: GROUP_FETCH_SUCCESS,
|
|
||||||
group,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const fetchGroupFail = (id, error) => ({
|
|
||||||
type: GROUP_FETCH_FAIL,
|
|
||||||
id,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
|
||||||
export function fetchGroupRelationships(groupIds) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
const loadedRelationships = getState().get('group_relationships');
|
|
||||||
const newGroupIds = groupIds.filter(id => loadedRelationships.get(id, null) === null);
|
|
||||||
|
|
||||||
if (newGroupIds.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(fetchGroupRelationshipsRequest(newGroupIds));
|
|
||||||
|
|
||||||
api(getState).get(`/api/v1/groups/${newGroupIds[0]}/relationships?${newGroupIds.map(id => `id[]=${id}`).join('&')}`).then(response => {
|
|
||||||
dispatch(fetchGroupRelationshipsSuccess(response.data));
|
|
||||||
}).catch(error => {
|
|
||||||
dispatch(fetchGroupRelationshipsFail(error));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchGroupRelationshipsRequest(ids) {
|
|
||||||
return {
|
|
||||||
type: GROUP_RELATIONSHIPS_FETCH_REQUEST,
|
|
||||||
ids,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchGroupRelationshipsSuccess(relationships) {
|
|
||||||
return {
|
|
||||||
type: GROUP_RELATIONSHIPS_FETCH_SUCCESS,
|
|
||||||
relationships,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchGroupRelationshipsFail(error) {
|
|
||||||
return {
|
|
||||||
type: GROUP_RELATIONSHIPS_FETCH_FAIL,
|
|
||||||
error,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchGroups = (tab) => (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
dispatch(fetchGroupsRequest());
|
|
||||||
|
|
||||||
api(getState).get('/api/v1/groups?tab=' + tab)
|
|
||||||
.then(({ data }) => {
|
|
||||||
dispatch(fetchGroupsSuccess(data, tab));
|
|
||||||
dispatch(fetchGroupRelationships(data.map(item => item.id)));
|
|
||||||
})
|
|
||||||
.catch(err => dispatch(fetchGroupsFail(err)));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchGroupsRequest = () => ({
|
|
||||||
type: GROUPS_FETCH_REQUEST,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const fetchGroupsSuccess = (groups, tab) => ({
|
|
||||||
type: GROUPS_FETCH_SUCCESS,
|
|
||||||
groups,
|
|
||||||
tab,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const fetchGroupsFail = error => ({
|
|
||||||
type: GROUPS_FETCH_FAIL,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
|
||||||
export function joinGroup(id) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
dispatch(joinGroupRequest(id));
|
|
||||||
|
|
||||||
api(getState).post(`/api/v1/groups/${id}/accounts`).then(response => {
|
|
||||||
dispatch(joinGroupSuccess(response.data));
|
|
||||||
}).catch(error => {
|
|
||||||
dispatch(joinGroupFail(id, error));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function leaveGroup(id) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
dispatch(leaveGroupRequest(id));
|
|
||||||
|
|
||||||
api(getState).delete(`/api/v1/groups/${id}/accounts`).then(response => {
|
|
||||||
dispatch(leaveGroupSuccess(response.data));
|
|
||||||
}).catch(error => {
|
|
||||||
dispatch(leaveGroupFail(id, error));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function joinGroupRequest(id) {
|
|
||||||
return {
|
|
||||||
type: GROUP_JOIN_REQUEST,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function joinGroupSuccess(relationship) {
|
|
||||||
return {
|
|
||||||
type: GROUP_JOIN_SUCCESS,
|
|
||||||
relationship,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function joinGroupFail(error) {
|
|
||||||
return {
|
|
||||||
type: GROUP_JOIN_FAIL,
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function leaveGroupRequest(id) {
|
|
||||||
return {
|
|
||||||
type: GROUP_LEAVE_REQUEST,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function leaveGroupSuccess(relationship) {
|
|
||||||
return {
|
|
||||||
type: GROUP_LEAVE_SUCCESS,
|
|
||||||
relationship,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function leaveGroupFail(error) {
|
|
||||||
return {
|
|
||||||
type: GROUP_LEAVE_FAIL,
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchMembers(id) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
dispatch(fetchMembersRequest(id));
|
|
||||||
|
|
||||||
api(getState).get(`/api/v1/groups/${id}/accounts`).then(response => {
|
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
|
||||||
|
|
||||||
dispatch(importFetchedAccounts(response.data));
|
|
||||||
dispatch(fetchMembersSuccess(id, response.data, next ? next.uri : null));
|
|
||||||
dispatch(fetchRelationships(response.data.map(item => item.id)));
|
|
||||||
}).catch(error => {
|
|
||||||
dispatch(fetchMembersFail(id, error));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchMembersRequest(id) {
|
|
||||||
return {
|
|
||||||
type: GROUP_MEMBERS_FETCH_REQUEST,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchMembersSuccess(id, accounts, next) {
|
|
||||||
return {
|
|
||||||
type: GROUP_MEMBERS_FETCH_SUCCESS,
|
|
||||||
id,
|
|
||||||
accounts,
|
|
||||||
next,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchMembersFail(id, error) {
|
|
||||||
return {
|
|
||||||
type: GROUP_MEMBERS_FETCH_FAIL,
|
|
||||||
id,
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expandMembers(id) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
const url = getState().getIn(['user_lists', 'groups', id, 'next']);
|
|
||||||
|
|
||||||
if (url === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(expandMembersRequest(id));
|
|
||||||
|
|
||||||
api(getState).get(url).then(response => {
|
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
|
||||||
|
|
||||||
dispatch(importFetchedAccounts(response.data));
|
|
||||||
dispatch(expandMembersSuccess(id, response.data, next ? next.uri : null));
|
|
||||||
dispatch(fetchRelationships(response.data.map(item => item.id)));
|
|
||||||
}).catch(error => {
|
|
||||||
dispatch(expandMembersFail(id, error));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expandMembersRequest(id) {
|
|
||||||
return {
|
|
||||||
type: GROUP_MEMBERS_EXPAND_REQUEST,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expandMembersSuccess(id, accounts, next) {
|
|
||||||
return {
|
|
||||||
type: GROUP_MEMBERS_EXPAND_SUCCESS,
|
|
||||||
id,
|
|
||||||
accounts,
|
|
||||||
next,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expandMembersFail(id, error) {
|
|
||||||
return {
|
|
||||||
type: GROUP_MEMBERS_EXPAND_FAIL,
|
|
||||||
id,
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchRemovedAccounts(id) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
dispatch(fetchRemovedAccountsRequest(id));
|
|
||||||
|
|
||||||
api(getState).get(`/api/v1/groups/${id}/removed_accounts`).then(response => {
|
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
|
||||||
|
|
||||||
dispatch(importFetchedAccounts(response.data));
|
|
||||||
dispatch(fetchRemovedAccountsSuccess(id, response.data, next ? next.uri : null));
|
|
||||||
dispatch(fetchRelationships(response.data.map(item => item.id)));
|
|
||||||
}).catch(error => {
|
|
||||||
dispatch(fetchRemovedAccountsFail(id, error));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchRemovedAccountsRequest(id) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchRemovedAccountsSuccess(id, accounts, next) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS,
|
|
||||||
id,
|
|
||||||
accounts,
|
|
||||||
next,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchRemovedAccountsFail(id, error) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVED_ACCOUNTS_FETCH_FAIL,
|
|
||||||
id,
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expandRemovedAccounts(id) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
const url = getState().getIn(['user_lists', 'groups_removed_accounts', id, 'next']);
|
|
||||||
|
|
||||||
if (url === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(expandRemovedAccountsRequest(id));
|
|
||||||
|
|
||||||
api(getState).get(url).then(response => {
|
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
|
||||||
|
|
||||||
dispatch(importFetchedAccounts(response.data));
|
|
||||||
dispatch(expandRemovedAccountsSuccess(id, response.data, next ? next.uri : null));
|
|
||||||
dispatch(fetchRelationships(response.data.map(item => item.id)));
|
|
||||||
}).catch(error => {
|
|
||||||
dispatch(expandRemovedAccountsFail(id, error));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expandRemovedAccountsRequest(id) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVED_ACCOUNTS_EXPAND_REQUEST,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expandRemovedAccountsSuccess(id, accounts, next) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS,
|
|
||||||
id,
|
|
||||||
accounts,
|
|
||||||
next,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expandRemovedAccountsFail(id, error) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL,
|
|
||||||
id,
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeRemovedAccount(groupId, id) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
dispatch(removeRemovedAccountRequest(groupId, id));
|
|
||||||
|
|
||||||
api(getState).delete(`/api/v1/groups/${groupId}/removed_accounts?account_id=${id}`).then(response => {
|
|
||||||
dispatch(removeRemovedAccountSuccess(groupId, id));
|
|
||||||
}).catch(error => {
|
|
||||||
dispatch(removeRemovedAccountFail(groupId, id, error));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeRemovedAccountRequest(groupId, id) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST,
|
|
||||||
groupId,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeRemovedAccountSuccess(groupId, id) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS,
|
|
||||||
groupId,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeRemovedAccountFail(groupId, id, error) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL,
|
|
||||||
groupId,
|
|
||||||
id,
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createRemovedAccount(groupId, id) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
dispatch(createRemovedAccountRequest(groupId, id));
|
|
||||||
|
|
||||||
api(getState).post(`/api/v1/groups/${groupId}/removed_accounts?account_id=${id}`).then(response => {
|
|
||||||
dispatch(createRemovedAccountSuccess(groupId, id));
|
|
||||||
}).catch(error => {
|
|
||||||
dispatch(createRemovedAccountFail(groupId, id, error));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createRemovedAccountRequest(groupId, id) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVED_ACCOUNTS_CREATE_REQUEST,
|
|
||||||
groupId,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createRemovedAccountSuccess(groupId, id) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS,
|
|
||||||
groupId,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createRemovedAccountFail(groupId, id, error) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVED_ACCOUNTS_CREATE_FAIL,
|
|
||||||
groupId,
|
|
||||||
id,
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function groupRemoveStatus(groupId, id) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
if (!isLoggedIn(getState)) return;
|
|
||||||
|
|
||||||
dispatch(groupRemoveStatusRequest(groupId, id));
|
|
||||||
|
|
||||||
api(getState).delete(`/api/v1/groups/${groupId}/statuses/${id}`).then(response => {
|
|
||||||
dispatch(groupRemoveStatusSuccess(groupId, id));
|
|
||||||
}).catch(error => {
|
|
||||||
dispatch(groupRemoveStatusFail(groupId, id, error));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function groupRemoveStatusRequest(groupId, id) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVE_STATUS_REQUEST,
|
|
||||||
groupId,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function groupRemoveStatusSuccess(groupId, id) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVE_STATUS_SUCCESS,
|
|
||||||
groupId,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function groupRemoveStatusFail(groupId, id, error) {
|
|
||||||
return {
|
|
||||||
type: GROUP_REMOVE_STATUS_FAIL,
|
|
||||||
groupId,
|
|
||||||
id,
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,550 @@
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
|
||||||
|
import { isLoggedIn } from 'soapbox/utils/auth';
|
||||||
|
|
||||||
|
import api, { getLinks } from '../api';
|
||||||
|
|
||||||
|
import { fetchRelationships } from './accounts';
|
||||||
|
import { importFetchedAccounts } from './importer';
|
||||||
|
|
||||||
|
import type { AppDispatch, RootState } from 'soapbox/store';
|
||||||
|
import type { APIEntity } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
const GROUP_FETCH_REQUEST = 'GROUP_FETCH_REQUEST';
|
||||||
|
const GROUP_FETCH_SUCCESS = 'GROUP_FETCH_SUCCESS';
|
||||||
|
const GROUP_FETCH_FAIL = 'GROUP_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 GROUPS_FETCH_REQUEST = 'GROUPS_FETCH_REQUEST';
|
||||||
|
const GROUPS_FETCH_SUCCESS = 'GROUPS_FETCH_SUCCESS';
|
||||||
|
const GROUPS_FETCH_FAIL = 'GROUPS_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_MEMBERS_FETCH_REQUEST = 'GROUP_MEMBERS_FETCH_REQUEST';
|
||||||
|
const GROUP_MEMBERS_FETCH_SUCCESS = 'GROUP_MEMBERS_FETCH_SUCCESS';
|
||||||
|
const GROUP_MEMBERS_FETCH_FAIL = 'GROUP_MEMBERS_FETCH_FAIL';
|
||||||
|
|
||||||
|
const GROUP_MEMBERS_EXPAND_REQUEST = 'GROUP_MEMBERS_EXPAND_REQUEST';
|
||||||
|
const GROUP_MEMBERS_EXPAND_SUCCESS = 'GROUP_MEMBERS_EXPAND_SUCCESS';
|
||||||
|
const GROUP_MEMBERS_EXPAND_FAIL = 'GROUP_MEMBERS_EXPAND_FAIL';
|
||||||
|
|
||||||
|
const GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST = 'GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST';
|
||||||
|
const GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS';
|
||||||
|
const GROUP_REMOVED_ACCOUNTS_FETCH_FAIL = 'GROUP_REMOVED_ACCOUNTS_FETCH_FAIL';
|
||||||
|
|
||||||
|
const GROUP_REMOVED_ACCOUNTS_EXPAND_REQUEST = 'GROUP_REMOVED_ACCOUNTS_EXPAND_REQUEST';
|
||||||
|
const GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS';
|
||||||
|
const GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL = 'GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL';
|
||||||
|
|
||||||
|
const GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST = 'GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST';
|
||||||
|
const GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS';
|
||||||
|
const GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL = 'GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL';
|
||||||
|
|
||||||
|
const GROUP_REMOVED_ACCOUNTS_CREATE_REQUEST = 'GROUP_REMOVED_ACCOUNTS_CREATE_REQUEST';
|
||||||
|
const GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS';
|
||||||
|
const GROUP_REMOVED_ACCOUNTS_CREATE_FAIL = 'GROUP_REMOVED_ACCOUNTS_CREATE_FAIL';
|
||||||
|
|
||||||
|
const GROUP_REMOVE_STATUS_REQUEST = 'GROUP_REMOVE_STATUS_REQUEST';
|
||||||
|
const GROUP_REMOVE_STATUS_SUCCESS = 'GROUP_REMOVE_STATUS_SUCCESS';
|
||||||
|
const GROUP_REMOVE_STATUS_FAIL = 'GROUP_REMOVE_STATUS_FAIL';
|
||||||
|
|
||||||
|
const fetchGroup = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
dispatch(fetchGroupRelationships([id]));
|
||||||
|
|
||||||
|
if (getState().groups.get(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(fetchGroupRequest(id));
|
||||||
|
|
||||||
|
api(getState).get(`/api/v1/groups/${id}`)
|
||||||
|
.then(({ 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 fetchGroupRelationships = (groupIds: string[]) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
const loadedRelationships = getState().group_relationships;
|
||||||
|
const newGroupIds = groupIds.filter(id => loadedRelationships.get(id, null) === null);
|
||||||
|
|
||||||
|
if (newGroupIds.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(fetchGroupRelationshipsRequest(newGroupIds));
|
||||||
|
|
||||||
|
api(getState).get(`/api/v1/groups/${newGroupIds[0]}/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,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchGroups = (tab: string) => (dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
dispatch(fetchGroupsRequest());
|
||||||
|
|
||||||
|
api(getState).get('/api/v1/groups?tab=' + tab)
|
||||||
|
.then(({ data }) => {
|
||||||
|
dispatch(fetchGroupsSuccess(data, tab));
|
||||||
|
dispatch(fetchGroupRelationships(data.map((item: APIEntity) => item.id)));
|
||||||
|
})
|
||||||
|
.catch(err => dispatch(fetchGroupsFail(err)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchGroupsRequest = () => ({
|
||||||
|
type: GROUPS_FETCH_REQUEST,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchGroupsSuccess = (groups: APIEntity[], tab: string) => ({
|
||||||
|
type: GROUPS_FETCH_SUCCESS,
|
||||||
|
groups,
|
||||||
|
tab,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchGroupsFail = (error: AxiosError) => ({
|
||||||
|
type: GROUPS_FETCH_FAIL,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const joinGroup = (id: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
dispatch(joinGroupRequest(id));
|
||||||
|
|
||||||
|
api(getState).post(`/api/v1/groups/${id}/accounts`).then(response => {
|
||||||
|
dispatch(joinGroupSuccess(response.data));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(joinGroupFail(id, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const leaveGroup = (id: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
dispatch(leaveGroupRequest(id));
|
||||||
|
|
||||||
|
api(getState).delete(`/api/v1/groups/${id}/accounts`).then(response => {
|
||||||
|
dispatch(leaveGroupSuccess(response.data));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(leaveGroupFail(id, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const joinGroupRequest = (id: string) => ({
|
||||||
|
type: GROUP_JOIN_REQUEST,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const joinGroupSuccess = (relationship: APIEntity) => ({
|
||||||
|
type: GROUP_JOIN_SUCCESS,
|
||||||
|
relationship,
|
||||||
|
});
|
||||||
|
|
||||||
|
const joinGroupFail = (id: string, error: AxiosError) => ({
|
||||||
|
type: GROUP_JOIN_FAIL,
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const leaveGroupRequest = (id: string) => ({
|
||||||
|
type: GROUP_LEAVE_REQUEST,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const leaveGroupSuccess = (relationship: APIEntity) => ({
|
||||||
|
type: GROUP_LEAVE_SUCCESS,
|
||||||
|
relationship,
|
||||||
|
});
|
||||||
|
|
||||||
|
const leaveGroupFail = (id: string, error: AxiosError) => ({
|
||||||
|
type: GROUP_LEAVE_FAIL,
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchMembers = (id: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
dispatch(fetchMembersRequest(id));
|
||||||
|
|
||||||
|
api(getState).get(`/api/v1/groups/${id}/accounts`).then(response => {
|
||||||
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
|
dispatch(importFetchedAccounts(response.data));
|
||||||
|
dispatch(fetchMembersSuccess(id, response.data, next ? next.uri : null));
|
||||||
|
dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id)));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(fetchMembersFail(id, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchMembersRequest = (id: string) => ({
|
||||||
|
type: GROUP_MEMBERS_FETCH_REQUEST,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchMembersSuccess = (id: string, accounts: APIEntity[], next: string | null) => ({
|
||||||
|
type: GROUP_MEMBERS_FETCH_SUCCESS,
|
||||||
|
id,
|
||||||
|
accounts,
|
||||||
|
next,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchMembersFail = (id: string, error: AxiosError) => ({
|
||||||
|
type: GROUP_MEMBERS_FETCH_FAIL,
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expandMembers = (id: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
const url = getState().user_lists.getIn(['groups', id, 'next']);
|
||||||
|
|
||||||
|
if (url === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(expandMembersRequest(id));
|
||||||
|
|
||||||
|
api(getState).get(url).then(response => {
|
||||||
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
|
dispatch(importFetchedAccounts(response.data));
|
||||||
|
dispatch(expandMembersSuccess(id, response.data, next ? next.uri : null));
|
||||||
|
dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id)));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(expandMembersFail(id, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const expandMembersRequest = (id: string) => ({
|
||||||
|
type: GROUP_MEMBERS_EXPAND_REQUEST,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expandMembersSuccess = (id: string, accounts: APIEntity[], next: string | null) => ({
|
||||||
|
type: GROUP_MEMBERS_EXPAND_SUCCESS,
|
||||||
|
id,
|
||||||
|
accounts,
|
||||||
|
next,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expandMembersFail = (id: string, error: AxiosError) => ({
|
||||||
|
type: GROUP_MEMBERS_EXPAND_FAIL,
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchRemovedAccounts = (id: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
dispatch(fetchRemovedAccountsRequest(id));
|
||||||
|
|
||||||
|
api(getState).get(`/api/v1/groups/${id}/removed_accounts`).then(response => {
|
||||||
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
|
dispatch(importFetchedAccounts(response.data));
|
||||||
|
dispatch(fetchRemovedAccountsSuccess(id, response.data, next ? next.uri : null));
|
||||||
|
dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id)));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(fetchRemovedAccountsFail(id, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchRemovedAccountsRequest = (id: string) => ({
|
||||||
|
type: GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchRemovedAccountsSuccess = (id: string, accounts: APIEntity[], next: string | null) => ({
|
||||||
|
type: GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS,
|
||||||
|
id,
|
||||||
|
accounts,
|
||||||
|
next,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchRemovedAccountsFail = (id: string, error: AxiosError) => ({
|
||||||
|
type: GROUP_REMOVED_ACCOUNTS_FETCH_FAIL,
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expandRemovedAccounts = (id: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
const url = getState().user_lists.getIn(['groups_removed_accounts', id, 'next']);
|
||||||
|
|
||||||
|
if (url === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(expandRemovedAccountsRequest(id));
|
||||||
|
|
||||||
|
api(getState).get(url).then(response => {
|
||||||
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
|
dispatch(importFetchedAccounts(response.data));
|
||||||
|
dispatch(expandRemovedAccountsSuccess(id, response.data, next ? next.uri : null));
|
||||||
|
dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id)));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(expandRemovedAccountsFail(id, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const expandRemovedAccountsRequest = (id: string) => ({
|
||||||
|
type: GROUP_REMOVED_ACCOUNTS_EXPAND_REQUEST,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expandRemovedAccountsSuccess = (id: string, accounts: APIEntity[], next: string | null) => ({
|
||||||
|
type: GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS,
|
||||||
|
id,
|
||||||
|
accounts,
|
||||||
|
next,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expandRemovedAccountsFail = (id: string, error: AxiosError) => ({
|
||||||
|
type: GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL,
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const removeRemovedAccount = (groupId: string, id: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
dispatch(removeRemovedAccountRequest(groupId, id));
|
||||||
|
|
||||||
|
api(getState).delete(`/api/v1/groups/${groupId}/removed_accounts?account_id=${id}`).then(response => {
|
||||||
|
dispatch(removeRemovedAccountSuccess(groupId, id));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(removeRemovedAccountFail(groupId, id, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeRemovedAccountRequest = (groupId: string, id: string) => ({
|
||||||
|
type: GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST,
|
||||||
|
groupId,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const removeRemovedAccountSuccess = (groupId: string, id: string) => ({
|
||||||
|
type: GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS,
|
||||||
|
groupId,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const removeRemovedAccountFail = (groupId: string, id: string, error: AxiosError) => ({
|
||||||
|
type: GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL,
|
||||||
|
groupId,
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createRemovedAccount = (groupId: string, id: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
dispatch(createRemovedAccountRequest(groupId, id));
|
||||||
|
|
||||||
|
api(getState).post(`/api/v1/groups/${groupId}/removed_accounts?account_id=${id}`).then(response => {
|
||||||
|
dispatch(createRemovedAccountSuccess(groupId, id));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(createRemovedAccountFail(groupId, id, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createRemovedAccountRequest = (groupId: string, id: string) => ({
|
||||||
|
type: GROUP_REMOVED_ACCOUNTS_CREATE_REQUEST,
|
||||||
|
groupId,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createRemovedAccountSuccess = (groupId: string, id: string) => ({
|
||||||
|
type: GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS,
|
||||||
|
groupId,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createRemovedAccountFail = (groupId: string, id: string, error: AxiosError) => ({
|
||||||
|
type: GROUP_REMOVED_ACCOUNTS_CREATE_FAIL,
|
||||||
|
groupId,
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const groupRemoveStatus = (groupId: string, id: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
dispatch(groupRemoveStatusRequest(groupId, id));
|
||||||
|
|
||||||
|
api(getState).delete(`/api/v1/groups/${groupId}/statuses/${id}`).then(response => {
|
||||||
|
dispatch(groupRemoveStatusSuccess(groupId, id));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(groupRemoveStatusFail(groupId, id, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const groupRemoveStatusRequest = (groupId: string, id: string) => ({
|
||||||
|
type: GROUP_REMOVE_STATUS_REQUEST,
|
||||||
|
groupId,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const groupRemoveStatusSuccess = (groupId: string, id: string) => ({
|
||||||
|
type: GROUP_REMOVE_STATUS_SUCCESS,
|
||||||
|
groupId,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const groupRemoveStatusFail = (groupId: string, id: string, error: AxiosError) => ({
|
||||||
|
type: GROUP_REMOVE_STATUS_FAIL,
|
||||||
|
groupId,
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
export {
|
||||||
|
GROUP_FETCH_REQUEST,
|
||||||
|
GROUP_FETCH_SUCCESS,
|
||||||
|
GROUP_FETCH_FAIL,
|
||||||
|
GROUP_RELATIONSHIPS_FETCH_REQUEST,
|
||||||
|
GROUP_RELATIONSHIPS_FETCH_SUCCESS,
|
||||||
|
GROUP_RELATIONSHIPS_FETCH_FAIL,
|
||||||
|
GROUPS_FETCH_REQUEST,
|
||||||
|
GROUPS_FETCH_SUCCESS,
|
||||||
|
GROUPS_FETCH_FAIL,
|
||||||
|
GROUP_JOIN_REQUEST,
|
||||||
|
GROUP_JOIN_SUCCESS,
|
||||||
|
GROUP_JOIN_FAIL,
|
||||||
|
GROUP_LEAVE_REQUEST,
|
||||||
|
GROUP_LEAVE_SUCCESS,
|
||||||
|
GROUP_LEAVE_FAIL,
|
||||||
|
GROUP_MEMBERS_FETCH_REQUEST,
|
||||||
|
GROUP_MEMBERS_FETCH_SUCCESS,
|
||||||
|
GROUP_MEMBERS_FETCH_FAIL,
|
||||||
|
GROUP_MEMBERS_EXPAND_REQUEST,
|
||||||
|
GROUP_MEMBERS_EXPAND_SUCCESS,
|
||||||
|
GROUP_MEMBERS_EXPAND_FAIL,
|
||||||
|
GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST,
|
||||||
|
GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS,
|
||||||
|
GROUP_REMOVED_ACCOUNTS_FETCH_FAIL,
|
||||||
|
GROUP_REMOVED_ACCOUNTS_EXPAND_REQUEST,
|
||||||
|
GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS,
|
||||||
|
GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL,
|
||||||
|
GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST,
|
||||||
|
GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS,
|
||||||
|
GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL,
|
||||||
|
GROUP_REMOVED_ACCOUNTS_CREATE_REQUEST,
|
||||||
|
GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS,
|
||||||
|
GROUP_REMOVED_ACCOUNTS_CREATE_FAIL,
|
||||||
|
GROUP_REMOVE_STATUS_REQUEST,
|
||||||
|
GROUP_REMOVE_STATUS_SUCCESS,
|
||||||
|
GROUP_REMOVE_STATUS_FAIL,
|
||||||
|
fetchGroup,
|
||||||
|
fetchGroupRequest,
|
||||||
|
fetchGroupSuccess,
|
||||||
|
fetchGroupFail,
|
||||||
|
fetchGroupRelationships,
|
||||||
|
fetchGroupRelationshipsRequest,
|
||||||
|
fetchGroupRelationshipsSuccess,
|
||||||
|
fetchGroupRelationshipsFail,
|
||||||
|
fetchGroups,
|
||||||
|
fetchGroupsRequest,
|
||||||
|
fetchGroupsSuccess,
|
||||||
|
fetchGroupsFail,
|
||||||
|
joinGroup,
|
||||||
|
leaveGroup,
|
||||||
|
joinGroupRequest,
|
||||||
|
joinGroupSuccess,
|
||||||
|
joinGroupFail,
|
||||||
|
leaveGroupRequest,
|
||||||
|
leaveGroupSuccess,
|
||||||
|
leaveGroupFail,
|
||||||
|
fetchMembers,
|
||||||
|
fetchMembersRequest,
|
||||||
|
fetchMembersSuccess,
|
||||||
|
fetchMembersFail,
|
||||||
|
expandMembers,
|
||||||
|
expandMembersRequest,
|
||||||
|
expandMembersSuccess,
|
||||||
|
expandMembersFail,
|
||||||
|
fetchRemovedAccounts,
|
||||||
|
fetchRemovedAccountsRequest,
|
||||||
|
fetchRemovedAccountsSuccess,
|
||||||
|
fetchRemovedAccountsFail,
|
||||||
|
expandRemovedAccounts,
|
||||||
|
expandRemovedAccountsRequest,
|
||||||
|
expandRemovedAccountsSuccess,
|
||||||
|
expandRemovedAccountsFail,
|
||||||
|
removeRemovedAccount,
|
||||||
|
removeRemovedAccountRequest,
|
||||||
|
removeRemovedAccountSuccess,
|
||||||
|
removeRemovedAccountFail,
|
||||||
|
createRemovedAccount,
|
||||||
|
createRemovedAccountRequest,
|
||||||
|
createRemovedAccountSuccess,
|
||||||
|
createRemovedAccountFail,
|
||||||
|
groupRemoveStatus,
|
||||||
|
groupRemoveStatusRequest,
|
||||||
|
groupRemoveStatusSuccess,
|
||||||
|
groupRemoveStatusFail,
|
||||||
|
};
|
|
@ -27,7 +27,7 @@ const updateMrf = (host: string, restrictions: ImmutableMap<string, any>) =>
|
||||||
const simplePolicy = ConfigDB.toSimplePolicy(configs);
|
const simplePolicy = ConfigDB.toSimplePolicy(configs);
|
||||||
const merged = simplePolicyMerge(simplePolicy, host, restrictions);
|
const merged = simplePolicyMerge(simplePolicy, host, restrictions);
|
||||||
const config = ConfigDB.fromSimplePolicy(merged);
|
const config = ConfigDB.fromSimplePolicy(merged);
|
||||||
return dispatch(updateConfig(config));
|
return dispatch(updateConfig(config.toJS() as Array<Record<string, any>>));
|
||||||
});
|
});
|
||||||
|
|
||||||
export { updateMrf };
|
export { updateMrf };
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import api from '../api';
|
import api from '../api';
|
||||||
|
|
||||||
|
import type { AppDispatch, RootState } from 'soapbox/store';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LocalStorage 'soapbox:verification'
|
* LocalStorage 'soapbox:verification'
|
||||||
*
|
*
|
||||||
|
@ -22,99 +24,89 @@ const SET_NEXT_CHALLENGE = 'SET_NEXT_CHALLENGE';
|
||||||
const SET_CHALLENGES_COMPLETE = 'SET_CHALLENGES_COMPLETE';
|
const SET_CHALLENGES_COMPLETE = 'SET_CHALLENGES_COMPLETE';
|
||||||
const SET_LOADING = 'SET_LOADING';
|
const SET_LOADING = 'SET_LOADING';
|
||||||
|
|
||||||
const ChallengeTypes = {
|
const EMAIL: Challenge = 'email';
|
||||||
EMAIL: 'email',
|
const SMS: Challenge = 'sms';
|
||||||
SMS: 'sms',
|
const AGE: Challenge = 'age';
|
||||||
AGE: 'age',
|
|
||||||
|
export type Challenge = 'age' | 'sms' | 'email'
|
||||||
|
|
||||||
|
type Challenges = {
|
||||||
|
email?: 0 | 1,
|
||||||
|
sms?: number,
|
||||||
|
age?: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Verification = {
|
||||||
|
token?: string,
|
||||||
|
challenges?: Challenges,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch the state of the user's verification in local storage.
|
* Fetch the state of the user's verification in local storage.
|
||||||
*
|
|
||||||
* @returns {object}
|
|
||||||
* {
|
|
||||||
* token: String,
|
|
||||||
* challenges: {
|
|
||||||
* email: Number (0 = incomplete, 1 = complete),
|
|
||||||
* sms: Number,
|
|
||||||
* age: Number
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*/
|
*/
|
||||||
function fetchStoredVerification() {
|
const fetchStoredVerification = (): Verification | null => {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(localStorage.getItem(LOCAL_STORAGE_VERIFICATION_KEY));
|
return JSON.parse(localStorage.getItem(LOCAL_STORAGE_VERIFICATION_KEY) as string);
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the state of the user's verification from local storage.
|
* Remove the state of the user's verification from local storage.
|
||||||
*/
|
*/
|
||||||
function removeStoredVerification() {
|
const removeStoredVerification = () => {
|
||||||
localStorage.removeItem(LOCAL_STORAGE_VERIFICATION_KEY);
|
localStorage.removeItem(LOCAL_STORAGE_VERIFICATION_KEY);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch and return the Registration token for Pepe.
|
* Fetch and return the Registration token for Pepe.
|
||||||
* @returns {string}
|
|
||||||
*/
|
*/
|
||||||
function fetchStoredToken() {
|
const fetchStoredToken = () => {
|
||||||
try {
|
try {
|
||||||
const verification = fetchStoredVerification();
|
const verification: Verification | null = fetchStoredVerification();
|
||||||
return verification.token;
|
return verification!.token;
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch and return the state of the verification challenges.
|
* Fetch and return the state of the verification challenges.
|
||||||
* @returns {object}
|
|
||||||
* {
|
|
||||||
* challenges: {
|
|
||||||
* email: Number (0 = incomplete, 1 = complete),
|
|
||||||
* sms: Number,
|
|
||||||
* age: Number
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*/
|
*/
|
||||||
function fetchStoredChallenges() {
|
const fetchStoredChallenges = () => {
|
||||||
try {
|
try {
|
||||||
const verification = fetchStoredVerification();
|
const verification: Verification | null = fetchStoredVerification();
|
||||||
return verification.challenges;
|
return verification!.challenges;
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the verification object in local storage.
|
* Update the verification object in local storage.
|
||||||
*
|
*
|
||||||
* @param {*} verification object
|
* @param {*} verification object
|
||||||
*/
|
*/
|
||||||
function updateStorage({ ...updatedVerification }) {
|
const updateStorage = ({ ...updatedVerification }: Verification) => {
|
||||||
const verification = fetchStoredVerification();
|
const verification = fetchStoredVerification();
|
||||||
|
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
LOCAL_STORAGE_VERIFICATION_KEY,
|
LOCAL_STORAGE_VERIFICATION_KEY,
|
||||||
JSON.stringify({ ...verification, ...updatedVerification }),
|
JSON.stringify({ ...verification, ...updatedVerification }),
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch Pepe challenges and registration token
|
* Fetch Pepe challenges and registration token
|
||||||
* @returns {promise}
|
|
||||||
*/
|
*/
|
||||||
function fetchVerificationConfig() {
|
const fetchVerificationConfig = () =>
|
||||||
return async(dispatch) => {
|
async(dispatch: AppDispatch) => {
|
||||||
await dispatch(fetchPepeInstance());
|
await dispatch(fetchPepeInstance());
|
||||||
|
|
||||||
dispatch(fetchRegistrationToken());
|
dispatch(fetchRegistrationToken());
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the challenges in localStorage.
|
* Save the challenges in localStorage.
|
||||||
|
@ -125,13 +117,11 @@ function fetchVerificationConfig() {
|
||||||
* challenge to localStorage.
|
* challenge to localStorage.
|
||||||
* - Don't overwrite a challenge that has already been completed.
|
* - Don't overwrite a challenge that has already been completed.
|
||||||
* - Update localStorage to the new set of challenges.
|
* - Update localStorage to the new set of challenges.
|
||||||
*
|
|
||||||
* @param {array} challenges - ['age', 'sms', 'email']
|
|
||||||
*/
|
*/
|
||||||
function saveChallenges(challenges) {
|
function saveChallenges(challenges: Array<'age' | 'sms' | 'email'>) {
|
||||||
const currentChallenges = fetchStoredChallenges() || {};
|
const currentChallenges: Challenges = fetchStoredChallenges() || {};
|
||||||
|
|
||||||
const challengesToRemove = Object.keys(currentChallenges).filter((currentChallenge) => !challenges.includes(currentChallenge));
|
const challengesToRemove = Object.keys(currentChallenges).filter((currentChallenge) => !challenges.includes(currentChallenge as Challenge)) as Challenge[];
|
||||||
challengesToRemove.forEach((challengeToRemove) => delete currentChallenges[challengeToRemove]);
|
challengesToRemove.forEach((challengeToRemove) => delete currentChallenges[challengeToRemove]);
|
||||||
|
|
||||||
for (let i = 0; i < challenges.length; i++) {
|
for (let i = 0; i < challenges.length; i++) {
|
||||||
|
@ -147,10 +137,9 @@ function saveChallenges(challenges) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finish a challenge.
|
* Finish a challenge.
|
||||||
* @param {string} challenge - "sms" or "email" or "age"
|
|
||||||
*/
|
*/
|
||||||
function finishChallenge(challenge) {
|
function finishChallenge(challenge: Challenge) {
|
||||||
const currentChallenges = fetchStoredChallenges() || {};
|
const currentChallenges: Challenges = fetchStoredChallenges() || {};
|
||||||
// Set challenge to "complete"
|
// Set challenge to "complete"
|
||||||
currentChallenges[challenge] = 1;
|
currentChallenges[challenge] = 1;
|
||||||
|
|
||||||
|
@ -159,17 +148,16 @@ function finishChallenge(challenge) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch the next challenge
|
* Fetch the next challenge
|
||||||
* @returns {string} challenge - "sms" or "email" or "age"
|
|
||||||
*/
|
*/
|
||||||
function fetchNextChallenge() {
|
const fetchNextChallenge = (): Challenge => {
|
||||||
const currentChallenges = fetchStoredChallenges() || {};
|
const currentChallenges: Challenges = fetchStoredChallenges() || {};
|
||||||
return Object.keys(currentChallenges).find((challenge) => currentChallenges[challenge] === 0);
|
return Object.keys(currentChallenges).find((challenge) => currentChallenges[challenge as Challenge] === 0) as Challenge;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatch the next challenge or set to complete if all challenges are completed.
|
* Dispatch the next challenge or set to complete if all challenges are completed.
|
||||||
*/
|
*/
|
||||||
function dispatchNextChallenge(dispatch) {
|
const dispatchNextChallenge = (dispatch: AppDispatch) => {
|
||||||
const nextChallenge = fetchNextChallenge();
|
const nextChallenge = fetchNextChallenge();
|
||||||
|
|
||||||
if (nextChallenge) {
|
if (nextChallenge) {
|
||||||
|
@ -177,14 +165,13 @@ function dispatchNextChallenge(dispatch) {
|
||||||
} else {
|
} else {
|
||||||
dispatch({ type: SET_CHALLENGES_COMPLETE });
|
dispatch({ type: SET_CHALLENGES_COMPLETE });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch the challenges and age mininum from Pepe
|
* Fetch the challenges and age mininum from Pepe
|
||||||
* @returns {promise}
|
|
||||||
*/
|
*/
|
||||||
function fetchPepeInstance() {
|
const fetchPepeInstance = () =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: SET_LOADING });
|
dispatch({ type: SET_LOADING });
|
||||||
|
|
||||||
return api(getState).get('/api/v1/pepe/instance').then(response => {
|
return api(getState).get('/api/v1/pepe/instance').then(response => {
|
||||||
|
@ -203,14 +190,12 @@ function fetchPepeInstance() {
|
||||||
})
|
})
|
||||||
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch the regristration token from Pepe unless it's already been stored locally
|
* Fetch the regristration token from Pepe unless it's already been stored locally
|
||||||
* @returns {promise}
|
|
||||||
*/
|
*/
|
||||||
function fetchRegistrationToken() {
|
const fetchRegistrationToken = () =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: SET_LOADING });
|
dispatch({ type: SET_LOADING });
|
||||||
|
|
||||||
const token = fetchStoredToken();
|
const token = fetchStoredToken();
|
||||||
|
@ -234,10 +219,9 @@ function fetchRegistrationToken() {
|
||||||
})
|
})
|
||||||
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
function checkEmailAvailability(email) {
|
const checkEmailAvailability = (email: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: SET_LOADING });
|
dispatch({ type: SET_LOADING });
|
||||||
|
|
||||||
const token = fetchStoredToken();
|
const token = fetchStoredToken();
|
||||||
|
@ -248,15 +232,12 @@ function checkEmailAvailability(email) {
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
.then(() => dispatch({ type: SET_LOADING, value: false }));
|
.then(() => dispatch({ type: SET_LOADING, value: false }));
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send the user's email to Pepe to request confirmation
|
* Send the user's email to Pepe to request confirmation
|
||||||
* @param {string} email
|
|
||||||
* @returns {promise}
|
|
||||||
*/
|
*/
|
||||||
function requestEmailVerification(email) {
|
const requestEmailVerification = (email: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: SET_LOADING });
|
dispatch({ type: SET_LOADING });
|
||||||
|
|
||||||
const token = fetchStoredToken();
|
const token = fetchStoredToken();
|
||||||
|
@ -266,25 +247,21 @@ function requestEmailVerification(email) {
|
||||||
})
|
})
|
||||||
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
function checkEmailVerification() {
|
const checkEmailVerification = () =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const token = fetchStoredToken();
|
const token = fetchStoredToken();
|
||||||
|
|
||||||
return api(getState).get('/api/v1/pepe/verify_email', {
|
return api(getState).get('/api/v1/pepe/verify_email', {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Confirm the user's email with Pepe
|
* Confirm the user's email with Pepe
|
||||||
* @param {string} emailToken
|
|
||||||
* @returns {promise}
|
|
||||||
*/
|
*/
|
||||||
function confirmEmailVerification(emailToken) {
|
const confirmEmailVerification = (emailToken: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: SET_LOADING });
|
dispatch({ type: SET_LOADING });
|
||||||
|
|
||||||
const token = fetchStoredToken();
|
const token = fetchStoredToken();
|
||||||
|
@ -293,27 +270,23 @@ function confirmEmailVerification(emailToken) {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
finishChallenge(ChallengeTypes.EMAIL);
|
finishChallenge(EMAIL);
|
||||||
dispatchNextChallenge(dispatch);
|
dispatchNextChallenge(dispatch);
|
||||||
})
|
})
|
||||||
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
function postEmailVerification() {
|
const postEmailVerification = () =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch) => {
|
||||||
finishChallenge(ChallengeTypes.EMAIL);
|
finishChallenge(EMAIL);
|
||||||
dispatchNextChallenge(dispatch);
|
dispatchNextChallenge(dispatch);
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send the user's phone number to Pepe to request confirmation
|
* Send the user's phone number to Pepe to request confirmation
|
||||||
* @param {string} phone
|
|
||||||
* @returns {promise}
|
|
||||||
*/
|
*/
|
||||||
function requestPhoneVerification(phone) {
|
const requestPhoneVerification = (phone: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: SET_LOADING });
|
dispatch({ type: SET_LOADING });
|
||||||
|
|
||||||
const token = fetchStoredToken();
|
const token = fetchStoredToken();
|
||||||
|
@ -323,29 +296,23 @@ function requestPhoneVerification(phone) {
|
||||||
})
|
})
|
||||||
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send the user's phone number to Pepe to re-request confirmation
|
* Send the user's phone number to Pepe to re-request confirmation
|
||||||
* @param {string} phone
|
|
||||||
* @returns {promise}
|
|
||||||
*/
|
*/
|
||||||
function reRequestPhoneVerification(phone) {
|
const reRequestPhoneVerification = (phone: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: SET_LOADING });
|
dispatch({ type: SET_LOADING });
|
||||||
|
|
||||||
return api(getState).post('/api/v1/pepe/reverify_sms/request', { phone })
|
return api(getState).post('/api/v1/pepe/reverify_sms/request', { phone })
|
||||||
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Confirm the user's phone number with Pepe
|
* Confirm the user's phone number with Pepe
|
||||||
* @param {string} code
|
|
||||||
* @returns {promise}
|
|
||||||
*/
|
*/
|
||||||
function confirmPhoneVerification(code) {
|
const confirmPhoneVerification = (code: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: SET_LOADING });
|
dispatch({ type: SET_LOADING });
|
||||||
|
|
||||||
const token = fetchStoredToken();
|
const token = fetchStoredToken();
|
||||||
|
@ -354,34 +321,28 @@ function confirmPhoneVerification(code) {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
finishChallenge(ChallengeTypes.SMS);
|
finishChallenge(SMS);
|
||||||
dispatchNextChallenge(dispatch);
|
dispatchNextChallenge(dispatch);
|
||||||
})
|
})
|
||||||
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Re-Confirm the user's phone number with Pepe
|
* Re-Confirm the user's phone number with Pepe
|
||||||
* @param {string} code
|
|
||||||
* @returns {promise}
|
|
||||||
*/
|
*/
|
||||||
function reConfirmPhoneVerification(code) {
|
const reConfirmPhoneVerification = (code: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: SET_LOADING });
|
dispatch({ type: SET_LOADING });
|
||||||
|
|
||||||
return api(getState).post('/api/v1/pepe/reverify_sms/confirm', { code })
|
return api(getState).post('/api/v1/pepe/reverify_sms/confirm', { code })
|
||||||
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Confirm the user's age with Pepe
|
* Confirm the user's age with Pepe
|
||||||
* @param {date} birthday
|
|
||||||
* @returns {promise}
|
|
||||||
*/
|
*/
|
||||||
function verifyAge(birthday) {
|
const verifyAge = (birthday: Date) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: SET_LOADING });
|
dispatch({ type: SET_LOADING });
|
||||||
|
|
||||||
const token = fetchStoredToken();
|
const token = fetchStoredToken();
|
||||||
|
@ -390,21 +351,17 @@ function verifyAge(birthday) {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
finishChallenge(ChallengeTypes.AGE);
|
finishChallenge(AGE);
|
||||||
dispatchNextChallenge(dispatch);
|
dispatchNextChallenge(dispatch);
|
||||||
})
|
})
|
||||||
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the user's account with Pepe
|
* Create the user's account with Pepe
|
||||||
* @param {string} username
|
|
||||||
* @param {string} password
|
|
||||||
* @returns {promise}
|
|
||||||
*/
|
*/
|
||||||
function createAccount(username, password) {
|
const createAccount = (username: string, password: string) =>
|
||||||
return (dispatch, getState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: SET_LOADING });
|
dispatch({ type: SET_LOADING });
|
||||||
|
|
||||||
const token = fetchStoredToken();
|
const token = fetchStoredToken();
|
||||||
|
@ -413,7 +370,6 @@ function createAccount(username, password) {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
}).finally(() => dispatch({ type: SET_LOADING, value: false }));
|
}).finally(() => dispatch({ type: SET_LOADING, value: false }));
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
PEPE_FETCH_INSTANCE_SUCCESS,
|
PEPE_FETCH_INSTANCE_SUCCESS,
|
|
@ -59,7 +59,7 @@ interface IStatus extends RouteComponentProps {
|
||||||
account: AccountEntity,
|
account: AccountEntity,
|
||||||
otherAccounts: ImmutableList<AccountEntity>,
|
otherAccounts: ImmutableList<AccountEntity>,
|
||||||
onClick: () => void,
|
onClick: () => void,
|
||||||
onReply: (status: StatusEntity, history: History) => void,
|
onReply: (status: StatusEntity) => void,
|
||||||
onFavourite: (status: StatusEntity) => void,
|
onFavourite: (status: StatusEntity) => void,
|
||||||
onReblog: (status: StatusEntity, e?: KeyboardEvent) => void,
|
onReblog: (status: StatusEntity, e?: KeyboardEvent) => void,
|
||||||
onQuote: (status: StatusEntity) => void,
|
onQuote: (status: StatusEntity) => void,
|
||||||
|
@ -67,7 +67,7 @@ interface IStatus extends RouteComponentProps {
|
||||||
onEdit: (status: StatusEntity) => void,
|
onEdit: (status: StatusEntity) => void,
|
||||||
onDirect: (status: StatusEntity) => void,
|
onDirect: (status: StatusEntity) => void,
|
||||||
onChat: (status: StatusEntity) => void,
|
onChat: (status: StatusEntity) => void,
|
||||||
onMention: (account: StatusEntity['account'], history: History) => void,
|
onMention: (account: StatusEntity['account']) => void,
|
||||||
onPin: (status: StatusEntity) => void,
|
onPin: (status: StatusEntity) => void,
|
||||||
onOpenMedia: (media: ImmutableList<AttachmentEntity>, index: number) => void,
|
onOpenMedia: (media: ImmutableList<AttachmentEntity>, index: number) => void,
|
||||||
onOpenVideo: (media: ImmutableMap<string, any> | AttachmentEntity, startTime: number) => void,
|
onOpenVideo: (media: ImmutableMap<string, any> | AttachmentEntity, startTime: number) => void,
|
||||||
|
@ -229,7 +229,7 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
||||||
|
|
||||||
handleHotkeyReply = (e?: KeyboardEvent): void => {
|
handleHotkeyReply = (e?: KeyboardEvent): void => {
|
||||||
e?.preventDefault();
|
e?.preventDefault();
|
||||||
this.props.onReply(this._properStatus(), this.props.history);
|
this.props.onReply(this._properStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHotkeyFavourite = (): void => {
|
handleHotkeyFavourite = (): void => {
|
||||||
|
@ -242,7 +242,7 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
||||||
|
|
||||||
handleHotkeyMention = (e?: KeyboardEvent): void => {
|
handleHotkeyMention = (e?: KeyboardEvent): void => {
|
||||||
e?.preventDefault();
|
e?.preventDefault();
|
||||||
this.props.onMention(this._properStatus().account, this.props.history);
|
this.props.onMention(this._properStatus().account);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHotkeyOpen = (): void => {
|
handleHotkeyOpen = (): void => {
|
||||||
|
|
|
@ -71,16 +71,16 @@ interface IStatusActionBar extends RouteComponentProps {
|
||||||
status: Status,
|
status: Status,
|
||||||
onOpenUnauthorizedModal: (modalType?: string) => void,
|
onOpenUnauthorizedModal: (modalType?: string) => void,
|
||||||
onOpenReblogsModal: (acct: string, statusId: string) => void,
|
onOpenReblogsModal: (acct: string, statusId: string) => void,
|
||||||
onReply: (status: Status, history: History) => void,
|
onReply: (status: Status) => void,
|
||||||
onFavourite: (status: Status) => void,
|
onFavourite: (status: Status) => void,
|
||||||
onBookmark: (status: Status) => void,
|
onBookmark: (status: Status) => void,
|
||||||
onReblog: (status: Status, e: React.MouseEvent) => void,
|
onReblog: (status: Status, e: React.MouseEvent) => void,
|
||||||
onQuote: (status: Status, history: History) => void,
|
onQuote: (status: Status) => void,
|
||||||
onDelete: (status: Status, redraft?: boolean) => void,
|
onDelete: (status: Status, redraft?: boolean) => void,
|
||||||
onEdit: (status: Status) => void,
|
onEdit: (status: Status) => void,
|
||||||
onDirect: (account: any, history: History) => void,
|
onDirect: (account: any) => void,
|
||||||
onChat: (account: any, history: History) => void,
|
onChat: (account: any, history: History) => void,
|
||||||
onMention: (account: any, history: History) => void,
|
onMention: (account: any) => void,
|
||||||
onMute: (account: any) => void,
|
onMute: (account: any) => void,
|
||||||
onBlock: (status: Status) => void,
|
onBlock: (status: Status) => void,
|
||||||
onReport: (status: Status) => void,
|
onReport: (status: Status) => void,
|
||||||
|
@ -134,7 +134,7 @@ class StatusActionBar extends ImmutablePureComponent<IStatusActionBar, IStatusAc
|
||||||
const { me, onReply, onOpenUnauthorizedModal, status } = this.props;
|
const { me, onReply, onOpenUnauthorizedModal, status } = this.props;
|
||||||
|
|
||||||
if (me) {
|
if (me) {
|
||||||
onReply(status, this.props.history);
|
onReply(status);
|
||||||
} else {
|
} else {
|
||||||
onOpenUnauthorizedModal('REPLY');
|
onOpenUnauthorizedModal('REPLY');
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ class StatusActionBar extends ImmutablePureComponent<IStatusActionBar, IStatusAc
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const { me, onQuote, onOpenUnauthorizedModal, status } = this.props;
|
const { me, onQuote, onOpenUnauthorizedModal, status } = this.props;
|
||||||
if (me) {
|
if (me) {
|
||||||
onQuote(status, this.props.history);
|
onQuote(status);
|
||||||
} else {
|
} else {
|
||||||
onOpenUnauthorizedModal('REBLOG');
|
onOpenUnauthorizedModal('REBLOG');
|
||||||
}
|
}
|
||||||
|
@ -260,12 +260,12 @@ class StatusActionBar extends ImmutablePureComponent<IStatusActionBar, IStatusAc
|
||||||
|
|
||||||
handleMentionClick: React.EventHandler<React.MouseEvent> = (e) => {
|
handleMentionClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.props.onMention(this.props.status.account, this.props.history);
|
this.props.onMention(this.props.status.account);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDirectClick: React.EventHandler<React.MouseEvent> = (e) => {
|
handleDirectClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.props.onDirect(this.props.status.account, this.props.history);
|
this.props.onDirect(this.props.status.account);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChatClick: React.EventHandler<React.MouseEvent> = (e) => {
|
handleChatClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||||
|
|
|
@ -82,17 +82,17 @@ const mapDispatchToProps = (dispatch, { intl }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onReply(status, router) {
|
onReply(status) {
|
||||||
dispatch((_, getState) => {
|
dispatch((_, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
if (state.getIn(['compose', 'text']).trim().length !== 0) {
|
if (state.getIn(['compose', 'text']).trim().length !== 0) {
|
||||||
dispatch(openModal('CONFIRM', {
|
dispatch(openModal('CONFIRM', {
|
||||||
message: intl.formatMessage(messages.replyMessage),
|
message: intl.formatMessage(messages.replyMessage),
|
||||||
confirm: intl.formatMessage(messages.replyConfirm),
|
confirm: intl.formatMessage(messages.replyConfirm),
|
||||||
onConfirm: () => dispatch(replyCompose(status, router)),
|
onConfirm: () => dispatch(replyCompose(status)),
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
dispatch(replyCompose(status, router));
|
dispatch(replyCompose(status));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -110,17 +110,17 @@ const mapDispatchToProps = (dispatch, { intl }) => {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onQuote(status, router) {
|
onQuote(status) {
|
||||||
dispatch((_, getState) => {
|
dispatch((_, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
if (state.getIn(['compose', 'text']).trim().length !== 0) {
|
if (state.getIn(['compose', 'text']).trim().length !== 0) {
|
||||||
dispatch(openModal('CONFIRM', {
|
dispatch(openModal('CONFIRM', {
|
||||||
message: intl.formatMessage(messages.replyMessage),
|
message: intl.formatMessage(messages.replyMessage),
|
||||||
confirm: intl.formatMessage(messages.replyConfirm),
|
confirm: intl.formatMessage(messages.replyConfirm),
|
||||||
onConfirm: () => dispatch(quoteCompose(status, router)),
|
onConfirm: () => dispatch(quoteCompose(status)),
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
dispatch(quoteCompose(status, router));
|
dispatch(quoteCompose(status));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -177,16 +177,16 @@ const mapDispatchToProps = (dispatch, { intl }) => {
|
||||||
dispatch(editStatus(status.get('id')));
|
dispatch(editStatus(status.get('id')));
|
||||||
},
|
},
|
||||||
|
|
||||||
onDirect(account, router) {
|
onDirect(account) {
|
||||||
dispatch(directCompose(account, router));
|
dispatch(directCompose(account));
|
||||||
},
|
},
|
||||||
|
|
||||||
onChat(account, router) {
|
onChat(account, router) {
|
||||||
dispatch(launchChat(account.get('id'), router));
|
dispatch(launchChat(account.get('id'), router));
|
||||||
},
|
},
|
||||||
|
|
||||||
onMention(account, router) {
|
onMention(account) {
|
||||||
dispatch(mentionCompose(account, router));
|
dispatch(mentionCompose(account));
|
||||||
},
|
},
|
||||||
|
|
||||||
onOpenMedia(media, index) {
|
onOpenMedia(media, index) {
|
||||||
|
|
|
@ -44,7 +44,7 @@ const AccountGallery = () => {
|
||||||
const accountFetchError = (state.accounts.get(-1)?.username || '').toLowerCase() === username.toLowerCase();
|
const accountFetchError = (state.accounts.get(-1)?.username || '').toLowerCase() === username.toLowerCase();
|
||||||
const features = getFeatures(state.instance);
|
const features = getFeatures(state.instance);
|
||||||
|
|
||||||
let accountId: string | number | null = -1;
|
let accountId: string | -1 | null = -1;
|
||||||
let accountUsername = username;
|
let accountUsername = username;
|
||||||
if (accountFetchError) {
|
if (accountFetchError) {
|
||||||
accountId = null;
|
accountId = null;
|
||||||
|
|
|
@ -39,11 +39,11 @@ class Header extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMention = () => {
|
handleMention = () => {
|
||||||
this.props.onMention(this.props.account, this.props.history);
|
this.props.onMention(this.props.account);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDirect = () => {
|
handleDirect = () => {
|
||||||
this.props.onDirect(this.props.account, this.props.history);
|
this.props.onDirect(this.props.account);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReport = () => {
|
handleReport = () => {
|
||||||
|
|
|
@ -110,12 +110,12 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onMention(account, router) {
|
onMention(account) {
|
||||||
dispatch(mentionCompose(account, router));
|
dispatch(mentionCompose(account));
|
||||||
},
|
},
|
||||||
|
|
||||||
onDirect(account, router) {
|
onDirect(account) {
|
||||||
dispatch(directCompose(account, router));
|
dispatch(directCompose(account));
|
||||||
},
|
},
|
||||||
|
|
||||||
onReblogToggle(account) {
|
onReblogToggle(account) {
|
||||||
|
|
|
@ -30,8 +30,8 @@ const LatestAccountsPanel: React.FC<ILatestAccountsPanel> = ({ limit = 5 }) => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchUsers(['local', 'active'], 1, null, limit))
|
dispatch(fetchUsers(['local', 'active'], 1, null, limit))
|
||||||
.then((value: { count: number }) => {
|
.then((value) => {
|
||||||
setTotal(value.count);
|
setTotal((value as { count: number }).count);
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
@ -32,7 +32,7 @@ const AuthLayout = () => {
|
||||||
const features = useFeatures();
|
const features = useFeatures();
|
||||||
const instance = useAppSelector((state) => state.instance);
|
const instance = useAppSelector((state) => state.instance);
|
||||||
const isOpen = features.accountCreation && instance.registrations;
|
const isOpen = features.accountCreation && instance.registrations;
|
||||||
const pepeOpen = useAppSelector(state => state.verification.getIn(['instance', 'registrations'], false) === true);
|
const pepeOpen = useAppSelector(state => state.verification.instance.get('registrations') === true);
|
||||||
const isLoginPage = history.location.pathname === '/login';
|
const isLoginPage = history.location.pathname === '/login';
|
||||||
const shouldShowRegisterLink = (isLoginPage && (isOpen || (pepeEnabled && pepeOpen)));
|
const shouldShowRegisterLink = (isLoginPage && (isOpen || (pepeEnabled && pepeOpen)));
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ const LandingPage = () => {
|
||||||
const pepeEnabled = soapboxConfig.getIn(['extensions', 'pepe', 'enabled']) === true;
|
const pepeEnabled = soapboxConfig.getIn(['extensions', 'pepe', 'enabled']) === true;
|
||||||
|
|
||||||
const instance = useAppSelector((state) => state.instance);
|
const instance = useAppSelector((state) => state.instance);
|
||||||
const pepeOpen = useAppSelector(state => state.verification.getIn(['instance', 'registrations'], false) === true);
|
const pepeOpen = useAppSelector(state => state.verification.instance.get('registrations') === true);
|
||||||
|
|
||||||
/** Registrations are closed */
|
/** Registrations are closed */
|
||||||
const renderClosed = () => {
|
const renderClosed = () => {
|
||||||
|
|
|
@ -10,7 +10,6 @@ import AccountContainer from 'soapbox/containers/account_container';
|
||||||
import StatusContainer from 'soapbox/containers/status_container';
|
import StatusContainer from 'soapbox/containers/status_container';
|
||||||
import { useAppSelector } from 'soapbox/hooks';
|
import { useAppSelector } from 'soapbox/hooks';
|
||||||
|
|
||||||
import type { History } from 'history';
|
|
||||||
import type { ScrollPosition } from 'soapbox/components/status';
|
import type { ScrollPosition } from 'soapbox/components/status';
|
||||||
import type { NotificationType } from 'soapbox/normalizers/notification';
|
import type { NotificationType } from 'soapbox/normalizers/notification';
|
||||||
import type { Account, Status, Notification as NotificationEntity } from 'soapbox/types/entities';
|
import type { Account, Status, Notification as NotificationEntity } from 'soapbox/types/entities';
|
||||||
|
@ -131,7 +130,7 @@ interface INotificaton {
|
||||||
notification: NotificationEntity,
|
notification: NotificationEntity,
|
||||||
onMoveUp: (notificationId: string) => void,
|
onMoveUp: (notificationId: string) => void,
|
||||||
onMoveDown: (notificationId: string) => void,
|
onMoveDown: (notificationId: string) => void,
|
||||||
onMention: (account: Account, history: History) => void,
|
onMention: (account: Account) => void,
|
||||||
onFavourite: (status: Status) => void,
|
onFavourite: (status: Status) => void,
|
||||||
onReblog: (status: Status, e?: KeyboardEvent) => void,
|
onReblog: (status: Status, e?: KeyboardEvent) => void,
|
||||||
onToggleHidden: (status: Status) => void,
|
onToggleHidden: (status: Status) => void,
|
||||||
|
@ -182,7 +181,7 @@ const Notification: React.FC<INotificaton> = (props) => {
|
||||||
e?.preventDefault();
|
e?.preventDefault();
|
||||||
|
|
||||||
if (account && typeof account === 'object') {
|
if (account && typeof account === 'object') {
|
||||||
props.onMention(account, history);
|
props.onMention(account);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -31,8 +31,8 @@ const makeMapStateToProps = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
onMention: (account, router) => {
|
onMention: (account) => {
|
||||||
dispatch(mentionCompose(account, router));
|
dispatch(mentionCompose(account));
|
||||||
},
|
},
|
||||||
|
|
||||||
onModalReblog(status) {
|
onModalReblog(status) {
|
||||||
|
|
|
@ -34,7 +34,7 @@ const Header = () => {
|
||||||
const features = useFeatures();
|
const features = useFeatures();
|
||||||
const instance = useAppSelector((state) => state.instance);
|
const instance = useAppSelector((state) => state.instance);
|
||||||
const isOpen = features.accountCreation && instance.registrations;
|
const isOpen = features.accountCreation && instance.registrations;
|
||||||
const pepeOpen = useAppSelector(state => state.verification.getIn(['instance', 'registrations'], false) === true);
|
const pepeOpen = useAppSelector(state => state.verification.instance.get('registrations') === true);
|
||||||
|
|
||||||
const [isLoading, setLoading] = React.useState(false);
|
const [isLoading, setLoading] = React.useState(false);
|
||||||
const [username, setUsername] = React.useState('');
|
const [username, setUsername] = React.useState('');
|
||||||
|
|
|
@ -91,15 +91,15 @@ interface OwnProps {
|
||||||
status: StatusEntity,
|
status: StatusEntity,
|
||||||
onReply: (status: StatusEntity) => void,
|
onReply: (status: StatusEntity) => void,
|
||||||
onReblog: (status: StatusEntity, e: React.MouseEvent) => void,
|
onReblog: (status: StatusEntity, e: React.MouseEvent) => void,
|
||||||
onQuote: (status: StatusEntity, history: History) => void,
|
onQuote: (status: StatusEntity) => void,
|
||||||
onFavourite: (status: StatusEntity) => void,
|
onFavourite: (status: StatusEntity) => void,
|
||||||
onEmojiReact: (status: StatusEntity, emoji: string) => void,
|
onEmojiReact: (status: StatusEntity, emoji: string) => void,
|
||||||
onDelete: (status: StatusEntity, redraft?: boolean) => void,
|
onDelete: (status: StatusEntity, redraft?: boolean) => void,
|
||||||
onEdit: (status: StatusEntity) => void,
|
onEdit: (status: StatusEntity) => void,
|
||||||
onBookmark: (status: StatusEntity) => void,
|
onBookmark: (status: StatusEntity) => void,
|
||||||
onDirect: (account: AccountEntity, history: History) => void,
|
onDirect: (account: AccountEntity) => void,
|
||||||
onChat: (account: AccountEntity, history: History) => void,
|
onChat: (account: AccountEntity, history: History) => void,
|
||||||
onMention: (account: AccountEntity, history: History) => void,
|
onMention: (account: AccountEntity) => void,
|
||||||
onMute: (account: AccountEntity) => void,
|
onMute: (account: AccountEntity) => void,
|
||||||
onMuteConversation: (status: StatusEntity) => void,
|
onMuteConversation: (status: StatusEntity) => void,
|
||||||
onBlock: (status: StatusEntity) => void,
|
onBlock: (status: StatusEntity) => void,
|
||||||
|
@ -164,7 +164,7 @@ class ActionBar extends React.PureComponent<IActionBar, IActionBarState> {
|
||||||
handleQuoteClick: React.EventHandler<React.MouseEvent> = () => {
|
handleQuoteClick: React.EventHandler<React.MouseEvent> = () => {
|
||||||
const { me, onQuote, onOpenUnauthorizedModal, status } = this.props;
|
const { me, onQuote, onOpenUnauthorizedModal, status } = this.props;
|
||||||
if (me) {
|
if (me) {
|
||||||
onQuote(status, this.props.history);
|
onQuote(status);
|
||||||
} else {
|
} else {
|
||||||
onOpenUnauthorizedModal('REBLOG');
|
onOpenUnauthorizedModal('REBLOG');
|
||||||
}
|
}
|
||||||
|
@ -250,7 +250,7 @@ class ActionBar extends React.PureComponent<IActionBar, IActionBarState> {
|
||||||
handleDirectClick: React.EventHandler<React.MouseEvent> = () => {
|
handleDirectClick: React.EventHandler<React.MouseEvent> = () => {
|
||||||
const { account } = this.props.status;
|
const { account } = this.props.status;
|
||||||
if (!account || typeof account !== 'object') return;
|
if (!account || typeof account !== 'object') return;
|
||||||
this.props.onDirect(account, this.props.history);
|
this.props.onDirect(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChatClick: React.EventHandler<React.MouseEvent> = () => {
|
handleChatClick: React.EventHandler<React.MouseEvent> = () => {
|
||||||
|
@ -262,7 +262,7 @@ class ActionBar extends React.PureComponent<IActionBar, IActionBarState> {
|
||||||
handleMentionClick: React.EventHandler<React.MouseEvent> = () => {
|
handleMentionClick: React.EventHandler<React.MouseEvent> = () => {
|
||||||
const { account } = this.props.status;
|
const { account } = this.props.status;
|
||||||
if (!account || typeof account !== 'object') return;
|
if (!account || typeof account !== 'object') return;
|
||||||
this.props.onMention(account, this.props.history);
|
this.props.onMention(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMuteClick: React.EventHandler<React.MouseEvent> = () => {
|
handleMuteClick: React.EventHandler<React.MouseEvent> = () => {
|
||||||
|
|
|
@ -63,17 +63,17 @@ const makeMapStateToProps = () => {
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch, { intl }) => ({
|
const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
|
|
||||||
onReply(status, router) {
|
onReply(status) {
|
||||||
dispatch((_, getState) => {
|
dispatch((_, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
if (state.getIn(['compose', 'text']).trim().length !== 0) {
|
if (state.getIn(['compose', 'text']).trim().length !== 0) {
|
||||||
dispatch(openModal('CONFIRM', {
|
dispatch(openModal('CONFIRM', {
|
||||||
message: intl.formatMessage(messages.replyMessage),
|
message: intl.formatMessage(messages.replyMessage),
|
||||||
confirm: intl.formatMessage(messages.replyConfirm),
|
confirm: intl.formatMessage(messages.replyConfirm),
|
||||||
onConfirm: () => dispatch(replyCompose(status, router)),
|
onConfirm: () => dispatch(replyCompose(status)),
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
dispatch(replyCompose(status, router));
|
dispatch(replyCompose(status));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -149,16 +149,16 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
dispatch(editStatus(status.get('id')));
|
dispatch(editStatus(status.get('id')));
|
||||||
},
|
},
|
||||||
|
|
||||||
onDirect(account, router) {
|
onDirect(account) {
|
||||||
dispatch(directCompose(account, router));
|
dispatch(directCompose(account));
|
||||||
},
|
},
|
||||||
|
|
||||||
onChat(account, router) {
|
onChat(account, router) {
|
||||||
dispatch(launchChat(account.get('id'), router));
|
dispatch(launchChat(account.get('id'), router));
|
||||||
},
|
},
|
||||||
|
|
||||||
onMention(account, router) {
|
onMention(account) {
|
||||||
dispatch(mentionCompose(account, router));
|
dispatch(mentionCompose(account));
|
||||||
},
|
},
|
||||||
|
|
||||||
onOpenMedia(media, index) {
|
onOpenMedia(media, index) {
|
||||||
|
|
|
@ -273,10 +273,10 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
||||||
dispatch(openModal('CONFIRM', {
|
dispatch(openModal('CONFIRM', {
|
||||||
message: intl.formatMessage(messages.replyMessage),
|
message: intl.formatMessage(messages.replyMessage),
|
||||||
confirm: intl.formatMessage(messages.replyConfirm),
|
confirm: intl.formatMessage(messages.replyConfirm),
|
||||||
onConfirm: () => dispatch(replyCompose(status, this.props.history)),
|
onConfirm: () => dispatch(replyCompose(status)),
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
dispatch(replyCompose(status, this.props.history));
|
dispatch(replyCompose(status));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,10 +305,10 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
||||||
dispatch(openModal('CONFIRM', {
|
dispatch(openModal('CONFIRM', {
|
||||||
message: intl.formatMessage(messages.replyMessage),
|
message: intl.formatMessage(messages.replyMessage),
|
||||||
confirm: intl.formatMessage(messages.replyConfirm),
|
confirm: intl.formatMessage(messages.replyConfirm),
|
||||||
onConfirm: () => dispatch(quoteCompose(status, this.props.history)),
|
onConfirm: () => dispatch(quoteCompose(status)),
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
dispatch(quoteCompose(status, this.props.history));
|
dispatch(quoteCompose(status));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,16 +337,16 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
||||||
dispatch(editStatus(status.id));
|
dispatch(editStatus(status.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDirectClick = (account: AccountEntity, router: History) => {
|
handleDirectClick = (account: AccountEntity) => {
|
||||||
this.props.dispatch(directCompose(account, router));
|
this.props.dispatch(directCompose(account));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChatClick = (account: AccountEntity, router: History) => {
|
handleChatClick = (account: AccountEntity, router: History) => {
|
||||||
this.props.dispatch(launchChat(account.id, router));
|
this.props.dispatch(launchChat(account.id, router));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMentionClick = (account: AccountEntity, router: History) => {
|
handleMentionClick = (account: AccountEntity) => {
|
||||||
this.props.dispatch(mentionCompose(account, router));
|
this.props.dispatch(mentionCompose(account));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOpenMedia = (media: ImmutableList<AttachmentEntity>, index: number) => {
|
handleOpenMedia = (media: ImmutableList<AttachmentEntity>, index: number) => {
|
||||||
|
@ -475,7 +475,7 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
||||||
e?.preventDefault();
|
e?.preventDefault();
|
||||||
const { account } = this.props.status;
|
const { account } = this.props.status;
|
||||||
if (!account || typeof account !== 'object') return;
|
if (!account || typeof account !== 'object') return;
|
||||||
this.handleMentionClick(account, this.props.history);
|
this.handleMentionClick(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHotkeyOpenProfile = () => {
|
handleHotkeyOpenProfile = () => {
|
||||||
|
|
|
@ -29,7 +29,7 @@ const LandingPageModal: React.FC<ILandingPageModal> = ({ onClose }) => {
|
||||||
const features = useFeatures();
|
const features = useFeatures();
|
||||||
|
|
||||||
const isOpen = features.accountCreation && instance.registrations;
|
const isOpen = features.accountCreation && instance.registrations;
|
||||||
const pepeOpen = useAppSelector(state => state.verification.getIn(['instance', 'registrations'], false) === true);
|
const pepeOpen = useAppSelector(state => state.verification.instance.get('registrations') === true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
|
|
@ -29,7 +29,7 @@ const VerifySmsModal: React.FC<IVerifySmsModal> = ({ onClose }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const accessToken = useAppSelector((state) => getAccessToken(state));
|
const accessToken = useAppSelector((state) => getAccessToken(state));
|
||||||
const title = useAppSelector((state) => state.instance.title);
|
const title = useAppSelector((state) => state.instance.title);
|
||||||
const isLoading = useAppSelector((state) => state.verification.get('isLoading') as boolean);
|
const isLoading = useAppSelector((state) => state.verification.isLoading);
|
||||||
|
|
||||||
const [status, setStatus] = useState<Statuses>(Statuses.IDLE);
|
const [status, setStatus] = useState<Statuses>(Statuses.IDLE);
|
||||||
const [phone, setPhone] = useState<string>('');
|
const [phone, setPhone] = useState<string>('');
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Map as ImmutableMap } from 'immutable';
|
import { Map as ImmutableMap, Record as ImmutableRecord } from 'immutable';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Route, Switch } from 'react-router-dom';
|
import { Route, Switch } from 'react-router-dom';
|
||||||
|
|
||||||
|
@ -26,13 +26,17 @@ describe('<Verification />', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
store = {
|
store = {
|
||||||
verification: ImmutableMap({
|
verification: ImmutableRecord({
|
||||||
instance: {
|
instance: ImmutableMap({
|
||||||
isReady: true,
|
isReady: true,
|
||||||
registrations: true,
|
registrations: true,
|
||||||
},
|
|
||||||
isComplete: false,
|
|
||||||
}),
|
}),
|
||||||
|
ageMinimum: null,
|
||||||
|
currentChallenge: null,
|
||||||
|
isLoading: false,
|
||||||
|
isComplete: false,
|
||||||
|
token: null,
|
||||||
|
})(),
|
||||||
};
|
};
|
||||||
|
|
||||||
__stub(mock => {
|
__stub(mock => {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useDispatch } from 'react-redux';
|
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import snackbar from 'soapbox/actions/snackbar';
|
import snackbar from 'soapbox/actions/snackbar';
|
||||||
import { confirmEmailVerification } from 'soapbox/actions/verification';
|
import { confirmEmailVerification } from 'soapbox/actions/verification';
|
||||||
import { Icon, Spinner, Stack, Text } from 'soapbox/components/ui';
|
import { Icon, Spinner, Stack, Text } from 'soapbox/components/ui';
|
||||||
|
import { useAppDispatch } from 'soapbox/hooks';
|
||||||
|
|
||||||
import type { AxiosError } from 'axios';
|
import type { AxiosError } from 'axios';
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ const TokenExpired = () => {
|
||||||
const EmailPassThru = () => {
|
const EmailPassThru = () => {
|
||||||
const { token } = useParams<{ token: string }>();
|
const { token } = useParams<{ token: string }>();
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const [status, setStatus] = React.useState(Statuses.IDLE);
|
const [status, setStatus] = React.useState(Statuses.IDLE);
|
||||||
|
|
|
@ -25,10 +25,10 @@ const verificationSteps = {
|
||||||
const Verification = () => {
|
const Verification = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const isInstanceReady = useAppSelector((state) => state.verification.getIn(['instance', 'isReady'], false) === true);
|
const isInstanceReady = useAppSelector((state) => state.verification.instance.get('isReady') === true);
|
||||||
const isRegistrationOpen = useAppSelector(state => state.verification.getIn(['instance', 'registrations'], false) === true);
|
const isRegistrationOpen = useAppSelector(state => state.verification.instance.get('registrations') === true);
|
||||||
const currentChallenge = useAppSelector((state) => state.verification.getIn(['currentChallenge']) as ChallengeTypes);
|
const currentChallenge = useAppSelector((state) => state.verification.currentChallenge as ChallengeTypes);
|
||||||
const isVerificationComplete = useAppSelector((state) => state.verification.get('isComplete'));
|
const isVerificationComplete = useAppSelector((state) => state.verification.isComplete);
|
||||||
const StepToRender = verificationSteps[currentChallenge];
|
const StepToRender = verificationSteps[currentChallenge];
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
import { useDispatch } from 'react-redux';
|
|
||||||
import { Redirect } from 'react-router-dom';
|
import { Redirect } from 'react-router-dom';
|
||||||
|
|
||||||
import { logIn, verifyCredentials } from 'soapbox/actions/auth';
|
import { logIn, verifyCredentials } from 'soapbox/actions/auth';
|
||||||
|
@ -9,7 +8,7 @@ import { startOnboarding } from 'soapbox/actions/onboarding';
|
||||||
import snackbar from 'soapbox/actions/snackbar';
|
import snackbar from 'soapbox/actions/snackbar';
|
||||||
import { createAccount, removeStoredVerification } from 'soapbox/actions/verification';
|
import { createAccount, removeStoredVerification } from 'soapbox/actions/verification';
|
||||||
import { Button, Form, FormGroup, Input } from 'soapbox/components/ui';
|
import { Button, Form, FormGroup, Input } from 'soapbox/components/ui';
|
||||||
import { useAppSelector } from 'soapbox/hooks';
|
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||||
import { getRedirectUrl } from 'soapbox/utils/redirect';
|
import { getRedirectUrl } from 'soapbox/utils/redirect';
|
||||||
|
|
||||||
import PasswordIndicator from './components/password-indicator';
|
import PasswordIndicator from './components/password-indicator';
|
||||||
|
@ -37,10 +36,10 @@ const initialState = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Registration = () => {
|
const Registration = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const isLoading = useAppSelector((state) => state.verification.get('isLoading') as boolean);
|
const isLoading = useAppSelector((state) => state.verification.isLoading as boolean);
|
||||||
const siteTitle = useAppSelector((state) => state.instance.title);
|
const siteTitle = useAppSelector((state) => state.instance.title);
|
||||||
|
|
||||||
const [state, setState] = React.useState(initialState);
|
const [state, setState] = React.useState(initialState);
|
||||||
|
|
|
@ -25,8 +25,8 @@ const AgeVerification = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const isLoading = useAppSelector((state) => state.verification.get('isLoading')) as boolean;
|
const isLoading = useAppSelector((state) => state.verification.isLoading) as boolean;
|
||||||
const ageMinimum = useAppSelector((state) => state.verification.get('ageMinimum')) as any;
|
const ageMinimum = useAppSelector((state) => state.verification.ageMinimum) as any;
|
||||||
const siteTitle = useAppSelector((state) => state.instance.title);
|
const siteTitle = useAppSelector((state) => state.instance.title);
|
||||||
|
|
||||||
const [date, setDate] = React.useState('');
|
const [date, setDate] = React.useState('');
|
||||||
|
|
|
@ -53,7 +53,7 @@ const EmailVerification = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const isLoading = useAppSelector((state) => state.verification.get('isLoading')) as boolean;
|
const isLoading = useAppSelector((state) => state.verification.isLoading) as boolean;
|
||||||
|
|
||||||
const [email, setEmail] = React.useState('');
|
const [email, setEmail] = React.useState('');
|
||||||
const [status, setStatus] = React.useState(Statuses.IDLE);
|
const [status, setStatus] = React.useState(Statuses.IDLE);
|
||||||
|
|
|
@ -21,7 +21,7 @@ const SmsVerification = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const isLoading = useAppSelector((state) => state.verification.get('isLoading')) as boolean;
|
const isLoading = useAppSelector((state) => state.verification.isLoading) as boolean;
|
||||||
|
|
||||||
const [phone, setPhone] = React.useState('');
|
const [phone, setPhone] = React.useState('');
|
||||||
const [status, setStatus] = React.useState(Statuses.IDLE);
|
const [status, setStatus] = React.useState(Statuses.IDLE);
|
||||||
|
|
|
@ -1,117 +1,177 @@
|
||||||
import { Map as ImmutableMap } from 'immutable';
|
import { Map as ImmutableMap, Record as ImmutableRecord } from 'immutable';
|
||||||
|
|
||||||
import { SET_LOADING } from 'soapbox/actions/verification';
|
import {
|
||||||
|
Challenge,
|
||||||
|
FETCH_CHALLENGES_SUCCESS,
|
||||||
|
FETCH_TOKEN_SUCCESS,
|
||||||
|
SET_CHALLENGES_COMPLETE,
|
||||||
|
SET_LOADING,
|
||||||
|
SET_NEXT_CHALLENGE,
|
||||||
|
} from 'soapbox/actions/verification';
|
||||||
|
|
||||||
import { FETCH_CHALLENGES_SUCCESS, FETCH_TOKEN_SUCCESS, SET_CHALLENGES_COMPLETE, SET_NEXT_CHALLENGE } from '../../actions/verification';
|
|
||||||
import reducer from '../verification';
|
import reducer from '../verification';
|
||||||
|
|
||||||
describe('verfication reducer', () => {
|
describe('verfication reducer', () => {
|
||||||
it('returns the initial state', () => {
|
it('returns the initial state', () => {
|
||||||
expect(reducer(undefined, {})).toEqual(ImmutableMap({
|
expect(reducer(undefined, {} as any)).toMatchObject({
|
||||||
ageMinimum: null,
|
ageMinimum: null,
|
||||||
currentChallenge: null,
|
currentChallenge: null,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isComplete: false,
|
isComplete: false,
|
||||||
token: null,
|
token: null,
|
||||||
instance: ImmutableMap(),
|
instance: ImmutableMap(),
|
||||||
}));
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('FETCH_CHALLENGES_SUCCESS', () => {
|
describe('FETCH_CHALLENGES_SUCCESS', () => {
|
||||||
it('sets the state', () => {
|
it('sets the state', () => {
|
||||||
const state = ImmutableMap({
|
const state = ImmutableRecord({
|
||||||
untouched: 'hello',
|
|
||||||
ageMinimum: null,
|
ageMinimum: null,
|
||||||
currentChallenge: null,
|
currentChallenge: null,
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
isComplete: null,
|
isComplete: null,
|
||||||
});
|
token: null,
|
||||||
|
instance: ImmutableMap<string, any>(),
|
||||||
|
})();
|
||||||
const action = {
|
const action = {
|
||||||
type: FETCH_CHALLENGES_SUCCESS,
|
type: FETCH_CHALLENGES_SUCCESS,
|
||||||
ageMinimum: 13,
|
ageMinimum: 13,
|
||||||
currentChallenge: 'email',
|
currentChallenge: 'email',
|
||||||
isComplete: false,
|
isComplete: false,
|
||||||
};
|
};
|
||||||
const expected = ImmutableMap({
|
const expected = {
|
||||||
untouched: 'hello',
|
|
||||||
ageMinimum: 13,
|
ageMinimum: 13,
|
||||||
currentChallenge: 'email',
|
currentChallenge: 'email',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isComplete: false,
|
isComplete: false,
|
||||||
});
|
token: null,
|
||||||
|
instance: ImmutableMap(),
|
||||||
|
};
|
||||||
|
|
||||||
expect(reducer(state, action)).toEqual(expected);
|
expect(reducer(state, action)).toMatchObject(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('FETCH_TOKEN_SUCCESS', () => {
|
describe('FETCH_TOKEN_SUCCESS', () => {
|
||||||
it('sets the state', () => {
|
it('sets the state', () => {
|
||||||
const state = ImmutableMap({
|
const state = ImmutableRecord({
|
||||||
|
ageMinimum: null,
|
||||||
|
currentChallenge: 'email' as Challenge,
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
|
isComplete: false,
|
||||||
token: null,
|
token: null,
|
||||||
});
|
instance: ImmutableMap<string, any>(),
|
||||||
|
})();
|
||||||
const action = { type: FETCH_TOKEN_SUCCESS, value: '123' };
|
const action = { type: FETCH_TOKEN_SUCCESS, value: '123' };
|
||||||
const expected = ImmutableMap({
|
const expected = {
|
||||||
|
ageMinimum: null,
|
||||||
|
currentChallenge: 'email',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
isComplete: false,
|
||||||
token: '123',
|
token: '123',
|
||||||
});
|
instance: ImmutableMap(),
|
||||||
|
};
|
||||||
|
|
||||||
expect(reducer(state, action)).toEqual(expected);
|
expect(reducer(state, action)).toMatchObject(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('SET_CHALLENGES_COMPLETE', () => {
|
describe('SET_CHALLENGES_COMPLETE', () => {
|
||||||
it('sets the state', () => {
|
it('sets the state', () => {
|
||||||
const state = ImmutableMap({
|
const state = ImmutableRecord({
|
||||||
|
ageMinimum: null,
|
||||||
|
currentChallenge: null,
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
isComplete: false,
|
isComplete: false,
|
||||||
});
|
token: null,
|
||||||
|
instance: ImmutableMap<string, any>(),
|
||||||
|
})();
|
||||||
const action = { type: SET_CHALLENGES_COMPLETE };
|
const action = { type: SET_CHALLENGES_COMPLETE };
|
||||||
const expected = ImmutableMap({
|
const expected = {
|
||||||
|
ageMinimum: null,
|
||||||
|
currentChallenge: null,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isComplete: true,
|
isComplete: true,
|
||||||
});
|
token: null,
|
||||||
|
instance: ImmutableMap(),
|
||||||
|
};
|
||||||
|
|
||||||
expect(reducer(state, action)).toEqual(expected);
|
expect(reducer(state, action)).toMatchObject(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('SET_NEXT_CHALLENGE', () => {
|
describe('SET_NEXT_CHALLENGE', () => {
|
||||||
it('sets the state', () => {
|
it('sets the state', () => {
|
||||||
const state = ImmutableMap({
|
const state = ImmutableRecord({
|
||||||
|
ageMinimum: null,
|
||||||
currentChallenge: null,
|
currentChallenge: null,
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
});
|
isComplete: false,
|
||||||
|
token: null,
|
||||||
|
instance: ImmutableMap<string, any>(),
|
||||||
|
})();
|
||||||
const action = {
|
const action = {
|
||||||
type: SET_NEXT_CHALLENGE,
|
type: SET_NEXT_CHALLENGE,
|
||||||
challenge: 'sms',
|
challenge: 'sms',
|
||||||
};
|
};
|
||||||
const expected = ImmutableMap({
|
const expected = {
|
||||||
|
ageMinimum: null,
|
||||||
currentChallenge: 'sms',
|
currentChallenge: 'sms',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
});
|
isComplete: false,
|
||||||
|
token: null,
|
||||||
|
instance: ImmutableMap(),
|
||||||
|
};
|
||||||
|
|
||||||
expect(reducer(state, action)).toEqual(expected);
|
expect(reducer(state, action)).toMatchObject(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('SET_LOADING with no value', () => {
|
describe('SET_LOADING with no value', () => {
|
||||||
it('sets the state', () => {
|
it('sets the state', () => {
|
||||||
const state = ImmutableMap({ isLoading: false });
|
const state = ImmutableRecord({
|
||||||
|
ageMinimum: null,
|
||||||
|
currentChallenge: null,
|
||||||
|
isLoading: false,
|
||||||
|
isComplete: false,
|
||||||
|
token: null,
|
||||||
|
instance: ImmutableMap<string, any>(),
|
||||||
|
})();
|
||||||
const action = { type: SET_LOADING };
|
const action = { type: SET_LOADING };
|
||||||
const expected = ImmutableMap({ isLoading: true });
|
const expected = {
|
||||||
|
ageMinimum: null,
|
||||||
|
currentChallenge: null,
|
||||||
|
isLoading: true,
|
||||||
|
isComplete: false,
|
||||||
|
token: null,
|
||||||
|
instance: ImmutableMap(),
|
||||||
|
};
|
||||||
|
|
||||||
expect(reducer(state, action)).toEqual(expected);
|
expect(reducer(state, action)).toMatchObject(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('SET_LOADING with a value', () => {
|
describe('SET_LOADING with a value', () => {
|
||||||
it('sets the state', () => {
|
it('sets the state', () => {
|
||||||
const state = ImmutableMap({ isLoading: true });
|
const state = ImmutableRecord({
|
||||||
|
ageMinimum: null,
|
||||||
|
currentChallenge: null,
|
||||||
|
isLoading: true,
|
||||||
|
isComplete: false,
|
||||||
|
token: null,
|
||||||
|
instance: ImmutableMap<string, any>(),
|
||||||
|
})();
|
||||||
const action = { type: SET_LOADING, value: false };
|
const action = { type: SET_LOADING, value: false };
|
||||||
const expected = ImmutableMap({ isLoading: false });
|
const expected = {
|
||||||
|
ageMinimum: null,
|
||||||
|
currentChallenge: null,
|
||||||
|
isLoading: false,
|
||||||
|
isComplete: false,
|
||||||
|
token: null,
|
||||||
|
instance: ImmutableMap(),
|
||||||
|
};
|
||||||
|
|
||||||
expect(reducer(state, action)).toEqual(expected);
|
expect(reducer(state, action)).toMatchObject(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
import { Map as ImmutableMap, Record as ImmutableRecord, fromJS } from 'immutable';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PEPE_FETCH_INSTANCE_SUCCESS,
|
PEPE_FETCH_INSTANCE_SUCCESS,
|
||||||
|
@ -7,21 +7,24 @@ import {
|
||||||
SET_CHALLENGES_COMPLETE,
|
SET_CHALLENGES_COMPLETE,
|
||||||
SET_LOADING,
|
SET_LOADING,
|
||||||
SET_NEXT_CHALLENGE,
|
SET_NEXT_CHALLENGE,
|
||||||
|
Challenge,
|
||||||
} from '../actions/verification';
|
} from '../actions/verification';
|
||||||
|
|
||||||
const initialState = ImmutableMap({
|
import type { AnyAction } from 'redux';
|
||||||
ageMinimum: null,
|
|
||||||
currentChallenge: null,
|
const ReducerRecord = ImmutableRecord({
|
||||||
|
ageMinimum: null as string | null,
|
||||||
|
currentChallenge: null as Challenge | null,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isComplete: false,
|
isComplete: false as boolean | null,
|
||||||
token: null,
|
token: null as string | null,
|
||||||
instance: ImmutableMap(),
|
instance: ImmutableMap<string, any>(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function verification(state = initialState, action) {
|
export default function verification(state = ReducerRecord(), action: AnyAction) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case PEPE_FETCH_INSTANCE_SUCCESS:
|
case PEPE_FETCH_INSTANCE_SUCCESS:
|
||||||
return state.set('instance', fromJS(action.instance));
|
return state.set('instance', ImmutableMap(fromJS(action.instance)));
|
||||||
case FETCH_CHALLENGES_SUCCESS:
|
case FETCH_CHALLENGES_SUCCESS:
|
||||||
return state
|
return state
|
||||||
.set('ageMinimum', action.ageMinimum)
|
.set('ageMinimum', action.ageMinimum)
|
Loading…
Reference in New Issue