pl-fe: Improve types

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-10-27 21:05:23 +01:00
parent 4cccfcdff1
commit 72aa5c69d0
11 changed files with 216 additions and 146 deletions

View file

@ -6,6 +6,26 @@ const FETCH_ABOUT_PAGE_REQUEST = 'FETCH_ABOUT_PAGE_REQUEST' as const;
const FETCH_ABOUT_PAGE_SUCCESS = 'FETCH_ABOUT_PAGE_SUCCESS' as const;
const FETCH_ABOUT_PAGE_FAIL = 'FETCH_ABOUT_PAGE_FAIL' as const;
interface FetchAboutPageRequestAction {
type: typeof FETCH_ABOUT_PAGE_REQUEST;
slug: string;
locale?: string;
}
interface FetchAboutPageSuccessAction {
type: typeof FETCH_ABOUT_PAGE_SUCCESS;
slug: string;
locale?: string;
html: string;
}
interface FetchAboutPageFailAction {
type: typeof FETCH_ABOUT_PAGE_FAIL;
slug: string;
locale?: string;
error: any;
}
const fetchAboutPage = (slug = 'index', locale?: string) => (dispatch: AppDispatch, getState: () => RootState) => {
dispatch({ type: FETCH_ABOUT_PAGE_REQUEST, slug, locale });
@ -21,9 +41,15 @@ const fetchAboutPage = (slug = 'index', locale?: string) => (dispatch: AppDispat
});
};
type AboutAction =
| FetchAboutPageRequestAction
| FetchAboutPageSuccessAction
| FetchAboutPageFailAction;
export {
fetchAboutPage,
FETCH_ABOUT_PAGE_REQUEST,
FETCH_ABOUT_PAGE_SUCCESS,
FETCH_ABOUT_PAGE_FAIL,
type AboutAction,
};

View file

