pl-hooks migration works
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
92497207a8
commit
742e66c72b
14 changed files with 46 additions and 369 deletions
2
compat/bootstrap-compat.scss
vendored
2
compat/bootstrap-compat.scss
vendored
|
@ -504,7 +504,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&__inner {
|
&__inner {
|
||||||
@apply flex rtl:space-x-reverse items-center space-x-1 grow;
|
@apply flex items-center gap-x-1 grow;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@apply font-normal;
|
@apply font-normal;
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
import { importEntities } from 'pl-hooks';
|
|
||||||
|
|
||||||
import client from 'bigbuffet/client';
|
|
||||||
|
|
||||||
import type { AppDispatch, RootState } from 'bigbuffet/store';
|
|
||||||
import type { StatusEdit } from 'pl-api';
|
|
||||||
|
|
||||||
const HISTORY_FETCH_REQUEST = 'HISTORY_FETCH_REQUEST' as const;
|
|
||||||
const HISTORY_FETCH_SUCCESS = 'HISTORY_FETCH_SUCCESS' as const;
|
|
||||||
const HISTORY_FETCH_FAIL = 'HISTORY_FETCH_FAIL' as const;
|
|
||||||
|
|
||||||
const fetchHistory = (statusId: string) =>
|
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
|
||||||
const loading = getState().history.get(statusId)?.loading;
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(fetchHistoryRequest(statusId));
|
|
||||||
|
|
||||||
return client.statuses.getStatusHistory(statusId).then(data => {
|
|
||||||
importEntities({ accounts: data.map((x) => x.account) });
|
|
||||||
dispatch(fetchHistorySuccess(statusId, data));
|
|
||||||
}).catch(error => dispatch(fetchHistoryFail(statusId, error)));
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchHistoryRequest = (statusId: string) => ({
|
|
||||||
type: HISTORY_FETCH_REQUEST,
|
|
||||||
statusId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const fetchHistorySuccess = (statusId: string, history: Array<StatusEdit>) => ({
|
|
||||||
type: HISTORY_FETCH_SUCCESS,
|
|
||||||
statusId,
|
|
||||||
history,
|
|
||||||
});
|
|
||||||
|
|
||||||
const fetchHistoryFail = (statusId: string, error: unknown) => ({
|
|
||||||
type: HISTORY_FETCH_FAIL,
|
|
||||||
statusId,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
|
||||||
type HistoryAction =
|
|
||||||
| ReturnType<typeof fetchHistoryRequest>
|
|
||||||
| ReturnType<typeof fetchHistorySuccess>
|
|
||||||
| ReturnType<typeof fetchHistoryFail>;
|
|
||||||
|
|
||||||
export {
|
|
||||||
HISTORY_FETCH_REQUEST,
|
|
||||||
HISTORY_FETCH_SUCCESS,
|
|
||||||
HISTORY_FETCH_FAIL,
|
|
||||||
fetchHistory,
|
|
||||||
fetchHistoryRequest,
|
|
||||||
fetchHistorySuccess,
|
|
||||||
fetchHistoryFail,
|
|
||||||
type HistoryAction,
|
|
||||||
};
|
|
|
@ -1,17 +1,13 @@
|
||||||
import type { DirectoryAction } from './directory';
|
import type { DirectoryAction } from './directory';
|
||||||
import type { HashtagsAction } from './hashtags';
|
import type { HashtagsAction } from './hashtags';
|
||||||
import type { HistoryAction } from './history';
|
|
||||||
import type { SearchAction } from './search';
|
import type { SearchAction } from './search';
|
||||||
import type { StatusQuotesAction } from './status-quotes';
|
|
||||||
import type { StatusesAction } from './statuses';
|
import type { StatusesAction } from './statuses';
|
||||||
import type { TimelineAction } from './timelines';
|
import type { TimelineAction } from './timelines';
|
||||||
|
|
||||||
type ActionType =
|
type ActionType =
|
||||||
| DirectoryAction
|
| DirectoryAction
|
||||||
| HashtagsAction
|
| HashtagsAction
|
||||||
| HistoryAction
|
|
||||||
| SearchAction
|
| SearchAction
|
||||||
| StatusQuotesAction
|
|
||||||
| StatusesAction
|
| StatusesAction
|
||||||
| TimelineAction;
|
| TimelineAction;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
import client from 'bigbuffet/client';
|
|
||||||
|
|
||||||
const remoteInteraction = (ap_id: string, profile: string) =>
|
|
||||||
client.accounts.remoteInteraction(ap_id, profile)
|
|
||||||
.then((data) => data.url)
|
|
||||||
.catch(error => {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
|
|
||||||
export { remoteInteraction };
|
|
|
@ -1,135 +0,0 @@
|
||||||
import { importEntities } from 'pl-hooks';
|
|
||||||
|
|
||||||
import client from 'bigbuffet/client';
|
|
||||||
|
|
||||||
import type { AppDispatch, RootState } from 'bigbuffet/store';
|
|
||||||
import type { Status as BaseStatus, PaginatedResponse } from 'pl-api';
|
|
||||||
|
|
||||||
const STATUS_QUOTES_FETCH_REQUEST = 'STATUS_QUOTES_FETCH_REQUEST';
|
|
||||||
const STATUS_QUOTES_FETCH_SUCCESS = 'STATUS_QUOTES_FETCH_SUCCESS';
|
|
||||||
const STATUS_QUOTES_FETCH_FAIL = 'STATUS_QUOTES_FETCH_FAIL';
|
|
||||||
|
|
||||||
const STATUS_QUOTES_EXPAND_REQUEST = 'STATUS_QUOTES_EXPAND_REQUEST';
|
|
||||||
const STATUS_QUOTES_EXPAND_SUCCESS = 'STATUS_QUOTES_EXPAND_SUCCESS';
|
|
||||||
const STATUS_QUOTES_EXPAND_FAIL = 'STATUS_QUOTES_EXPAND_FAIL';
|
|
||||||
|
|
||||||
const noOp = () => new Promise(f => f(null));
|
|
||||||
|
|
||||||
interface FetchStatusQuotesRequestAction {
|
|
||||||
type: typeof STATUS_QUOTES_FETCH_REQUEST;
|
|
||||||
statusId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FetchStatusQuotesSuccessAction {
|
|
||||||
type: typeof STATUS_QUOTES_FETCH_SUCCESS;
|
|
||||||
statusId: string;
|
|
||||||
statuses: Array<BaseStatus>;
|
|
||||||
next: (() => Promise<PaginatedResponse<BaseStatus>>) | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FetchStatusQuotesFailAction {
|
|
||||||
type: typeof STATUS_QUOTES_FETCH_FAIL;
|
|
||||||
statusId: string;
|
|
||||||
error: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchStatusQuotes = (statusId: string) =>
|
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
|
||||||
if (getState().status_lists.get(`quotes:${statusId}`)?.isLoading) {
|
|
||||||
return dispatch(noOp);
|
|
||||||
}
|
|
||||||
|
|
||||||
const action: FetchStatusQuotesRequestAction = { type: STATUS_QUOTES_FETCH_REQUEST, statusId };
|
|
||||||
dispatch(action);
|
|
||||||
|
|
||||||
return client.statuses.getStatusQuotes(statusId).then(response => {
|
|
||||||
importEntities({ statuses: response.items });
|
|
||||||
|
|
||||||
const action: FetchStatusQuotesSuccessAction = {
|
|
||||||
type: STATUS_QUOTES_FETCH_SUCCESS,
|
|
||||||
statusId,
|
|
||||||
statuses: response.items,
|
|
||||||
next: response.next,
|
|
||||||
};
|
|
||||||
return dispatch(action);
|
|
||||||
}).catch(error => {
|
|
||||||
const action: FetchStatusQuotesFailAction = {
|
|
||||||
type: STATUS_QUOTES_FETCH_FAIL,
|
|
||||||
statusId,
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
dispatch(action);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
interface ExpandStatusQuotesRequestAction {
|
|
||||||
type: typeof STATUS_QUOTES_EXPAND_REQUEST;
|
|
||||||
statusId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ExpandStatusQuotesSuccessAction {
|
|
||||||
type: typeof STATUS_QUOTES_EXPAND_SUCCESS;
|
|
||||||
statusId: string;
|
|
||||||
statuses: Array<BaseStatus>;
|
|
||||||
next: (() => Promise<PaginatedResponse<BaseStatus>>) | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ExpandStatusQuotesFailAction {
|
|
||||||
type: typeof STATUS_QUOTES_EXPAND_FAIL;
|
|
||||||
statusId: string;
|
|
||||||
error: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
const expandStatusQuotes = (statusId: string) =>
|
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
|
||||||
const next = getState().status_lists.get(`quotes:${statusId}`)?.next || null;
|
|
||||||
|
|
||||||
if (next === null || getState().status_lists.get(`quotes:${statusId}`)?.isLoading) {
|
|
||||||
return dispatch(noOp);
|
|
||||||
}
|
|
||||||
|
|
||||||
const action: ExpandStatusQuotesRequestAction = {
|
|
||||||
type: STATUS_QUOTES_EXPAND_REQUEST,
|
|
||||||
statusId,
|
|
||||||
};
|
|
||||||
dispatch(action);
|
|
||||||
|
|
||||||
return next().then(response => {
|
|
||||||
importEntities({ statuses: response.items });
|
|
||||||
|
|
||||||
const action: ExpandStatusQuotesSuccessAction = {
|
|
||||||
type: STATUS_QUOTES_EXPAND_SUCCESS,
|
|
||||||
statusId,
|
|
||||||
statuses: response.items,
|
|
||||||
next: response.next,
|
|
||||||
};
|
|
||||||
dispatch(action);
|
|
||||||
}).catch(error => {
|
|
||||||
const action: ExpandStatusQuotesFailAction = {
|
|
||||||
type: STATUS_QUOTES_EXPAND_FAIL,
|
|
||||||
statusId,
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
dispatch(action);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
type StatusQuotesAction =
|
|
||||||
| FetchStatusQuotesRequestAction
|
|
||||||
| FetchStatusQuotesSuccessAction
|
|
||||||
| FetchStatusQuotesFailAction
|
|
||||||
| ExpandStatusQuotesRequestAction
|
|
||||||
| ExpandStatusQuotesSuccessAction
|
|
||||||
| ExpandStatusQuotesFailAction;
|
|
||||||
|
|
||||||
export {
|
|
||||||
STATUS_QUOTES_FETCH_REQUEST,
|
|
||||||
STATUS_QUOTES_FETCH_SUCCESS,
|
|
||||||
STATUS_QUOTES_FETCH_FAIL,
|
|
||||||
STATUS_QUOTES_EXPAND_REQUEST,
|
|
||||||
STATUS_QUOTES_EXPAND_SUCCESS,
|
|
||||||
STATUS_QUOTES_EXPAND_FAIL,
|
|
||||||
fetchStatusQuotes,
|
|
||||||
expandStatusQuotes,
|
|
||||||
type StatusQuotesAction,
|
|
||||||
};
|
|
|
@ -1,44 +1,33 @@
|
||||||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||||
import debounce from 'lodash/debounce';
|
import { useStatusQuotes } from 'pl-hooks';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { expandStatusQuotes, fetchStatusQuotes } from 'bigbuffet/actions/status-quotes';
|
|
||||||
import StatusList from 'bigbuffet/components/status-list';
|
import StatusList from 'bigbuffet/components/status-list';
|
||||||
import Column from 'bigbuffet/components/ui/column';
|
import Column from 'bigbuffet/components/ui/column';
|
||||||
import { useAppDispatch } from 'bigbuffet/hooks/useAppDispatch';
|
|
||||||
import { useAppSelector } from 'bigbuffet/hooks/useAppSelector';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
heading: { id: 'column.quotes', defaultMessage: 'Post quotes' },
|
heading: { id: 'column.quotes', defaultMessage: 'Post quotes' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleLoadMore = debounce((statusId: string, dispatch: React.Dispatch<any>) =>
|
|
||||||
dispatch(expandStatusQuotes(statusId)), 300, { leading: true });
|
|
||||||
|
|
||||||
const Quotes: React.FC = () => {
|
const Quotes: React.FC = () => {
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { statusId } = useParams<{ statusId: string }>();
|
const { statusId } = useParams<{ statusId: string }>();
|
||||||
|
|
||||||
const statusIds = useAppSelector((state) => state.status_lists.getIn([`quotes:${statusId}`, 'items'], ImmutableOrderedSet<string>()));
|
const { data, isLoading, hasNextPage, fetchNextPage } = useStatusQuotes(statusId);
|
||||||
const isLoading = useAppSelector((state) => state.status_lists.getIn([`quotes:${statusId}`, 'isLoading'], true));
|
|
||||||
const hasMore = useAppSelector((state) => !!state.status_lists.getIn([`quotes:${statusId}`, 'next']));
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
const statusIds = ImmutableOrderedSet(data);
|
||||||
dispatch(fetchStatusQuotes(statusId));
|
|
||||||
}, [statusId]);
|
|
||||||
|
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.quotes' defaultMessage='This post has not been quoted yet.' />;
|
const emptyMessage = <FormattedMessage id='empty_column.quotes' defaultMessage='This post has not been quoted yet.' />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column label={intl.formatMessage(messages.heading)} transparent>
|
<Column label={intl.formatMessage(messages.heading)} transparent>
|
||||||
<StatusList
|
<StatusList
|
||||||
statusIds={statusIds as ImmutableOrderedSet<string>}
|
statusIds={statusIds}
|
||||||
hasMore={hasMore}
|
hasMore={hasNextPage}
|
||||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||||
onLoadMore={() => handleLoadMore(statusId, dispatch)}
|
onLoadMore={() => fetchNextPage()}
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
divideType='space'
|
divideType='space'
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import React, { useEffect } from 'react';
|
import { useStatusHistory } from 'pl-hooks';
|
||||||
|
import React from 'react';
|
||||||
import { FormattedDate, FormattedMessage } from 'react-intl';
|
import { FormattedDate, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { fetchHistory } from 'bigbuffet/actions/history';
|
|
||||||
import AttachmentThumbs from 'bigbuffet/components/attachment-thumbs';
|
import AttachmentThumbs from 'bigbuffet/components/attachment-thumbs';
|
||||||
import { ParsedContent } from 'bigbuffet/components/parsed-content';
|
import { ParsedContent } from 'bigbuffet/components/parsed-content';
|
||||||
import Modal from 'bigbuffet/components/ui/modal';
|
import Modal from 'bigbuffet/components/ui/modal';
|
||||||
import Spinner from 'bigbuffet/components/ui/spinner';
|
import Spinner from 'bigbuffet/components/ui/spinner';
|
||||||
import Emojify from 'bigbuffet/features/emoji';
|
import Emojify from 'bigbuffet/features/emoji';
|
||||||
import { useAppDispatch } from 'bigbuffet/hooks/useAppDispatch';
|
|
||||||
import { useAppSelector } from 'bigbuffet/hooks/useAppSelector';
|
|
||||||
|
|
||||||
import { BaseModalProps } from '../modal-root';
|
import { BaseModalProps } from '../modal-root';
|
||||||
|
|
||||||
|
@ -18,28 +16,22 @@ interface CompareHistoryModalProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const CompareHistoryModal: React.FC<BaseModalProps & CompareHistoryModalProps> = ({ onClose, statusId }) => {
|
const CompareHistoryModal: React.FC<BaseModalProps & CompareHistoryModalProps> = ({ onClose, statusId }) => {
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
|
|
||||||
const loading = useAppSelector(state => state.history.get(statusId)?.loading);
|
const { isLoading, data: versions } = useStatusHistory(statusId);
|
||||||
const versions = useAppSelector(state => state.history.get(statusId)?.items);
|
|
||||||
|
|
||||||
const onClickClose = () => {
|
const onClickClose = () => {
|
||||||
onClose('COMPARE_HISTORY');
|
onClose('COMPARE_HISTORY');
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch(fetchHistory(statusId));
|
|
||||||
}, [statusId]);
|
|
||||||
|
|
||||||
let body: JSX.Element;
|
let body: JSX.Element;
|
||||||
|
|
||||||
if (loading) {
|
if (isLoading) {
|
||||||
body = <Spinner />;
|
body = <Spinner />;
|
||||||
} else {
|
} else {
|
||||||
body = (
|
body = (
|
||||||
<div className='compare-history'>
|
<div className='compare-history'>
|
||||||
{versions?.map((version) => {
|
{versions?.map((version) => {
|
||||||
const poll = typeof version.poll !== 'string' && version.poll;
|
const poll = version.poll;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='compare-history__version'>
|
<div className='compare-history__version'>
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { useAccount } from 'pl-hooks';
|
import { useAccount, usePlHooksApiClient } from 'pl-hooks';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { remoteInteraction } from 'bigbuffet/actions/interactions';
|
|
||||||
import Input from 'bigbuffet/components/ui/input';
|
import Input from 'bigbuffet/components/ui/input';
|
||||||
import Modal from 'bigbuffet/components/ui/modal';
|
import Modal from 'bigbuffet/components/ui/modal';
|
||||||
import toast from 'bigbuffet/toast';
|
import toast from 'bigbuffet/toast';
|
||||||
|
@ -27,6 +26,7 @@ interface UnauthorizedModalProps {
|
||||||
/** Modal to display when a logged-out user tries to do something that requires login. */
|
/** Modal to display when a logged-out user tries to do something that requires login. */
|
||||||
const UnauthorizedModal: React.FC<BaseModalProps & UnauthorizedModalProps> = ({ action, onClose, account: accountId, ap_id: apId }) => {
|
const UnauthorizedModal: React.FC<BaseModalProps & UnauthorizedModalProps> = ({ action, onClose, account: accountId, ap_id: apId }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const { client } = usePlHooksApiClient();
|
||||||
|
|
||||||
const accountQuery = useAccount(accountId);
|
const accountQuery = useAccount(accountId);
|
||||||
|
|
||||||
|
@ -41,8 +41,8 @@ const UnauthorizedModal: React.FC<BaseModalProps & UnauthorizedModalProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
remoteInteraction(apId!, account)
|
client.accounts.remoteInteraction(apId!, account)
|
||||||
.then(url => {
|
.then(({ url }) => {
|
||||||
window.open(url, '_new', 'noopener,noreferrer');
|
window.open(url, '_new', 'noopener,noreferrer');
|
||||||
onClose('UNAUTHORIZED');
|
onClose('UNAUTHORIZED');
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
import { List as ImmutableList, Map as ImmutableMap, Record as ImmutableRecord } from 'immutable';
|
|
||||||
|
|
||||||
import { HISTORY_FETCH_REQUEST, HISTORY_FETCH_SUCCESS, HISTORY_FETCH_FAIL, type HistoryAction } from 'bigbuffet/actions/history';
|
|
||||||
|
|
||||||
import type { StatusEdit } from 'pl-api';
|
|
||||||
|
|
||||||
const HistoryRecord = ImmutableRecord({
|
|
||||||
loading: false,
|
|
||||||
items: ImmutableList<StatusEdit>(),
|
|
||||||
});
|
|
||||||
|
|
||||||
type State = ImmutableMap<string, ReturnType<typeof HistoryRecord>>;
|
|
||||||
|
|
||||||
const initialState: State = ImmutableMap();
|
|
||||||
|
|
||||||
const history = (state: State = initialState, action: HistoryAction) => {
|
|
||||||
switch (action.type) {
|
|
||||||
case HISTORY_FETCH_REQUEST:
|
|
||||||
return state.update(action.statusId, HistoryRecord(), history => history!.withMutations(map => {
|
|
||||||
map.set('loading', true);
|
|
||||||
map.set('items', ImmutableList());
|
|
||||||
}));
|
|
||||||
case HISTORY_FETCH_SUCCESS:
|
|
||||||
return state.update(action.statusId, HistoryRecord(), history => history!.withMutations(map => {
|
|
||||||
map.set('loading', false);
|
|
||||||
map.set('items', ImmutableList(action.history.map((x, i: number) => ({ ...x, original: i === 0 })).toReversed()));
|
|
||||||
}));
|
|
||||||
case HISTORY_FETCH_FAIL:
|
|
||||||
return state.update(action.statusId, HistoryRecord(), history => history!.set('loading', false));
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default history;
|
|
|
@ -2,18 +2,14 @@ import { combineReducers } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
import contexts from './contexts';
|
import contexts from './contexts';
|
||||||
import hashtags from './hashtags';
|
import hashtags from './hashtags';
|
||||||
import history from './history';
|
|
||||||
import search from './search';
|
import search from './search';
|
||||||
import status_lists from './status-lists';
|
|
||||||
import timelines from './timelines';
|
import timelines from './timelines';
|
||||||
import user_lists from './user-lists';
|
import user_lists from './user-lists';
|
||||||
|
|
||||||
const reducers = {
|
const reducers = {
|
||||||
contexts,
|
contexts,
|
||||||
hashtags,
|
hashtags,
|
||||||
history,
|
|
||||||
search,
|
search,
|
||||||
status_lists,
|
|
||||||
timelines,
|
timelines,
|
||||||
user_lists,
|
user_lists,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
import {
|
|
||||||
Map as ImmutableMap,
|
|
||||||
OrderedSet as ImmutableOrderedSet,
|
|
||||||
Record as ImmutableRecord,
|
|
||||||
} from 'immutable';
|
|
||||||
|
|
||||||
import {
|
|
||||||
STATUS_QUOTES_EXPAND_FAIL,
|
|
||||||
STATUS_QUOTES_EXPAND_REQUEST,
|
|
||||||
STATUS_QUOTES_EXPAND_SUCCESS,
|
|
||||||
STATUS_QUOTES_FETCH_FAIL,
|
|
||||||
STATUS_QUOTES_FETCH_REQUEST,
|
|
||||||
STATUS_QUOTES_FETCH_SUCCESS,
|
|
||||||
type StatusQuotesAction,
|
|
||||||
} from 'bigbuffet/actions/status-quotes';
|
|
||||||
|
|
||||||
import type { PaginatedResponse, Status } from 'pl-api';
|
|
||||||
|
|
||||||
export const StatusListRecord = ImmutableRecord({
|
|
||||||
next: null as (() => Promise<PaginatedResponse<Status>>) | null,
|
|
||||||
loaded: false,
|
|
||||||
isLoading: null as boolean | null,
|
|
||||||
items: ImmutableOrderedSet<string>(),
|
|
||||||
});
|
|
||||||
|
|
||||||
type State = ImmutableMap<string, StatusList>;
|
|
||||||
type StatusList = ReturnType<typeof StatusListRecord>;
|
|
||||||
|
|
||||||
const initialState: State = ImmutableMap({});
|
|
||||||
|
|
||||||
const getStatusId = (status: string | Pick<Status, 'id'>) => typeof status === 'string' ? status : status.id;
|
|
||||||
|
|
||||||
const getStatusIds = (statuses: Array<string | Pick<Status, 'id'>> = []) => (
|
|
||||||
ImmutableOrderedSet(statuses.map(getStatusId))
|
|
||||||
);
|
|
||||||
|
|
||||||
const setLoading = (state: State, listType: string, loading: boolean) => state.setIn([listType, 'isLoading'], loading);
|
|
||||||
|
|
||||||
const normalizeList = (state: State, listType: string, statuses: Array<string | Pick<Status, 'id'>>, next: (() => Promise<PaginatedResponse<Status>>) | null) =>
|
|
||||||
state.update(listType, StatusListRecord(), listMap => listMap.withMutations(map => {
|
|
||||||
map.set('next', next);
|
|
||||||
map.set('loaded', true);
|
|
||||||
map.set('isLoading', false);
|
|
||||||
map.set('items', getStatusIds(statuses));
|
|
||||||
}));
|
|
||||||
|
|
||||||
const appendToList = (state: State, listType: string, statuses: Array<string | Pick<Status, 'id'>>, next: (() => Promise<PaginatedResponse<Status>>) | null) => {
|
|
||||||
const newIds = getStatusIds(statuses);
|
|
||||||
|
|
||||||
return state.update(listType, StatusListRecord(), listMap => listMap.withMutations(map => {
|
|
||||||
map.set('next', next);
|
|
||||||
map.set('isLoading', false);
|
|
||||||
map.update('items', items => items.union(newIds));
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const statusLists = (state = initialState, action: StatusQuotesAction) => {
|
|
||||||
switch (action.type) {
|
|
||||||
case STATUS_QUOTES_FETCH_REQUEST:
|
|
||||||
case STATUS_QUOTES_EXPAND_REQUEST:
|
|
||||||
return setLoading(state, `quotes:${action.statusId}`, true);
|
|
||||||
case STATUS_QUOTES_FETCH_FAIL:
|
|
||||||
case STATUS_QUOTES_EXPAND_FAIL:
|
|
||||||
return setLoading(state, `quotes:${action.statusId}`, false);
|
|
||||||
case STATUS_QUOTES_FETCH_SUCCESS:
|
|
||||||
return normalizeList(state, `quotes:${action.statusId}`, action.statuses, action.next);
|
|
||||||
case STATUS_QUOTES_EXPAND_SUCCESS:
|
|
||||||
return appendToList(state, `quotes:${action.statusId}`, action.statuses, action.next);
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default statusLists;
|
|
|
@ -1,18 +1,27 @@
|
||||||
.account-card {
|
.account-card {
|
||||||
@apply flex shrink-0 items-center justify-between w-full;
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
&__container {
|
&__container {
|
||||||
@apply shrink-0 block w-full;
|
flex-shrink: 0;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__account {
|
&__account {
|
||||||
@apply flex rtl:space-x-reverse items-center space-x-3 overflow-hidden;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
&__name {
|
&__name {
|
||||||
@apply grow overflow-hidden;
|
@apply grow overflow-hidden;
|
||||||
|
|
||||||
&__display-name {
|
&__display-name {
|
||||||
@apply flex rtl:space-x-reverse items-center space-x-1 grow;
|
@apply flex rtl:space-x-reverse items-center gap-x-1 grow;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@apply truncate text-sm text-gray-900 dark:text-gray-100 font-semibold tracking-normal font-sans normal-case;
|
@apply truncate text-sm text-gray-900 dark:text-gray-100 font-semibold tracking-normal font-sans normal-case;
|
||||||
|
@ -138,7 +147,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&__name {
|
&__name {
|
||||||
@apply flex flex-col;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__display-name {
|
&__display-name {
|
||||||
|
@ -312,11 +322,12 @@
|
||||||
@apply flex flex-col space-y-2;
|
@apply flex flex-col space-y-2;
|
||||||
|
|
||||||
&__container {
|
&__container {
|
||||||
@apply relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
@apply flex flex-col;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__banner {
|
&__banner {
|
||||||
|
@ -340,7 +351,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&__name {
|
&__name {
|
||||||
@apply flex flex-col;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__display-name {
|
&__display-name {
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&__details {
|
&__details {
|
||||||
@apply flex flex-col;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__name {
|
&__name {
|
||||||
|
@ -20,15 +21,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&__sparklines {
|
&__sparklines {
|
||||||
@apply w-[2.5rem];
|
width: 2.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.hashtags-panel {
|
.hashtags-panel {
|
||||||
@apply flex flex-col space-y-2;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
.hashtag-link {
|
.hashtag-link {
|
||||||
@apply flex flex-col;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
> p:first-child {
|
> p:first-child {
|
||||||
@apply text-base leading-5 text-gray-900 dark:text-gray-100 font-normal tracking-normal font-sans normal-case;
|
@apply text-base leading-5 text-gray-900 dark:text-gray-100 font-normal tracking-normal font-sans normal-case;
|
||||||
|
|
|
@ -246,7 +246,8 @@
|
||||||
|
|
||||||
.poll {
|
.poll {
|
||||||
> div {
|
> div {
|
||||||
@apply flex flex-col;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-option {
|
&-option {
|
||||||
|
|
Loading…
Reference in a new issue