diff --git a/package.json b/package.json index aeec5ff4d..8c37f6832 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,6 @@ "react-swipeable-views": "^0.14.0", "react-virtuoso": "^4.10.4", "redux": "^5.0.0", - "redux-immutable": "^4.0.0", "redux-thunk": "^3.1.0", "reselect": "^5.0.0", "sass": "^1.69.5", diff --git a/src/actions/accounts.test.ts b/src/actions/accounts.test.ts index 0f6af8cb0..34da76b33 100644 --- a/src/actions/accounts.test.ts +++ b/src/actions/accounts.test.ts @@ -73,15 +73,17 @@ describe('fetchAccount()', () => { avatar: 'test.jpg', }); - const state = rootState - .set('entities', { + const state = { + ...rootState, + entities: { 'ACCOUNTS': { store: { [id]: account, }, lists: {}, }, - }); + }, + }; store = mockStore(state); @@ -170,15 +172,17 @@ describe('fetchAccountByUsername()', () => { birthday: undefined, }); - state = rootState - .set('entities', { + state = { + ...rootState, + entities: { 'ACCOUNTS': { store: { [id]: account, }, lists: {}, }, - }); + }, + }; store = mockStore(state); @@ -189,16 +193,19 @@ describe('fetchAccountByUsername()', () => { describe('when "accountByUsername" feature is enabled', () => { beforeEach(() => { - const state = rootState - .set('instance', buildInstance({ + const state = { + ...rootState, + me: '123', + instance: buildInstance({ version: '2.7.2 (compatible; Pleroma 2.4.52-1337-g4779199e.gleasonator+soapbox)', pleroma: { metadata: { features: [], }, }, - })) - .set('me', '123'); + }), + }; + store = mockStore(state); }); @@ -252,16 +259,19 @@ describe('fetchAccountByUsername()', () => { describe('when "accountLookup" feature is enabled', () => { beforeEach(() => { - const state = rootState - .set('instance', buildInstance({ + const state = { + ...rootState, + me: '123', + instance: buildInstance({ version: '3.4.1 (compatible; TruthSocial 1.0.0)', pleroma: { metadata: { features: [], }, }, - })) - .set('me', '123'); + }), + }; + store = mockStore(state); }); @@ -317,7 +327,7 @@ describe('fetchAccountByUsername()', () => { describe('when using the accountSearch function', () => { beforeEach(() => { - const state = rootState.set('me', '123'); + const state = { ...rootState, me: '123' }; store = mockStore(state); }); @@ -384,7 +394,7 @@ describe('blockAccount()', () => { describe('when logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -398,7 +408,7 @@ describe('blockAccount()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState.set('me', '123'); + const state = { ...rootState, me: '123' }; store = mockStore(state); }); @@ -451,7 +461,7 @@ describe('unblockAccount()', () => { describe('when logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -465,7 +475,7 @@ describe('unblockAccount()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState.set('me', '123'); + const state = { ...rootState, me: '123' }; store = mockStore(state); }); @@ -517,7 +527,7 @@ describe('muteAccount()', () => { describe('when logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -531,7 +541,7 @@ describe('muteAccount()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState.set('me', '123'); + const state = { ...rootState, me: '123' }; store = mockStore(state); }); @@ -584,7 +594,7 @@ describe('unmuteAccount()', () => { describe('when logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -598,7 +608,7 @@ describe('unmuteAccount()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState.set('me', '123'); + const state = { ...rootState, me: '123' }; store = mockStore(state); }); @@ -650,7 +660,7 @@ describe('subscribeAccount()', () => { describe('when logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -664,7 +674,7 @@ describe('subscribeAccount()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState.set('me', '123'); + const state = { ...rootState, me: '123' }; store = mockStore(state); }); @@ -716,7 +726,7 @@ describe('unsubscribeAccount()', () => { describe('when logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -730,7 +740,7 @@ describe('unsubscribeAccount()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState.set('me', '123'); + const state = { ...rootState, me: '123' }; store = mockStore(state); }); @@ -782,7 +792,7 @@ describe('removeFromFollowers()', () => { describe('when logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -796,7 +806,7 @@ describe('removeFromFollowers()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState.set('me', '123'); + const state = { ...rootState, me: '123' }; store = mockStore(state); }); @@ -848,7 +858,7 @@ describe('fetchFollowers()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState.set('me', '123'); + const state = { ...rootState, me: '123' }; store = mockStore(state); }); @@ -905,7 +915,7 @@ describe('expandFollowers()', () => { describe('when logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -919,29 +929,35 @@ describe('expandFollowers()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState - .set('user_lists', ReducerRecord({ + const state = { + ...rootState, + me: '123', + user_lists: ReducerRecord({ followers: ImmutableMap({ [id]: ListRecord({ next: 'next_url', }), }), - })) - .set('me', '123'); + }), + }; + store = mockStore(state); }); describe('when the url is null', () => { beforeEach(() => { - const state = rootState - .set('user_lists', ReducerRecord({ + const state = { + ...rootState, + me: '123', + user_lists: ReducerRecord({ followers: ImmutableMap({ [id]: ListRecord({ next: null, }), }), - })) - .set('me', '123'); + }), + }; + store = mockStore(state); }); @@ -1006,7 +1022,7 @@ describe('fetchFollowing()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState.set('me', '123'); + const state = { ...rootState, me: '123' }; store = mockStore(state); }); @@ -1063,7 +1079,7 @@ describe('expandFollowing()', () => { describe('when logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -1077,29 +1093,35 @@ describe('expandFollowing()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState - .set('user_lists', ReducerRecord({ + const state = { + ...rootState, + me: '123', + user_lists: ReducerRecord({ following: ImmutableMap({ [id]: ListRecord({ next: 'next_url', }), }), - })) - .set('me', '123'); + }), + }; + store = mockStore(state); }); describe('when the url is null', () => { beforeEach(() => { - const state = rootState - .set('user_lists', ReducerRecord({ + const state = { + ...rootState, + me: '123', + user_lists: ReducerRecord({ following: ImmutableMap({ [id]: ListRecord({ next: null, }), }), - })) - .set('me', '123'); + }), + }; + store = mockStore(state); }); @@ -1164,7 +1186,7 @@ describe('fetchRelationships()', () => { describe('when logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -1178,16 +1200,18 @@ describe('fetchRelationships()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState - .set('me', '123'); + const state = { ...rootState, me: '123' }; store = mockStore(state); }); describe('without newAccountIds', () => { beforeEach(() => { - const state = rootState - .set('relationships', ImmutableMap({ [id]: buildRelationship() })) - .set('me', '123'); + const state = { + ...rootState, + me: '123', + relationships: ImmutableMap({ [id]: buildRelationship() }), + }; + store = mockStore(state); }); @@ -1201,9 +1225,12 @@ describe('fetchRelationships()', () => { describe('with a successful API request', () => { beforeEach(() => { - const state = rootState - .set('relationships', ImmutableMap({})) - .set('me', '123'); + const state = { + ...rootState, + me: '123', + relationships: ImmutableMap(), + }; + store = mockStore(state); __stub((mock) => { @@ -1255,7 +1282,7 @@ describe('fetchRelationships()', () => { describe('fetchFollowRequests()', () => { describe('when logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -1269,16 +1296,18 @@ describe('fetchFollowRequests()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState - .set('me', '123'); + const state = { ...rootState, me: '123' }; store = mockStore(state); }); describe('with a successful API request', () => { beforeEach(() => { - const state = rootState - .set('relationships', ImmutableMap({})) - .set('me', '123'); + const state = { + ...rootState, + me: '123', + relationships: ImmutableMap(), + }; + store = mockStore(state); __stub((mock) => { @@ -1329,7 +1358,7 @@ describe('fetchFollowRequests()', () => { describe('expandFollowRequests()', () => { describe('when logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -1343,25 +1372,29 @@ describe('expandFollowRequests()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState - .set('user_lists', ReducerRecord({ + const state = { + ...rootState, + me: '123', + user_lists: ReducerRecord({ follow_requests: ListRecord({ next: 'next_url', }), - })) - .set('me', '123'); + }), + }; store = mockStore(state); }); describe('when the url is null', () => { beforeEach(() => { - const state = rootState - .set('user_lists', ReducerRecord({ + const state = { + ...rootState, + me: '123', + user_lists: ReducerRecord({ follow_requests: ListRecord({ next: null, }), - })) - .set('me', '123'); + }), + }; store = mockStore(state); }); @@ -1425,7 +1458,7 @@ describe('authorizeFollowRequest()', () => { describe('when logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -1439,7 +1472,7 @@ describe('authorizeFollowRequest()', () => { describe('when logged in', () => { beforeEach(() => { - const state = rootState.set('me', '123'); + const state = { ...rootState, me: '123' }; store = mockStore(state); }); diff --git a/src/actions/blocks.test.ts b/src/actions/blocks.test.ts index 4c96052df..bca50fe12 100644 --- a/src/actions/blocks.test.ts +++ b/src/actions/blocks.test.ts @@ -16,7 +16,7 @@ describe('fetchBlocks()', () => { describe('if logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -30,7 +30,7 @@ describe('fetchBlocks()', () => { describe('if logged in', () => { beforeEach(() => { - const state = rootState.set('me', '1234'); + const state = { ...rootState, me: '1234' }; store = mockStore(state); }); @@ -89,7 +89,7 @@ describe('expandBlocks()', () => { describe('if logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -103,15 +103,18 @@ describe('expandBlocks()', () => { describe('if logged in', () => { beforeEach(() => { - const state = rootState.set('me', '1234'); + const state = { ...rootState, me: '1234' }; store = mockStore(state); }); describe('without a url', () => { beforeEach(() => { - const state = rootState - .set('me', '1234') - .set('user_lists', UserListsRecord({ blocks: ListRecord({ next: null }) })); + const state = { + ...rootState, + me: '1234', + user_lists: UserListsRecord({ blocks: ListRecord({ next: null }) }), + }; + store = mockStore(state); }); @@ -125,9 +128,11 @@ describe('expandBlocks()', () => { describe('with a url', () => { beforeEach(() => { - const state = rootState - .set('me', '1234') - .set('user_lists', UserListsRecord({ blocks: ListRecord({ next: 'example' }) })); + const state = { + ...rootState, + me: '1234', + user_lists: UserListsRecord({ blocks: ListRecord({ next: 'example' }) }), + }; store = mockStore(state); }); diff --git a/src/actions/compose.test.ts b/src/actions/compose.test.ts index c2141cbb0..01bd8b716 100644 --- a/src/actions/compose.test.ts +++ b/src/actions/compose.test.ts @@ -25,10 +25,12 @@ describe('uploadCompose()', () => { }, }); - const state = rootState - .set('me', '1234') - .set('instance', instance) - .setIn(['compose', 'home'], ReducerCompose()); + const state = { + ...rootState, + me: '1234', + instance, + compose: rootState.compose.set('home', ReducerCompose()), + }; store = mockStore(state); files = [{ @@ -71,10 +73,12 @@ describe('uploadCompose()', () => { }, }); - const state = rootState - .set('me', '1234') - .set('instance', instance) - .setIn(['compose', 'home'], ReducerCompose()); + const state = { + ...rootState, + me: '1234', + instance, + compose: rootState.compose.set('home', ReducerCompose()), + }; store = mockStore(state); files = [{ @@ -105,9 +109,11 @@ describe('uploadCompose()', () => { describe('submitCompose()', () => { it('inserts mentions from text', async() => { - const state = rootState - .set('me', '123') - .setIn(['compose', 'home'], ReducerCompose({ text: '@alex hello @mkljczk@pl.fediverse.pl @gg@汉语/漢語.com alex@alexgleason.me' })); + const state = { + ...rootState, + me: '1234', + compose: rootState.compose.set('home', ReducerCompose({ text: '@alex hello @mkljczk@pl.fediverse.pl @gg@汉语/漢語.com alex@alexgleason.me' })), + }; const store = mockStore(state); await store.dispatch(submitCompose('home')); diff --git a/src/actions/me.test.ts b/src/actions/me.test.ts index 63f22eac4..fe6ddd13a 100644 --- a/src/actions/me.test.ts +++ b/src/actions/me.test.ts @@ -37,23 +37,25 @@ describe('fetchMe()', () => { const token = '123'; beforeEach(() => { - const state = rootState - .set('auth', ReducerRecord({ + const state = { + ...rootState, + auth: ReducerRecord({ me: accountUrl, users: ImmutableMap({ [accountUrl]: AuthUserRecord({ 'access_token': token, }), }), - })) - .set('entities', { + }), + entities: { 'ACCOUNTS': { store: { [accountUrl]: buildAccount({ url: accountUrl }), }, lists: {}, }, - }); + }, + }; store = mockStore(state); }); diff --git a/src/actions/notifications.test.ts b/src/actions/notifications.test.ts index 0ead1491a..c46040216 100644 --- a/src/actions/notifications.test.ts +++ b/src/actions/notifications.test.ts @@ -14,10 +14,14 @@ describe('markReadNotifications()', () => { '10': normalizeNotification({ id: '10' }), }); - const state = rootState - .set('me', '123') - .setIn(['notifications', 'lastRead'], '9') - .setIn(['notifications', 'items'], items); + const state = { + ...rootState, + me: '123', + notifications: rootState.notifications.merge({ + lastRead: '9', + items, + }), + }; const store = mockStore(state); diff --git a/src/actions/onboarding.test.ts b/src/actions/onboarding.test.ts index dc91f8a03..3f638ece9 100644 --- a/src/actions/onboarding.test.ts +++ b/src/actions/onboarding.test.ts @@ -16,7 +16,8 @@ describe('checkOnboarding()', () => { it('does nothing if localStorage item is not set', async() => { mockGetItem = vi.fn().mockReturnValue(null); - const state = rootState.setIn(['onboarding', 'needsOnboarding'], false); + const state = { ...rootState }; + state.onboarding.needsOnboarding = false; const store = mockStore(state); await store.dispatch(checkOnboardingStatus()); @@ -29,7 +30,8 @@ describe('checkOnboarding()', () => { it('does nothing if localStorage item is invalid', async() => { mockGetItem = vi.fn().mockReturnValue('invalid'); - const state = rootState.setIn(['onboarding', 'needsOnboarding'], false); + const state = { ...rootState }; + state.onboarding.needsOnboarding = false; const store = mockStore(state); await store.dispatch(checkOnboardingStatus()); @@ -42,7 +44,8 @@ describe('checkOnboarding()', () => { it('dispatches the correct action', async() => { mockGetItem = vi.fn().mockReturnValue('1'); - const state = rootState.setIn(['onboarding', 'needsOnboarding'], false); + const state = { ...rootState }; + state.onboarding.needsOnboarding = false; const store = mockStore(state); await store.dispatch(checkOnboardingStatus()); @@ -65,7 +68,8 @@ describe('startOnboarding()', () => { }); it('dispatches the correct action', async() => { - const state = rootState.setIn(['onboarding', 'needsOnboarding'], false); + const state = { ...rootState }; + state.onboarding.needsOnboarding = false; const store = mockStore(state); await store.dispatch(startOnboarding()); @@ -88,7 +92,8 @@ describe('endOnboarding()', () => { }); it('dispatches the correct action', async() => { - const state = rootState.setIn(['onboarding', 'needsOnboarding'], false); + const state = { ...rootState }; + state.onboarding.needsOnboarding = false; const store = mockStore(state); await store.dispatch(endOnboarding()); diff --git a/src/actions/soapbox.test.ts b/src/actions/soapbox.test.ts deleted file mode 100644 index 1a698a36c..000000000 --- a/src/actions/soapbox.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { rootState } from 'soapbox/jest/test-helpers'; -import { RootState } from 'soapbox/store'; - -import { getSoapboxConfig } from './soapbox'; - -const ASCII_HEART = '❤'; // '\u2764\uFE0F' -const RED_HEART_RGI = '❤️'; // '\u2764' - -describe('getSoapboxConfig()', () => { - it('returns RGI heart on Pleroma > 2.3', () => { - const state = rootState.setIn(['instance', 'version'], '2.7.2 (compatible; Pleroma 2.3.0)') as RootState; - expect(getSoapboxConfig(state).allowedEmoji.includes(RED_HEART_RGI)).toBe(true); - expect(getSoapboxConfig(state).allowedEmoji.includes(ASCII_HEART)).toBe(false); - }); - - it('returns an ASCII heart on Pleroma < 2.3', () => { - const state = rootState.setIn(['instance', 'version'], '2.7.2 (compatible; Pleroma 2.0.0)') as RootState; - expect(getSoapboxConfig(state).allowedEmoji.includes(ASCII_HEART)).toBe(true); - expect(getSoapboxConfig(state).allowedEmoji.includes(RED_HEART_RGI)).toBe(false); - }); -}); diff --git a/src/actions/status-quotes.test.ts b/src/actions/status-quotes.test.ts index 85edb8994..6f53b48c6 100644 --- a/src/actions/status-quotes.test.ts +++ b/src/actions/status-quotes.test.ts @@ -26,7 +26,7 @@ describe('fetchStatusQuotes()', () => { let store: ReturnType; beforeEach(() => { - const state = rootState.set('me', '1234'); + const state = { ...rootState, me: '1234' }; store = mockStore(state); }); @@ -81,9 +81,12 @@ describe('expandStatusQuotes()', () => { describe('without a url', () => { beforeEach(() => { - const state = rootState - .set('me', '1234') - .set('status_lists', ImmutableMap({ [`quotes:${statusId}`]: StatusListRecord({ next: null }) })); + const state = { + ...rootState, + me: '1234', + status_lists: ImmutableMap({ [`quotes:${statusId}`]: StatusListRecord({ next: null }) }), + }; + store = mockStore(state); }); @@ -97,8 +100,12 @@ describe('expandStatusQuotes()', () => { describe('with a url', () => { beforeEach(() => { - const state = rootState.set('me', '1234') - .set('status_lists', ImmutableMap({ [`quotes:${statusId}`]: StatusListRecord({ next: 'example' }) })); + const state = { + ...rootState, + status_lists: ImmutableMap({ [`quotes:${statusId}`]: StatusListRecord({ next: 'example' }) }), + me: '1234', + }; + store = mockStore(state); }); diff --git a/src/actions/statuses.test.ts b/src/actions/statuses.test.ts index ba2200480..dd4831dc7 100644 --- a/src/actions/statuses.test.ts +++ b/src/actions/statuses.test.ts @@ -30,7 +30,7 @@ describe('deleteStatus()', () => { describe('if logged out', () => { beforeEach(() => { - const state = rootState.set('me', null); + const state = { ...rootState, me: null }; store = mockStore(state); }); @@ -49,11 +49,14 @@ describe('deleteStatus()', () => { }); beforeEach(() => { - const state = rootState - .set('me', '1234') - .set('statuses', fromJS({ + const state = { + ...rootState, + me: '1234', + statuses: fromJS({ [statusId]: cachedStatus, - }) as any); + }) as any, + }; + store = mockStore(state); }); diff --git a/src/api/hooks/groups/useGroupLookup.test.ts b/src/api/hooks/groups/useGroupLookup.test.ts index ce3903ae3..6b659f605 100644 --- a/src/api/hooks/groups/useGroupLookup.test.ts +++ b/src/api/hooks/groups/useGroupLookup.test.ts @@ -5,7 +5,13 @@ import { renderHook, rootState, waitFor } from 'soapbox/jest/test-helpers'; import { useGroupLookup } from './useGroupLookup'; const group = buildGroup({ id: '1', slug: 'soapbox' }); -const state = rootState.setIn(['instance', 'version'], '3.4.1 (compatible; TruthSocial 1.0.0)'); +const state = { + ...rootState, + instance: { + ...rootState.instance, + version: '3.4.1 (compatible; TruthSocial 1.0.0)', + }, +}; describe('useGroupLookup hook', () => { describe('with a successful request', () => { diff --git a/src/api/index.ts b/src/api/index.ts index dda1fa508..3e5f637a7 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -105,8 +105,8 @@ export default (getState: () => RootState, authType: string = 'user'): AxiosInst const me = state.me; const baseURL = me ? getAuthBaseURL(state, me) : ''; - const relayUrl = state.getIn(['instance', 'nostr', 'relay']) as string | undefined; - const pubkey = state.getIn(['instance', 'nostr', 'pubkey']) as string | undefined; + const relayUrl = state.instance?.nostr?.relay; + const pubkey = state.instance?.nostr?.pubkey; const nostrSign = Boolean(relayUrl && pubkey); return baseClient(accessToken, baseURL, nostrSign); diff --git a/src/components/quoted-status.test.tsx b/src/components/quoted-status.test.tsx index ca52fa126..ffb1419b4 100644 --- a/src/components/quoted-status.test.tsx +++ b/src/components/quoted-status.test.tsx @@ -22,7 +22,7 @@ describe('', () => { contentHtml: 'hello world', }) as ReducerStatus; - const state = rootState.setIn(['accounts', '1'], account); + const state = rootState/*.accounts.set('1', account)*/; render(, undefined, state); screen.getByText(/hello world/i); diff --git a/src/components/status.test.tsx b/src/components/status.test.tsx index b1c2cdeab..a37c966cd 100644 --- a/src/components/status.test.tsx +++ b/src/components/status.test.tsx @@ -21,7 +21,7 @@ const status = normalizeStatus({ }) as ReducerStatus; describe('', () => { - const state = rootState.setIn(['accounts', '1'], account); + const state = rootState/*.accounts.set('1', account)*/; it('renders content', () => { render(, undefined, state); diff --git a/src/components/statuses/sensitive-content-overlay.test.tsx b/src/components/statuses/sensitive-content-overlay.test.tsx index 4f93ef58c..9bace5784 100644 --- a/src/components/statuses/sensitive-content-overlay.test.tsx +++ b/src/components/statuses/sensitive-content-overlay.test.tsx @@ -94,10 +94,12 @@ describe('', () => { beforeEach(() => { status = normalizeStatus({ sensitive: true }) as ReducerStatus; - store = rootState - .set('settings', ImmutableMap({ + store = { + ...rootState, + settings: ImmutableMap({ displayMedia: 'show_all', - })); + }), + }; }); it('displays the "Under review" warning', () => { diff --git a/src/features/account-timeline/index.tsx b/src/features/account-timeline/index.tsx index 57daf7c51..e363f7a7c 100644 --- a/src/features/account-timeline/index.tsx +++ b/src/features/account-timeline/index.tsx @@ -39,8 +39,8 @@ const AccountTimeline: React.FC = ({ params, withReplies = fal const isBlocked = useAppSelector(state => state.relationships.getIn([account?.id, 'blocked_by']) === true); const unavailable = isBlocked && !features.blockersVisible; const patronEnabled = soapboxConfig.getIn(['extensions', 'patron', 'enabled']) === true; - const isLoading = useAppSelector(state => state.getIn(['timelines', `account:${path}`, 'isLoading']) === true); - const hasMore = useAppSelector(state => state.getIn(['timelines', `account:${path}`, 'hasMore']) === true); + const isLoading = useAppSelector(state => state.timelines.getIn([`account:${path}`, 'isLoading']) === true); + const hasMore = useAppSelector(state => state.timelines.getIn([`account:${path}`, 'hasMore']) === true); const next = useAppSelector(state => state.timelines.get(`account:${path}`)?.next); const accountUsername = account?.username || params.username; diff --git a/src/features/chats/components/chat-composer.tsx b/src/features/chats/components/chat-composer.tsx index 5ba1a76e9..785538f20 100644 --- a/src/features/chats/components/chat-composer.tsx +++ b/src/features/chats/components/chat-composer.tsx @@ -74,8 +74,8 @@ const ChatComposer = React.forwardRef const { chat } = useChatContext(); - const isBlocked = useAppSelector((state) => state.getIn(['relationships', chat?.account?.id, 'blocked_by'])); - const isBlocking = useAppSelector((state) => state.getIn(['relationships', chat?.account?.id, 'blocking'])); + const isBlocked = useAppSelector((state) => state.relationships.getIn([chat?.account?.id, 'blocked_by'])); + const isBlocking = useAppSelector((state) => state.relationships.getIn([chat?.account?.id, 'blocking'])); const maxCharacterCount = useAppSelector((state) => state.instance.configuration.chats.max_characters); const attachmentLimit = useAppSelector(state => state.instance.configuration.chats.max_media_attachments); diff --git a/src/features/chats/components/chat-list-item.tsx b/src/features/chats/components/chat-list-item.tsx index 3988cca46..97a27bc60 100644 --- a/src/features/chats/components/chat-list-item.tsx +++ b/src/features/chats/components/chat-list-item.tsx @@ -35,8 +35,8 @@ const ChatListItem: React.FC = ({ chat, onClick }) => { const { isUsingMainChatPage } = useChatContext(); const { deleteChat } = useChatActions(chat?.id as string); - const isBlocked = useAppSelector((state) => state.getIn(['relationships', chat.account.id, 'blocked_by'])); - const isBlocking = useAppSelector((state) => state.getIn(['relationships', chat?.account?.id, 'blocking'])); + const isBlocked = useAppSelector((state) => state.relationships.getIn([chat.account.id, 'blocked_by'])); + const isBlocking = useAppSelector((state) => state.relationships.getIn([chat?.account?.id, 'blocking'])); const menu = useMemo((): Menu => [{ text: intl.formatMessage(messages.leaveChat), diff --git a/src/features/chats/components/chat-message-list.test.tsx b/src/features/chats/components/chat-message-list.test.tsx index 23c522e81..7ea47173b 100644 --- a/src/features/chats/components/chat-message-list.test.tsx +++ b/src/features/chats/components/chat-message-list.test.tsx @@ -68,9 +68,11 @@ Object.assign(navigator, { }, }); -const store = rootState - .set('me', '1') - .set('instance', buildInstance({ version: '3.4.1 (compatible; TruthSocial 1.0.0+unreleased)' })); +const store = { + ...rootState, + me: '1', + instance: buildInstance({ version: '3.4.1 (compatible; TruthSocial 1.0.0+unreleased)' }), +}; const renderComponentWithChatContext = () => render( diff --git a/src/features/chats/components/chat-message-list.tsx b/src/features/chats/components/chat-message-list.tsx index 0d8272cd0..cce1feff4 100644 --- a/src/features/chats/components/chat-message-list.tsx +++ b/src/features/chats/components/chat-message-list.tsx @@ -91,7 +91,7 @@ const ChatMessageList: React.FC = ({ chat }) => { const formattedChatMessages = chatMessages || []; - const isBlocked = useAppSelector((state) => state.getIn(['relationships', chat.account.id, 'blocked_by'])); + const isBlocked = useAppSelector((state) => state.relationships.getIn([chat.account.id, 'blocked_by'])); const lastChatMessage = chatMessages ? chatMessages[chatMessages.length - 1] : null; diff --git a/src/features/chats/components/chat-page/components/chat-page-main.tsx b/src/features/chats/components/chat-page/components/chat-page-main.tsx index 15178e0c7..860d2a1c9 100644 --- a/src/features/chats/components/chat-page/components/chat-page-main.tsx +++ b/src/features/chats/components/chat-page/components/chat-page-main.tsx @@ -60,7 +60,7 @@ const ChatPageMain = () => { const handleUpdateChat = (value: MessageExpirationValues) => updateChat.mutate({ message_expiration: value }); - const isBlocking = useAppSelector((state) => state.getIn(['relationships', chat?.account?.id, 'blocking'])); + const isBlocking = useAppSelector((state) => state.relationships.getIn([chat?.account?.id, 'blocking'])); const handleBlockUser = () => { dispatch(openModal('CONFIRM', { diff --git a/src/features/chats/components/chat-widget.test.tsx b/src/features/chats/components/chat-widget.test.tsx index a183be076..933a2a570 100644 --- a/src/features/chats/components/chat-widget.test.tsx +++ b/src/features/chats/components/chat-widget.test.tsx @@ -17,16 +17,18 @@ const account = buildAccount({ }, }); -const store = rootState - .set('me', id) - .set('entities', { +const store = { + ...rootState, + me: id, + entities: { 'ACCOUNTS': { store: { [id]: account, }, lists: {}, }, - }); + }, +}; describe('', () => { describe('when on the /chats endpoint', () => { diff --git a/src/features/chats/components/chat-widget/chat-settings.tsx b/src/features/chats/components/chat-widget/chat-settings.tsx index e1b1cabf6..3d383cf17 100644 --- a/src/features/chats/components/chat-widget/chat-settings.tsx +++ b/src/features/chats/components/chat-widget/chat-settings.tsx @@ -40,7 +40,7 @@ const ChatSettings = () => { const handleUpdateChat = (value: MessageExpirationValues) => updateChat.mutate({ message_expiration: value }); - const isBlocking = useAppSelector((state) => state.getIn(['relationships', chat?.account?.id, 'blocking'])); + const isBlocking = useAppSelector((state) => state.relationships.getIn([chat?.account?.id, 'blocking'])); const closeSettings = () => { changeScreen(ChatWidgetScreens.CHAT, chat?.id); diff --git a/src/queries/chats.test.ts b/src/queries/chats.test.ts index 21a7ebd0d..003017b84 100644 --- a/src/queries/chats.test.ts +++ b/src/queries/chats.test.ts @@ -116,11 +116,11 @@ describe('useChatMessages', () => { describe('when the user is blocked', () => { beforeEach(() => { - const state = rootState - .set( - 'relationships', - ImmutableMap({ '1': buildRelationship({ blocked_by: true }) }), - ); + const state = { + ...rootState, + relationships: ImmutableMap({ '1': buildRelationship({ blocked_by: true }) }), + }; + store = mockStore(state); }); diff --git a/src/queries/chats.ts b/src/queries/chats.ts index 8bf382900..01d67987f 100644 --- a/src/queries/chats.ts +++ b/src/queries/chats.ts @@ -79,7 +79,7 @@ const isLastMessage = (chatMessageId: string): boolean => { const useChatMessages = (chat: IChat) => { const api = useApi(); - const isBlocked = useAppSelector((state) => state.getIn(['relationships', chat.account.id, 'blocked_by'])); + const isBlocked = useAppSelector((state) => state.relationships.getIn([chat.account.id, 'blocked_by'])); const getChatMessages = async (chatId: string, pageParam?: any): Promise> => { const nextPageLink = pageParam?.link; diff --git a/src/reducers/index.ts b/src/reducers/index.ts index a5d45c95f..a6df8e026 100644 --- a/src/reducers/index.ts +++ b/src/reducers/index.ts @@ -1,5 +1,4 @@ -import { Record as ImmutableRecord } from 'immutable'; -import { combineReducers } from 'redux-immutable'; +import { combineReducers } from '@reduxjs/toolkit'; import { AUTH_LOGGED_OUT } from 'soapbox/actions/auth'; import * as BuildConfig from 'soapbox/build-config'; @@ -120,39 +119,29 @@ const reducers = { user_lists, }; -// Build a default state from all reducers: it has the key and `undefined` -export const StateRecord = ImmutableRecord( - Object.keys(reducers).reduce((params: Record, reducer) => { - params[reducer] = undefined; - return params; - }, {}), -); +const appReducer = combineReducers(reducers); -const appReducer = combineReducers(reducers, StateRecord); +type AppState = ReturnType; // Clear the state (mostly) when the user logs out -const logOut = (state: any = StateRecord()): ReturnType => { +const logOut = (state: AppState): ReturnType => { if (BuildConfig.NODE_ENV === 'production') { location.href = '/login'; } - const whitelist: string[] = ['instance', 'soapbox', 'custom_emojis', 'auth']; + const newState = rootReducer(undefined, { type: '' }); - return StateRecord( - whitelist.reduce((acc: Record, curr) => { - acc[curr] = state.get(curr); - return acc; - }, {}), - ) as unknown as ReturnType; + const { instance, soapbox, custom_emojis, auth } = state; + return { ...newState, instance, soapbox, custom_emojis, auth }; }; const rootReducer: typeof appReducer = (state, action) => { switch (action.type) { case AUTH_LOGGED_OUT: - return appReducer(logOut(state), action); + return appReducer(logOut(state as AppState), action); default: return appReducer(state, action); } }; -export default rootReducer; +export default appReducer; diff --git a/types/redux-immutable/index.d.ts b/types/redux-immutable/index.d.ts deleted file mode 100644 index fbaecd198..000000000 --- a/types/redux-immutable/index.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Type definitions for redux-immutable v4.0.0 -// Project: https://github.com/gajus/redux-immutable -// Definitions by: Sebastian Sebald -// Gavin Gregory -// Kanitkorn Sujautra -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.3 - -declare module 'redux-immutable' { - import { Collection, Record } from 'immutable'; - import { ReducersMapObject, Reducer, Action } from 'redux'; - - export function combineReducers(reducers: ReducersMapObject, getDefaultState?: () => Collection.Keyed): Reducer; - export function combineReducers(reducers: ReducersMapObject, getDefaultState?: () => Collection.Indexed): Reducer; - export function combineReducers(reducers: ReducersMapObject, getDefaultState?: () => Collection.Indexed): Reducer; - export function combineReducers(reducers: ReducersMapObject, getDefaultState?: Record.Factory): Reducer>>; -} diff --git a/yarn.lock b/yarn.lock index e59e33255..92c73e699 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7443,11 +7443,6 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -redux-immutable@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/redux-immutable/-/redux-immutable-4.0.0.tgz#3a1a32df66366462b63691f0e1dc35e472bbc9f3" - integrity sha1-Ohoy32Y2ZGK2NpHw4dw15HK7yfM= - redux-thunk@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3"