pl-fe: support exclusive lists and replies policy
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -10,7 +10,7 @@ import { filteredArray } from './utils';
|
||||
const listSchema = v.object({
|
||||
id: v.pipe(v.unknown(), v.transform(String)),
|
||||
title: v.string(),
|
||||
replies_policy: v.fallback(v.optional(v.string()), undefined),
|
||||
replies_policy: v.fallback(v.optional(v.picklist(['none', 'list', 'followed'])), undefined),
|
||||
exclusive: v.fallback(v.optional(v.boolean()), undefined),
|
||||
antennas: filteredArray(v.lazy(() => antennaSchema)),
|
||||
notify: v.fallback(v.optional(v.boolean()), undefined),
|
||||
|
||||
@ -1100,6 +1100,11 @@ const getFeatures = (instance: Instance) => {
|
||||
v.software === TAKAHE && gte(v.version, '0.12.0'),
|
||||
]),
|
||||
|
||||
listsExclusive: any([
|
||||
v.software === GOTOSOCIAL,
|
||||
v.software === MASTODON && gte(v.version, '4.2.0'),
|
||||
]),
|
||||
|
||||
/**
|
||||
* Can add a list to favourites.
|
||||
* @see POST /api/v1/lists/:list_id/favourite
|
||||
@ -1107,6 +1112,11 @@ const getFeatures = (instance: Instance) => {
|
||||
*/
|
||||
listsFavourites: instance.api_versions['favourite_list.fedibird.pl-api'] >= 1,
|
||||
|
||||
listsRepliesPolicy: any([
|
||||
v.software === GOTOSOCIAL,
|
||||
v.software === MASTODON && gte(v.version, '3.3.0'),
|
||||
]),
|
||||
|
||||
/**
|
||||
* Can load latest activities from outbox.
|
||||
* @see POST /api/v1/accounts/:id/load_activities
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pl-api",
|
||||
"version": "1.0.0-rc.84",
|
||||
"version": "1.0.0-rc.85",
|
||||
"type": "module",
|
||||
"homepage": "https://codeberg.org/mkljczk/pl-fe/src/branch/develop/packages/pl-api",
|
||||
"repository": {
|
||||
|
||||
@ -105,7 +105,7 @@
|
||||
"multiselect-react-dropdown": "^2.0.25",
|
||||
"mutative": "^1.1.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"pl-api": "^1.0.0-rc.84",
|
||||
"pl-api": "^1.0.0-rc.85",
|
||||
"postcss": "^8.5.3",
|
||||
"process": "^0.11.10",
|
||||
"punycode": "^2.1.1",
|
||||
|
||||
@ -11,6 +11,8 @@ import type { Account, List, PaginatedResponse } from 'pl-api';
|
||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||
|
||||
const LIST_EDITOR_TITLE_CHANGE = 'LIST_EDITOR_TITLE_CHANGE' as const;
|
||||
const LIST_EDITOR_REPLIES_POLICY_CHANGE = 'LIST_EDITOR_REPLIES_POLICY_CHANGE' as const;
|
||||
const LIST_EDITOR_EXCLUSIVE_CHANGE = 'LIST_EDITOR_EXCLUSIVE_CHANGE' as const;
|
||||
const LIST_EDITOR_RESET = 'LIST_EDITOR_RESET' as const;
|
||||
const LIST_EDITOR_SETUP = 'LIST_EDITOR_SETUP' as const;
|
||||
|
||||
@ -55,6 +57,16 @@ const changeListEditorTitle = (value: string) => ({
|
||||
value,
|
||||
});
|
||||
|
||||
const changeListEditorRepliesPolicy = (repliesPolicy: List['replies_policy']) => ({
|
||||
type: LIST_EDITOR_REPLIES_POLICY_CHANGE,
|
||||
repliesPolicy,
|
||||
});
|
||||
|
||||
const changeListEditorExclusive = (exclusive: boolean) => ({
|
||||
type: LIST_EDITOR_EXCLUSIVE_CHANGE,
|
||||
exclusive,
|
||||
});
|
||||
|
||||
const resetListEditor = () => ({
|
||||
type: LIST_EDITOR_RESET,
|
||||
});
|
||||
@ -204,6 +216,8 @@ const removeFromListAdder = (listId: string) => (dispatch: AppDispatch, getState
|
||||
type ListsAction =
|
||||
| ListEditorSetupAction
|
||||
| ReturnType<typeof changeListEditorTitle>
|
||||
| ReturnType<typeof changeListEditorRepliesPolicy>
|
||||
| ReturnType<typeof changeListEditorExclusive>
|
||||
| ReturnType<typeof resetListEditor>
|
||||
| ReturnType<typeof fetchListAccountsRequest>
|
||||
| ReturnType<typeof fetchListAccountsSuccess>
|
||||
@ -221,6 +235,8 @@ type ListsAction =
|
||||
|
||||
export {
|
||||
LIST_EDITOR_TITLE_CHANGE,
|
||||
LIST_EDITOR_REPLIES_POLICY_CHANGE,
|
||||
LIST_EDITOR_EXCLUSIVE_CHANGE,
|
||||
LIST_EDITOR_RESET,
|
||||
LIST_EDITOR_SETUP,
|
||||
LIST_ACCOUNTS_FETCH_REQUEST,
|
||||
@ -238,6 +254,8 @@ export {
|
||||
LIST_ADDER_LISTS_FETCH_FAIL,
|
||||
setupListEditor,
|
||||
changeListEditorTitle,
|
||||
changeListEditorRepliesPolicy,
|
||||
changeListEditorExclusive,
|
||||
resetListEditor,
|
||||
fetchListSuggestions,
|
||||
clearListSuggestions,
|
||||
|
||||
@ -188,11 +188,11 @@
|
||||
"admin.relays.new.url_placeholder": "Instance relay URL",
|
||||
"admin.relays.unfollow": "Unfollow",
|
||||
"admin.relays.url": "Instance URL:",
|
||||
"admin.reports.actions.close": "Close",
|
||||
"admin.reports.account": "Reported by:",
|
||||
"admin.reports.actions.view_status": "View post",
|
||||
"admin.reports.comment": "Comment:",
|
||||
"admin.reports.empty_message": "There are no open reports. If a user gets reported, they will show up here.",
|
||||
"admin.reports.report_closed_message": "Report on @{name} was closed",
|
||||
"admin.reports.report_title": "Report on {acct}",
|
||||
"admin.reports.statuses": "Reported posts:",
|
||||
"admin.rule.priority": "Priority:",
|
||||
"admin.rules.action": "Create rule",
|
||||
"admin.rules.delete": "Delete",
|
||||
@ -459,6 +459,7 @@
|
||||
"column.reactions": "Reactions",
|
||||
"column.reblogs": "Reposts",
|
||||
"column.registration": "Sign up",
|
||||
"column.report": "Report #{id}",
|
||||
"column.scheduled_statuses": "Scheduled posts",
|
||||
"column.search": "Search",
|
||||
"column.settings_store": "Settings store",
|
||||
@ -1076,11 +1077,18 @@
|
||||
"lists.account.remove": "Remove from list",
|
||||
"lists.delete": "Delete list",
|
||||
"lists.edit": "Edit list",
|
||||
"lists.edit.submit": "Change title",
|
||||
"lists.edit.save": "Save list",
|
||||
"lists.edit.show_replies_to": "Include replies from list members to",
|
||||
"lists.edit.title": "List title",
|
||||
"lists.exclusive": "Hide members in Home",
|
||||
"lists.exclusive_hint": "If someone is on this list, hide them in your Home feed to avoid seeing their posts twice.",
|
||||
"lists.new.create": "Add list",
|
||||
"lists.new.create_title": "Add list",
|
||||
"lists.new.save_title": "Save title",
|
||||
"lists.new.save": "Save list",
|
||||
"lists.new.title_placeholder": "New list title",
|
||||
"lists.replies_policy.followed": "Any followed user",
|
||||
"lists.replies_policy.list": "Members of the list",
|
||||
"lists.replies_policy.none": "No one",
|
||||
"lists.search": "Search among people you follow",
|
||||
"lists.subheading": "Your lists",
|
||||
"loading_indicator.label": "Loading…",
|
||||
|
||||
@ -1,25 +1,33 @@
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { changeListEditorTitle } from 'pl-fe/actions/lists';
|
||||
import { changeListEditorExclusive, changeListEditorRepliesPolicy, changeListEditorTitle } from 'pl-fe/actions/lists';
|
||||
import List, { ListItem } from 'pl-fe/components/list';
|
||||
import Button from 'pl-fe/components/ui/button';
|
||||
import Form from 'pl-fe/components/ui/form';
|
||||
import HStack from 'pl-fe/components/ui/hstack';
|
||||
import FormActions from 'pl-fe/components/ui/form-actions';
|
||||
import FormGroup from 'pl-fe/components/ui/form-group';
|
||||
import Input from 'pl-fe/components/ui/input';
|
||||
import Toggle from 'pl-fe/components/ui/toggle';
|
||||
import { SelectDropdown } from 'pl-fe/features/forms';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useUpdateList } from 'pl-fe/queries/accounts/use-lists';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'lists.edit.submit', defaultMessage: 'Change title' },
|
||||
save: { id: 'lists.new.save_title', defaultMessage: 'Save title' },
|
||||
save: { id: 'lists.new.save', defaultMessage: 'Save list' },
|
||||
repliesPolicyNone: { id: 'lists.replies_policy.none', defaultMessage: 'No one' },
|
||||
repliesPolicyList: { id: 'lists.replies_policy.list', defaultMessage: 'Members of the list' },
|
||||
repliesPolicyFollowed: { id: 'lists.replies_policy.followed', defaultMessage: 'Any followed user' },
|
||||
});
|
||||
|
||||
const ListForm = () => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const features = useFeatures();
|
||||
|
||||
const { title: value, listId } = useAppSelector((state) => state.listEditor);
|
||||
const { title: value, listId, repliesPolicy, exclusive } = useAppSelector((state) => state.listEditor);
|
||||
|
||||
const { mutate: updateList, isPending: disabled } = useUpdateList(listId!);
|
||||
|
||||
@ -29,29 +37,73 @@ const ListForm = () => {
|
||||
|
||||
const handleSubmit: React.FormEventHandler<Element> = e => {
|
||||
e.preventDefault();
|
||||
updateList({ title: value });
|
||||
updateList({ title: value, replies_policy: repliesPolicy, exclusive });
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
updateList({ title: value });
|
||||
updateList({ title: value, replies_policy: repliesPolicy, exclusive });
|
||||
};
|
||||
|
||||
const save = intl.formatMessage(messages.save);
|
||||
const handleChangeRepliesPolicy = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
dispatch(changeListEditorRepliesPolicy(e.target.value as 'none'));
|
||||
};
|
||||
|
||||
const handleChangeExclusive = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(changeListEditorExclusive(e.target.checked));
|
||||
};
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<HStack space={2}>
|
||||
<FormGroup
|
||||
labelText={<FormattedMessage id='lists.edit.title' defaultMessage='List title' />}
|
||||
>
|
||||
<Input
|
||||
outerClassName='grow'
|
||||
type='text'
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{(features.listsRepliesPolicy || features.listsExclusive) && (
|
||||
<List>
|
||||
{features.listsRepliesPolicy && (
|
||||
<ListItem
|
||||
label={<FormattedMessage id='lists.edit.show_replies_to' defaultMessage='Include replies from list members to' />}
|
||||
>
|
||||
<SelectDropdown
|
||||
key={repliesPolicy}
|
||||
className='max-w-fit'
|
||||
items={{
|
||||
none: intl.formatMessage(messages.repliesPolicyNone),
|
||||
list: intl.formatMessage(messages.repliesPolicyList),
|
||||
followed: intl.formatMessage(messages.repliesPolicyFollowed),
|
||||
}}
|
||||
defaultValue={repliesPolicy || 'list'}
|
||||
onChange={handleChangeRepliesPolicy}
|
||||
/>
|
||||
</ListItem>
|
||||
)}
|
||||
|
||||
{features.listsExclusive && (
|
||||
<ListItem
|
||||
label={<FormattedMessage id='lists.exclusive' defaultMessage='Hide members in Home' />}
|
||||
hint={<FormattedMessage id='lists.exclusive_hint' defaultMessage='If someone is on this list, hide them in your Home feed to avoid seeing their posts twice.' />}
|
||||
>
|
||||
<Toggle
|
||||
checked={exclusive}
|
||||
onChange={handleChangeExclusive}
|
||||
/>
|
||||
</ListItem>
|
||||
)}
|
||||
</List>
|
||||
)}
|
||||
|
||||
<FormActions>
|
||||
<Button onClick={handleClick} disabled={disabled}>
|
||||
{save}
|
||||
<FormattedMessage id='lists.edit.save' defaultMessage='Save list' />
|
||||
</Button>
|
||||
</HStack>
|
||||
</FormActions>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
@ -14,7 +14,6 @@ import Search from './components/search';
|
||||
import type { BaseModalProps } from 'pl-fe/features/ui/components/modal-root';
|
||||
|
||||
const messages = defineMessages({
|
||||
changeTitle: { id: 'lists.edit.submit', defaultMessage: 'Change title' },
|
||||
addToList: { id: 'lists.account.add', defaultMessage: 'Add to list' },
|
||||
removeFromList: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
|
||||
editList: { id: 'lists.edit', defaultMessage: 'Edit list' },
|
||||
@ -48,11 +47,7 @@ const ListEditorModal: React.FC<BaseModalProps & ListEditorModalProps> = ({ list
|
||||
title={<FormattedMessage id='lists.edit' defaultMessage='Edit list' />}
|
||||
onClose={onClickClose}
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle title={intl.formatMessage(messages.changeTitle)} />
|
||||
</CardHeader>
|
||||
<EditListForm />
|
||||
<br />
|
||||
|
||||
{accountIds.length > 0 && (
|
||||
<div>
|
||||
|
||||
@ -13,12 +13,19 @@ import {
|
||||
LIST_EDITOR_ADD_SUCCESS,
|
||||
LIST_EDITOR_REMOVE_SUCCESS,
|
||||
type ListsAction,
|
||||
LIST_EDITOR_EXCLUSIVE_CHANGE,
|
||||
LIST_EDITOR_REPLIES_POLICY_CHANGE,
|
||||
} from '../actions/lists';
|
||||
|
||||
import type { List } from 'pl-api';
|
||||
|
||||
|
||||
interface State {
|
||||
listId: string | null;
|
||||
isSubmitting: boolean;
|
||||
title: string;
|
||||
repliesPolicy: List['replies_policy'];
|
||||
exclusive?: boolean;
|
||||
|
||||
accounts: {
|
||||
items: Array<string>;
|
||||
@ -36,6 +43,8 @@ const initialState: State = {
|
||||
listId: null,
|
||||
isSubmitting: false,
|
||||
title: '',
|
||||
repliesPolicy: undefined,
|
||||
exclusive: false,
|
||||
|
||||
accounts: {
|
||||
items: [],
|
||||
@ -54,15 +63,26 @@ const listEditorReducer = (state: State = initialState, action: ListsAction): St
|
||||
case LIST_EDITOR_RESET:
|
||||
return initialState;
|
||||
case LIST_EDITOR_SETUP:
|
||||
console.log(action.list);
|
||||
return create(state, (draft) => {
|
||||
draft.listId = action.list.id;
|
||||
draft.title = action.list.title;
|
||||
draft.repliesPolicy = action.list.replies_policy;
|
||||
draft.exclusive = action.list.exclusive;
|
||||
draft.isSubmitting = false;
|
||||
});
|
||||
case LIST_EDITOR_TITLE_CHANGE:
|
||||
return create(state, (draft) => {
|
||||
draft.title = action.value;
|
||||
});
|
||||
case LIST_EDITOR_EXCLUSIVE_CHANGE:
|
||||
return create(state, (draft) => {
|
||||
draft.exclusive = action.exclusive;
|
||||
});
|
||||
case LIST_EDITOR_REPLIES_POLICY_CHANGE:
|
||||
return create(state, (draft) => {
|
||||
draft.repliesPolicy = action.repliesPolicy;
|
||||
});
|
||||
case LIST_ACCOUNTS_FETCH_REQUEST:
|
||||
return create(state, (draft) => {
|
||||
draft.accounts.isLoading = true;
|
||||
|
||||
@ -5913,10 +5913,10 @@ isomorphic-dompurify@^2.19.0:
|
||||
dompurify "^3.2.3"
|
||||
jsdom "^25.0.1"
|
||||
|
||||
isows@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.6.tgz#0da29d706fa51551c663c627ace42769850f86e7"
|
||||
integrity sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==
|
||||
isows@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.7.tgz#1c06400b7eed216fbba3bcbd68f12490fc342915"
|
||||
integrity sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==
|
||||
|
||||
iterator.prototype@^1.1.4:
|
||||
version "1.1.4"
|
||||
@ -6998,19 +6998,19 @@ pkg-dir@^4.1.0:
|
||||
dependencies:
|
||||
find-up "^4.0.0"
|
||||
|
||||
pl-api@^1.0.0-rc.84:
|
||||
version "1.0.0-rc.84"
|
||||
resolved "https://registry.yarnpkg.com/pl-api/-/pl-api-1.0.0-rc.84.tgz#3bb2a3befee692159ecae80b10a6fd5fd01705fa"
|
||||
integrity sha512-8STLCsVEqNynFUoXTECpzPbpdsyEQXJcgdSWLXQA/KghQeFAUfVMzCY8v6BEK2N5CmG+qFGDmCxiWaFlwchP3g==
|
||||
pl-api@^1.0.0-rc.85:
|
||||
version "1.0.0-rc.85"
|
||||
resolved "https://registry.yarnpkg.com/pl-api/-/pl-api-1.0.0-rc.85.tgz#2e23aeb55506f6fe896e4b6f894eee090f36d63b"
|
||||
integrity sha512-G6pSQhd7mANwZajTls4RPSV20GXqfKTOI6lNuB0HaYhl3bmDHxcv+RFsUAIvx75hK5/WJzNg08hY4sqdZSsWXw==
|
||||
dependencies:
|
||||
blurhash "^2.0.5"
|
||||
http-link-header "^1.1.3"
|
||||
isows "^1.0.6"
|
||||
isows "^1.0.7"
|
||||
lodash.omit "^4.5.0"
|
||||
lodash.pick "^4.4.0"
|
||||
object-to-formdata "^4.5.1"
|
||||
query-string "^9.1.1"
|
||||
semver "^7.7.1"
|
||||
query-string "^9.2.2"
|
||||
semver "^7.7.2"
|
||||
valibot "^1.1.0"
|
||||
|
||||
possible-typed-array-names@^1.0.0:
|
||||
@ -7356,7 +7356,7 @@ qrcode.react@^4.2.0:
|
||||
resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-4.2.0.tgz#1bce8363f348197d145c0da640929a24c83cbca3"
|
||||
integrity sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==
|
||||
|
||||
query-string@^9.1.1, query-string@^9.2.2:
|
||||
query-string@^9.2.2:
|
||||
version "9.2.2"
|
||||
resolved "https://registry.yarnpkg.com/query-string/-/query-string-9.2.2.tgz#a0104824edfdd2c1db2f18af71cef7abf6a3b20f"
|
||||
integrity sha512-pDSIZJ9sFuOp6VnD+5IkakSVf+rICAuuU88Hcsr6AKL0QtxSIfVuKiVP2oahFI7tk3CRSexwV+Ya6MOoTxzg9g==
|
||||
@ -8046,10 +8046,10 @@ semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
|
||||
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||
|
||||
semver@^7.7.1:
|
||||
version "7.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f"
|
||||
integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==
|
||||
semver@^7.7.2:
|
||||
version "7.7.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58"
|
||||
integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
|
||||
|
||||
serialize-javascript@^6.0.1, serialize-javascript@^6.0.2:
|
||||
version "6.0.2"
|
||||
|
||||
Reference in New Issue
Block a user