@ -57,69 +57,6 @@ const AUTH_ACCOUNT_REMEMBER_REQUEST = 'AUTH_ACCOUNT_REMEMBER_REQUEST' as const;
const AUTH_ACCOUNT_REMEMBER_SUCCESS = 'AUTH_ACCOUNT_REMEMBER_SUCCESS' as const;
const AUTH_ACCOUNT_REMEMBER_FAIL = 'AUTH_ACCOUNT_REMEMBER_FAIL' as const;
interface SwitchAccountAction {
type: typeof SWITCH_ACCOUNT;
account?: Account;
background: boolean;
}
interface AuthAppCreatedAction {
type: typeof AUTH_APP_CREATED;
app: Application;
}
interface AuthAppAuthorizedAction {
type: typeof AUTH_APP_AUTHORIZED;
app: Application;
token: Token;
}
interface AuthLoggedInAction {
type: typeof AUTH_LOGGED_IN;
token: Token;
}
interface AuthLoggedOutAction {
type: typeof AUTH_LOGGED_OUT;
account: Account;
standalone: boolean;
}
interface VerifyCredentialsRequestAction {
type: typeof VERIFY_CREDENTIALS_REQUEST;
token: string;
}
interface VerifyCredentialsSuccessAction {
type: typeof VERIFY_CREDENTIALS_SUCCESS;
token: string;
account: CredentialAccount;
}
interface VerifyCredentialsFailAction {
type: typeof VERIFY_CREDENTIALS_FAIL;
token: string;
error: any;
}
interface AuthAccountRememberRequestAction {
type: typeof AUTH_ACCOUNT_REMEMBER_REQUEST;
accountUrl: string;
}
interface AuthAccountRememberSuccessAction {
type: typeof AUTH_ACCOUNT_REMEMBER_SUCCESS;
accountUrl: string;
account: CredentialAccount;
}
interface AuthAccountRememberFailAction {
type: typeof AUTH_ACCOUNT_REMEMBER_FAIL;
error: any;
accountUrl: string;
skipAlert: boolean;
}
const customApp = custom('app');
const messages = defineMessages({
@ -136,6 +73,11 @@ const createAppAndToken = () =>
dispatch(createAppToken()),
);
interface AuthAppCreatedAction {
type: typeof AUTH_APP_CREATED;
app: Application;
}
/** Create an auth app, or use it from build config */
const getAuthApp = () =>
(dispatch: AppDispatch) => {
@ -160,6 +102,12 @@ const createAuthApp = () =>
);
};
interface AuthAppAuthorizedAction {
type: typeof AUTH_APP_AUTHORIZED;
app: Application;
token: Token;
}
const createAppToken = () =>
(dispatch: AppDispatch, getState: () => RootState) => {
const app = getState().auth.app!;
@ -212,6 +160,23 @@ const otpVerify = (code: string, mfa_token: string) =>
}).then((token) => dispatch(authLoggedIn(token)));
};
interface VerifyCredentialsRequestAction {
type: typeof VERIFY_CREDENTIALS_REQUEST;
token: string;
}
interface VerifyCredentialsSuccessAction {
type: typeof VERIFY_CREDENTIALS_SUCCESS;
token: string;
account: CredentialAccount;
}
interface VerifyCredentialsFailAction {
type: typeof VERIFY_CREDENTIALS_FAIL;
token: string;
error: any;
}
const verifyCredentials = (token: string, accountUrl?: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const baseURL = parseBaseURL(accountUrl) || BuildConfig.BACKEND_URL;
@ -242,6 +207,24 @@ const verifyCredentials = (token: string, accountUrl?: string) =>
});
};
interface AuthAccountRememberRequestAction {
type: typeof AUTH_ACCOUNT_REMEMBER_REQUEST;
accountUrl: string;
}
interface AuthAccountRememberSuccessAction {
type: typeof AUTH_ACCOUNT_REMEMBER_SUCCESS;
accountUrl: string;
account: CredentialAccount;
}
interface AuthAccountRememberFailAction {
type: typeof AUTH_ACCOUNT_REMEMBER_FAIL;
error: any;
accountUrl: string;
skipAlert: boolean;
}
const rememberAuthAccount = (accountUrl: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch<AuthAccountRememberRequestAction>({ type: AUTH_ACCOUNT_REMEMBER_REQUEST, accountUrl });
@ -275,6 +258,12 @@ const logIn = (username: string, password: string) =>
throw error;
});
interface AuthLoggedOutAction {
type: typeof AUTH_LOGGED_OUT;
account: Account;
standalone: boolean;
}
const logOut = () =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
@ -304,6 +293,12 @@ const logOut = () =>
});
};
interface SwitchAccountAction {
type: typeof SWITCH_ACCOUNT;
account?: Account;
background: boolean;
}
const switchAccount = (accountId: string, background = false) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const account = selectAccount(getState(), accountId);
@ -341,6 +336,11 @@ const register = (params: CreateAccountParams) =>
const fetchCaptcha = () =>
(_dispatch: AppDispatch, getState: () => RootState) => getClient(getState).oauth.getCaptcha();
interface AuthLoggedInAction {
type: typeof AUTH_LOGGED_IN;
token: Token;
}
const authLoggedIn = (token: Token) =>
(dispatch: AppDispatch) => {
dispatch<AuthLoggedInAction>({ type: AUTH_LOGGED_IN, token });

View file

@ -137,7 +137,7 @@ const setComposeToStatus = (
const client = getClient(getState);
const { createStatusExplicitAddressing: explicitAddressing, version: v } = client.features;
const action: ComposeSetStatusAction = {
dispatch<ComposeSetStatusAction>({
type: COMPOSE_SET_STATUS,
composeId: 'compose-modal',
status,
@ -150,9 +150,7 @@ const setComposeToStatus = (
withRedraft,
draftId,
editorState,
};
dispatch(action);
});
};
const changeCompose = (composeId: string, text: string) => ({
@ -184,7 +182,7 @@ const replyCompose = (
if (!account) return;
const action: ComposeReplyAction = {
dispatch<ComposeReplyAction>({
type: COMPOSE_REPLY,
composeId: 'compose-modal',
status,
@ -192,9 +190,7 @@ const replyCompose = (
explicitAddressing,
preserveSpoilers,
rebloggedBy,
};
dispatch(action);
});
useModalsStore.getState().openModal('COMPOSE');
};
@ -216,15 +212,13 @@ const quoteCompose = (status: ComposeQuoteAction['status']) =>
const state = getState();
const { createStatusExplicitAddressing: explicitAddressing } = state.auth.client.features;
const action: ComposeQuoteAction = {
dispatch<ComposeQuoteAction>({
type: COMPOSE_QUOTE,
composeId: 'compose-modal',
status,
account: selectOwnAccount(state),
explicitAddressing,
};
dispatch(action);
});
useModalsStore.getState().openModal('COMPOSE');
};
@ -256,13 +250,11 @@ const mentionCompose = (account: ComposeMentionAction['account']) =>
(dispatch: AppDispatch, getState: () => RootState) => {
if (!getState().me) return;
const action: ComposeMentionAction = {
dispatch<ComposeMentionAction>({
type: COMPOSE_MENTION,
composeId: 'compose-modal',
account: account,
};
dispatch(action);
});
useModalsStore.getState().openModal('COMPOSE');
};
@ -274,13 +266,11 @@ interface ComposeDirectAction {
const directCompose = (account: ComposeDirectAction['account']) =>
(dispatch: AppDispatch) => {
const action: ComposeDirectAction = {
dispatch<ComposeDirectAction>({
type: COMPOSE_DIRECT,
composeId: 'compose-modal',
account,
};
dispatch(action);
});
useModalsStore.getState().openModal('COMPOSE');
};
@ -289,13 +279,11 @@ const directComposeById = (accountId: string) =>
const account = selectAccount(getState(), accountId);
if (!account) return;
const action: ComposeDirectAction = {
dispatch<ComposeDirectAction>({
type: COMPOSE_DIRECT,
composeId: 'compose-modal',
account,
};
dispatch(action);
});
useModalsStore.getState().openModal('COMPOSE');
};
@ -701,16 +689,14 @@ const selectComposeSuggestion = (composeId: string, position: number, token: str
startPosition = position;
}
const action: ComposeSuggestionSelectAction = {
dispatch<ComposeSuggestionSelectAction>({
type: COMPOSE_SUGGESTION_SELECT,
composeId,
position: startPosition,
token,
completion,
path,
};
dispatch(action);
});
};
const updateSuggestionTags = (composeId: string, token: string, tags: Array<Tag>) => ({
@ -870,13 +856,11 @@ const addToMentions = (composeId: string, accountId: string) =>
const account = selectAccount(state, accountId);
if (!account) return;
const action: ComposeAddToMentionsAction = {
return dispatch<ComposeAddToMentionsAction>({
type: COMPOSE_ADD_TO_MENTIONS,
composeId,
account: account.acct,
};
return dispatch(action);
});
};
interface ComposeRemoveFromMentionsAction {
@ -891,13 +875,11 @@ const removeFromMentions = (composeId: string, accountId: string) =>
const account = selectAccount(state, accountId);
if (!account) return;
const action: ComposeRemoveFromMentionsAction = {
return dispatch<ComposeRemoveFromMentionsAction>({
type: COMPOSE_REMOVE_FROM_MENTIONS,
composeId,
account: account.acct,
};
return dispatch(action);
});
};
interface ComposeEventReplyAction {

View file

@ -33,8 +33,7 @@ const fetchInstance = () => async (dispatch: AppDispatch, getState: () => RootSt
try {
const instance = await getClient(getState).instance.getInstance();
const action: InstanceFetchSuccessAction = { type: INSTANCE_FETCH_SUCCESS, instance };
dispatch(action);
dispatch<InstanceFetchSuccessAction>({ type: INSTANCE_FETCH_SUCCESS, instance });
} catch (error) {
dispatch({ type: INSTANCE_FETCH_FAIL, error });
}

View file

@ -39,25 +39,22 @@ const fetchStatusQuotes = (statusId: string) =>
return dispatch(noOp);
}
const action: FetchStatusQuotesRequestAction = { type: STATUS_QUOTES_FETCH_REQUEST, statusId };
dispatch(action);
dispatch<FetchStatusQuotesRequestAction>({ type: STATUS_QUOTES_FETCH_REQUEST, statusId });
return getClient(getState).statuses.getStatusQuotes(statusId).then(response => {
dispatch(importEntities({ statuses: response.items }));
const action: FetchStatusQuotesSuccessAction = {
return dispatch<FetchStatusQuotesSuccessAction>({
type: STATUS_QUOTES_FETCH_SUCCESS,
statusId,
statuses: response.items,
next: response.next,
};
return dispatch(action);
});
}).catch(error => {
const action: FetchStatusQuotesFailAction = {
dispatch<FetchStatusQuotesFailAction>({
type: STATUS_QUOTES_FETCH_FAIL,
statusId,
error,
};
dispatch(action);
});
});
};
@ -87,28 +84,25 @@ const expandStatusQuotes = (statusId: string) =>
return dispatch(noOp);
}
const action: ExpandStatusQuotesRequestAction = {
dispatch<ExpandStatusQuotesRequestAction>({
type: STATUS_QUOTES_EXPAND_REQUEST,
statusId,
};
dispatch(action);
});
return next().then(response => {
dispatch(importEntities({ statuses: response.items }));
const action: ExpandStatusQuotesSuccessAction = {
dispatch<ExpandStatusQuotesSuccessAction>({
type: STATUS_QUOTES_EXPAND_SUCCESS,
statusId,
statuses: response.items,
next: response.next,
};
dispatch(action);
});
}).catch(error => {
const action: ExpandStatusQuotesFailAction = {
dispatch<ExpandStatusQuotesFailAction>({
type: STATUS_QUOTES_EXPAND_FAIL,
statusId,
error,
};
dispatch(action);
});
});
};

View file

@ -4,12 +4,28 @@ import { fetchRelationships } from './accounts';
import { importEntities } from './importer';
import { insertSuggestionsIntoTimeline } from './timelines';
import type { Suggestion } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store';
const SUGGESTIONS_FETCH_REQUEST = 'SUGGESTIONS_FETCH_REQUEST' as const;
const SUGGESTIONS_FETCH_SUCCESS = 'SUGGESTIONS_FETCH_SUCCESS' as const;
const SUGGESTIONS_FETCH_FAIL = 'SUGGESTIONS_FETCH_FAIL' as const;
interface SuggestionsFetchRequestAction {
type: typeof SUGGESTIONS_FETCH_REQUEST;
}
interface SuggestionsFetchSuccessAction {
type: typeof SUGGESTIONS_FETCH_SUCCESS;
suggestions: Array<Suggestion>;
}
interface SuggestionsFetchFailAction {
type: typeof SUGGESTIONS_FETCH_FAIL;
error: any;
skipAlert: true;
}
const fetchSuggestions = (limit = 50) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
@ -19,18 +35,18 @@ const fetchSuggestions = (limit = 50) =>
if (!me) return null;
if (client.features.suggestions) {
dispatch({ type: SUGGESTIONS_FETCH_REQUEST });
dispatch<SuggestionsFetchRequestAction>({ type: SUGGESTIONS_FETCH_REQUEST });
return getClient(getState).myAccount.getSuggestions(limit).then((suggestions) => {
const accounts = suggestions.map(({ account }) => account);
dispatch(importEntities({ accounts }));
dispatch({ type: SUGGESTIONS_FETCH_SUCCESS, suggestions });
dispatch<SuggestionsFetchSuccessAction>({ type: SUGGESTIONS_FETCH_SUCCESS, suggestions });
dispatch(fetchRelationships(accounts.map(({ id }) => id)));
return suggestions;
}).catch(error => {
dispatch({ type: SUGGESTIONS_FETCH_FAIL, error, skipAlert: true });
dispatch<SuggestionsFetchFailAction>({ type: SUGGESTIONS_FETCH_FAIL, error, skipAlert: true });
throw error;
});
} else {
@ -43,10 +59,16 @@ const fetchSuggestionsForTimeline = () => (dispatch: AppDispatch) => {
dispatch(fetchSuggestions(20))?.then(() => dispatch(insertSuggestionsIntoTimeline()));
};
type SuggestionsAction =
| SuggestionsFetchRequestAction
| SuggestionsFetchSuccessAction
| SuggestionsFetchFailAction;
export {
SUGGESTIONS_FETCH_REQUEST,
SUGGESTIONS_FETCH_SUCCESS,
SUGGESTIONS_FETCH_FAIL,
fetchSuggestions,
fetchSuggestionsForTimeline,
type SuggestionsAction,
};

View file

@ -156,6 +156,23 @@ const expandFollowedHashtagsFail = (error: unknown) => ({
error,
});
type TagsAction =
| ReturnType<typeof fetchHashtagRequest>
| ReturnType<typeof fetchHashtagSuccess>
| ReturnType<typeof fetchHashtagFail>
| ReturnType<typeof followHashtagRequest>
| ReturnType<typeof followHashtagSuccess>
| ReturnType<typeof followHashtagFail>
| ReturnType<typeof unfollowHashtagRequest>
| ReturnType<typeof unfollowHashtagSuccess>
| ReturnType<typeof unfollowHashtagFail>
| ReturnType<typeof fetchFollowedHashtagsRequest>
| ReturnType<typeof fetchFollowedHashtagsSuccess>
| ReturnType<typeof fetchFollowedHashtagsFail>
| ReturnType<typeof expandFollowedHashtagsRequest>
| ReturnType<typeof expandFollowedHashtagsSuccess>
| ReturnType<typeof expandFollowedHashtagsFail>;
export {
HASHTAG_FETCH_REQUEST,
HASHTAG_FETCH_SUCCESS,
@ -192,4 +209,5 @@ export {
expandFollowedHashtagsRequest,
expandFollowedHashtagsSuccess,
expandFollowedHashtagsFail,
type TagsAction,
};

View file

@ -61,18 +61,19 @@ const updateTimeline = (timeline: string, statusId: string) => ({
statusId,
});
const updateTimelineQueue = (timeline: string, statusId: string) =>
(dispatch: AppDispatch) => {
// if (typeof accept === 'function' && !accept(status)) {
// return;
// }
const updateTimelineQueue = (timeline: string, statusId: string) => ({
// if (typeof accept === 'function' && !accept(status)) {
// return;
// }
type: TIMELINE_UPDATE_QUEUE,
timeline,
statusId,
});
dispatch({
type: TIMELINE_UPDATE_QUEUE,
timeline,
statusId,
});
};
interface TimelineDequeueAction {
type: typeof TIMELINE_DEQUEUE;
timeline: string;
}
const dequeueTimeline = (timelineId: string, expandFunc?: (lastStatusId: string) => void) =>
(dispatch: AppDispatch, getState: () => RootState) => {
@ -82,7 +83,7 @@ const dequeueTimeline = (timelineId: string, expandFunc?: (lastStatusId: string)
if (queuedCount <= 0) return;
if (queuedCount <= MAX_QUEUED_ITEMS) {
dispatch({ type: TIMELINE_DEQUEUE, timeline: timelineId });
dispatch<TimelineDequeueAction>({ type: TIMELINE_DEQUEUE, timeline: timelineId });
return;
}
@ -115,15 +116,13 @@ const deleteFromTimelines = (statusId: string) =>
const references = getState().statuses.filter(status => status.reblog_id === statusId).map(status => [status.id, status.account_id] as const);
const reblogOf = getState().statuses.get(statusId)?.reblog_id || null;
const action: TimelineDeleteAction = {
dispatch<TimelineDeleteAction>({
type: TIMELINE_DELETE,
statusId,
accountId,
references,
reblogOf,
};
dispatch(action);
});
};
const clearTimeline = (timeline: string) => ({ type: TIMELINE_CLEAR, timeline });
@ -324,12 +323,20 @@ const scrollTopTimeline = (timeline: string, top: boolean) => ({
top,
});
const insertSuggestionsIntoTimeline = () => (dispatch: AppDispatch, getState: () => RootState) => {
dispatch({ type: TIMELINE_INSERT, timeline: 'home' });
};
const insertSuggestionsIntoTimeline = () => ({ type: TIMELINE_INSERT, timeline: 'home' });
// TODO: other actions
type TimelineAction = TimelineDeleteAction;
type TimelineAction =
| ReturnType<typeof updateTimeline>
| TimelineDeleteAction
| ReturnType<typeof clearTimeline>
| ReturnType<typeof updateTimelineQueue>
| TimelineDequeueAction
| ReturnType<typeof scrollTopTimeline>
| ReturnType<typeof expandTimelineRequest>
| ReturnType<typeof expandTimelineSuccess>
| ReturnType<typeof expandTimelineFail>
| ReturnType<typeof insertSuggestionsIntoTimeline>;
export {
TIMELINE_UPDATE,

View file

@ -2,12 +2,27 @@ import { getClient } from '../api';
import { importEntities } from './importer';
import type { Status } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store';
const TRENDING_STATUSES_FETCH_REQUEST = 'TRENDING_STATUSES_FETCH_REQUEST' as const;
const TRENDING_STATUSES_FETCH_SUCCESS = 'TRENDING_STATUSES_FETCH_SUCCESS' as const;
const TRENDING_STATUSES_FETCH_FAIL = 'TRENDING_STATUSES_FETCH_FAIL' as const;
interface TrendingStatusesFetchRequestAction {
type: typeof TRENDING_STATUSES_FETCH_REQUEST;
}
interface TrendingStatusesFetchSuccessAction {
type: typeof TRENDING_STATUSES_FETCH_SUCCESS;
statuses: Array<Status>;
}
interface TrendingStatusesFetchFailAction {
type: typeof TRENDING_STATUSES_FETCH_FAIL;
error: any;
}
const fetchTrendingStatuses = () =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
@ -15,20 +30,26 @@ const fetchTrendingStatuses = () =>
if (!client.features.trendingStatuses) return;
dispatch({ type: TRENDING_STATUSES_FETCH_REQUEST });
dispatch<TrendingStatusesFetchRequestAction>({ type: TRENDING_STATUSES_FETCH_REQUEST });
return client.trends.getTrendingStatuses().then((statuses) => {
dispatch(importEntities({ statuses }));
dispatch({ type: TRENDING_STATUSES_FETCH_SUCCESS, statuses });
dispatch<TrendingStatusesFetchSuccessAction>({ type: TRENDING_STATUSES_FETCH_SUCCESS, statuses });
return statuses;
}).catch(error => {
dispatch({ type: TRENDING_STATUSES_FETCH_FAIL, error });
dispatch<TrendingStatusesFetchFailAction>({ type: TRENDING_STATUSES_FETCH_FAIL, error });
});
};
type TrendingStatusesAction =
| TrendingStatusesFetchRequestAction
| TrendingStatusesFetchSuccessAction
| TrendingStatusesFetchFailAction;
export {
TRENDING_STATUSES_FETCH_REQUEST,
TRENDING_STATUSES_FETCH_SUCCESS,
TRENDING_STATUSES_FETCH_FAIL,
fetchTrendingStatuses,
type TrendingStatusesAction,
};

View file

@ -64,7 +64,7 @@ import {
import { EVENT_COMPOSE_CANCEL, EVENT_FORM_SET, type EventsAction } from '../actions/events';
import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS, MeAction } from '../actions/me';
import { FE_NAME } from '../actions/settings';
import { TIMELINE_DELETE, TimelineAction } from '../actions/timelines';
import { TIMELINE_DELETE, type TimelineAction } from '../actions/timelines';
import { unescapeHTML } from '../utils/html';
import type { Emoji } from 'pl-fe/features/emoji';

View file

@ -6,6 +6,7 @@ import {
SUGGESTIONS_FETCH_REQUEST,
SUGGESTIONS_FETCH_SUCCESS,
SUGGESTIONS_FETCH_FAIL,
type SuggestionsAction,
} from 'pl-fe/actions/suggestions';
import type { Suggestion as SuggestionEntity } from 'pl-api';
@ -37,7 +38,7 @@ const dismissAccount = (state: State, accountId: string) =>
const dismissAccounts = (state: State, accountIds: string[]) =>
state.update('items', items => items.filter(item => !accountIds.includes(item.account_id)));
const suggestionsReducer = (state: State = ReducerRecord(), action: AnyAction | DomainBlocksAction) => {
const suggestionsReducer = (state: State = ReducerRecord(), action: AnyAction | DomainBlocksAction | SuggestionsAction) => {
switch (action.type) {
case SUGGESTIONS_FETCH_REQUEST:
return state.set('isLoading', true);