diff --git a/packages/pl-api/lib/entities/list.ts b/packages/pl-api/lib/entities/list.ts index 32b09af64..6e97df3d3 100644 --- a/packages/pl-api/lib/entities/list.ts +++ b/packages/pl-api/lib/entities/list.ts @@ -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), diff --git a/packages/pl-api/lib/features.ts b/packages/pl-api/lib/features.ts index 2c5880ab8..b559363ef 100644 --- a/packages/pl-api/lib/features.ts +++ b/packages/pl-api/lib/features.ts @@ -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 diff --git a/packages/pl-api/package.json b/packages/pl-api/package.json index c5105ffd3..2e1d17304 100644 --- a/packages/pl-api/package.json +++ b/packages/pl-api/package.json @@ -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": { diff --git a/packages/pl-fe/package.json b/packages/pl-fe/package.json index 964309cdc..12b1d97d0 100644 --- a/packages/pl-fe/package.json +++ b/packages/pl-fe/package.json @@ -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", diff --git a/packages/pl-fe/src/actions/lists.ts b/packages/pl-fe/src/actions/lists.ts index 9e69f2c36..83cd633f5 100644 --- a/packages/pl-fe/src/actions/lists.ts +++ b/packages/pl-fe/src/actions/lists.ts @@ -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 + | ReturnType + | ReturnType | ReturnType | ReturnType | ReturnType @@ -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, diff --git a/packages/pl-fe/src/locales/en.json b/packages/pl-fe/src/locales/en.json index 4646216a0..8b32593cb 100644 --- a/packages/pl-fe/src/locales/en.json +++ b/packages/pl-fe/src/locales/en.json @@ -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…", diff --git a/packages/pl-fe/src/modals/list-editor-modal/components/edit-list-form.tsx b/packages/pl-fe/src/modals/list-editor-modal/components/edit-list-form.tsx index 7ae7d5a7f..b13ddb667 100644 --- a/packages/pl-fe/src/modals/list-editor-modal/components/edit-list-form.tsx +++ b/packages/pl-fe/src/modals/list-editor-modal/components/edit-list-form.tsx @@ -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 = 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) => { + dispatch(changeListEditorRepliesPolicy(e.target.value as 'none')); + }; + + const handleChangeExclusive = (e: React.ChangeEvent) => { + dispatch(changeListEditorExclusive(e.target.checked)); + }; return (
- + } + > + + {(features.listsRepliesPolicy || features.listsExclusive) && ( + + {features.listsRepliesPolicy && ( + } + > + + + )} + + {features.listsExclusive && ( + } + hint={} + > + + + )} + + )} + + - +
); }; diff --git a/packages/pl-fe/src/modals/list-editor-modal/index.tsx b/packages/pl-fe/src/modals/list-editor-modal/index.tsx index 6e3999202..1de13cd52 100644 --- a/packages/pl-fe/src/modals/list-editor-modal/index.tsx +++ b/packages/pl-fe/src/modals/list-editor-modal/index.tsx @@ -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 = ({ list title={} onClose={onClickClose} > - - - -
{accountIds.length > 0 && (
diff --git a/packages/pl-fe/src/reducers/list-editor.ts b/packages/pl-fe/src/reducers/list-editor.ts index 56fbc978a..9f150b91f 100644 --- a/packages/pl-fe/src/reducers/list-editor.ts +++ b/packages/pl-fe/src/reducers/list-editor.ts @@ -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; @@ -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; diff --git a/packages/pl-fe/yarn.lock b/packages/pl-fe/yarn.lock index efa7f6f57..33e3f582d 100644 --- a/packages/pl-fe/yarn.lock +++ b/packages/pl-fe/yarn.lock @@ -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"