diff --git a/app/soapbox/actions/alerts.ts b/app/soapbox/actions/alerts.ts index 3e5aed4b32..8f200563a3 100644 --- a/app/soapbox/actions/alerts.ts +++ b/app/soapbox/actions/alerts.ts @@ -5,7 +5,7 @@ import { httpErrorMessages } from 'soapbox/utils/errors'; import type { SnackbarActionSeverity } from './snackbar'; import type { AnyAction } from '@reduxjs/toolkit'; import type { AxiosError } from 'axios'; -import type { NotificationObject } from 'soapbox/react-notification'; +import type { NotificationObject } from 'react-notification'; const messages = defineMessages({ unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' }, diff --git a/app/soapbox/actions/group_editor.ts b/app/soapbox/actions/group_editor.ts deleted file mode 100644 index 23f3491ad6..0000000000 --- a/app/soapbox/actions/group_editor.ts +++ /dev/null @@ -1,143 +0,0 @@ -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, -}; diff --git a/app/soapbox/actions/groups.ts b/app/soapbox/actions/groups.ts deleted file mode 100644 index 808cc3204e..0000000000 --- a/app/soapbox/actions/groups.ts +++ /dev/null @@ -1,550 +0,0 @@ -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.groups.get(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.groups_removed_accounts.get(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, -}; diff --git a/app/soapbox/components/avatar_composite.js b/app/soapbox/components/avatar_composite.js deleted file mode 100644 index 59e4bab96e..0000000000 Binary files a/app/soapbox/components/avatar_composite.js and /dev/null differ diff --git a/app/soapbox/components/error_boundary.tsx b/app/soapbox/components/error_boundary.tsx index 436b40134c..76adf97284 100644 --- a/app/soapbox/components/error_boundary.tsx +++ b/app/soapbox/components/error_boundary.tsx @@ -8,6 +8,7 @@ import { Text, Stack } from 'soapbox/components/ui'; import { captureException } from 'soapbox/monitoring'; import KVStore from 'soapbox/storage/kv_store'; import sourceCode from 'soapbox/utils/code'; +import { unregisterSw } from 'soapbox/utils/sw'; import SiteLogo from './site-logo'; @@ -15,16 +16,6 @@ import type { RootState } from 'soapbox/store'; const goHome = () => location.href = '/'; -/** Unregister the ServiceWorker */ -// https://stackoverflow.com/a/49771828/8811886 -const unregisterSw = async(): Promise => { - if (navigator.serviceWorker) { - const registrations = await navigator.serviceWorker.getRegistrations(); - const unregisterAll = registrations.map(r => r.unregister()); - await Promise.all(unregisterAll); - } -}; - const mapStateToProps = (state: RootState) => { const { links, logo } = getSoapboxConfig(state); diff --git a/app/soapbox/components/filter_bar.js b/app/soapbox/components/filter_bar.js deleted file mode 100644 index 2d6b71d9d7..0000000000 Binary files a/app/soapbox/components/filter_bar.js and /dev/null differ diff --git a/app/soapbox/components/quoted-status.tsx b/app/soapbox/components/quoted-status.tsx index d7ecfaf05d..9755e7120a 100644 --- a/app/soapbox/components/quoted-status.tsx +++ b/app/soapbox/components/quoted-status.tsx @@ -1,15 +1,18 @@ import classNames from 'clsx'; -import React, { useState } from 'react'; -import { defineMessages, useIntl, FormattedMessage, FormattedList } from 'react-intl'; +import React, { MouseEventHandler, useState } from 'react'; +import { defineMessages, useIntl } from 'react-intl'; import { useHistory } from 'react-router-dom'; import StatusMedia from 'soapbox/components/status-media'; -import { Stack, Text } from 'soapbox/components/ui'; +import { Stack } from 'soapbox/components/ui'; import AccountContainer from 'soapbox/containers/account_container'; import { useSettings } from 'soapbox/hooks'; import { defaultMediaVisibility } from 'soapbox/utils/status'; import OutlineBox from './outline-box'; +import StatusReplyMentions from './status-reply-mentions'; +import StatusContent from './status_content'; +import SensitiveContentOverlay from './statuses/sensitive-content-overlay'; import type { Account as AccountEntity, Status as StatusEntity } from 'soapbox/types/entities'; @@ -36,7 +39,7 @@ const QuotedStatus: React.FC = ({ status, onCancel, compose }) => const [showMedia, setShowMedia] = useState(defaultMediaVisibility(status, displayMedia)); - const handleExpandClick = (e: React.MouseEvent) => { + const handleExpandClick: MouseEventHandler = (e) => { if (!status) return; const account = status.account as AccountEntity; @@ -57,57 +60,6 @@ const QuotedStatus: React.FC = ({ status, onCancel, compose }) => setShowMedia(!showMedia); }; - const renderReplyMentions = () => { - if (!status?.in_reply_to_id) { - return null; - } - - const account = status.account as AccountEntity; - const to = status.mentions || []; - - if (to.size === 0) { - if (status.in_reply_to_account_id === account.id) { - return ( -
- -
- ); - } else { - return ( -
- -
- ); - } - } - - const accounts = to.slice(0, 2).map(account => <>@{account.username}).toArray(); - - if (to.size > 2) { - accounts.push( - , - ); - } - - return ( -
- , - }} - /> -
- ); - }; - if (!status) { return null; } @@ -127,7 +79,7 @@ const QuotedStatus: React.FC = ({ status, onCancel, compose }) => return ( @@ -144,20 +96,36 @@ const QuotedStatus: React.FC = ({ status, onCancel, compose }) => withLinkToProfile={!compose} /> - {renderReplyMentions()} + - + + {(status.hidden) && ( + + )} - + + + + {(status.media_attachments.size > 0) && ( + + )} + + ); diff --git a/app/soapbox/components/setting_text.js b/app/soapbox/components/setting_text.js deleted file mode 100644 index e4972f1027..0000000000 Binary files a/app/soapbox/components/setting_text.js and /dev/null differ diff --git a/app/soapbox/components/status-content.css b/app/soapbox/components/status-content.css index 7c1317d16b..997df73f4d 100644 Binary files a/app/soapbox/components/status-content.css and b/app/soapbox/components/status-content.css differ diff --git a/app/soapbox/components/status.tsx b/app/soapbox/components/status.tsx index 3e30a3032b..02eca62fea 100644 --- a/app/soapbox/components/status.tsx +++ b/app/soapbox/components/status.tsx @@ -65,7 +65,6 @@ const Status: React.FC = (props) => { hidden, featured, unread, - group, hideActionBar, variant = 'rounded', withDismiss, @@ -296,8 +295,8 @@ const Status: React.FC = (props) => { const accountAction = props.accountAction || reblogElement; - const inReview = status.visibility === 'self'; - const isSensitive = status.hidden; + const inReview = actualStatus.visibility === 'self'; + const isSensitive = actualStatus.hidden; return ( @@ -349,6 +348,8 @@ const Status: React.FC = (props) => {
+ + = (props) => { }) } > - {(inReview || isSensitive) ? ( + {(inReview || isSensitive) && ( - ) : null} - - {!group && actualStatus.group && ( -
- Posted in {String(actualStatus.getIn(['group', 'title']))} -
)} - + + - + {(quote || actualStatus.media_attachments.size > 0) && ( + + - - - {quote} + {quote} + + )} +
{!hideActionBar && ( diff --git a/app/soapbox/components/sub_navigation.tsx b/app/soapbox/components/sub_navigation.tsx index b8e2b310d8..1e6afb85a7 100644 --- a/app/soapbox/components/sub_navigation.tsx +++ b/app/soapbox/components/sub_navigation.tsx @@ -4,34 +4,22 @@ import { defineMessages, useIntl } from 'react-intl'; // import { connect } from 'react-redux'; import { useHistory } from 'react-router-dom'; -// import { openModal } from 'soapbox/actions/modals'; -// import { useAppDispatch } from 'soapbox/hooks'; - import { CardHeader, CardTitle } from './ui'; const messages = defineMessages({ back: { id: 'column_back_button.label', defaultMessage: 'Back' }, - settings: { id: 'column_header.show_settings', defaultMessage: 'Show settings' }, }); interface ISubNavigation { - message: String, + message: React.ReactNode, + /** @deprecated Unused. */ settings?: React.ComponentType, } const SubNavigation: React.FC = ({ message }) => { const intl = useIntl(); - // const dispatch = useAppDispatch(); const history = useHistory(); - // const ref = useRef(null); - - // const [scrolled, setScrolled] = useState(false); - - // const onOpenSettings = () => { - // dispatch(openModal('COMPONENT', { component: Settings })); - // }; - const handleBackClick = () => { if (window.history && window.history.length === 1) { history.push('/'); @@ -40,36 +28,6 @@ const SubNavigation: React.FC = ({ message }) => { } }; - // const handleBackKeyUp = (e) => { - // if (e.key === 'Enter') { - // handleClick(); - // } - // } - - // const handleOpenSettings = () => { - // onOpenSettings(); - // } - - // useEffect(() => { - // const handleScroll = throttle(() => { - // if (this.node) { - // const { offsetTop } = this.node; - - // if (offsetTop > 0) { - // setScrolled(true); - // } else { - // setScrolled(false); - // } - // } - // }, 150, { trailing: true }); - - // window.addEventListener('scroll', handleScroll); - - // return () => { - // window.removeEventListener('scroll', handleScroll); - // }; - // }, []); - return ( { return ( - +
+ +
+ = ({ state = 'inactive', size = 'sm' }) => { + return ( +
+ ); +}; + +export default Indicator; \ No newline at end of file diff --git a/app/soapbox/features/developers/developers-menu.tsx b/app/soapbox/features/developers/developers-menu.tsx index 76320e5303..a94eb09a60 100644 --- a/app/soapbox/features/developers/developers-menu.tsx +++ b/app/soapbox/features/developers/developers-menu.tsx @@ -89,6 +89,14 @@ const Developers: React.FC = () => { + + + + + + + + diff --git a/app/soapbox/features/developers/service-worker-info.tsx b/app/soapbox/features/developers/service-worker-info.tsx new file mode 100644 index 0000000000..9b77515876 --- /dev/null +++ b/app/soapbox/features/developers/service-worker-info.tsx @@ -0,0 +1,140 @@ +import React, { useEffect, useState } from 'react'; +import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; + +import List, { ListItem } from 'soapbox/components/list'; +import { HStack, Text, Column, FormActions, Button, Stack, Icon } from 'soapbox/components/ui'; +import { unregisterSw } from 'soapbox/utils/sw'; + +import Indicator from './components/indicator'; + +const messages = defineMessages({ + heading: { id: 'column.developers.service_worker', defaultMessage: 'Service Worker' }, + status: { id: 'sw.status', defaultMessage: 'Status' }, + url: { id: 'sw.url', defaultMessage: 'Script URL' }, +}); + +/** Hook that returns the active ServiceWorker registration. */ +const useRegistration = () => { + const [isLoading, setLoading] = useState(true); + const [registration, setRegistration] = useState(); + + const isSupported = 'serviceWorker' in navigator; + + useEffect(() => { + if (isSupported) { + navigator.serviceWorker.getRegistration() + .then(r => { + setRegistration(r); + setLoading(false); + }) + .catch(() => setLoading(false)); + } else { + setLoading(false); + } + }, []); + + return { + isLoading, + registration, + }; +}; + +interface IServiceWorkerInfo { +} + +/** Mini ServiceWorker debugging component. */ +const ServiceWorkerInfo: React.FC = () => { + const intl = useIntl(); + const { isLoading, registration } = useRegistration(); + + const url = registration?.active?.scriptURL; + + const getState = () => { + if (registration?.waiting) { + return 'pending'; + } else if (registration?.active) { + return 'active'; + } else { + return 'inactive'; + } + }; + + const getMessage = () => { + if (isLoading) { + return ( + + ); + } else if (!isLoading && !registration) { + return ( + + ); + } else if (registration?.waiting) { + return ( + + ); + } else if (registration?.active) { + return ( + + ); + } else { + return ( + + ); + } + }; + + const handleRestart = async() => { + await unregisterSw(); + window.location.reload(); + }; + + return ( + + + + + + + {getMessage()} + + + + {url && ( + + + {url} + + + + )} + + + + + + + + ); +}; + +export default ServiceWorkerInfo; \ No newline at end of file diff --git a/app/soapbox/features/hashtag-timeline/index.tsx b/app/soapbox/features/hashtag-timeline/index.tsx index a8b97cbab0..7ead1a9f57 100644 --- a/app/soapbox/features/hashtag-timeline/index.tsx +++ b/app/soapbox/features/hashtag-timeline/index.tsx @@ -3,10 +3,10 @@ import { FormattedMessage } from 'react-intl'; import { connectHashtagStream } from 'soapbox/actions/streaming'; import { expandHashtagTimeline, clearTimeline } from 'soapbox/actions/timelines'; -import ColumnHeader from 'soapbox/components/column_header'; +import SubNavigation from 'soapbox/components/sub_navigation'; import { Column } from 'soapbox/components/ui'; import Timeline from 'soapbox/features/ui/components/timeline'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch } from 'soapbox/hooks'; import type { Tag as TagEntity } from 'soapbox/types/entities'; @@ -27,7 +27,6 @@ export const HashtagTimeline: React.FC = ({ params }) => { const tags = params?.tags || { any: [], all: [], none: [] }; const dispatch = useAppDispatch(); - const hasUnread = useAppSelector(state => (state.timelines.getIn([`hashtag:${id}`, 'unread']) as number) > 0); const disconnects = useRef<(() => void)[]>([]); // Mastodon supports displaying results from multiple hashtags. @@ -100,7 +99,10 @@ export const HashtagTimeline: React.FC = ({ params }) => { return ( - +
+ +
+ { return ( - +
+ +
+ + {showExplanationBox &&
} diff --git a/app/soapbox/features/status/components/detailed-status.tsx b/app/soapbox/features/status/components/detailed-status.tsx index 4896d1a91f..28c2d020c1 100644 --- a/app/soapbox/features/status/components/detailed-status.tsx +++ b/app/soapbox/features/status/components/detailed-status.tsx @@ -29,7 +29,6 @@ interface IDetailedStatus { const DetailedStatus: React.FC = ({ status, - onToggleHidden, onOpenCompareHistoryModal, onToggleMediaVisibility, showMedia, @@ -93,23 +92,29 @@ const DetailedStatus: React.FC = ({ }) } > - {(isUnderReview || isSensitive) ? ( + {(isUnderReview || isSensitive) && ( - ) : null} + )} - + + - + {(quote || actualStatus.media_attachments.size > 0) && ( + + - {quote} + {quote} + + )} + diff --git a/app/soapbox/features/ui/components/account_list_panel.js b/app/soapbox/features/ui/components/account_list_panel.js deleted file mode 100644 index 61b703e4fd..0000000000 Binary files a/app/soapbox/features/ui/components/account_list_panel.js and /dev/null differ diff --git a/app/soapbox/features/ui/containers/notifications_container.tsx b/app/soapbox/features/ui/containers/notifications_container.tsx index 79d8724c13..2119228bf8 100644 --- a/app/soapbox/features/ui/containers/notifications_container.tsx +++ b/app/soapbox/features/ui/containers/notifications_container.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { useIntl, MessageDescriptor } from 'react-intl'; +import { NotificationStack, NotificationObject, StyleFactoryFn } from 'react-notification'; import { useHistory } from 'react-router-dom'; import { dismissAlert } from 'soapbox/actions/alerts'; import { Button } from 'soapbox/components/ui'; import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; -import { NotificationStack, NotificationObject, StyleFactoryFn } from 'soapbox/react-notification'; import type { Alert } from 'soapbox/reducers/alerts'; diff --git a/app/soapbox/features/ui/index.tsx b/app/soapbox/features/ui/index.tsx index 08d96e9205..973a2187c4 100644 --- a/app/soapbox/features/ui/index.tsx +++ b/app/soapbox/features/ui/index.tsx @@ -112,6 +112,7 @@ import { TestTimeline, LogoutPage, AuthTokenList, + ServiceWorkerInfo, } from './util/async-components'; import { WrappedRoute } from './util/react_router_helpers'; @@ -311,6 +312,7 @@ const SwitchingColumnsArea: React.FC = ({ children }) => { + new Promise((_resolve, reject) => reject())} content={children} /> diff --git a/app/soapbox/features/ui/util/async-components.ts b/app/soapbox/features/ui/util/async-components.ts index f416c859ac..773eed795c 100644 --- a/app/soapbox/features/ui/util/async-components.ts +++ b/app/soapbox/features/ui/util/async-components.ts @@ -470,6 +470,10 @@ export function TestTimeline() { return import(/* webpackChunkName: "features/test_timeline" */'../../test_timeline'); } +export function ServiceWorkerInfo() { + return import(/* webpackChunkName: "features/developers" */'../../developers/service-worker-info'); +} + export function DatePicker() { return import(/* webpackChunkName: "date_picker" */'../../birthdays/date_picker'); } diff --git a/app/soapbox/react-notification/defaultPropTypes.js b/app/soapbox/react-notification/defaultPropTypes.js deleted file mode 100644 index 1a3dd9d4e8..0000000000 Binary files a/app/soapbox/react-notification/defaultPropTypes.js and /dev/null differ diff --git a/app/soapbox/react-notification/index.d.ts b/app/soapbox/react-notification/index.d.ts deleted file mode 100644 index 22f0211c8a..0000000000 --- a/app/soapbox/react-notification/index.d.ts +++ /dev/null @@ -1,88 +0,0 @@ -declare module 'soapbox/react-notification' { - import { Component, ReactElement } from 'react'; - - interface StyleFactoryFn { - (index: number, style: object | void, notification: NotificationProps): object; - } - - interface OnClickNotificationProps { - /** - * Callback function to run when the action is clicked. - * @param notification Notification currently being clicked - * @param deactivate Function that can be called to set the notification to inactive. - * Used to activate notification exit animation on click. - */ - onClick?(notification: NotificationProps, deactivate: () => void): void; - } - - interface NotificationProps extends OnClickNotificationProps { - /** The name of the action, e.g., "close" or "undo". */ - action?: string; - /** Custom action styles. */ - actionStyle?: object; - /** Custom snackbar styles when the bar is active. */ - activeBarStyle?: object; - /** - * Custom class to apply to the top-level component when active. - * @default 'notification-bar-active' - */ - activeClassName?: string; - /** Custom snackbar styles. */ - barStyle?: object; - /** Custom class to apply to the top-level component. */ - className?: string; - /** - * Timeout for onDismiss event. - * @default 2000 - */ - dismissAfter?: boolean | number; - /** - * If true, the notification is visible. - * @default false - */ - isActive?: boolean; - /** The message or component for the notification. */ - message: string | ReactElement; - /** Setting this prop to `false` will disable all inline styles. */ - style?: boolean; - /** The title for the notification. */ - title?: string | ReactElement; - /** Custom title styles. */ - titleStyle?: object; - - /** - * Callback function to run when dismissAfter timer runs out - * @param notification Notification currently being dismissed. - */ - onDismiss?(notification: NotificationProps): void; - } - - interface NotificationStackProps extends OnClickNotificationProps { - /** Create the style of the actions. */ - actionStyleFactory?: StyleFactoryFn; - /** Create the style of the active notification. */ - activeBarStyleFactory?: StyleFactoryFn; - /** Create the style of the notification. */ - barStyleFactory?: StyleFactoryFn; - /** - * If false, notification dismiss timers start immediately. - * @default true - */ - dismissInOrder?: boolean; - /** Array of notifications to render. */ - notifications: NotificationObject[]; - /** - * Callback function to run when dismissAfter timer runs out - * @param notification Notification currently being dismissed. - */ - onDismiss?(notification: NotificationObject): void; - } - - export interface NotificationObject extends NotificationProps { - key: number | string; - } - - export class Notification extends Component {} - - export class NotificationStack extends Component {} -} diff --git a/app/soapbox/react-notification/index.js b/app/soapbox/react-notification/index.js deleted file mode 100644 index 3d7da7ceeb..0000000000 Binary files a/app/soapbox/react-notification/index.js and /dev/null differ diff --git a/app/soapbox/react-notification/notification.js b/app/soapbox/react-notification/notification.js deleted file mode 100644 index ab1cddf9b6..0000000000 Binary files a/app/soapbox/react-notification/notification.js and /dev/null differ diff --git a/app/soapbox/react-notification/notificationStack.js b/app/soapbox/react-notification/notificationStack.js deleted file mode 100644 index dc9c2459b5..0000000000 Binary files a/app/soapbox/react-notification/notificationStack.js and /dev/null differ diff --git a/app/soapbox/react-notification/stackedNotification.js b/app/soapbox/react-notification/stackedNotification.js deleted file mode 100644 index c8d7200d49..0000000000 Binary files a/app/soapbox/react-notification/stackedNotification.js and /dev/null differ diff --git a/app/soapbox/reducers/__tests__/group_editor.test.ts b/app/soapbox/reducers/__tests__/group_editor.test.ts deleted file mode 100644 index 516b6df438..0000000000 --- a/app/soapbox/reducers/__tests__/group_editor.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Map as ImmutableMap } from 'immutable'; - -import reducer from '../group_editor'; - -describe('group_editor reducer', () => { - it('should return the initial state', () => { - expect(reducer(undefined, {} as any)).toEqual(ImmutableMap({ - groupId: null, - isSubmitting: false, - isChanged: false, - title: '', - description: '', - coverImage: null, - })); - }); -}); diff --git a/app/soapbox/reducers/__tests__/group_lists.test.ts b/app/soapbox/reducers/__tests__/group_lists.test.ts deleted file mode 100644 index 46527f6829..0000000000 --- a/app/soapbox/reducers/__tests__/group_lists.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; - -import reducer from '../group_lists'; - -describe('group_lists reducer', () => { - it('should return the initial state', () => { - expect(reducer(undefined, {} as any)).toEqual(ImmutableMap({ - featured: ImmutableList(), - member: ImmutableList(), - admin: ImmutableList(), - })); - }); -}); diff --git a/app/soapbox/reducers/__tests__/group_relationships.test.ts b/app/soapbox/reducers/__tests__/group_relationships.test.ts deleted file mode 100644 index 31e3e354fb..0000000000 --- a/app/soapbox/reducers/__tests__/group_relationships.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Map as ImmutableMap } from 'immutable'; - -import reducer from '../group_relationships'; - -describe('group_relationships reducer', () => { - it('should return the initial state', () => { - expect(reducer(undefined, {} as any)).toEqual(ImmutableMap()); - }); -}); diff --git a/app/soapbox/reducers/__tests__/groups.test.ts b/app/soapbox/reducers/__tests__/groups.test.ts deleted file mode 100644 index 05b88402f6..0000000000 --- a/app/soapbox/reducers/__tests__/groups.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Map as ImmutableMap } from 'immutable'; - -import reducer from '../groups'; - -describe('groups reducer', () => { - it('should return the initial state', () => { - expect(reducer(undefined, {} as any)).toEqual(ImmutableMap()); - }); -}); diff --git a/app/soapbox/reducers/__tests__/list_editor-test.js b/app/soapbox/reducers/__tests__/list_editor-test.js deleted file mode 100644 index 1b351bca1e..0000000000 Binary files a/app/soapbox/reducers/__tests__/list_editor-test.js and /dev/null differ diff --git a/app/soapbox/reducers/__tests__/notifications-test.js b/app/soapbox/reducers/__tests__/notifications-test.js deleted file mode 100644 index 1ea296c60a..0000000000 Binary files a/app/soapbox/reducers/__tests__/notifications-test.js and /dev/null differ diff --git a/app/soapbox/reducers/__tests__/search-test.js b/app/soapbox/reducers/__tests__/search-test.js deleted file mode 100644 index 497f5b08c3..0000000000 Binary files a/app/soapbox/reducers/__tests__/search-test.js and /dev/null differ diff --git a/app/soapbox/reducers/__tests__/user_lists.test.ts b/app/soapbox/reducers/__tests__/user_lists.test.ts index 5fbd5f1eaf..fa23f845f1 100644 --- a/app/soapbox/reducers/__tests__/user_lists.test.ts +++ b/app/soapbox/reducers/__tests__/user_lists.test.ts @@ -14,8 +14,6 @@ describe('user_lists reducer', () => { blocks: { next: null, items: ImmutableOrderedSet(), isLoading: false }, mutes: { next: null, items: ImmutableOrderedSet(), isLoading: false }, directory: { next: null, items: ImmutableOrderedSet(), isLoading: true }, - groups: {}, - groups_removed_accounts: {}, pinned: {}, birthday_reminders: {}, familiar_followers: {}, diff --git a/app/soapbox/reducers/group_editor.js b/app/soapbox/reducers/group_editor.js deleted file mode 100644 index 109182b1fc..0000000000 Binary files a/app/soapbox/reducers/group_editor.js and /dev/null differ diff --git a/app/soapbox/reducers/group_lists.js b/app/soapbox/reducers/group_lists.js deleted file mode 100644 index 25b62648ce..0000000000 Binary files a/app/soapbox/reducers/group_lists.js and /dev/null differ diff --git a/app/soapbox/reducers/group_relationships.js b/app/soapbox/reducers/group_relationships.js deleted file mode 100644 index f74fdb02ed..0000000000 Binary files a/app/soapbox/reducers/group_relationships.js and /dev/null differ diff --git a/app/soapbox/reducers/groups.js b/app/soapbox/reducers/groups.js deleted file mode 100644 index 8e301e4681..0000000000 Binary files a/app/soapbox/reducers/groups.js and /dev/null differ diff --git a/app/soapbox/reducers/index.ts b/app/soapbox/reducers/index.ts index 87750381b3..8c59c014f4 100644 --- a/app/soapbox/reducers/index.ts +++ b/app/soapbox/reducers/index.ts @@ -25,10 +25,6 @@ import custom_emojis from './custom_emojis'; import domain_lists from './domain_lists'; import dropdown_menu from './dropdown_menu'; import filters from './filters'; -import group_editor from './group_editor'; -import group_lists from './group_lists'; -import group_relationships from './group_relationships'; -import groups from './groups'; import history from './history'; import instance from './instance'; import listAdder from './list_adder'; @@ -95,10 +91,6 @@ const reducers = { suggestions, polls, trends, - groups, - group_relationships, - group_lists, - group_editor, sidebar, patron, soapbox, diff --git a/app/soapbox/reducers/timelines.ts b/app/soapbox/reducers/timelines.ts index def2574073..97975d6586 100644 --- a/app/soapbox/reducers/timelines.ts +++ b/app/soapbox/reducers/timelines.ts @@ -12,7 +12,6 @@ import { ACCOUNT_MUTE_SUCCESS, ACCOUNT_UNFOLLOW_SUCCESS, } from '../actions/accounts'; -import { GROUP_REMOVE_STATUS_SUCCESS } from '../actions/groups'; import { STATUS_CREATE_REQUEST, STATUS_CREATE_SUCCESS, @@ -210,10 +209,6 @@ const filterTimelines = (state: State, relationship: APIEntity, statuses: Immuta }); }; -const removeStatusFromGroup = (state: State, groupId: string, statusId: string) => { - return state.updateIn([`group:${groupId}`, 'items'], ImmutableOrderedSet(), ids => (ids as ImmutableOrderedSet).delete(statusId)); -}; - const timelineDequeue = (state: State, timelineId: string) => { const top = state.getIn([timelineId, 'top']); @@ -348,8 +343,6 @@ export default function timelines(state: State = initialState, action: AnyAction return timelineConnect(state, action.timeline); case TIMELINE_DISCONNECT: return timelineDisconnect(state, action.timeline); - case GROUP_REMOVE_STATUS_SUCCESS: - return removeStatusFromGroup(state, action.groupId, action.id); case TIMELINE_REPLACE: return state .update('home', TimelineRecord(), timeline => timeline.withMutations(timeline => { diff --git a/app/soapbox/reducers/user_lists.ts b/app/soapbox/reducers/user_lists.ts index 38017f0bb6..4c84f1836e 100644 --- a/app/soapbox/reducers/user_lists.ts +++ b/app/soapbox/reducers/user_lists.ts @@ -32,13 +32,6 @@ import { import { FAMILIAR_FOLLOWERS_FETCH_SUCCESS, } from '../actions/familiar_followers'; -import { - GROUP_MEMBERS_FETCH_SUCCESS, - GROUP_MEMBERS_EXPAND_SUCCESS, - GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS, - GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS, - GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS, -} from '../actions/groups'; import { REBLOGS_FETCH_SUCCESS, FAVOURITES_FETCH_SUCCESS, @@ -82,8 +75,6 @@ export const ReducerRecord = ImmutableRecord({ blocks: ListRecord(), mutes: ListRecord(), directory: ListRecord({ isLoading: true }), - groups: ImmutableMap(), - groups_removed_accounts: ImmutableMap(), pinned: ImmutableMap(), birthday_reminders: ImmutableMap(), familiar_followers: ImmutableMap(), @@ -94,7 +85,7 @@ export type List = ReturnType; type Reaction = ReturnType; type ReactionList = ReturnType; type Items = ImmutableOrderedSet; -type NestedListPath = ['followers' | 'following' | 'reblogged_by' | 'favourited_by' | 'reactions' | 'groups' | 'groups_removed_accounts' | 'pinned' | 'birthday_reminders' | 'familiar_followers', string]; +type NestedListPath = ['followers' | 'following' | 'reblogged_by' | 'favourited_by' | 'reactions' | 'pinned' | 'birthday_reminders' | 'familiar_followers', string]; type ListPath = ['follow_requests' | 'blocks' | 'mutes' | 'directory']; const normalizeList = (state: State, path: NestedListPath | ListPath, accounts: APIEntity[], next?: string | null) => { @@ -170,16 +161,6 @@ export default function userLists(state = ReducerRecord(), action: AnyAction) { case DIRECTORY_FETCH_FAIL: case DIRECTORY_EXPAND_FAIL: return state.setIn(['directory', 'isLoading'], false); - case GROUP_MEMBERS_FETCH_SUCCESS: - return normalizeList(state, ['groups', action.id], action.accounts, action.next); - case GROUP_MEMBERS_EXPAND_SUCCESS: - return appendToList(state, ['groups', action.id], action.accounts, action.next); - case GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS: - return normalizeList(state, ['groups_removed_accounts', action.id], action.accounts, action.next); - case GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS: - return appendToList(state, ['groups_removed_accounts', action.id], action.accounts, action.next); - case GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS: - return removeFromList(state, ['groups_removed_accounts', action.groupId], action.id); case PINNED_ACCOUNTS_FETCH_SUCCESS: return normalizeList(state, ['pinned', action.id], action.accounts, action.next); case BIRTHDAY_REMINDERS_FETCH_SUCCESS: diff --git a/app/soapbox/utils/sw.ts b/app/soapbox/utils/sw.ts new file mode 100644 index 0000000000..910d7189a2 --- /dev/null +++ b/app/soapbox/utils/sw.ts @@ -0,0 +1,15 @@ +/** Unregister the ServiceWorker */ +// https://stackoverflow.com/a/49771828/8811886 +const unregisterSw = async(): Promise => { + if (navigator.serviceWorker) { + // FIXME: this only works if using a single tab. + // Send a message to sw.js instead to refresh all tabs. + const registrations = await navigator.serviceWorker.getRegistrations(); + const unregisterAll = registrations.map(r => r.unregister()); + await Promise.all(unregisterAll); + } +}; + +export { + unregisterSw, +}; \ No newline at end of file diff --git a/app/styles/components/media-gallery.scss b/app/styles/components/media-gallery.scss index 311af3b646..e9bb144252 100644 --- a/app/styles/components/media-gallery.scss +++ b/app/styles/components/media-gallery.scss @@ -1,6 +1,5 @@ .media-gallery { box-sizing: border-box; - margin-top: 8px; overflow: hidden; border-radius: 10px; position: relative; diff --git a/app/styles/components/reply-mentions.scss b/app/styles/components/reply-mentions.scss index 39b70a585a..e767e5a03a 100644 --- a/app/styles/components/reply-mentions.scss +++ b/app/styles/components/reply-mentions.scss @@ -10,7 +10,6 @@ .detailed-status { .reply-mentions { display: block; - margin: 4px 0 0 0; span { cursor: pointer; diff --git a/app/styles/components/status.scss b/app/styles/components/status.scss index a8c8e4561f..4a2c57d3eb 100644 --- a/app/styles/components/status.scss +++ b/app/styles/components/status.scss @@ -14,11 +14,6 @@ opacity: 1; animation: fade 150ms linear; - .video-player, - .audio-player { - margin-top: 8px; - } - &.light { .display-name { strong { diff --git a/app/styles/components/video-player.scss b/app/styles/components/video-player.scss index ada5b26f5f..b4e87e9b60 100644 --- a/app/styles/components/video-player.scss +++ b/app/styles/components/video-player.scss @@ -7,7 +7,6 @@ flex-direction: column; height: 100%; justify-content: center; - margin-top: 8px; position: relative; text-align: center; z-index: 100; diff --git a/package.json b/package.json index 5c44913210..e4813bf503 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@sentry/browser": "^7.11.1", "@sentry/react": "^7.11.1", "@sentry/tracing": "^7.11.1", - "@tabler/icons": "^1.73.0", + "@tabler/icons": "^1.109.0", "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.7", "@tanstack/react-query": "^4.0.10", @@ -166,6 +166,7 @@ "react-inlinesvg": "^3.0.0", "react-intl": "^5.0.0", "react-motion": "^0.5.2", + "react-notification": "^6.8.5", "react-otp-input": "^2.4.0", "react-overlays": "^0.9.0", "react-popper": "^2.3.0", diff --git a/yarn.lock b/yarn.lock index 2b4a3a0cfd..9c2729f93a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2271,10 +2271,10 @@ remark "^13.0.0" unist-util-find-all-after "^3.0.2" -"@tabler/icons@^1.73.0": - version "1.73.0" - resolved "https://registry.yarnpkg.com/@tabler/icons/-/icons-1.73.0.tgz#26d81858baf41be939504e1f9b4b32835eda6fdb" - integrity sha512-MhAHFzVj79ZWlAIRD++7Mk55PZsdlEdkfkjO3DD257mqj8iJZQRAQtkx2UFJXVs2mMrcOUu1qtj4rlVC8BfnKA== +"@tabler/icons@^1.109.0": + version "1.109.0" + resolved "https://registry.yarnpkg.com/@tabler/icons/-/icons-1.109.0.tgz#11626c3fc097f2f70c4c197e4b9909fb05380752" + integrity sha512-B0YetE4pB6HY2Wa57v/LJ3NgkJzKYPze4U0DurIqPoKSptatKv2ga76FZSkO6EUpkYfHMtGPM6QjpJljfuCmAQ== "@tailwindcss/forms@^0.5.3": version "0.5.3" @@ -9922,6 +9922,13 @@ react-motion@^0.5.2: prop-types "^15.5.8" raf "^3.1.0" +react-notification@^6.8.5: + version "6.8.5" + resolved "https://registry.yarnpkg.com/react-notification/-/react-notification-6.8.5.tgz#7ea90a633bb2a280d899e30c93cf372265cce4f0" + integrity sha512-3pJPhSsWNYizpyeMeWuC+jVthqE9WKqQ6rHq2naiiP4fLGN4irwL2Xp2Q8Qn7agW/e4BIDxarab6fJOUp1cKUw== + dependencies: + prop-types "^15.6.2" + react-onclickoutside@^6.12.0: version "6.12.1" resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.12.1.tgz#92dddd28f55e483a1838c5c2930e051168c1e96b"