Manage event
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
fe7333ddb0
commit
04b4a57e06
|
@ -6,8 +6,13 @@ import resizeImage from 'soapbox/utils/resize_image';
|
||||||
|
|
||||||
import { importFetchedAccounts, importFetchedStatus } from './importer';
|
import { importFetchedAccounts, importFetchedStatus } from './importer';
|
||||||
import { fetchMedia, uploadMedia } from './media';
|
import { fetchMedia, uploadMedia } from './media';
|
||||||
import { closeModal } from './modals';
|
import { closeModal, openModal } from './modals';
|
||||||
import snackbar from './snackbar';
|
import snackbar from './snackbar';
|
||||||
|
import {
|
||||||
|
STATUS_FETCH_SOURCE_FAIL,
|
||||||
|
STATUS_FETCH_SOURCE_REQUEST,
|
||||||
|
STATUS_FETCH_SOURCE_SUCCESS,
|
||||||
|
} from './statuses';
|
||||||
|
|
||||||
import type { AxiosError } from 'axios';
|
import type { AxiosError } from 'axios';
|
||||||
import type { AppDispatch, RootState } from 'soapbox/store';
|
import type { AppDispatch, RootState } from 'soapbox/store';
|
||||||
|
@ -17,13 +22,13 @@ const LOCATION_SEARCH_REQUEST = 'LOCATION_SEARCH_REQUEST';
|
||||||
const LOCATION_SEARCH_SUCCESS = 'LOCATION_SEARCH_SUCCESS';
|
const LOCATION_SEARCH_SUCCESS = 'LOCATION_SEARCH_SUCCESS';
|
||||||
const LOCATION_SEARCH_FAIL = 'LOCATION_SEARCH_FAIL';
|
const LOCATION_SEARCH_FAIL = 'LOCATION_SEARCH_FAIL';
|
||||||
|
|
||||||
const CREATE_EVENT_NAME_CHANGE = 'CREATE_EVENT_NAME_CHANGE';
|
const EDIT_EVENT_NAME_CHANGE = 'EDIT_EVENT_NAME_CHANGE';
|
||||||
const CREATE_EVENT_DESCRIPTION_CHANGE = 'CREATE_EVENT_DESCRIPTION_CHANGE';
|
const EDIT_EVENT_DESCRIPTION_CHANGE = 'EDIT_EVENT_DESCRIPTION_CHANGE';
|
||||||
const CREATE_EVENT_START_TIME_CHANGE = 'CREATE_EVENT_START_TIME_CHANGE';
|
const EDIT_EVENT_START_TIME_CHANGE = 'EDIT_EVENT_START_TIME_CHANGE';
|
||||||
const CREATE_EVENT_HAS_END_TIME_CHANGE = 'CREATE_EVENT_HAS_END_TIME_CHANGE';
|
const EDIT_EVENT_HAS_END_TIME_CHANGE = 'EDIT_EVENT_HAS_END_TIME_CHANGE';
|
||||||
const CREATE_EVENT_END_TIME_CHANGE = 'CREATE_EVENT_END_TIME_CHANGE';
|
const EDIT_EVENT_END_TIME_CHANGE = 'EDIT_EVENT_END_TIME_CHANGE';
|
||||||
const CREATE_EVENT_APPROVAL_REQUIRED_CHANGE = 'CREATE_EVENT_APPROVAL_REQUIRED_CHANGE';
|
const EDIT_EVENT_APPROVAL_REQUIRED_CHANGE = 'EDIT_EVENT_APPROVAL_REQUIRED_CHANGE';
|
||||||
const CREATE_EVENT_LOCATION_CHANGE = 'CREATE_EVENT_LOCATION_CHANGE';
|
const EDIT_EVENT_LOCATION_CHANGE = 'EDIT_EVENT_LOCATION_CHANGE';
|
||||||
|
|
||||||
const EVENT_BANNER_UPLOAD_REQUEST = 'EVENT_BANNER_UPLOAD_REQUEST';
|
const EVENT_BANNER_UPLOAD_REQUEST = 'EVENT_BANNER_UPLOAD_REQUEST';
|
||||||
const EVENT_BANNER_UPLOAD_PROGRESS = 'EVENT_BANNER_UPLOAD_PROGRESS';
|
const EVENT_BANNER_UPLOAD_PROGRESS = 'EVENT_BANNER_UPLOAD_PROGRESS';
|
||||||
|
@ -59,14 +64,28 @@ const EVENT_PARTICIPATION_REQUESTS_EXPAND_REQUEST = 'EVENT_PARTICIPATION_REQUEST
|
||||||
const EVENT_PARTICIPATION_REQUESTS_EXPAND_SUCCESS = 'EVENT_PARTICIPATION_REQUESTS_EXPAND_SUCCESS';
|
const EVENT_PARTICIPATION_REQUESTS_EXPAND_SUCCESS = 'EVENT_PARTICIPATION_REQUESTS_EXPAND_SUCCESS';
|
||||||
const EVENT_PARTICIPATION_REQUESTS_EXPAND_FAIL = 'EVENT_PARTICIPATION_REQUESTS_EXPAND_FAIL';
|
const EVENT_PARTICIPATION_REQUESTS_EXPAND_FAIL = 'EVENT_PARTICIPATION_REQUESTS_EXPAND_FAIL';
|
||||||
|
|
||||||
|
const EVENT_PARTICIPATION_REQUEST_AUTHORIZE_REQUEST = 'EVENT_PARTICIPATION_REQUEST_AUTHORIZE_REQUEST';
|
||||||
|
const EVENT_PARTICIPATION_REQUEST_AUTHORIZE_SUCCESS = 'EVENT_PARTICIPATION_REQUEST_AUTHORIZE_SUCCESS';
|
||||||
|
const EVENT_PARTICIPATION_REQUEST_AUTHORIZE_FAIL = 'EVENT_PARTICIPATION_REQUEST_AUTHORIZE_FAIL';
|
||||||
|
|
||||||
|
const EVENT_PARTICIPATION_REQUEST_REJECT_REQUEST = 'EVENT_PARTICIPATION_REQUEST_REJECT_REQUEST';
|
||||||
|
const EVENT_PARTICIPATION_REQUEST_REJECT_SUCCESS = 'EVENT_PARTICIPATION_REQUEST_REJECT_SUCCESS';
|
||||||
|
const EVENT_PARTICIPATION_REQUEST_REJECT_FAIL = 'EVENT_PARTICIPATION_REQUEST_REJECT_FAIL';
|
||||||
|
|
||||||
|
const EVENT_COMPOSE_CANCEL = 'EVENT_COMPOSE_CANCEL';
|
||||||
|
|
||||||
|
const EVENT_FORM_SET = 'EVENT_FORM_SET';
|
||||||
|
|
||||||
const noOp = () => new Promise(f => f(undefined));
|
const noOp = () => new Promise(f => f(undefined));
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
exceededImageSizeLimit: { id: 'upload_error.image_size_limit', defaultMessage: 'Image exceeds the current file size limit ({limit})' },
|
exceededImageSizeLimit: { id: 'upload_error.image_size_limit', defaultMessage: 'Image exceeds the current file size limit ({limit})' },
|
||||||
success: { id: 'create_event.submit_success', defaultMessage: 'Your event was created' },
|
success: { id: 'compose_event.submit_success', defaultMessage: 'Your event was created' },
|
||||||
joinSuccess: { id: 'join_event.success', defaultMessage: 'Joined the event' },
|
joinSuccess: { id: 'join_event.success', defaultMessage: 'Joined the event' },
|
||||||
joinRequestSuccess: { id: 'join_event.request_success', defaultMessage: 'Requested to join the event' },
|
joinRequestSuccess: { id: 'join_event.request_success', defaultMessage: 'Requested to join the event' },
|
||||||
view: { id: 'snackbar.view', defaultMessage: 'View' },
|
view: { id: 'snackbar.view', defaultMessage: 'View' },
|
||||||
|
authorized: { id: 'compose_event.participation_requests.authorize_success', defaultMessage: 'User accepted' },
|
||||||
|
rejected: { id: 'compose_event.participation_requests.reject_success', defaultMessage: 'User rejected' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const locationSearch = (query: string, signal?: AbortSignal) =>
|
const locationSearch = (query: string, signal?: AbortSignal) =>
|
||||||
|
@ -81,37 +100,37 @@ const locationSearch = (query: string, signal?: AbortSignal) =>
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeCreateEventName = (value: string) => ({
|
const changeEditEventName = (value: string) => ({
|
||||||
type: CREATE_EVENT_NAME_CHANGE,
|
type: EDIT_EVENT_NAME_CHANGE,
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
|
|
||||||
const changeCreateEventDescription = (value: string) => ({
|
const changeEditEventDescription = (value: string) => ({
|
||||||
type: CREATE_EVENT_DESCRIPTION_CHANGE,
|
type: EDIT_EVENT_DESCRIPTION_CHANGE,
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
|
|
||||||
const changeCreateEventStartTime = (value: Date) => ({
|
const changeEditEventStartTime = (value: Date) => ({
|
||||||
type: CREATE_EVENT_START_TIME_CHANGE,
|
type: EDIT_EVENT_START_TIME_CHANGE,
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
|
|
||||||
const changeCreateEventEndTime = (value: Date) => ({
|
const changeEditEventEndTime = (value: Date) => ({
|
||||||
type: CREATE_EVENT_END_TIME_CHANGE,
|
type: EDIT_EVENT_END_TIME_CHANGE,
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
|
|
||||||
const changeCreateEventHasEndTime = (value: boolean) => ({
|
const changeEditEventHasEndTime = (value: boolean) => ({
|
||||||
type: CREATE_EVENT_HAS_END_TIME_CHANGE,
|
type: EDIT_EVENT_HAS_END_TIME_CHANGE,
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
|
|
||||||
const changeCreateEventApprovalRequired = (value: boolean) => ({
|
const changeEditEventApprovalRequired = (value: boolean) => ({
|
||||||
type: CREATE_EVENT_APPROVAL_REQUIRED_CHANGE,
|
type: EDIT_EVENT_APPROVAL_REQUIRED_CHANGE,
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
|
|
||||||
const changeCreateEventLocation = (value: string | null) =>
|
const changeEditEventLocation = (value: string | null) =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
let location = null;
|
let location = null;
|
||||||
|
|
||||||
|
@ -120,7 +139,7 @@ const changeCreateEventLocation = (value: string | null) =>
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CREATE_EVENT_LOCATION_CHANGE,
|
type: EDIT_EVENT_LOCATION_CHANGE,
|
||||||
value: location,
|
value: location,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -202,15 +221,15 @@ const submitEvent = () =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
const name = state.create_event.name;
|
const name = state.compose_event.name;
|
||||||
const status = state.create_event.status;
|
const status = state.compose_event.status;
|
||||||
const banner = state.create_event.banner;
|
const banner = state.compose_event.banner;
|
||||||
const startTime = state.create_event.start_time;
|
const startTime = state.compose_event.start_time;
|
||||||
const endTime = state.create_event.end_time;
|
const endTime = state.compose_event.end_time;
|
||||||
const joinMode = state.create_event.approval_required ? 'restricted' : 'free';
|
const joinMode = state.compose_event.approval_required ? 'restricted' : 'free';
|
||||||
const location = state.create_event.location;
|
const location = state.compose_event.location;
|
||||||
|
|
||||||
if (!status || !status.length) {
|
if (!name || !name.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +247,7 @@ const submitEvent = () =>
|
||||||
if (location) params.location_id = location.origin_id;
|
if (location) params.location_id = location.origin_id;
|
||||||
|
|
||||||
return api(getState).post('/api/v1/pleroma/events', params).then(({ data }) => {
|
return api(getState).post('/api/v1/pleroma/events', params).then(({ data }) => {
|
||||||
dispatch(closeModal('CREATE_EVENT'));
|
dispatch(closeModal('COMPOSE_EVENT'));
|
||||||
dispatch(importFetchedStatus(data));
|
dispatch(importFetchedStatus(data));
|
||||||
dispatch(submitEventSuccess(data));
|
dispatch(submitEventSuccess(data));
|
||||||
dispatch(snackbar.success(messages.success, messages.view, `/@${data.account.acct}/events/${data.id}`));
|
dispatch(snackbar.success(messages.success, messages.view, `/@${data.account.acct}/events/${data.id}`));
|
||||||
|
@ -261,7 +280,9 @@ const joinEvent = (id: string, participationMessage?: string) =>
|
||||||
|
|
||||||
dispatch(joinEventRequest(status));
|
dispatch(joinEventRequest(status));
|
||||||
|
|
||||||
return api(getState).post(`/api/v1/pleroma/events/${id}/join`, { participationMessage }).then(({ data }) => {
|
return api(getState).post(`/api/v1/pleroma/events/${id}/join`, {
|
||||||
|
participation_message: participationMessage,
|
||||||
|
}).then(({ data }) => {
|
||||||
dispatch(importFetchedStatus(data));
|
dispatch(importFetchedStatus(data));
|
||||||
dispatch(joinEventSuccess(data));
|
dispatch(joinEventSuccess(data));
|
||||||
dispatch(snackbar.success(
|
dispatch(snackbar.success(
|
||||||
|
@ -461,21 +482,108 @@ const expandEventParticipationRequestsFail = (id: string, error: AxiosError) =>
|
||||||
error,
|
error,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const authorizeEventParticipationRequest = (id: string, accountId: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
dispatch(authorizeEventParticipationRequestRequest(id, accountId));
|
||||||
|
|
||||||
|
return api(getState)
|
||||||
|
.post(`/api/v1/pleroma/events/${id}/participation_requests/${accountId}/authorize`)
|
||||||
|
.then(() => {
|
||||||
|
dispatch(authorizeEventParticipationRequestSuccess(id, accountId));
|
||||||
|
dispatch(snackbar.success(messages.authorized));
|
||||||
|
})
|
||||||
|
.catch(error => dispatch(authorizeEventParticipationRequestFail(id, accountId, error)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const authorizeEventParticipationRequestRequest = (id: string, accountId: string) => ({
|
||||||
|
type: EVENT_PARTICIPATION_REQUEST_AUTHORIZE_REQUEST,
|
||||||
|
id,
|
||||||
|
accountId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const authorizeEventParticipationRequestSuccess = (id: string, accountId: string) => ({
|
||||||
|
type: EVENT_PARTICIPATION_REQUEST_AUTHORIZE_SUCCESS,
|
||||||
|
id,
|
||||||
|
accountId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const authorizeEventParticipationRequestFail = (id: string, accountId: string, error: AxiosError) => ({
|
||||||
|
type: EVENT_PARTICIPATION_REQUEST_AUTHORIZE_FAIL,
|
||||||
|
id,
|
||||||
|
accountId,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const rejectEventParticipationRequest = (id: string, accountId: string) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
dispatch(rejectEventParticipationRequestRequest(id, accountId));
|
||||||
|
|
||||||
|
return api(getState)
|
||||||
|
.post(`/api/v1/pleroma/events/${id}/participation_requests/${accountId}/reject`)
|
||||||
|
.then(() => {
|
||||||
|
dispatch(rejectEventParticipationRequestSuccess(id, accountId));
|
||||||
|
dispatch(snackbar.success(messages.rejected));
|
||||||
|
})
|
||||||
|
.catch(error => dispatch(rejectEventParticipationRequestFail(id, accountId, error)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const rejectEventParticipationRequestRequest = (id: string, accountId: string) => ({
|
||||||
|
type: EVENT_PARTICIPATION_REQUEST_REJECT_REQUEST,
|
||||||
|
id,
|
||||||
|
accountId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const rejectEventParticipationRequestSuccess = (id: string, accountId: string) => ({
|
||||||
|
type: EVENT_PARTICIPATION_REQUEST_REJECT_SUCCESS,
|
||||||
|
id,
|
||||||
|
accountId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const rejectEventParticipationRequestFail = (id: string, accountId: string, error: AxiosError) => ({
|
||||||
|
type: EVENT_PARTICIPATION_REQUEST_REJECT_FAIL,
|
||||||
|
id,
|
||||||
|
accountId,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
const fetchEventIcs = (id: string) =>
|
const fetchEventIcs = (id: string) =>
|
||||||
(dispatch: any, getState: () => RootState) =>
|
(dispatch: any, getState: () => RootState) =>
|
||||||
api(getState).get(`/api/v1/pleroma/events/${id}/ics`);
|
api(getState).get(`/api/v1/pleroma/events/${id}/ics`);
|
||||||
|
|
||||||
|
const cancelEventCompose = () => ({
|
||||||
|
type: EVENT_COMPOSE_CANCEL,
|
||||||
|
});
|
||||||
|
|
||||||
|
const editEvent = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
const status = getState().statuses.get(id)!;
|
||||||
|
|
||||||
|
dispatch({ type: STATUS_FETCH_SOURCE_REQUEST });
|
||||||
|
|
||||||
|
api(getState).get(`/api/v1/statuses/${id}/source`).then(response => {
|
||||||
|
dispatch({ type: STATUS_FETCH_SOURCE_SUCCESS });
|
||||||
|
dispatch({
|
||||||
|
type: EVENT_FORM_SET,
|
||||||
|
status,
|
||||||
|
text: response.data.text,
|
||||||
|
location: response.data.location,
|
||||||
|
});
|
||||||
|
dispatch(openModal('COMPOSE_EVENT'));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch({ type: STATUS_FETCH_SOURCE_FAIL, error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
LOCATION_SEARCH_REQUEST,
|
LOCATION_SEARCH_REQUEST,
|
||||||
LOCATION_SEARCH_SUCCESS,
|
LOCATION_SEARCH_SUCCESS,
|
||||||
LOCATION_SEARCH_FAIL,
|
LOCATION_SEARCH_FAIL,
|
||||||
CREATE_EVENT_NAME_CHANGE,
|
EDIT_EVENT_NAME_CHANGE,
|
||||||
CREATE_EVENT_DESCRIPTION_CHANGE,
|
EDIT_EVENT_DESCRIPTION_CHANGE,
|
||||||
CREATE_EVENT_START_TIME_CHANGE,
|
EDIT_EVENT_START_TIME_CHANGE,
|
||||||
CREATE_EVENT_END_TIME_CHANGE,
|
EDIT_EVENT_END_TIME_CHANGE,
|
||||||
CREATE_EVENT_HAS_END_TIME_CHANGE,
|
EDIT_EVENT_HAS_END_TIME_CHANGE,
|
||||||
CREATE_EVENT_APPROVAL_REQUIRED_CHANGE,
|
EDIT_EVENT_APPROVAL_REQUIRED_CHANGE,
|
||||||
CREATE_EVENT_LOCATION_CHANGE,
|
EDIT_EVENT_LOCATION_CHANGE,
|
||||||
EVENT_BANNER_UPLOAD_REQUEST,
|
EVENT_BANNER_UPLOAD_REQUEST,
|
||||||
EVENT_BANNER_UPLOAD_PROGRESS,
|
EVENT_BANNER_UPLOAD_PROGRESS,
|
||||||
EVENT_BANNER_UPLOAD_SUCCESS,
|
EVENT_BANNER_UPLOAD_SUCCESS,
|
||||||
|
@ -502,14 +610,22 @@ export {
|
||||||
EVENT_PARTICIPATION_REQUESTS_EXPAND_REQUEST,
|
EVENT_PARTICIPATION_REQUESTS_EXPAND_REQUEST,
|
||||||
EVENT_PARTICIPATION_REQUESTS_EXPAND_SUCCESS,
|
EVENT_PARTICIPATION_REQUESTS_EXPAND_SUCCESS,
|
||||||
EVENT_PARTICIPATION_REQUESTS_EXPAND_FAIL,
|
EVENT_PARTICIPATION_REQUESTS_EXPAND_FAIL,
|
||||||
|
EVENT_PARTICIPATION_REQUEST_AUTHORIZE_REQUEST,
|
||||||
|
EVENT_PARTICIPATION_REQUEST_AUTHORIZE_SUCCESS,
|
||||||
|
EVENT_PARTICIPATION_REQUEST_AUTHORIZE_FAIL,
|
||||||
|
EVENT_PARTICIPATION_REQUEST_REJECT_REQUEST,
|
||||||
|
EVENT_PARTICIPATION_REQUEST_REJECT_SUCCESS,
|
||||||
|
EVENT_PARTICIPATION_REQUEST_REJECT_FAIL,
|
||||||
|
EVENT_COMPOSE_CANCEL,
|
||||||
|
EVENT_FORM_SET,
|
||||||
locationSearch,
|
locationSearch,
|
||||||
changeCreateEventName,
|
changeEditEventName,
|
||||||
changeCreateEventDescription,
|
changeEditEventDescription,
|
||||||
changeCreateEventStartTime,
|
changeEditEventStartTime,
|
||||||
changeCreateEventEndTime,
|
changeEditEventEndTime,
|
||||||
changeCreateEventHasEndTime,
|
changeEditEventHasEndTime,
|
||||||
changeCreateEventApprovalRequired,
|
changeEditEventApprovalRequired,
|
||||||
changeCreateEventLocation,
|
changeEditEventLocation,
|
||||||
uploadEventBanner,
|
uploadEventBanner,
|
||||||
uploadEventBannerRequest,
|
uploadEventBannerRequest,
|
||||||
uploadEventBannerProgress,
|
uploadEventBannerProgress,
|
||||||
|
@ -544,5 +660,15 @@ export {
|
||||||
expandEventParticipationRequestsRequest,
|
expandEventParticipationRequestsRequest,
|
||||||
expandEventParticipationRequestsSuccess,
|
expandEventParticipationRequestsSuccess,
|
||||||
expandEventParticipationRequestsFail,
|
expandEventParticipationRequestsFail,
|
||||||
|
authorizeEventParticipationRequest,
|
||||||
|
authorizeEventParticipationRequestRequest,
|
||||||
|
authorizeEventParticipationRequestSuccess,
|
||||||
|
authorizeEventParticipationRequestFail,
|
||||||
|
rejectEventParticipationRequest,
|
||||||
|
rejectEventParticipationRequestRequest,
|
||||||
|
rejectEventParticipationRequestSuccess,
|
||||||
|
rejectEventParticipationRequestFail,
|
||||||
fetchEventIcs,
|
fetchEventIcs,
|
||||||
|
cancelEventCompose,
|
||||||
|
editEvent,
|
||||||
};
|
};
|
||||||
|
|
|
@ -63,6 +63,7 @@ interface IAccount {
|
||||||
withRelationship?: boolean,
|
withRelationship?: boolean,
|
||||||
showEdit?: boolean,
|
showEdit?: boolean,
|
||||||
emoji?: string,
|
emoji?: string,
|
||||||
|
note?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const Account = ({
|
const Account = ({
|
||||||
|
@ -86,6 +87,7 @@ const Account = ({
|
||||||
withRelationship = true,
|
withRelationship = true,
|
||||||
showEdit = false,
|
showEdit = false,
|
||||||
emoji,
|
emoji,
|
||||||
|
note,
|
||||||
}: IAccount) => {
|
}: IAccount) => {
|
||||||
const overflowRef = React.useRef<HTMLDivElement>(null);
|
const overflowRef = React.useRef<HTMLDivElement>(null);
|
||||||
const actionRef = React.useRef<HTMLDivElement>(null);
|
const actionRef = React.useRef<HTMLDivElement>(null);
|
||||||
|
@ -163,7 +165,7 @@ const Account = ({
|
||||||
return (
|
return (
|
||||||
<div data-testid='account' className='flex-shrink-0 group block w-full' ref={overflowRef}>
|
<div data-testid='account' className='flex-shrink-0 group block w-full' ref={overflowRef}>
|
||||||
<HStack alignItems={actionAlignment} justifyContent='between'>
|
<HStack alignItems={actionAlignment} justifyContent='between'>
|
||||||
<HStack alignItems={withAccountNote ? 'top' : 'center'} space={3}>
|
<HStack alignItems={withAccountNote || note ? 'top' : 'center'} space={3}>
|
||||||
<ProfilePopper
|
<ProfilePopper
|
||||||
condition={showProfileHoverCard}
|
condition={showProfileHoverCard}
|
||||||
wrapper={(children) => <HoverRefWrapper className='relative' accountId={account.id} inline>{children}</HoverRefWrapper>}
|
wrapper={(children) => <HoverRefWrapper className='relative' accountId={account.id} inline>{children}</HoverRefWrapper>}
|
||||||
|
@ -206,7 +208,7 @@ const Account = ({
|
||||||
</LinkEl>
|
</LinkEl>
|
||||||
</ProfilePopper>
|
</ProfilePopper>
|
||||||
|
|
||||||
<Stack space={withAccountNote ? 1 : 0}>
|
<Stack space={withAccountNote || note ? 1 : 0}>
|
||||||
<HStack alignItems='center' space={1} style={style}>
|
<HStack alignItems='center' space={1} style={style}>
|
||||||
<Text theme='muted' size='sm' truncate>@{username}</Text>
|
<Text theme='muted' size='sm' truncate>@{username}</Text>
|
||||||
|
|
||||||
|
@ -237,7 +239,14 @@ const Account = ({
|
||||||
) : null}
|
) : null}
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|
||||||
{withAccountNote && (
|
{note ? (
|
||||||
|
<Text
|
||||||
|
size='sm'
|
||||||
|
className='mr-2'
|
||||||
|
>
|
||||||
|
{note}
|
||||||
|
</Text>
|
||||||
|
) : withAccountNote && (
|
||||||
<Text
|
<Text
|
||||||
size='sm'
|
size='sm'
|
||||||
dangerouslySetInnerHTML={{ __html: account.note_emojified }}
|
dangerouslySetInnerHTML={{ __html: account.note_emojified }}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { fetchEventIcs } from 'soapbox/actions/events';
|
import { editEvent, fetchEventIcs } from 'soapbox/actions/events';
|
||||||
import { openModal } from 'soapbox/actions/modals';
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
import { deleteStatusModal, toggleStatusSensitivityModal } from 'soapbox/actions/moderation';
|
import { deleteStatusModal, toggleStatusSensitivityModal } from 'soapbox/actions/moderation';
|
||||||
import Icon from 'soapbox/components/icon';
|
import Icon from 'soapbox/components/icon';
|
||||||
|
@ -159,9 +159,7 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
|
||||||
const handleManageClick: React.MouseEventHandler = e => {
|
const handleManageClick: React.MouseEventHandler = e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
dispatch(openModal('MANAGE_EVENT', {
|
dispatch(editEvent(status.id));
|
||||||
statusId: status.id,
|
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleParticipantsClick: React.MouseEventHandler = e => {
|
const handleParticipantsClick: React.MouseEventHandler = e => {
|
||||||
|
@ -228,7 +226,6 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
|
||||||
size='sm'
|
size='sm'
|
||||||
theme='secondary'
|
theme='secondary'
|
||||||
onClick={handleManageClick}
|
onClick={handleManageClick}
|
||||||
to={`/@${account.acct}/events/${status.id}`}
|
|
||||||
>
|
>
|
||||||
<FormattedMessage id='event.manage' defaultMessage='Manage' />
|
<FormattedMessage id='event.manage' defaultMessage='Manage' />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -53,6 +53,9 @@ const icons: Record<NotificationType, string> = {
|
||||||
'pleroma:emoji_reaction': require('@tabler/icons/mood-happy.svg'),
|
'pleroma:emoji_reaction': require('@tabler/icons/mood-happy.svg'),
|
||||||
user_approved: require('@tabler/icons/user-plus.svg'),
|
user_approved: require('@tabler/icons/user-plus.svg'),
|
||||||
update: require('@tabler/icons/pencil.svg'),
|
update: require('@tabler/icons/pencil.svg'),
|
||||||
|
'pleroma:event_reminder': require('@tabler/icons/calendar-time.svg'),
|
||||||
|
'pleroma:participation_request': require('@tabler/icons/calendar-event.svg'),
|
||||||
|
'pleroma:participation_accepted': require('@tabler/icons/calendar-event.svg'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const messages: Record<NotificationType, MessageDescriptor> = defineMessages({
|
const messages: Record<NotificationType, MessageDescriptor> = defineMessages({
|
||||||
|
@ -104,6 +107,18 @@ const messages: Record<NotificationType, MessageDescriptor> = defineMessages({
|
||||||
id: 'notification.update',
|
id: 'notification.update',
|
||||||
defaultMessage: '{name} edited a post you interacted with',
|
defaultMessage: '{name} edited a post you interacted with',
|
||||||
},
|
},
|
||||||
|
'pleroma:event_reminder': {
|
||||||
|
id: 'notification.pleroma:event_reminder',
|
||||||
|
defaultMessage: 'An event you are participating in starts soon',
|
||||||
|
},
|
||||||
|
'pleroma:participation_request': {
|
||||||
|
id: 'notification.pleroma:participation_request',
|
||||||
|
defaultMessage: '{name} wants to join your event',
|
||||||
|
},
|
||||||
|
'pleroma:participation_accepted': {
|
||||||
|
id: 'notification.pleroma:participation_accepted',
|
||||||
|
defaultMessage: 'You were accepted to join the event',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const buildMessage = (
|
const buildMessage = (
|
||||||
|
@ -302,6 +317,9 @@ const Notification: React.FC<INotificaton> = (props) => {
|
||||||
case 'poll':
|
case 'poll':
|
||||||
case 'update':
|
case 'update':
|
||||||
case 'pleroma:emoji_reaction':
|
case 'pleroma:emoji_reaction':
|
||||||
|
case 'pleroma:event_reminder':
|
||||||
|
case 'pleroma:participation_accepted':
|
||||||
|
case 'pleroma:participation_request':
|
||||||
return status && typeof status === 'object' ? (
|
return status && typeof status === 'object' ? (
|
||||||
<StatusContainer
|
<StatusContainer
|
||||||
id={status.id}
|
id={status.id}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { Button } from 'soapbox/components/ui';
|
||||||
|
|
||||||
const ComposeButton = () => {
|
const ComposeButton = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const onOpenCompose = () => dispatch(openModal('CREATE_EVENT'));
|
const onOpenCompose = () => dispatch(openModal('COMPOSE_EVENT'));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-4'>
|
<div className='mt-4'>
|
||||||
|
|
|
@ -32,7 +32,7 @@ import {
|
||||||
CompareHistoryModal,
|
CompareHistoryModal,
|
||||||
VerifySmsModal,
|
VerifySmsModal,
|
||||||
FamiliarFollowersModal,
|
FamiliarFollowersModal,
|
||||||
CreateEventModal,
|
ComposeEventModal,
|
||||||
JoinEventModal,
|
JoinEventModal,
|
||||||
AccountModerationModal,
|
AccountModerationModal,
|
||||||
EventMapModal,
|
EventMapModal,
|
||||||
|
@ -74,7 +74,7 @@ const MODAL_COMPONENTS = {
|
||||||
'COMPARE_HISTORY': CompareHistoryModal,
|
'COMPARE_HISTORY': CompareHistoryModal,
|
||||||
'VERIFY_SMS': VerifySmsModal,
|
'VERIFY_SMS': VerifySmsModal,
|
||||||
'FAMILIAR_FOLLOWERS': FamiliarFollowersModal,
|
'FAMILIAR_FOLLOWERS': FamiliarFollowersModal,
|
||||||
'CREATE_EVENT': CreateEventModal,
|
'COMPOSE_EVENT': ComposeEventModal,
|
||||||
'JOIN_EVENT': JoinEventModal,
|
'JOIN_EVENT': JoinEventModal,
|
||||||
'ACCOUNT_MODERATION': AccountModerationModal,
|
'ACCOUNT_MODERATION': AccountModerationModal,
|
||||||
'EVENT_MAP': EventMapModal,
|
'EVENT_MAP': EventMapModal,
|
||||||
|
|
|
@ -0,0 +1,320 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
import Toggle from 'react-toggle';
|
||||||
|
|
||||||
|
import {
|
||||||
|
changeEditEventApprovalRequired,
|
||||||
|
changeEditEventDescription,
|
||||||
|
changeEditEventEndTime,
|
||||||
|
changeEditEventHasEndTime,
|
||||||
|
changeEditEventName,
|
||||||
|
changeEditEventStartTime,
|
||||||
|
changeEditEventLocation,
|
||||||
|
uploadEventBanner,
|
||||||
|
undoUploadEventBanner,
|
||||||
|
submitEvent,
|
||||||
|
fetchEventParticipationRequests,
|
||||||
|
rejectEventParticipationRequest,
|
||||||
|
authorizeEventParticipationRequest,
|
||||||
|
} from 'soapbox/actions/events';
|
||||||
|
import { ADDRESS_ICONS } from 'soapbox/components/autosuggest-location';
|
||||||
|
import LocationSearch from 'soapbox/components/location-search';
|
||||||
|
import { Button, Form, FormGroup, HStack, Icon, IconButton, Input, Modal, Spinner, Stack, Tabs, Text, Textarea } from 'soapbox/components/ui';
|
||||||
|
import AccountContainer from 'soapbox/containers/account_container';
|
||||||
|
import { isCurrentOrFutureDate } from 'soapbox/features/compose/components/schedule_form';
|
||||||
|
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
|
||||||
|
import { DatePicker } from 'soapbox/features/ui/util/async-components';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||||
|
|
||||||
|
import UploadButton from './upload-button';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
eventNamePlaceholder: { id: 'compose_event.fields.name_placeholder', defaultMessage: 'Name' },
|
||||||
|
eventDescriptionPlaceholder: { id: 'compose_event.fields.description_placeholder', defaultMessage: 'Description' },
|
||||||
|
eventStartTimePlaceholder: { id: 'compose_event.fields.start_time_placeholder', defaultMessage: 'Event begins on…' },
|
||||||
|
eventEndTimePlaceholder: { id: 'compose_event.fields.end_time_placeholder', defaultMessage: 'Event ends on…' },
|
||||||
|
resetLocation: { id: 'compose_event.reset_location', defaultMessage: 'Reset location' },
|
||||||
|
edit: { id: 'compose_event.tabs.edit', defaultMessage: 'Edit details' },
|
||||||
|
pending: { id: 'compose_event.tabs.pending', defaultMessage: 'Manage requests' },
|
||||||
|
authorize: { id: 'compose_event.participation_requests.authorize', defaultMessage: 'Authorize' },
|
||||||
|
reject: { id: 'compose_event.participation_requests.reject', defaultMessage: 'Reject' },
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
interface IAccount {
|
||||||
|
eventId: string,
|
||||||
|
id: string,
|
||||||
|
participationMessage: string | null,
|
||||||
|
}
|
||||||
|
|
||||||
|
const Account: React.FC<IAccount> = ({ eventId, id, participationMessage }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const handleAuthorize = () => {
|
||||||
|
dispatch(authorizeEventParticipationRequest(eventId, id));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReject = () => {
|
||||||
|
dispatch(rejectEventParticipationRequest(eventId, id));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AccountContainer
|
||||||
|
id={id}
|
||||||
|
note={participationMessage || undefined}
|
||||||
|
action={
|
||||||
|
<HStack space={2}>
|
||||||
|
<Button
|
||||||
|
theme='secondary'
|
||||||
|
size='sm'
|
||||||
|
text={intl.formatMessage(messages.authorize)}
|
||||||
|
onClick={handleAuthorize}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
theme='danger'
|
||||||
|
size='sm'
|
||||||
|
text={intl.formatMessage(messages.reject)}
|
||||||
|
onClick={handleReject}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IComposeEventModal {
|
||||||
|
onClose: (type?: string) => void,
|
||||||
|
}
|
||||||
|
|
||||||
|
const ComposeEventModal: React.FC<IComposeEventModal> = ({ onClose }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const [tab, setTab] = useState<'edit' | 'pending'>('edit');
|
||||||
|
|
||||||
|
const banner = useAppSelector((state) => state.compose_event.banner);
|
||||||
|
const isUploading = useAppSelector((state) => state.compose_event.is_uploading);
|
||||||
|
|
||||||
|
const name = useAppSelector((state) => state.compose_event.name);
|
||||||
|
const description = useAppSelector((state) => state.compose_event.status);
|
||||||
|
const startTime = useAppSelector((state) => state.compose_event.start_time);
|
||||||
|
const endTime = useAppSelector((state) => state.compose_event.end_time);
|
||||||
|
const approvalRequired = useAppSelector((state) => state.compose_event.approval_required);
|
||||||
|
const location = useAppSelector((state) => state.compose_event.location);
|
||||||
|
|
||||||
|
const id = useAppSelector((state) => state.compose_event.id);
|
||||||
|
|
||||||
|
const isSubmitting = useAppSelector((state) => state.compose_event.is_submitting);
|
||||||
|
|
||||||
|
const onChangeName: React.ChangeEventHandler<HTMLInputElement> = ({ target }) => {
|
||||||
|
dispatch(changeEditEventName(target.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChangeDescription: React.ChangeEventHandler<HTMLTextAreaElement> = ({ target }) => {
|
||||||
|
dispatch(changeEditEventDescription(target.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChangeStartTime = (date: Date) => {
|
||||||
|
dispatch(changeEditEventStartTime(date));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChangeEndTime = (date: Date) => {
|
||||||
|
dispatch(changeEditEventEndTime(date));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChangeHasEndTime: React.ChangeEventHandler<HTMLInputElement> = ({ target }) => {
|
||||||
|
dispatch(changeEditEventHasEndTime(target.checked));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChangeApprovalRequired: React.ChangeEventHandler<HTMLInputElement> = ({ target }) => {
|
||||||
|
dispatch(changeEditEventApprovalRequired(target.checked));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChangeLocation = (value: string | null) => {
|
||||||
|
dispatch(changeEditEventLocation(value));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClickClose = () => {
|
||||||
|
onClose('COMPOSE_EVENT');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFiles = (files: FileList) => {
|
||||||
|
dispatch(uploadEventBanner(files[0], intl));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClearBanner = () => {
|
||||||
|
dispatch(undoUploadEventBanner());
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
dispatch(submitEvent());
|
||||||
|
};
|
||||||
|
|
||||||
|
const accounts = useAppSelector((state) => state.user_lists.event_participation_requests.get(id!)?.items);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) dispatch(fetchEventParticipationRequests(id));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const renderLocation = () => location && (
|
||||||
|
<HStack className='h-[38px] text-gray-700 dark:text-gray-500' alignItems='center' space={2}>
|
||||||
|
<Icon src={ADDRESS_ICONS[location.type] || require('@tabler/icons/map-pin.svg')} />
|
||||||
|
<Stack className='flex-grow'>
|
||||||
|
<Text>{location.description}</Text>
|
||||||
|
<Text theme='muted' size='xs'>{[location.street, location.locality, location.country].filter(val => val.trim()).join(' · ')}</Text>
|
||||||
|
</Stack>
|
||||||
|
<IconButton title={intl.formatMessage(messages.resetLocation)} src={require('@tabler/icons/x.svg')} onClick={() => onChangeLocation(null)} />
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderTabs = () => {
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
text: intl.formatMessage(messages.edit),
|
||||||
|
action: () => setTab('edit'),
|
||||||
|
name: 'edit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: intl.formatMessage(messages.pending),
|
||||||
|
action: () => setTab('pending'),
|
||||||
|
name: 'pending',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return <Tabs items={items} activeItem={tab} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={id
|
||||||
|
? <FormattedMessage id='navigation_bar.compose_event' defaultMessage='Manage event' />
|
||||||
|
: <FormattedMessage id='navigation_bar.create_event' defaultMessage='Create new event' />}
|
||||||
|
confirmationAction={tab === 'edit' ? handleSubmit : undefined}
|
||||||
|
confirmationText={id
|
||||||
|
? <FormattedMessage id='compose_event.update' defaultMessage='Update' />
|
||||||
|
: <FormattedMessage id='compose_event.create' defaultMessage='Create' />}
|
||||||
|
confirmationDisabled={isSubmitting}
|
||||||
|
onClose={onClickClose}
|
||||||
|
>
|
||||||
|
<Stack space={2}>
|
||||||
|
{id && renderTabs()}
|
||||||
|
{tab === 'edit' ? (
|
||||||
|
<Form>
|
||||||
|
<FormGroup
|
||||||
|
labelText={<FormattedMessage id='compose_event.fields.banner_label' defaultMessage='Event banner' />}
|
||||||
|
>
|
||||||
|
<div className='flex items-center justify-center bg-gray-200 dark:bg-gray-900/50 rounded-lg text-black dark:text-white sm:shadow dark:sm:shadow-inset overflow-hidden h-24 sm:h-32 relative'>
|
||||||
|
{banner ? (
|
||||||
|
<>
|
||||||
|
<img className='h-full w-full object-cover' src={banner.url} alt='' />
|
||||||
|
<IconButton className='absolute top-2 right-2' src={require('@tabler/icons/x.svg')} onClick={handleClearBanner} />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<UploadButton disabled={isUploading} onSelectFile={handleFiles} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
labelText={<FormattedMessage id='compose_event.fields.name_label' defaultMessage='Event name' />}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
type='text'
|
||||||
|
placeholder={intl.formatMessage(messages.eventNamePlaceholder)}
|
||||||
|
value={name}
|
||||||
|
onChange={onChangeName}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
labelText={<FormattedMessage id='compose_event.fields.description_label' defaultMessage='Event description' />}
|
||||||
|
hintText={<FormattedMessage id='compose_event.fields.description_hint' defaultMessage='Markdown syntax is supported' />}
|
||||||
|
>
|
||||||
|
<Textarea
|
||||||
|
autoComplete='off'
|
||||||
|
placeholder={intl.formatMessage(messages.eventDescriptionPlaceholder)}
|
||||||
|
value={description}
|
||||||
|
onChange={onChangeDescription}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
labelText={<FormattedMessage id='compose_event.fields.location_label' defaultMessage='Event location' />}
|
||||||
|
>
|
||||||
|
{location ? renderLocation() : (
|
||||||
|
<LocationSearch
|
||||||
|
onSelected={onChangeLocation}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
labelText={<FormattedMessage id='compose_event.fields.start_time_label' defaultMessage='Event start date' />}
|
||||||
|
>
|
||||||
|
<BundleContainer fetchComponent={DatePicker}>
|
||||||
|
{Component => (<Component
|
||||||
|
showTimeSelect
|
||||||
|
dateFormat='MMMM d, yyyy h:mm aa'
|
||||||
|
timeIntervals={15}
|
||||||
|
wrapperClassName='react-datepicker-wrapper'
|
||||||
|
placeholderText={intl.formatMessage(messages.eventStartTimePlaceholder)}
|
||||||
|
filterDate={isCurrentOrFutureDate}
|
||||||
|
selected={startTime}
|
||||||
|
onChange={onChangeStartTime}
|
||||||
|
/>)}
|
||||||
|
</BundleContainer>
|
||||||
|
</FormGroup>
|
||||||
|
<HStack alignItems='center' space={2}>
|
||||||
|
<Toggle
|
||||||
|
icons={false}
|
||||||
|
checked={!!endTime}
|
||||||
|
onChange={onChangeHasEndTime}
|
||||||
|
/>
|
||||||
|
<Text tag='span' theme='muted'>
|
||||||
|
<FormattedMessage id='compose_event.fields.has_end_time' defaultMessage='The event has end date' />
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
{endTime && (
|
||||||
|
<FormGroup
|
||||||
|
labelText={<FormattedMessage id='compose_event.fields.end_time_label' defaultMessage='Event end date' />}
|
||||||
|
>
|
||||||
|
<BundleContainer fetchComponent={DatePicker}>
|
||||||
|
{Component => (<Component
|
||||||
|
showTimeSelect
|
||||||
|
dateFormat='MMMM d, yyyy h:mm aa'
|
||||||
|
timeIntervals={15}
|
||||||
|
wrapperClassName='react-datepicker-wrapper'
|
||||||
|
placeholderText={intl.formatMessage(messages.eventEndTimePlaceholder)}
|
||||||
|
filterDate={isCurrentOrFutureDate}
|
||||||
|
selected={endTime}
|
||||||
|
onChange={onChangeEndTime}
|
||||||
|
/>)}
|
||||||
|
</BundleContainer>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
<HStack alignItems='center' space={2}>
|
||||||
|
<Toggle
|
||||||
|
icons={false}
|
||||||
|
checked={approvalRequired}
|
||||||
|
onChange={onChangeApprovalRequired}
|
||||||
|
/>
|
||||||
|
<Text tag='span' theme='muted'>
|
||||||
|
<FormattedMessage id='compose_event.fields.approval_required' defaultMessage='I want to approve participation requests manually' />
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
</Form>
|
||||||
|
) : accounts ? (
|
||||||
|
<Stack space={3}>
|
||||||
|
{accounts.size > 0 ? (
|
||||||
|
accounts.map(({ account, participation_message }) =>
|
||||||
|
<Account key={account} eventId={id!} id={account} participationMessage={participation_message} />,
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<FormattedMessage id='empty_column.event_participant_requests' defaultMessage='There are no pending event participation requests.' />
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
) : <Spinner />}
|
||||||
|
</Stack>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ComposeEventModal;
|
|
@ -7,7 +7,7 @@ import { useAppSelector } from 'soapbox/hooks';
|
||||||
import type { List as ImmutableList } from 'immutable';
|
import type { List as ImmutableList } from 'immutable';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
upload: { id: 'create_event.upload_banner', defaultMessage: 'Upload event banner' },
|
upload: { id: 'compose_event.upload_banner', defaultMessage: 'Upload event banner' },
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IUploadButton {
|
interface IUploadButton {
|
|
@ -1,223 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
|
||||||
import Toggle from 'react-toggle';
|
|
||||||
|
|
||||||
import {
|
|
||||||
changeCreateEventApprovalRequired,
|
|
||||||
changeCreateEventDescription,
|
|
||||||
changeCreateEventEndTime,
|
|
||||||
changeCreateEventHasEndTime,
|
|
||||||
changeCreateEventName,
|
|
||||||
changeCreateEventStartTime,
|
|
||||||
changeCreateEventLocation,
|
|
||||||
uploadEventBanner,
|
|
||||||
undoUploadEventBanner,
|
|
||||||
submitEvent,
|
|
||||||
} from 'soapbox/actions/events';
|
|
||||||
import { ADDRESS_ICONS } from 'soapbox/components/autosuggest-location';
|
|
||||||
import LocationSearch from 'soapbox/components/location-search';
|
|
||||||
import { Form, FormGroup, HStack, Icon, IconButton, Input, Modal, Stack, Text, Textarea } from 'soapbox/components/ui';
|
|
||||||
import { isCurrentOrFutureDate } from 'soapbox/features/compose/components/schedule_form';
|
|
||||||
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
|
|
||||||
import { DatePicker } from 'soapbox/features/ui/util/async-components';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
|
||||||
|
|
||||||
import UploadButton from './upload-button';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
eventNamePlaceholder: { id: 'create_event.fields.name_placeholder', defaultMessage: 'Name' },
|
|
||||||
eventDescriptionPlaceholder: { id: 'create_event.fields.description_placeholder', defaultMessage: 'Description' },
|
|
||||||
eventStartTimePlaceholder: { id: 'create_event.fields.start_time_placeholder', defaultMessage: 'Event begins on…' },
|
|
||||||
eventEndTimePlaceholder: { id: 'create_event.fields.end_time_placeholder', defaultMessage: 'Event ends on…' },
|
|
||||||
resetLocation: { id: 'create_event.reset_location', defaultMessage: 'Reset location' },
|
|
||||||
});
|
|
||||||
|
|
||||||
interface ICreateEventModal {
|
|
||||||
onClose: (type?: string) => void,
|
|
||||||
}
|
|
||||||
|
|
||||||
const CreateEventModal: React.FC<ICreateEventModal> = ({ onClose }) => {
|
|
||||||
const intl = useIntl();
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
|
|
||||||
const banner = useAppSelector((state) => state.create_event.banner);
|
|
||||||
const isUploading = useAppSelector((state) => state.create_event.is_uploading);
|
|
||||||
|
|
||||||
const name = useAppSelector((state) => state.create_event.name);
|
|
||||||
const description = useAppSelector((state) => state.create_event.status);
|
|
||||||
const startTime = useAppSelector((state) => state.create_event.start_time);
|
|
||||||
const endTime = useAppSelector((state) => state.create_event.end_time);
|
|
||||||
const approvalRequired = useAppSelector((state) => state.create_event.approval_required);
|
|
||||||
const location = useAppSelector((state) => state.create_event.location);
|
|
||||||
|
|
||||||
const isSubmitting = useAppSelector((state) => state.create_event.is_submitting);
|
|
||||||
|
|
||||||
const onChangeName: React.ChangeEventHandler<HTMLInputElement> = ({ target }) => {
|
|
||||||
dispatch(changeCreateEventName(target.value));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onChangeDescription: React.ChangeEventHandler<HTMLTextAreaElement> = ({ target }) => {
|
|
||||||
dispatch(changeCreateEventDescription(target.value));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onChangeStartTime = (date: Date) => {
|
|
||||||
dispatch(changeCreateEventStartTime(date));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onChangeEndTime = (date: Date) => {
|
|
||||||
dispatch(changeCreateEventEndTime(date));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onChangeHasEndTime: React.ChangeEventHandler<HTMLInputElement> = ({ target }) => {
|
|
||||||
dispatch(changeCreateEventHasEndTime(target.checked));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onChangeApprovalRequired: React.ChangeEventHandler<HTMLInputElement> = ({ target }) => {
|
|
||||||
dispatch(changeCreateEventApprovalRequired(target.checked));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onChangeLocation = (value: string | null) => {
|
|
||||||
dispatch(changeCreateEventLocation(value));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onClickClose = () => {
|
|
||||||
onClose('CREATE_EVENT');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFiles = (files: FileList) => {
|
|
||||||
dispatch(uploadEventBanner(files[0], intl));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClearBanner = () => {
|
|
||||||
dispatch(undoUploadEventBanner());
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
dispatch(submitEvent());
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderLocation = () => location && (
|
|
||||||
<HStack className='h-[38px] text-gray-700 dark:text-gray-500' alignItems='center' space={2}>
|
|
||||||
<Icon src={ADDRESS_ICONS[location.type] || require('@tabler/icons/map-pin.svg')} />
|
|
||||||
<Stack className='flex-grow'>
|
|
||||||
<Text>{location.description}</Text>
|
|
||||||
<Text theme='muted' size='xs'>{[location.street, location.locality, location.country].filter(val => val.trim()).join(' · ')}</Text>
|
|
||||||
</Stack>
|
|
||||||
<IconButton title={intl.formatMessage(messages.resetLocation)} src={require('@tabler/icons/x.svg')} onClick={() => onChangeLocation(null)} />
|
|
||||||
</HStack>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title={<FormattedMessage id='navigation_bar.create_event' defaultMessage='Create new event' />}
|
|
||||||
confirmationAction={handleSubmit}
|
|
||||||
confirmationText={<FormattedMessage id='create_event.create' defaultMessage='Create' />}
|
|
||||||
confirmationDisabled={isSubmitting}
|
|
||||||
onClose={onClickClose}
|
|
||||||
>
|
|
||||||
<Form>
|
|
||||||
<FormGroup
|
|
||||||
labelText={<FormattedMessage id='create_event.fields.banner_label' defaultMessage='Event banner' />}
|
|
||||||
>
|
|
||||||
<div className='flex items-center justify-center bg-gray-200 dark:bg-gray-900/50 rounded-lg text-black dark:text-white sm:shadow dark:sm:shadow-inset overflow-hidden h-24 sm:h-32 relative'>
|
|
||||||
{banner ? (
|
|
||||||
<>
|
|
||||||
<img className='h-full w-full object-cover' src={banner.url} alt='' />
|
|
||||||
<IconButton className='absolute top-2 right-2' src={require('@tabler/icons/x.svg')} onClick={handleClearBanner} />
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<UploadButton disabled={isUploading} onSelectFile={handleFiles} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
labelText={<FormattedMessage id='create_event.fields.name_label' defaultMessage='Event name' />}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
type='text'
|
|
||||||
placeholder={intl.formatMessage(messages.eventNamePlaceholder)}
|
|
||||||
value={name}
|
|
||||||
onChange={onChangeName}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
labelText={<FormattedMessage id='create_event.fields.description_label' defaultMessage='Event description' />}
|
|
||||||
hintText={<FormattedMessage id='create_event.fields.description_hint' defaultMessage='Markdown syntax is supported' />}
|
|
||||||
>
|
|
||||||
<Textarea
|
|
||||||
autoComplete='off'
|
|
||||||
placeholder={intl.formatMessage(messages.eventDescriptionPlaceholder)}
|
|
||||||
value={description}
|
|
||||||
onChange={onChangeDescription}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
labelText={<FormattedMessage id='create_event.fields.location_label' defaultMessage='Event location' />}
|
|
||||||
>
|
|
||||||
{location ? renderLocation() : (
|
|
||||||
<LocationSearch
|
|
||||||
onSelected={onChangeLocation}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
labelText={<FormattedMessage id='create_event.fields.start_time_label' defaultMessage='Event start date' />}
|
|
||||||
>
|
|
||||||
<BundleContainer fetchComponent={DatePicker}>
|
|
||||||
{Component => (<Component
|
|
||||||
showTimeSelect
|
|
||||||
dateFormat='MMMM d, yyyy h:mm aa'
|
|
||||||
timeIntervals={15}
|
|
||||||
wrapperClassName='react-datepicker-wrapper'
|
|
||||||
placeholderText={intl.formatMessage(messages.eventStartTimePlaceholder)}
|
|
||||||
filterDate={isCurrentOrFutureDate}
|
|
||||||
selected={startTime}
|
|
||||||
onChange={onChangeStartTime}
|
|
||||||
/>)}
|
|
||||||
</BundleContainer>
|
|
||||||
</FormGroup>
|
|
||||||
<HStack alignItems='center' space={2}>
|
|
||||||
<Toggle
|
|
||||||
icons={false}
|
|
||||||
checked={!!endTime}
|
|
||||||
onChange={onChangeHasEndTime}
|
|
||||||
/>
|
|
||||||
<Text tag='span' theme='muted'>
|
|
||||||
<FormattedMessage id='create_event.fields.has_end_time' defaultMessage='The event has end date' />
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
|
||||||
{endTime && (
|
|
||||||
<FormGroup
|
|
||||||
labelText={<FormattedMessage id='create_event.fields.end_time_label' defaultMessage='Event end date' />}
|
|
||||||
>
|
|
||||||
<BundleContainer fetchComponent={DatePicker}>
|
|
||||||
{Component => (<Component
|
|
||||||
showTimeSelect
|
|
||||||
dateFormat='MMMM d, yyyy h:mm aa'
|
|
||||||
timeIntervals={15}
|
|
||||||
wrapperClassName='react-datepicker-wrapper'
|
|
||||||
placeholderText={intl.formatMessage(messages.eventEndTimePlaceholder)}
|
|
||||||
filterDate={isCurrentOrFutureDate}
|
|
||||||
selected={endTime}
|
|
||||||
onChange={onChangeEndTime}
|
|
||||||
/>)}
|
|
||||||
</BundleContainer>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
<HStack alignItems='center' space={2}>
|
|
||||||
<Toggle
|
|
||||||
icons={false}
|
|
||||||
checked={approvalRequired}
|
|
||||||
onChange={onChangeApprovalRequired}
|
|
||||||
/>
|
|
||||||
<Text tag='span' theme='muted'>
|
|
||||||
<FormattedMessage id='create_event.fields.approval_required' defaultMessage='I want to approve participation requests manually' />
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CreateEventModal;
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { cancelReplyCompose } from 'soapbox/actions/compose';
|
import { cancelReplyCompose } from 'soapbox/actions/compose';
|
||||||
|
import { cancelEventCompose } from 'soapbox/actions/events';
|
||||||
import { closeModal } from 'soapbox/actions/modals';
|
import { closeModal } from 'soapbox/actions/modals';
|
||||||
import { cancelReport } from 'soapbox/actions/reports';
|
import { cancelReport } from 'soapbox/actions/reports';
|
||||||
|
|
||||||
|
@ -24,6 +25,9 @@ const mapDispatchToProps = (dispatch) => ({
|
||||||
case 'COMPOSE':
|
case 'COMPOSE':
|
||||||
dispatch(cancelReplyCompose());
|
dispatch(cancelReplyCompose());
|
||||||
break;
|
break;
|
||||||
|
case 'COMPOSE_EVENT':
|
||||||
|
dispatch(cancelEventCompose());
|
||||||
|
break;
|
||||||
case 'REPORT':
|
case 'REPORT':
|
||||||
dispatch(cancelReport());
|
dispatch(cancelReport());
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -506,8 +506,8 @@ export function AnnouncementsPanel() {
|
||||||
return import(/* webpackChunkName: "features/announcements" */'../../../components/announcements/announcements-panel');
|
return import(/* webpackChunkName: "features/announcements" */'../../../components/announcements/announcements-panel');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CreateEventModal() {
|
export function ComposeEventModal() {
|
||||||
return import(/* webpackChunkName: "features/create_event_modal" */'../components/modals/create-event-modal/create-event-modal');
|
return import(/* webpackChunkName: "features/compose_event_modal" */'../components/modals/compose-event-modal/compose-event-modal');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function JoinEventModal() {
|
export function JoinEventModal() {
|
||||||
|
|
|
@ -2,13 +2,13 @@ import { fromJS, Record as ImmutableRecord } from 'immutable';
|
||||||
import { AnyAction } from 'redux';
|
import { AnyAction } from 'redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CREATE_EVENT_APPROVAL_REQUIRED_CHANGE,
|
EDIT_EVENT_APPROVAL_REQUIRED_CHANGE,
|
||||||
CREATE_EVENT_DESCRIPTION_CHANGE,
|
EDIT_EVENT_DESCRIPTION_CHANGE,
|
||||||
CREATE_EVENT_END_TIME_CHANGE,
|
EDIT_EVENT_END_TIME_CHANGE,
|
||||||
CREATE_EVENT_HAS_END_TIME_CHANGE,
|
EDIT_EVENT_HAS_END_TIME_CHANGE,
|
||||||
CREATE_EVENT_LOCATION_CHANGE,
|
EDIT_EVENT_LOCATION_CHANGE,
|
||||||
CREATE_EVENT_NAME_CHANGE,
|
EDIT_EVENT_NAME_CHANGE,
|
||||||
CREATE_EVENT_START_TIME_CHANGE,
|
EDIT_EVENT_START_TIME_CHANGE,
|
||||||
EVENT_BANNER_UPLOAD_REQUEST,
|
EVENT_BANNER_UPLOAD_REQUEST,
|
||||||
EVENT_BANNER_UPLOAD_PROGRESS,
|
EVENT_BANNER_UPLOAD_PROGRESS,
|
||||||
EVENT_BANNER_UPLOAD_SUCCESS,
|
EVENT_BANNER_UPLOAD_SUCCESS,
|
||||||
|
@ -17,12 +17,15 @@ import {
|
||||||
EVENT_SUBMIT_REQUEST,
|
EVENT_SUBMIT_REQUEST,
|
||||||
EVENT_SUBMIT_SUCCESS,
|
EVENT_SUBMIT_SUCCESS,
|
||||||
EVENT_SUBMIT_FAIL,
|
EVENT_SUBMIT_FAIL,
|
||||||
|
EVENT_COMPOSE_CANCEL,
|
||||||
|
EVENT_FORM_SET,
|
||||||
} from 'soapbox/actions/events';
|
} from 'soapbox/actions/events';
|
||||||
import { normalizeAttachment } from 'soapbox/normalizers';
|
import { normalizeAttachment, normalizeLocation } from 'soapbox/normalizers';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Attachment as AttachmentEntity,
|
Attachment as AttachmentEntity,
|
||||||
Location as LocationEntity,
|
Location as LocationEntity,
|
||||||
|
Status as StatusEntity,
|
||||||
} from 'soapbox/types/entities';
|
} from 'soapbox/types/entities';
|
||||||
|
|
||||||
export const ReducerRecord = ImmutableRecord({
|
export const ReducerRecord = ImmutableRecord({
|
||||||
|
@ -36,6 +39,7 @@ export const ReducerRecord = ImmutableRecord({
|
||||||
progress: 0,
|
progress: 0,
|
||||||
is_uploading: false,
|
is_uploading: false,
|
||||||
is_submitting: false,
|
is_submitting: false,
|
||||||
|
id: null as string | null,
|
||||||
});
|
});
|
||||||
|
|
||||||
type State = ReturnType<typeof ReducerRecord>;
|
type State = ReturnType<typeof ReducerRecord>;
|
||||||
|
@ -48,22 +52,22 @@ const setHasEndTime = (state: State) => {
|
||||||
return state.set('end_time', endTime);
|
return state.set('end_time', endTime);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function create_event(state = ReducerRecord(), action: AnyAction): State {
|
export default function compose_event(state = ReducerRecord(), action: AnyAction): State {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case CREATE_EVENT_NAME_CHANGE:
|
case EDIT_EVENT_NAME_CHANGE:
|
||||||
return state.set('name', action.value);
|
return state.set('name', action.value);
|
||||||
case CREATE_EVENT_DESCRIPTION_CHANGE:
|
case EDIT_EVENT_DESCRIPTION_CHANGE:
|
||||||
return state.set('status', action.value);
|
return state.set('status', action.value);
|
||||||
case CREATE_EVENT_START_TIME_CHANGE:
|
case EDIT_EVENT_START_TIME_CHANGE:
|
||||||
return state.set('start_time', action.value);
|
return state.set('start_time', action.value);
|
||||||
case CREATE_EVENT_END_TIME_CHANGE:
|
case EDIT_EVENT_END_TIME_CHANGE:
|
||||||
return state.set('end_time', action.value);
|
return state.set('end_time', action.value);
|
||||||
case CREATE_EVENT_HAS_END_TIME_CHANGE:
|
case EDIT_EVENT_HAS_END_TIME_CHANGE:
|
||||||
if (action.value) return setHasEndTime(state);
|
if (action.value) return setHasEndTime(state);
|
||||||
return state.set('end_time', null);
|
return state.set('end_time', null);
|
||||||
case CREATE_EVENT_APPROVAL_REQUIRED_CHANGE:
|
case EDIT_EVENT_APPROVAL_REQUIRED_CHANGE:
|
||||||
return state.set('approval_required', action.value);
|
return state.set('approval_required', action.value);
|
||||||
case CREATE_EVENT_LOCATION_CHANGE:
|
case EDIT_EVENT_LOCATION_CHANGE:
|
||||||
return state.set('location', action.value);
|
return state.set('location', action.value);
|
||||||
case EVENT_BANNER_UPLOAD_REQUEST:
|
case EVENT_BANNER_UPLOAD_REQUEST:
|
||||||
return state.set('is_uploading', true);
|
return state.set('is_uploading', true);
|
||||||
|
@ -80,6 +84,23 @@ export default function create_event(state = ReducerRecord(), action: AnyAction)
|
||||||
case EVENT_SUBMIT_SUCCESS:
|
case EVENT_SUBMIT_SUCCESS:
|
||||||
case EVENT_SUBMIT_FAIL:
|
case EVENT_SUBMIT_FAIL:
|
||||||
return state.set('is_submitting', false);
|
return state.set('is_submitting', false);
|
||||||
|
case EVENT_COMPOSE_CANCEL:
|
||||||
|
return ReducerRecord();
|
||||||
|
case EVENT_FORM_SET:
|
||||||
|
return ReducerRecord({
|
||||||
|
name: action.status.event.name,
|
||||||
|
status: action.text,
|
||||||
|
// location: null as LocationEntity | null,
|
||||||
|
start_time: new Date(action.status.event.start_time),
|
||||||
|
end_time: action.status.event.start_time ? new Date(action.status.event.end_time) : null,
|
||||||
|
approval_required: action.status.event.join_mode !== 'free',
|
||||||
|
banner: (action.status as StatusEntity).media_attachments.find(({ description }) => description === 'Banner') || null,
|
||||||
|
location: action.location ? normalizeLocation(action.location) : null,
|
||||||
|
progress: 0,
|
||||||
|
is_uploading: false,
|
||||||
|
is_submitting: false,
|
||||||
|
id: action.status.id,
|
||||||
|
});
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
|
@ -19,9 +19,9 @@ import chat_message_lists from './chat_message_lists';
|
||||||
import chat_messages from './chat_messages';
|
import chat_messages from './chat_messages';
|
||||||
import chats from './chats';
|
import chats from './chats';
|
||||||
import compose from './compose';
|
import compose from './compose';
|
||||||
|
import compose_event from './compose_event';
|
||||||
import contexts from './contexts';
|
import contexts from './contexts';
|
||||||
import conversations from './conversations';
|
import conversations from './conversations';
|
||||||
import create_event from './create_event';
|
|
||||||
import custom_emojis from './custom_emojis';
|
import custom_emojis from './custom_emojis';
|
||||||
import domain_lists from './domain_lists';
|
import domain_lists from './domain_lists';
|
||||||
import dropdown_menu from './dropdown_menu';
|
import dropdown_menu from './dropdown_menu';
|
||||||
|
@ -127,7 +127,7 @@ const reducers = {
|
||||||
rules,
|
rules,
|
||||||
history,
|
history,
|
||||||
announcements,
|
announcements,
|
||||||
create_event,
|
compose_event,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build a default state from all reducers: it has the key and `undefined`
|
// Build a default state from all reducers: it has the key and `undefined`
|
||||||
|
|
|
@ -34,6 +34,8 @@ import {
|
||||||
EVENT_PARTICIPATIONS_FETCH_SUCCESS,
|
EVENT_PARTICIPATIONS_FETCH_SUCCESS,
|
||||||
EVENT_PARTICIPATION_REQUESTS_EXPAND_SUCCESS,
|
EVENT_PARTICIPATION_REQUESTS_EXPAND_SUCCESS,
|
||||||
EVENT_PARTICIPATION_REQUESTS_FETCH_SUCCESS,
|
EVENT_PARTICIPATION_REQUESTS_FETCH_SUCCESS,
|
||||||
|
EVENT_PARTICIPATION_REQUEST_AUTHORIZE_SUCCESS,
|
||||||
|
EVENT_PARTICIPATION_REQUEST_REJECT_SUCCESS,
|
||||||
} from 'soapbox/actions/events';
|
} from 'soapbox/actions/events';
|
||||||
import {
|
import {
|
||||||
FAMILIAR_FOLLOWERS_FETCH_SUCCESS,
|
FAMILIAR_FOLLOWERS_FETCH_SUCCESS,
|
||||||
|
@ -212,7 +214,7 @@ export default function userLists(state = ReducerRecord(), action: AnyAction) {
|
||||||
case EVENT_PARTICIPATIONS_EXPAND_SUCCESS:
|
case EVENT_PARTICIPATIONS_EXPAND_SUCCESS:
|
||||||
return appendToList(state, ['event_participations', action.id], action.accounts, action.next);
|
return appendToList(state, ['event_participations', action.id], action.accounts, action.next);
|
||||||
case EVENT_PARTICIPATION_REQUESTS_FETCH_SUCCESS:
|
case EVENT_PARTICIPATION_REQUESTS_FETCH_SUCCESS:
|
||||||
return state.setIn(['event_participations', action.id], ParticipationRequestListRecord({
|
return state.setIn(['event_participation_requests', action.id], ParticipationRequestListRecord({
|
||||||
next: action.next,
|
next: action.next,
|
||||||
items: ImmutableOrderedSet(action.participations.map(({ account, participation_message }: APIEntity) => ParticipationRequestRecord({
|
items: ImmutableOrderedSet(action.participations.map(({ account, participation_message }: APIEntity) => ParticipationRequestRecord({
|
||||||
account: account.id,
|
account: account.id,
|
||||||
|
@ -221,13 +223,19 @@ export default function userLists(state = ReducerRecord(), action: AnyAction) {
|
||||||
}));
|
}));
|
||||||
case EVENT_PARTICIPATION_REQUESTS_EXPAND_SUCCESS:
|
case EVENT_PARTICIPATION_REQUESTS_EXPAND_SUCCESS:
|
||||||
return state.updateIn(
|
return state.updateIn(
|
||||||
['event_participations', action.id, 'items'],
|
['event_participation_requests', action.id, 'items'],
|
||||||
(items) => (items as ImmutableOrderedSet<ParticipationRequest>)
|
(items) => (items as ImmutableOrderedSet<ParticipationRequest>)
|
||||||
.union(action.participations.map(({ account, participation_message }: APIEntity) => ParticipationRequestRecord({
|
.union(action.participations.map(({ account, participation_message }: APIEntity) => ParticipationRequestRecord({
|
||||||
account: account.id,
|
account: account.id,
|
||||||
participation_message,
|
participation_message,
|
||||||
}))),
|
}))),
|
||||||
);
|
);
|
||||||
|
case EVENT_PARTICIPATION_REQUEST_AUTHORIZE_SUCCESS:
|
||||||
|
case EVENT_PARTICIPATION_REQUEST_REJECT_SUCCESS:
|
||||||
|
return state.updateIn(
|
||||||
|
['event_participation_requests', action.id, 'items'],
|
||||||
|
items => (items as ImmutableOrderedSet<ParticipationRequest>).filter(({ account }) => account !== action.accountId),
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,9 @@ const NOTIFICATION_TYPES = [
|
||||||
'pleroma:emoji_reaction',
|
'pleroma:emoji_reaction',
|
||||||
'user_approved',
|
'user_approved',
|
||||||
'update',
|
'update',
|
||||||
|
'pleroma:event_reminder',
|
||||||
|
'pleroma:participation_request',
|
||||||
|
'pleroma:participation_accepted',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
type NotificationType = typeof NOTIFICATION_TYPES[number];
|
type NotificationType = typeof NOTIFICATION_TYPES[number];
|
||||||
|
|
Loading…
Reference in New Issue