Merge pull request #2883 from ClearlyClaire/glitch-soc/backports-4.3

Port changes from upstream to stable-4.3
This commit is contained in:
Claire 2024-10-14 21:47:27 +02:00 committed by GitHub
commit a3f40309fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 133 additions and 76 deletions

View file

@ -13,7 +13,7 @@ export interface ApiAccountRoleJSON {
} }
// See app/serializers/rest/account_serializer.rb // See app/serializers/rest/account_serializer.rb
export interface ApiAccountJSON { export interface BaseApiAccountJSON {
acct: string; acct: string;
avatar: string; avatar: string;
avatar_static: string; avatar_static: string;
@ -45,3 +45,12 @@ export interface ApiAccountJSON {
memorial?: boolean; memorial?: boolean;
hide_collections: 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;

View file

@ -16,6 +16,7 @@ import {
import type { IconProp } from 'flavours/glitch/components/icon'; import type { IconProp } from 'flavours/glitch/components/icon';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import Status from 'flavours/glitch/containers/status_container'; import Status from 'flavours/glitch/containers/status_container';
import { getStatusHidden } from 'flavours/glitch/selectors/filters';
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store'; import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
import { DisplayedName } from './displayed_name'; import { DisplayedName } from './displayed_name';
@ -51,6 +52,12 @@ export const NotificationWithStatus: React.FC<{
(state) => state.statuses.getIn([statusId, 'visibility']) === 'direct', (state) => state.statuses.getIn([statusId, 'visibility']) === 'direct',
); );
const isFiltered = useAppSelector(
(state) =>
statusId &&
getStatusHidden(state, { id: statusId, contextType: 'notifications' }),
);
const handlers = useMemo( const handlers = useMemo(
() => ({ () => ({
open: () => { open: () => {
@ -77,7 +84,7 @@ export const NotificationWithStatus: React.FC<{
[dispatch, statusId], [dispatch, statusId],
); );
if (!statusId) return null; if (!statusId || isFiltered) return null;
return ( return (
<HotKeys handlers={handlers}> <HotKeys handlers={handlers}>

View file

@ -95,6 +95,9 @@ export const accountDefaultValues: AccountShape = {
limited: false, limited: false,
moved: null, moved: null,
hide_collections: false, hide_collections: false,
// This comes from `ApiMutedAccountJSON`, but we should eventually
// store that in a different object.
mute_expires_at: null,
}; };
const AccountFactory = ImmutableRecord<AccountShape>(accountDefaultValues); const AccountFactory = ImmutableRecord<AccountShape>(accountDefaultValues);

View file

@ -559,7 +559,10 @@ export const notificationGroupsReducer = createReducer<NotificationGroupsState>(
compareId(state.lastReadId, mostRecentGroup.page_max_id) < 0 compareId(state.lastReadId, mostRecentGroup.page_max_id) < 0
) )
state.lastReadId = mostRecentGroup.page_max_id; 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) => { .addCase(fetchMarkers.fulfilled, (state, action) => {
if ( if (

View file

@ -0,0 +1,50 @@
import { createSelector } from '@reduxjs/toolkit';
import type { RootState } from 'flavours/glitch/store';
import { toServerSideType } from 'flavours/glitch/utils/filters';
// TODO: move to `app/javascript/flavours/glitch/models` and use more globally
type Filter = Immutable.Map<string, unknown>;
// TODO: move to `app/javascript/flavours/glitch/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',
);
};

View file

@ -1,23 +1,12 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { List as ImmutableList, Map as ImmutableMap } from 'immutable'; import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
import { toServerSideType } from 'flavours/glitch/utils/filters';
import { me } from '../initial_state'; import { me } from '../initial_state';
import { getFilters } from './filters';
export { makeGetAccount } from "./accounts"; 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 = () => { export const makeGetStatus = () => {
return createSelector( return createSelector(
[ [

View file

@ -1050,6 +1050,12 @@ a.name-tag,
color: var(--user-role-accent); color: var(--user-role-accent);
} }
.applications-list {
.icon {
vertical-align: middle;
}
}
.announcements-list, .announcements-list,
.filters-list { .filters-list {
border: 1px solid var(--background-border-color); border: 1px solid var(--background-border-color);

View file

@ -8525,79 +8525,23 @@ noscript {
background: rgba($base-overlay-background, 0.5); background: rgba($base-overlay-background, 0.5);
} }
.list-adder,
.list-editor { .list-editor {
background: $ui-base-color; backdrop-filter: var(--background-filter);
background: var(--modal-background-color);
border: 1px solid var(--modal-border-color);
flex-direction: column; flex-direction: column;
border-radius: 8px; border-radius: 8px;
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
width: 380px; width: 380px;
overflow: hidden; overflow: hidden;
@media screen and (width <= 420px) { @media screen and (width <= 420px) {
width: 90%; width: 90%;
} }
h4 {
padding: 15px 0;
background: lighten($ui-base-color, 13%);
font-weight: 500;
font-size: 16px;
text-align: center;
border-radius: 8px 8px 0 0;
}
.drawer__pager {
height: 50vh;
border-radius: 4px;
}
.drawer__inner {
border-radius: 0 0 8px 8px;
&.backdrop {
width: calc(100% - 60px);
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
border-radius: 0 0 0 8px;
}
}
&__accounts {
overflow-y: auto;
}
.account__display-name {
&:hover strong {
text-decoration: none;
}
}
.account__avatar {
cursor: default;
}
.search {
margin-bottom: 0;
}
} }
.list-adder { .list-adder {
background: $ui-base-color;
flex-direction: column;
border-radius: 8px;
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
width: 380px;
overflow: hidden;
@media screen and (width <= 420px) {
width: 90%;
}
&__account {
background: lighten($ui-base-color, 13%);
}
&__lists { &__lists {
background: lighten($ui-base-color, 13%);
height: 50vh; height: 50vh;
border-radius: 0 0 8px 8px; border-radius: 0 0 8px 8px;
overflow-y: auto; overflow-y: auto;
@ -8618,6 +8562,52 @@ noscript {
text-decoration: none; text-decoration: none;
font-size: 16px; font-size: 16px;
padding: 10px; padding: 10px;
display: flex;
align-items: center;
gap: 4px;
}
}
.list-editor {
h4 {
padding: 15px 0;
background: lighten($ui-base-color, 13%);
font-weight: 500;
font-size: 16px;
text-align: center;
border-radius: 8px 8px 0 0;
}
.drawer__pager {
height: 50vh;
border: 0;
}
.drawer__inner {
&.backdrop {
width: calc(100% - 60px);
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
border-radius: 0 0 0 8px;
}
}
&__accounts {
background: unset;
overflow-y: auto;
}
.account__display-name {
&:hover strong {
text-decoration: none;
}
}
.account__avatar {
cursor: default;
}
.search {
margin-bottom: 0;
} }
} }