kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Add normalizers, fix tests
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>environments/review-develop-3zknud/deployments/243^2
rodzic
6c45dcb109
commit
a66c174c2d
|
@ -10,16 +10,14 @@ import { shortNumberFormat } from '../utils/numbers';
|
||||||
import Permalink from './permalink';
|
import Permalink from './permalink';
|
||||||
import { HStack, Stack, Text } from './ui';
|
import { HStack, Stack, Text } from './ui';
|
||||||
|
|
||||||
import type { Hashtag as HashtagEntity } from 'soapbox/reducers/search';
|
import type { Tag } from 'soapbox/types/entities';
|
||||||
import type { TrendingHashtag } from 'soapbox/reducers/trends';
|
|
||||||
|
|
||||||
interface IHashtag {
|
interface IHashtag {
|
||||||
hashtag: HashtagEntity | TrendingHashtag,
|
hashtag: Tag,
|
||||||
}
|
}
|
||||||
|
|
||||||
const Hashtag: React.FC<IHashtag> = ({ hashtag }) => {
|
const Hashtag: React.FC<IHashtag> = ({ hashtag }) => {
|
||||||
const history = (hashtag as TrendingHashtag).history;
|
const count = Number(hashtag.history?.get(0)?.accounts);
|
||||||
const count = Number(history?.get(0)?.accounts);
|
|
||||||
const brandColor = useSelector((state) => getSoapboxConfig(state).brandColor);
|
const brandColor = useSelector((state) => getSoapboxConfig(state).brandColor);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -29,7 +27,7 @@ const Hashtag: React.FC<IHashtag> = ({ hashtag }) => {
|
||||||
<Text tag='span' size='sm' weight='semibold'>#{hashtag.name}</Text>
|
<Text tag='span' size='sm' weight='semibold'>#{hashtag.name}</Text>
|
||||||
</Permalink>
|
</Permalink>
|
||||||
|
|
||||||
{history && (
|
{hashtag.history && (
|
||||||
<Text theme='muted' size='sm'>
|
<Text theme='muted' size='sm'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='trends.count_by_accounts'
|
id='trends.count_by_accounts'
|
||||||
|
@ -43,12 +41,12 @@ const Hashtag: React.FC<IHashtag> = ({ hashtag }) => {
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{history && (
|
{hashtag.history && (
|
||||||
<div className='w-[40px]' data-testid='sparklines'>
|
<div className='w-[40px]' data-testid='sparklines'>
|
||||||
<Sparklines
|
<Sparklines
|
||||||
width={40}
|
width={40}
|
||||||
height={28}
|
height={28}
|
||||||
data={history.reverse().map((day) => +day.uses).toArray()}
|
data={hashtag.history.reverse().map((day) => +day.uses).toArray()}
|
||||||
>
|
>
|
||||||
<SparklinesCurve style={{ fill: 'none' }} color={brandColor} />
|
<SparklinesCurve style={{ fill: 'none' }} color={brandColor} />
|
||||||
</Sparklines>
|
</Sparklines>
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
import { List as ImmutableList } from 'immutable';
|
import { List as ImmutableList, Record as ImmutableRecord } from 'immutable';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { render, screen } from '../../../../jest/test-helpers';
|
import { render, screen } from '../../../../jest/test-helpers';
|
||||||
|
import { normalizeTag } from '../../../../normalizers';
|
||||||
import TrendsPanel from '../trends-panel';
|
import TrendsPanel from '../trends-panel';
|
||||||
|
|
||||||
describe('<TrendsPanel />', () => {
|
describe('<TrendsPanel />', () => {
|
||||||
it('renders trending hashtags', () => {
|
it('renders trending hashtags', () => {
|
||||||
const store = {
|
const store = {
|
||||||
trends: {
|
trends: ImmutableRecord({
|
||||||
items: ImmutableList([{
|
items: ImmutableList([
|
||||||
name: 'hashtag 1',
|
normalizeTag({
|
||||||
history: ImmutableList([{
|
name: 'hashtag 1',
|
||||||
day: '1652745600',
|
history: [{
|
||||||
uses: '294',
|
day: '1652745600',
|
||||||
accounts: '180',
|
uses: '294',
|
||||||
}]),
|
accounts: '180',
|
||||||
}]),
|
}],
|
||||||
},
|
}),
|
||||||
|
]),
|
||||||
|
isLoading: false,
|
||||||
|
})(),
|
||||||
};
|
};
|
||||||
|
|
||||||
render(<TrendsPanel limit={1} />, null, store);
|
render(<TrendsPanel limit={1} />, null, store);
|
||||||
|
@ -27,18 +31,19 @@ describe('<TrendsPanel />', () => {
|
||||||
|
|
||||||
it('renders multiple trends', () => {
|
it('renders multiple trends', () => {
|
||||||
const store = {
|
const store = {
|
||||||
trends: {
|
trends: ImmutableRecord({
|
||||||
items: ImmutableList([
|
items: ImmutableList([
|
||||||
{
|
normalizeTag({
|
||||||
name: 'hashtag 1',
|
name: 'hashtag 1',
|
||||||
history: ImmutableList([{ accounts: [] }]),
|
history: ImmutableList([{ accounts: [] }]),
|
||||||
},
|
}),
|
||||||
{
|
normalizeTag({
|
||||||
name: 'hashtag 2',
|
name: 'hashtag 2',
|
||||||
history: ImmutableList([{ accounts: [] }]),
|
history: ImmutableList([{ accounts: [] }]),
|
||||||
},
|
}),
|
||||||
]),
|
]),
|
||||||
},
|
isLoading: false,
|
||||||
|
})(),
|
||||||
};
|
};
|
||||||
|
|
||||||
render(<TrendsPanel limit={3} />, null, store);
|
render(<TrendsPanel limit={3} />, null, store);
|
||||||
|
@ -47,18 +52,19 @@ describe('<TrendsPanel />', () => {
|
||||||
|
|
||||||
it('respects the limit prop', () => {
|
it('respects the limit prop', () => {
|
||||||
const store = {
|
const store = {
|
||||||
trends: {
|
trends: ImmutableRecord({
|
||||||
items: ImmutableList([
|
items: ImmutableList([
|
||||||
{
|
normalizeTag({
|
||||||
name: 'hashtag 1',
|
name: 'hashtag 1',
|
||||||
history: ImmutableList([{ accounts: [] }]),
|
history: [{ accounts: [] }],
|
||||||
},
|
}),
|
||||||
{
|
normalizeTag({
|
||||||
name: 'hashtag 2',
|
name: 'hashtag 2',
|
||||||
history: ImmutableList([{ accounts: [] }]),
|
history: [{ accounts: [] }],
|
||||||
},
|
}),
|
||||||
]),
|
]),
|
||||||
},
|
isLoading: false,
|
||||||
|
})(),
|
||||||
};
|
};
|
||||||
|
|
||||||
render(<TrendsPanel limit={1} />, null, store);
|
render(<TrendsPanel limit={1} />, null, store);
|
||||||
|
@ -67,9 +73,10 @@ describe('<TrendsPanel />', () => {
|
||||||
|
|
||||||
it('renders empty', () => {
|
it('renders empty', () => {
|
||||||
const store = {
|
const store = {
|
||||||
trends: {
|
trends: ImmutableRecord({
|
||||||
items: ImmutableList([]),
|
items: ImmutableList([]),
|
||||||
},
|
isLoading: false,
|
||||||
|
})(),
|
||||||
};
|
};
|
||||||
|
|
||||||
render(<TrendsPanel limit={1} />, null, store);
|
render(<TrendsPanel limit={1} />, null, store);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { Map as ImmutableMap, Set as ImmutableSet } from 'immutable';
|
import { Map as ImmutableMap, Record as ImmutableRecord, Set as ImmutableSet } from 'immutable';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { __stub } from 'soapbox/api';
|
import { __stub } from 'soapbox/api';
|
||||||
|
@ -24,13 +24,13 @@ describe('<ReportModal />', () => {
|
||||||
avatar: 'test.jpg',
|
avatar: 'test.jpg',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
reports: ImmutableMap({
|
reports: ImmutableRecord({
|
||||||
new: {
|
new: ImmutableRecord({
|
||||||
account_id: '1',
|
account_id: '1',
|
||||||
status_ids: ImmutableSet(['1']),
|
status_ids: ImmutableSet(['1']),
|
||||||
rule_ids: ImmutableSet(),
|
rule_ids: ImmutableSet(),
|
||||||
},
|
})(),
|
||||||
}),
|
})(),
|
||||||
statuses: ImmutableMap({
|
statuses: ImmutableMap({
|
||||||
'1': normalizeStatus(status),
|
'1': normalizeStatus(status),
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -14,7 +14,7 @@ interface ITrendsPanel {
|
||||||
const TrendsPanel = ({ limit }: ITrendsPanel) => {
|
const TrendsPanel = ({ limit }: ITrendsPanel) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const trends = useAppSelector((state) => state.trends.get('items'));
|
const trends = useAppSelector((state) => state.trends.items);
|
||||||
|
|
||||||
const sortedTrends = React.useMemo(() => {
|
const sortedTrends = React.useMemo(() => {
|
||||||
return trends.sort((a, b) => {
|
return trends.sort((a, b) => {
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* History normalizer:
|
||||||
|
* Converts API daily usage history of a hashtag into our internal format.
|
||||||
|
* @see {@link https://docs.joinmastodon.org/entities/history/}
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
Map as ImmutableMap,
|
||||||
|
Record as ImmutableRecord,
|
||||||
|
fromJS,
|
||||||
|
} from 'immutable';
|
||||||
|
|
||||||
|
// https://docs.joinmastodon.org/entities/history/
|
||||||
|
export const HistoryRecord = ImmutableRecord({
|
||||||
|
accounts: '',
|
||||||
|
day: '',
|
||||||
|
uses: '',
|
||||||
|
});
|
||||||
|
export const normalizeHistory = (history: Record<string, any>) => {
|
||||||
|
return HistoryRecord(
|
||||||
|
ImmutableMap(fromJS(history)),
|
||||||
|
);
|
||||||
|
};
|
|
@ -6,6 +6,7 @@ export { CardRecord, normalizeCard } from './card';
|
||||||
export { ChatRecord, normalizeChat } from './chat';
|
export { ChatRecord, normalizeChat } from './chat';
|
||||||
export { ChatMessageRecord, normalizeChatMessage } from './chat_message';
|
export { ChatMessageRecord, normalizeChatMessage } from './chat_message';
|
||||||
export { EmojiRecord, normalizeEmoji } from './emoji';
|
export { EmojiRecord, normalizeEmoji } from './emoji';
|
||||||
|
export { HistoryRecord, normalizeHistory } from './history';
|
||||||
export { InstanceRecord, normalizeInstance } from './instance';
|
export { InstanceRecord, normalizeInstance } from './instance';
|
||||||
export { ListRecord, normalizeList } from './list';
|
export { ListRecord, normalizeList } from './list';
|
||||||
export { MentionRecord, normalizeMention } from './mention';
|
export { MentionRecord, normalizeMention } from './mention';
|
||||||
|
@ -14,5 +15,6 @@ export { PollRecord, PollOptionRecord, normalizePoll } from './poll';
|
||||||
export { RelationshipRecord, normalizeRelationship } from './relationship';
|
export { RelationshipRecord, normalizeRelationship } from './relationship';
|
||||||
export { StatusRecord, normalizeStatus } from './status';
|
export { StatusRecord, normalizeStatus } from './status';
|
||||||
export { StatusEditRecord, normalizeStatusEdit } from './status_edit';
|
export { StatusEditRecord, normalizeStatusEdit } from './status_edit';
|
||||||
|
export { TagRecord, normalizeTag } from './tag';
|
||||||
|
|
||||||
export { SoapboxConfigRecord, normalizeSoapboxConfig } from './soapbox/soapbox_config';
|
export { SoapboxConfigRecord, normalizeSoapboxConfig } from './soapbox/soapbox_config';
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
* Tag normalizer:
|
||||||
|
* Converts API tags into our internal format.
|
||||||
|
* @see {@link https://docs.joinmastodon.org/entities/tag/}
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
List as ImmutableList,
|
||||||
|
Map as ImmutableMap,
|
||||||
|
Record as ImmutableRecord,
|
||||||
|
fromJS,
|
||||||
|
} from 'immutable';
|
||||||
|
|
||||||
|
import { normalizeHistory } from './history';
|
||||||
|
|
||||||
|
import type { History } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
// https://docs.joinmastodon.org/entities/tag/
|
||||||
|
export const TagRecord = ImmutableRecord({
|
||||||
|
name: '',
|
||||||
|
url: '',
|
||||||
|
history: null as ImmutableList<History> | null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const normalizeHistoryList = (tag: ImmutableMap<string, any>) => {
|
||||||
|
if (tag.get('history')){
|
||||||
|
return tag.update('history', ImmutableList(), attachments => {
|
||||||
|
return attachments.map(normalizeHistory);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return tag.set('history', null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const normalizeTag = (tag: Record<string, any>) => {
|
||||||
|
return TagRecord(
|
||||||
|
ImmutableMap(fromJS(tag)).withMutations(tag => {
|
||||||
|
normalizeHistoryList(tag);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
import { Map as ImmutableMap, List as ImmutableList, Record as ImmutableRecord } from 'immutable';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SEARCH_CHANGE,
|
SEARCH_CHANGE,
|
||||||
|
@ -10,14 +10,24 @@ import reducer from '../search';
|
||||||
|
|
||||||
describe('search reducer', () => {
|
describe('search reducer', () => {
|
||||||
it('should return the initial state', () => {
|
it('should return the initial state', () => {
|
||||||
expect(reducer(undefined, {})).toEqual(ImmutableMap({
|
expect(reducer(undefined, {}).toJS()).toEqual({
|
||||||
value: '',
|
value: '',
|
||||||
submitted: false,
|
submitted: false,
|
||||||
submittedValue: '',
|
submittedValue: '',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
results: ImmutableMap(),
|
results: {
|
||||||
|
accounts: [],
|
||||||
|
statuses: [],
|
||||||
|
hashtags: [],
|
||||||
|
accountsHasMore: false,
|
||||||
|
statusesHasMore: false,
|
||||||
|
hashtagsHasMore: false,
|
||||||
|
accountsLoaded: false,
|
||||||
|
statusesLoaded: false,
|
||||||
|
hashtagsLoaded: false,
|
||||||
|
},
|
||||||
filter: 'accounts',
|
filter: 'accounts',
|
||||||
}));
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('SEARCH_CHANGE', () => {
|
describe('SEARCH_CHANGE', () => {
|
||||||
|
@ -30,42 +40,54 @@ describe('search reducer', () => {
|
||||||
|
|
||||||
describe('SEARCH_CLEAR', () => {
|
describe('SEARCH_CLEAR', () => {
|
||||||
it('resets the state', () => {
|
it('resets the state', () => {
|
||||||
const state = ImmutableMap({
|
const state = ImmutableRecord({
|
||||||
value: 'hello world',
|
value: 'hello world',
|
||||||
submitted: true,
|
submitted: true,
|
||||||
submittedValue: 'hello world',
|
submittedValue: 'hello world',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
results: ImmutableMap(),
|
results: ImmutableRecord({})(),
|
||||||
filter: 'statuses',
|
filter: 'statuses',
|
||||||
});
|
})();
|
||||||
|
|
||||||
const action = { type: SEARCH_CLEAR };
|
const action = { type: SEARCH_CLEAR };
|
||||||
|
|
||||||
const expected = ImmutableMap({
|
const expected = {
|
||||||
value: '',
|
value: '',
|
||||||
submitted: false,
|
submitted: false,
|
||||||
submittedValue: '',
|
submittedValue: '',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
results: ImmutableMap(),
|
results: {
|
||||||
|
accounts: [],
|
||||||
|
statuses: [],
|
||||||
|
hashtags: [],
|
||||||
|
accountsHasMore: false,
|
||||||
|
statusesHasMore: false,
|
||||||
|
hashtagsHasMore: false,
|
||||||
|
accountsLoaded: false,
|
||||||
|
statusesLoaded: false,
|
||||||
|
hashtagsLoaded: false,
|
||||||
|
},
|
||||||
filter: 'accounts',
|
filter: 'accounts',
|
||||||
});
|
};
|
||||||
|
|
||||||
expect(reducer(state, action)).toEqual(expected);
|
expect(reducer(state, action).toJS()).toEqual(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe(SEARCH_EXPAND_SUCCESS, () => {
|
describe(SEARCH_EXPAND_SUCCESS, () => {
|
||||||
it('imports hashtags as maps', () => {
|
it('imports hashtags as maps', () => {
|
||||||
const state = ImmutableMap({
|
const state = ImmutableRecord({
|
||||||
value: 'artist',
|
value: 'artist',
|
||||||
submitted: true,
|
submitted: true,
|
||||||
submittedValue: 'artist',
|
submittedValue: 'artist',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
results: ImmutableMap({
|
results: ImmutableRecord({
|
||||||
hashtags: ImmutableList(),
|
hashtags: ImmutableList(),
|
||||||
}),
|
hashtagsHasMore: false,
|
||||||
|
hashtagsLoaded: true,
|
||||||
|
})(),
|
||||||
filter: 'hashtags',
|
filter: 'hashtags',
|
||||||
});
|
})();
|
||||||
|
|
||||||
const action = {
|
const action = {
|
||||||
type: SEARCH_EXPAND_SUCCESS,
|
type: SEARCH_EXPAND_SUCCESS,
|
||||||
|
@ -82,24 +104,26 @@ describe('search reducer', () => {
|
||||||
searchType: 'hashtags',
|
searchType: 'hashtags',
|
||||||
};
|
};
|
||||||
|
|
||||||
const expected = ImmutableMap({
|
const expected = {
|
||||||
value: 'artist',
|
value: 'artist',
|
||||||
submitted: true,
|
submitted: true,
|
||||||
submittedValue: 'artist',
|
submittedValue: 'artist',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
results: ImmutableMap({
|
results: {
|
||||||
hashtags: fromJS([{
|
hashtags: [
|
||||||
name: 'artist',
|
{
|
||||||
url: 'https://gleasonator.com/tags/artist',
|
name: 'artist',
|
||||||
history: [],
|
url: 'https://gleasonator.com/tags/artist',
|
||||||
}]),
|
history: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
hashtagsHasMore: false,
|
hashtagsHasMore: false,
|
||||||
hashtagsLoaded: true,
|
hashtagsLoaded: true,
|
||||||
}),
|
},
|
||||||
filter: 'hashtags',
|
filter: 'hashtags',
|
||||||
});
|
};
|
||||||
|
|
||||||
expect(reducer(state, action)).toEqual(expected);
|
expect(reducer(state, action).toJS()).toEqual(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { OrderedSet as ImmutableOrderedSet, Record as ImmutableRecord, fromJS } from 'immutable';
|
import { OrderedSet as ImmutableOrderedSet, Record as ImmutableRecord, fromJS } from 'immutable';
|
||||||
|
|
||||||
import { APIEntity } from 'soapbox/types/entities';
|
import { normalizeTag } from 'soapbox/normalizers';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
COMPOSE_MENTION,
|
COMPOSE_MENTION,
|
||||||
|
@ -20,16 +20,12 @@ import {
|
||||||
} from '../actions/search';
|
} from '../actions/search';
|
||||||
|
|
||||||
import type { AnyAction } from 'redux';
|
import type { AnyAction } from 'redux';
|
||||||
|
import type { APIEntity, Tag } from 'soapbox/types/entities';
|
||||||
const HashtagRecord = ImmutableRecord({
|
|
||||||
name: '',
|
|
||||||
url: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const ResultsRecord = ImmutableRecord({
|
const ResultsRecord = ImmutableRecord({
|
||||||
accounts: ImmutableOrderedSet<string>(),
|
accounts: ImmutableOrderedSet<string>(),
|
||||||
statuses: ImmutableOrderedSet<string>(),
|
statuses: ImmutableOrderedSet<string>(),
|
||||||
hashtags: ImmutableOrderedSet<Hashtag>(), // it's a list of maps
|
hashtags: ImmutableOrderedSet<Tag>(), // it's a list of maps
|
||||||
accountsHasMore: false,
|
accountsHasMore: false,
|
||||||
statusesHasMore: false,
|
statusesHasMore: false,
|
||||||
hashtagsHasMore: false,
|
hashtagsHasMore: false,
|
||||||
|
@ -49,7 +45,6 @@ const ReducerRecord = ImmutableRecord({
|
||||||
|
|
||||||
type State = ReturnType<typeof ReducerRecord>;
|
type State = ReturnType<typeof ReducerRecord>;
|
||||||
type APIEntities = Array<APIEntity>;
|
type APIEntities = Array<APIEntity>;
|
||||||
export type Hashtag = ReturnType<typeof HashtagRecord>;
|
|
||||||
export type SearchFilter = 'accounts' | 'statuses' | 'hashtags';
|
export type SearchFilter = 'accounts' | 'statuses' | 'hashtags';
|
||||||
|
|
||||||
const toIds = (items: APIEntities) => {
|
const toIds = (items: APIEntities) => {
|
||||||
|
@ -62,7 +57,7 @@ const importResults = (state: State, results: APIEntity, searchTerm: string, sea
|
||||||
state.set('results', ResultsRecord({
|
state.set('results', ResultsRecord({
|
||||||
accounts: toIds(results.accounts),
|
accounts: toIds(results.accounts),
|
||||||
statuses: toIds(results.statuses),
|
statuses: toIds(results.statuses),
|
||||||
hashtags: ImmutableOrderedSet(results.hashtags.map(HashtagRecord)), // it's a list of maps
|
hashtags: ImmutableOrderedSet(results.hashtags.map(normalizeTag)), // it's a list of records
|
||||||
accountsHasMore: results.accounts.length >= 20,
|
accountsHasMore: results.accounts.length >= 20,
|
||||||
statusesHasMore: results.statuses.length >= 20,
|
statusesHasMore: results.statuses.length >= 20,
|
||||||
hashtagsHasMore: results.hashtags.length >= 20,
|
hashtagsHasMore: results.hashtags.length >= 20,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { List as ImmutableList, Record as ImmutableRecord } from 'immutable';
|
import { List as ImmutableList, Record as ImmutableRecord } from 'immutable';
|
||||||
|
|
||||||
|
import { normalizeTag } from 'soapbox/normalizers';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TRENDS_FETCH_REQUEST,
|
TRENDS_FETCH_REQUEST,
|
||||||
TRENDS_FETCH_SUCCESS,
|
TRENDS_FETCH_SUCCESS,
|
||||||
|
@ -7,28 +9,14 @@ import {
|
||||||
} from '../actions/trends';
|
} from '../actions/trends';
|
||||||
|
|
||||||
import type { AnyAction } from 'redux';
|
import type { AnyAction } from 'redux';
|
||||||
import type { APIEntity } from 'soapbox/types/entities';
|
import type { APIEntity, Tag } from 'soapbox/types/entities';
|
||||||
|
|
||||||
const HistoryRecord = ImmutableRecord({
|
|
||||||
accounts: '',
|
|
||||||
day: '',
|
|
||||||
uses: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const TrendingHashtagRecord = ImmutableRecord({
|
|
||||||
name: '',
|
|
||||||
url: '',
|
|
||||||
history: ImmutableList<History>(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const ReducerRecord = ImmutableRecord({
|
const ReducerRecord = ImmutableRecord({
|
||||||
items: ImmutableList<TrendingHashtag>(),
|
items: ImmutableList<Tag>(),
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
type State = ReturnType<typeof ReducerRecord>;
|
type State = ReturnType<typeof ReducerRecord>;
|
||||||
type History = ReturnType<typeof HistoryRecord>;
|
|
||||||
export type TrendingHashtag = ReturnType<typeof TrendingHashtagRecord>;
|
|
||||||
|
|
||||||
export default function trendsReducer(state: State = ReducerRecord(), action: AnyAction) {
|
export default function trendsReducer(state: State = ReducerRecord(), action: AnyAction) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
@ -36,7 +24,7 @@ export default function trendsReducer(state: State = ReducerRecord(), action: An
|
||||||
return state.set('isLoading', true);
|
return state.set('isLoading', true);
|
||||||
case TRENDS_FETCH_SUCCESS:
|
case TRENDS_FETCH_SUCCESS:
|
||||||
return state.withMutations(map => {
|
return state.withMutations(map => {
|
||||||
map.set('items', ImmutableList(action.tags.map((item: APIEntity) => TrendingHashtagRecord({ ...item, history: ImmutableList(item.history.map(HistoryRecord)) }))));
|
map.set('items', ImmutableList(action.tags.map((item: APIEntity) => normalizeTag(item))));
|
||||||
map.set('isLoading', false);
|
map.set('isLoading', false);
|
||||||
});
|
});
|
||||||
case TRENDS_FETCH_FAIL:
|
case TRENDS_FETCH_FAIL:
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
ChatMessageRecord,
|
ChatMessageRecord,
|
||||||
EmojiRecord,
|
EmojiRecord,
|
||||||
FieldRecord,
|
FieldRecord,
|
||||||
|
HistoryRecord,
|
||||||
InstanceRecord,
|
InstanceRecord,
|
||||||
ListRecord,
|
ListRecord,
|
||||||
MentionRecord,
|
MentionRecord,
|
||||||
|
@ -17,6 +18,7 @@ import {
|
||||||
RelationshipRecord,
|
RelationshipRecord,
|
||||||
StatusEditRecord,
|
StatusEditRecord,
|
||||||
StatusRecord,
|
StatusRecord,
|
||||||
|
TagRecord,
|
||||||
} from 'soapbox/normalizers';
|
} from 'soapbox/normalizers';
|
||||||
|
|
||||||
import type { Record as ImmutableRecord } from 'immutable';
|
import type { Record as ImmutableRecord } from 'immutable';
|
||||||
|
@ -29,6 +31,7 @@ type Chat = ReturnType<typeof ChatRecord>;
|
||||||
type ChatMessage = ReturnType<typeof ChatMessageRecord>;
|
type ChatMessage = ReturnType<typeof ChatMessageRecord>;
|
||||||
type Emoji = ReturnType<typeof EmojiRecord>;
|
type Emoji = ReturnType<typeof EmojiRecord>;
|
||||||
type Field = ReturnType<typeof FieldRecord>;
|
type Field = ReturnType<typeof FieldRecord>;
|
||||||
|
type History = ReturnType<typeof HistoryRecord>;
|
||||||
type Instance = ReturnType<typeof InstanceRecord>;
|
type Instance = ReturnType<typeof InstanceRecord>;
|
||||||
type List = ReturnType<typeof ListRecord>;
|
type List = ReturnType<typeof ListRecord>;
|
||||||
type Mention = ReturnType<typeof MentionRecord>;
|
type Mention = ReturnType<typeof MentionRecord>;
|
||||||
|
@ -37,6 +40,7 @@ type Poll = ReturnType<typeof PollRecord>;
|
||||||
type PollOption = ReturnType<typeof PollOptionRecord>;
|
type PollOption = ReturnType<typeof PollOptionRecord>;
|
||||||
type Relationship = ReturnType<typeof RelationshipRecord>;
|
type Relationship = ReturnType<typeof RelationshipRecord>;
|
||||||
type StatusEdit = ReturnType<typeof StatusEditRecord>;
|
type StatusEdit = ReturnType<typeof StatusEditRecord>;
|
||||||
|
type Tag = ReturnType<typeof TagRecord>;
|
||||||
|
|
||||||
interface Account extends ReturnType<typeof AccountRecord> {
|
interface Account extends ReturnType<typeof AccountRecord> {
|
||||||
// HACK: we can't do a circular reference in the Record definition itself,
|
// HACK: we can't do a circular reference in the Record definition itself,
|
||||||
|
@ -64,6 +68,7 @@ export {
|
||||||
ChatMessage,
|
ChatMessage,
|
||||||
Emoji,
|
Emoji,
|
||||||
Field,
|
Field,
|
||||||
|
History,
|
||||||
Instance,
|
Instance,
|
||||||
List,
|
List,
|
||||||
Mention,
|
Mention,
|
||||||
|
@ -73,6 +78,7 @@ export {
|
||||||
Relationship,
|
Relationship,
|
||||||
Status,
|
Status,
|
||||||
StatusEdit,
|
StatusEdit,
|
||||||
|
Tag,
|
||||||
|
|
||||||
// Utility types
|
// Utility types
|
||||||
APIEntity,
|
APIEntity,
|
||||||
|
|
Ładowanie…
Reference in New Issue