pl-fe: allow to force implicit addressing

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-11-25 13:13:00 +01:00
parent 6a9921a409
commit 11d9d844bd
8 changed files with 30 additions and 20 deletions

View file

@ -29,6 +29,7 @@ Changes made since the project forked from Soapbox in April 2024.
- Language detection is done client-side for composed posts, utilizing `fasttext.wasm.js`. - Language detection is done client-side for composed posts, utilizing `fasttext.wasm.js`.
- Draft posts. They are stored locally only and work with any backend. - Draft posts. They are stored locally only and work with any backend.
- New visibility scopes are supported local-only and list-only for Pleroma. Local-only is a separate switch on GoToSocial. - New visibility scopes are supported local-only and list-only for Pleroma. Local-only is a separate switch on GoToSocial.
- On backends that support explicit mentioning, you can choose to include mentions in your replies body.
**Features:** **Features:**
- The most recent scrobble is displayed on user profile/card. - The most recent scrobble is displayed on user profile/card.

View file

@ -20,7 +20,7 @@ import { uploadFile, updateMedia } from './media';
import { createStatus } from './statuses'; import { createStatus } from './statuses';
import type { EditorState } from 'lexical'; import type { EditorState } from 'lexical';
import type { Account as BaseAccount, BackendVersion, CreateStatusParams, Group, MediaAttachment, Status as BaseStatus, Tag, Poll, ScheduledStatus } from 'pl-api'; import type { Account as BaseAccount, CreateStatusParams, Group, MediaAttachment, Status as BaseStatus, Tag, Poll, ScheduledStatus } from 'pl-api';
import type { AutoSuggestion } from 'pl-fe/components/autosuggest-input'; import type { AutoSuggestion } from 'pl-fe/components/autosuggest-input';
import type { Emoji } from 'pl-fe/features/emoji'; import type { Emoji } from 'pl-fe/features/emoji';
import type { Account } from 'pl-fe/normalizers/account'; import type { Account } from 'pl-fe/normalizers/account';
@ -118,7 +118,6 @@ interface ComposeSetStatusAction {
explicitAddressing: boolean; explicitAddressing: boolean;
spoilerText?: string; spoilerText?: string;
contentType?: string | false; contentType?: string | false;
v: BackendVersion;
withRedraft?: boolean; withRedraft?: boolean;
draftId?: string; draftId?: string;
editorState?: string | null; editorState?: string | null;
@ -135,8 +134,8 @@ const setComposeToStatus = (
editorState?: string | null, editorState?: string | null,
) => ) =>
(dispatch: AppDispatch, getState: () => RootState) => { (dispatch: AppDispatch, getState: () => RootState) => {
const client = getClient(getState); const { features } = getClient(getState);
const { createStatusExplicitAddressing: explicitAddressing, version: v } = client.features; const explicitAddressing = features.createStatusExplicitAddressing && !useSettingsStore.getState().settings.forceImplicitAddressing;
dispatch<ComposeSetStatusAction>({ dispatch<ComposeSetStatusAction>({
type: COMPOSE_SET_STATUS, type: COMPOSE_SET_STATUS,
@ -147,7 +146,6 @@ const setComposeToStatus = (
explicitAddressing, explicitAddressing,
spoilerText, spoilerText,
contentType, contentType,
v,
withRedraft, withRedraft,
draftId, draftId,
editorState, editorState,
@ -178,9 +176,9 @@ const replyCompose = (
) => ) =>
(dispatch: AppDispatch, getState: () => RootState) => { (dispatch: AppDispatch, getState: () => RootState) => {
const state = getState(); const state = getState();
const client = getClient(state); const { features } = getClient(getState);
const { createStatusExplicitAddressing: explicitAddressing } = client.features; const { forceImplicitAddressing, preserveSpoilers } = useSettingsStore.getState().settings;
const preserveSpoilers = useSettingsStore.getState().settings.preserveSpoilers; const explicitAddressing = features.createStatusExplicitAddressing && !forceImplicitAddressing;
const account = selectOwnAccount(state); const account = selectOwnAccount(state);
if (!account) return; if (!account) return;
@ -214,7 +212,8 @@ interface ComposeQuoteAction {
const quoteCompose = (status: ComposeQuoteAction['status']) => const quoteCompose = (status: ComposeQuoteAction['status']) =>
(dispatch: AppDispatch, getState: () => RootState) => { (dispatch: AppDispatch, getState: () => RootState) => {
const state = getState(); const state = getState();
const { createStatusExplicitAddressing: explicitAddressing } = state.auth.client.features; const { forceImplicitAddressing } = useSettingsStore.getState().settings;
const explicitAddressing = state.auth.client.features.createStatusExplicitAddressing && !forceImplicitAddressing;
dispatch<ComposeQuoteAction>({ dispatch<ComposeQuoteAction>({
type: COMPOSE_QUOTE, type: COMPOSE_QUOTE,
@ -347,10 +346,13 @@ const submitCompose = (composeId: string, opts: SubmitComposeOpts = {}) =>
const compose = state.compose[composeId]!; const compose = state.compose[composeId]!;
const status = compose.text; const status = compose.text;
const media = compose.media_attachments; const media = compose.media_attachments;
const statusId = compose.id; const statusId = compose.id;
let to = compose.to; let to = compose.to;
const { forceImplicitAddressing } = useSettingsStore.getState().settings;
const explicitAddressing = state.auth.client.features.createStatusExplicitAddressing && !forceImplicitAddressing;
if (!validateSchedule(state, composeId)) { if (!validateSchedule(state, composeId)) {
toast.error(messages.scheduleError); toast.error(messages.scheduleError);
@ -399,7 +401,7 @@ const submitCompose = (composeId: string, opts: SubmitComposeOpts = {}) =>
content_type: contentType, content_type: contentType,
scheduled_at: compose.schedule?.toISOString(), scheduled_at: compose.schedule?.toISOString(),
language: compose.language || compose.suggested_language || undefined, language: compose.language || compose.suggested_language || undefined,
to: to.length ? to : undefined, to: explicitAddressing && to.length ? to : undefined,
local_only: !compose.federated, local_only: !compose.federated,
}; };
@ -897,7 +899,8 @@ interface ComposeEventReplyAction {
const eventDiscussionCompose = (composeId: string, status: ComposeEventReplyAction['status']) => const eventDiscussionCompose = (composeId: string, status: ComposeEventReplyAction['status']) =>
(dispatch: AppDispatch, getState: () => RootState) => { (dispatch: AppDispatch, getState: () => RootState) => {
const state = getState(); const state = getState();
const { createStatusExplicitAddressing: explicitAddressing } = state.auth.client.features; const { forceImplicitAddressing } = useSettingsStore.getState().settings;
const explicitAddressing = state.auth.client.features.createStatusExplicitAddressing && !forceImplicitAddressing;
return dispatch({ return dispatch({
type: COMPOSE_EVENT_REPLY, type: COMPOSE_EVENT_REPLY,

View file

@ -4,6 +4,7 @@ import { FormattedList, FormattedMessage } from 'react-intl';
import { useCompose } from 'pl-fe/hooks/use-compose'; import { useCompose } from 'pl-fe/hooks/use-compose';
import { useFeatures } from 'pl-fe/hooks/use-features'; import { useFeatures } from 'pl-fe/hooks/use-features';
import { useModalsStore } from 'pl-fe/stores/modals'; import { useModalsStore } from 'pl-fe/stores/modals';
import { useSettingsStore } from 'pl-fe/stores/settings';
interface IReplyMentions { interface IReplyMentions {
composeId: string; composeId: string;
@ -15,7 +16,10 @@ const ReplyMentions: React.FC<IReplyMentions> = ({ composeId }) => {
const compose = useCompose(composeId); const compose = useCompose(composeId);
const to = compose.to; const to = compose.to;
if (!features.createStatusExplicitAddressing || !compose.in_reply_to || !to) { const { forceImplicitAddressing } = useSettingsStore().settings;
const explicitAddressing = features.createStatusExplicitAddressing && !forceImplicitAddressing;
if (!explicitAddressing || !compose.in_reply_to || !to) {
return null; return null;
} }

View file

@ -203,6 +203,12 @@ const Preferences = () => {
</ListItem> </ListItem>
)} )}
{features.createStatusExplicitAddressing && (
<ListItem label={<FormattedMessage id='preferences.fields.implicit_addressing_label' defaultMessage='Include mentions in post content when replying' />}>
<SettingToggle settings={settings} settingPath={['forceImplicitAddressing']} onChange={onToggleChange} />
</ListItem>
)}
<ListItem label={<FormattedMessage id='preferences.notifications.advanced' defaultMessage='Show all notification categories' />}> <ListItem label={<FormattedMessage id='preferences.notifications.advanced' defaultMessage='Show all notification categories' />}>
<SettingToggle settings={settings} settingPath={['notifications', 'quickFilter', 'advanced']} onChange={onToggleChange} /> <SettingToggle settings={settings} settingPath={['notifications', 'quickFilter', 'advanced']} onChange={onToggleChange} />
</ListItem> </ListItem>

View file

@ -1253,6 +1253,7 @@
"preferences.fields.display_media.default": "Hide posts marked as sensitive", "preferences.fields.display_media.default": "Hide posts marked as sensitive",
"preferences.fields.display_media.hide_all": "Always hide media posts", "preferences.fields.display_media.hide_all": "Always hide media posts",
"preferences.fields.display_media.show_all": "Always show posts", "preferences.fields.display_media.show_all": "Always show posts",
"preferences.fields.implicit_addressing_label": "Include mentions in post content when replying",
"preferences.fields.known_languages_label": "Languages you know", "preferences.fields.known_languages_label": "Languages you know",
"preferences.fields.language_label": "Display language", "preferences.fields.language_label": "Display language",
"preferences.fields.media_display_label": "Sensitive content", "preferences.fields.media_display_label": "Sensitive content",

View file

@ -1,10 +1,9 @@
import { create } from 'mutative'; import { create } from 'mutative';
import { PLEROMA, type CredentialAccount, type Instance, type MediaAttachment, type Tag } from 'pl-api'; import { type CredentialAccount, type Instance, type MediaAttachment, type Tag } from 'pl-api';
import { INSTANCE_FETCH_SUCCESS, InstanceAction } from 'pl-fe/actions/instance'; import { INSTANCE_FETCH_SUCCESS, InstanceAction } from 'pl-fe/actions/instance';
import { isNativeEmoji, type Emoji } from 'pl-fe/features/emoji'; import { isNativeEmoji, type Emoji } from 'pl-fe/features/emoji';
import { tagHistory } from 'pl-fe/settings'; import { tagHistory } from 'pl-fe/settings';
import { hasIntegerMediaIds } from 'pl-fe/utils/status';
import { import {
COMPOSE_CHANGE, COMPOSE_CHANGE,
@ -546,11 +545,7 @@ const compose = (state = initialState, action: ComposeAction | EventsAction | In
compose.quote = action.status.quote_id; compose.quote = action.status.quote_id;
compose.group_id = action.status.group_id; compose.group_id = action.status.group_id;
if (action.v?.software === PLEROMA && action.withRedraft && hasIntegerMediaIds(action.status)) { compose.media_attachments = action.status.media_attachments;
compose.media_attachments = [];
} else {
compose.media_attachments = action.status.media_attachments;
}
if (action.status.spoiler_text.length > 0) { if (action.status.spoiler_text.length > 0) {
compose.spoiler_text = action.status.spoiler_text; compose.spoiler_text = action.status.spoiler_text;

View file

@ -33,6 +33,7 @@ const settingsSchema = v.object({
autoloadTimelines: v.fallback(v.boolean(), true), autoloadTimelines: v.fallback(v.boolean(), true),
autoloadMore: v.fallback(v.boolean(), true), autoloadMore: v.fallback(v.boolean(), true),
preserveSpoilers: v.fallback(v.boolean(), false), preserveSpoilers: v.fallback(v.boolean(), false),
forceImplicitAddressing: v.fallback(v.boolean(), false),
autoTranslate: v.fallback(v.boolean(), false), autoTranslate: v.fallback(v.boolean(), false),
knownLanguages: v.fallback(v.array(v.string()), []), knownLanguages: v.fallback(v.array(v.string()), []),
showWrenchButton: v.fallback(v.boolean(), false), showWrenchButton: v.fallback(v.boolean(), false),

View file

@ -65,7 +65,6 @@ const useSettingsStore = create<State>()(mutative((set) => ({
changeSetting(state.userSettings, path, value); changeSetting(state.userSettings, path, value);
mergeSettings(state); mergeSettings(state);
console.log(JSON.parse(JSON.stringify(state.userSettings)));
}), }),
rememberEmojiUse: (emoji: Emoji) => set((state: State) => { rememberEmojiUse: (emoji: Emoji) => set((state: State) => {