Merge remote-tracking branch 'parent/main' into upstream-20241015
This commit is contained in:
commit
ebdf1ecf49
24 changed files with 560 additions and 439 deletions
|
@ -39,7 +39,7 @@ export interface ApiServerFeaturesJSON {
|
|||
}
|
||||
|
||||
// See app/serializers/rest/account_serializer.rb
|
||||
export interface ApiAccountJSON {
|
||||
export interface BaseApiAccountJSON {
|
||||
acct: string;
|
||||
avatar: string;
|
||||
avatar_static: string;
|
||||
|
@ -74,3 +74,12 @@ export interface ApiAccountJSON {
|
|||
memorial?: boolean;
|
||||
hide_collections: boolean;
|
||||
}
|
||||
|
||||
// See app/serializers/rest/muted_account_serializer.rb
|
||||
export interface ApiMutedAccountJSON extends BaseApiAccountJSON {
|
||||
mute_expires_at?: string | null;
|
||||
}
|
||||
|
||||
// For now, we have the same type representing both `Account` and `MutedAccount`
|
||||
// objects, but we should refactor this in the future.
|
||||
export type ApiAccountJSON = ApiMutedAccountJSON;
|
||||
|
|
|
@ -129,8 +129,13 @@ export const InlineFollowSuggestions = ({ hidden }) => {
|
|||
return;
|
||||
}
|
||||
|
||||
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
|
||||
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
|
||||
if (getComputedStyle(bodyRef.current).direction === 'rtl') {
|
||||
setCanScrollLeft((bodyRef.current.clientWidth - bodyRef.current.scrollLeft) < bodyRef.current.scrollWidth);
|
||||
setCanScrollRight(bodyRef.current.scrollLeft < 0);
|
||||
} else {
|
||||
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
|
||||
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
|
||||
}
|
||||
}, [setCanScrollRight, setCanScrollLeft, bodyRef, suggestions]);
|
||||
|
||||
const handleLeftNav = useCallback(() => {
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
import type { IconProp } from 'mastodon/components/icon';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import Status from 'mastodon/containers/status_container';
|
||||
import { getStatusHidden } from 'mastodon/selectors/filters';
|
||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import { DisplayedName } from './displayed_name';
|
||||
|
@ -50,6 +51,12 @@ export const NotificationWithStatus: React.FC<{
|
|||
(state) => state.statuses.getIn([statusId, 'visibility']) === 'direct',
|
||||
);
|
||||
|
||||
const isFiltered = useAppSelector(
|
||||
(state) =>
|
||||
statusId &&
|
||||
getStatusHidden(state, { id: statusId, contextType: 'notifications' }),
|
||||
);
|
||||
|
||||
const handlers = useMemo(
|
||||
() => ({
|
||||
open: () => {
|
||||
|
@ -75,7 +82,7 @@ export const NotificationWithStatus: React.FC<{
|
|||
[dispatch, statusId],
|
||||
);
|
||||
|
||||
if (!statusId) return null;
|
||||
if (!statusId || isFiltered) return null;
|
||||
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
|
|
|
@ -129,6 +129,9 @@ export const accountDefaultValues: AccountShape = {
|
|||
hide_collections: false,
|
||||
other_settings: AccountOtherSettingsFactory(),
|
||||
subscribable: true,
|
||||
// This comes from `ApiMutedAccountJSON`, but we should eventually
|
||||
// store that in a different object.
|
||||
mute_expires_at: null,
|
||||
};
|
||||
|
||||
const AccountFactory = ImmutableRecord<AccountShape>(accountDefaultValues);
|
||||
|
|
|
@ -594,7 +594,10 @@ export const notificationGroupsReducer = createReducer<NotificationGroupsState>(
|
|||
compareId(state.lastReadId, mostRecentGroup.page_max_id) < 0
|
||||
)
|
||||
state.lastReadId = mostRecentGroup.page_max_id;
|
||||
commitLastReadId(state);
|
||||
|
||||
// We don't call `commitLastReadId`, because that is conditional
|
||||
// and we want to unconditionally update the state instead.
|
||||
state.readMarkerId = state.lastReadId;
|
||||
})
|
||||
.addCase(fetchMarkers.fulfilled, (state, action) => {
|
||||
if (
|
||||
|
|
50
app/javascript/mastodon/selectors/filters.ts
Normal file
50
app/javascript/mastodon/selectors/filters.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { createSelector } from '@reduxjs/toolkit';
|
||||
|
||||
import type { RootState } from 'mastodon/store';
|
||||
import { toServerSideType } from 'mastodon/utils/filters';
|
||||
|
||||
// TODO: move to `app/javascript/mastodon/models` and use more globally
|
||||
type Filter = Immutable.Map<string, unknown>;
|
||||
|
||||
// TODO: move to `app/javascript/mastodon/models` and use more globally
|
||||
type FilterResult = Immutable.Map<string, unknown>;
|
||||
|
||||
export const getFilters = createSelector(
|
||||
[
|
||||
(state: RootState) => state.filters as Immutable.Map<string, Filter>,
|
||||
(_, { contextType }: { contextType: string }) => contextType,
|
||||
],
|
||||
(filters, contextType) => {
|
||||
if (!contextType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const serverSideType = toServerSideType(contextType);
|
||||
|
||||
return filters.filter((filter) => {
|
||||
const context = filter.get('context') as Immutable.List<string>;
|
||||
const expiration = filter.get('expires_at') as Date | null;
|
||||
return (
|
||||
context.includes(serverSideType) &&
|
||||
(expiration === null || expiration > now)
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export const getStatusHidden = (
|
||||
state: RootState,
|
||||
{ id, contextType }: { id: string; contextType: string },
|
||||
) => {
|
||||
const filters = getFilters(state, { contextType });
|
||||
if (filters === null) return false;
|
||||
|
||||
const filtered = state.statuses.getIn([id, 'filtered']) as
|
||||
| Immutable.List<FilterResult>
|
||||
| undefined;
|
||||
return filtered?.some(
|
||||
(result) =>
|
||||
filters.getIn([result.get('filter'), 'filter_action']) === 'hide',
|
||||
);
|
||||
};
|
|
@ -1,23 +1,12 @@
|
|||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
|
||||
|
||||
import { toServerSideType } from 'mastodon/utils/filters';
|
||||
|
||||
import { me, isHideItem } from '../initial_state';
|
||||
|
||||
import { getFilters } from './filters';
|
||||
|
||||
export { makeGetAccount } from "./accounts";
|
||||
|
||||
const getFilters = createSelector([state => state.get('filters'), (_, { contextType }) => contextType], (filters, contextType) => {
|
||||
if (!contextType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const serverSideType = toServerSideType(contextType);
|
||||
|
||||
return filters.filter(filter => filter.get('context').includes(serverSideType) && (filter.get('expires_at') === null || filter.get('expires_at') > now));
|
||||
});
|
||||
|
||||
export const makeGetStatus = () => {
|
||||
return createSelector(
|
||||
[
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue