1
0
Fork 0
mirror of https://github.com/mastodon/mastodon.git synced 2024-08-20 21:08:15 -07:00

Compare commits

...

4 commits

Author SHA1 Message Date
Nick Schonning
0b56471f6b
Merge c8bc986323 into a50c8e951f 2024-07-31 14:05:05 +00:00
Claire
a50c8e951f
Fix issue with grouped notifications UI due to recent API change (#31224) 2024-07-31 13:23:08 +00:00
Claire
2c1e75727d
Change filtered notification banner design to take up less space (#31222) 2024-07-31 12:36:08 +00:00
Nick Schonning
c8bc986323
Opt-in streaming to Prettier 2024-07-12 11:27:36 -04:00
11 changed files with 694 additions and 498 deletions

View file

@ -83,3 +83,4 @@ AUTHORS.md
# Process a few selected JS files # Process a few selected JS files
!lint-staged.config.js !lint-staged.config.js
!/streaming/*.js

View file

@ -60,7 +60,7 @@ export interface BaseNotificationGroupJSON {
interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON { interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON {
type: NotificationWithStatusType; type: NotificationWithStatusType;
status: ApiStatusJSON; status_id: string;
} }
interface NotificationWithStatusJSON extends BaseNotificationJSON { interface NotificationWithStatusJSON extends BaseNotificationJSON {

View file

@ -49,22 +49,15 @@ export const FilteredNotificationsBanner: React.FC = () => {
<span> <span>
<FormattedMessage <FormattedMessage
id='filtered_notifications_banner.pending_requests' id='filtered_notifications_banner.pending_requests'
defaultMessage='Notifications from {count, plural, =0 {no one} one {one person} other {# people}} you may know' defaultMessage='From {count, plural, =0 {no one} one {one person} other {# people}} you may know'
values={{ count: policy.summary.pending_requests_count }} values={{ count: policy.summary.pending_requests_count }}
/> />
</span> </span>
</div> </div>
<div className='filtered-notifications-banner__badge'> <div className='filtered-notifications-banner__badge'>
<div className='filtered-notifications-banner__badge__badge'>
{toCappedNumber(policy.summary.pending_notifications_count)} {toCappedNumber(policy.summary.pending_notifications_count)}
</div> </div>
<FormattedMessage
id='filtered_notifications_banner.mentions'
defaultMessage='{count, plural, one {mention} other {mentions}}'
values={{ count: policy.summary.pending_notifications_count }}
/>
</div>
</Link> </Link>
); );
}; };

View file

@ -300,8 +300,7 @@
"filter_modal.select_filter.subtitle": "Use an existing category or create a new one", "filter_modal.select_filter.subtitle": "Use an existing category or create a new one",
"filter_modal.select_filter.title": "Filter this post", "filter_modal.select_filter.title": "Filter this post",
"filter_modal.title.status": "Filter a post", "filter_modal.title.status": "Filter a post",
"filtered_notifications_banner.mentions": "{count, plural, one {mention} other {mentions}}", "filtered_notifications_banner.pending_requests": "From {count, plural, =0 {no one} one {one person} other {# people}} you may know",
"filtered_notifications_banner.pending_requests": "Notifications from {count, plural, =0 {no one} one {one person} other {# people}} you may know",
"filtered_notifications_banner.title": "Filtered notifications", "filtered_notifications_banner.title": "Filtered notifications",
"firehose.all": "All", "firehose.all": "All",
"firehose.local": "This server", "firehose.local": "This server",

View file

@ -124,9 +124,9 @@ export function createNotificationGroupFromJSON(
case 'mention': case 'mention':
case 'poll': case 'poll':
case 'update': { case 'update': {
const { status, ...groupWithoutStatus } = group; const { status_id: statusId, ...groupWithoutStatus } = group;
return { return {
statusId: status.id, statusId,
sampleAccountIds, sampleAccountIds,
...groupWithoutStatus, ...groupWithoutStatus,
}; };

View file

@ -10170,27 +10170,12 @@ noscript {
} }
} }
&__badge {
display: flex;
align-items: center;
border-radius: 999px;
background: var(--background-border-color);
color: $darker-text-color;
padding: 4px;
padding-inline-end: 8px;
gap: 6px;
font-weight: 500;
font-size: 11px;
line-height: 16px;
word-break: keep-all;
&__badge { &__badge {
background: $ui-button-background-color; background: $ui-button-background-color;
color: $white; color: $white;
border-radius: 100px; border-radius: 100px;
padding: 2px 8px; padding: 2px 8px;
} }
}
} }
.notification-request { .notification-request {

View file

@ -29,7 +29,7 @@ export class RequestError extends Error {
*/ */
constructor(message) { constructor(message) {
super(message); super(message);
this.name = "RequestError"; this.name = 'RequestError';
this.status = 400; this.status = 400;
} }
} }
@ -40,7 +40,7 @@ export class AuthenticationError extends Error {
*/ */
constructor(message) { constructor(message) {
super(message); super(message);
this.name = "AuthenticationError"; this.name = 'AuthenticationError';
this.status = 401; this.status = 401;
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -31,18 +31,21 @@ function sanitizeRequestLog(req) {
const log = pinoHttpSerializers.req(req); const log = pinoHttpSerializers.req(req);
if (typeof log.url === 'string' && log.url.includes('access_token')) { if (typeof log.url === 'string' && log.url.includes('access_token')) {
// Doorkeeper uses SecureRandom.urlsafe_base64 per RFC 6749 / RFC 6750 // Doorkeeper uses SecureRandom.urlsafe_base64 per RFC 6749 / RFC 6750
log.url = log.url.replace(/(access_token)=([a-zA-Z0-9\-_]+)/gi, '$1=[Redacted]'); log.url = log.url.replace(
/(access_token)=([a-zA-Z0-9\-_]+)/gi,
'$1=[Redacted]',
);
} }
return log; return log;
} }
export const logger = pino({ export const logger = pino({
name: "streaming", name: 'streaming',
// Reformat the log level to a string: // Reformat the log level to a string:
formatters: { formatters: {
level: (label) => { level: (label) => {
return { return {
level: label level: label,
}; };
}, },
}, },
@ -54,17 +57,17 @@ export const logger = pino({
'req.headers["sec-websocket-protocol"]', 'req.headers["sec-websocket-protocol"]',
'req.headers.authorization', 'req.headers.authorization',
'req.headers.cookie', 'req.headers.cookie',
'req.query.access_token' 'req.query.access_token',
] ],
} },
}); });
export const httpLogger = pinoHttp({ export const httpLogger = pinoHttp({
logger, logger,
genReqId: generateRequestId, genReqId: generateRequestId,
serializers: { serializers: {
req: sanitizeRequestLog req: sanitizeRequestLog,
} },
}); });
/** /**
@ -90,11 +93,11 @@ export function createWebsocketLogger(request, resolvedAccount) {
return logger.child({ return logger.child({
req: { req: {
id: request.id id: request.id,
}, },
account: { account: {
id: resolvedAccount.accountId ?? null id: resolvedAccount.accountId ?? null,
} },
}); });
} }
@ -104,7 +107,10 @@ export function createWebsocketLogger(request, resolvedAccount) {
* @param {string} environment * @param {string} environment
*/ */
export function initializeLogLevel(env, environment) { export function initializeLogLevel(env, environment) {
if (env.LOG_LEVEL && Object.keys(logger.levels.values).includes(env.LOG_LEVEL)) { if (
env.LOG_LEVEL &&
Object.keys(logger.levels.values).includes(env.LOG_LEVEL)
) {
logger.level = env.LOG_LEVEL; logger.level = env.LOG_LEVEL;
} else if (environment === 'development') { } else if (environment === 'development') {
logger.level = 'debug'; logger.level = 'debug';

View file

@ -55,7 +55,7 @@ export function setupMetrics(channels, pgPool) {
const connectedChannels = new metrics.Gauge({ const connectedChannels = new metrics.Gauge({
name: 'connected_channels', name: 'connected_channels',
help: 'The number of channels the streaming server is streaming to', help: 'The number of channels the streaming server is streaming to',
labelNames: [ 'type', 'channel' ] labelNames: ['type', 'channel'],
}); });
const redisSubscriptions = new metrics.Gauge({ const redisSubscriptions = new metrics.Gauge({
@ -65,13 +65,13 @@ export function setupMetrics(channels, pgPool) {
const redisMessagesReceived = new metrics.Counter({ const redisMessagesReceived = new metrics.Counter({
name: 'redis_messages_received_total', name: 'redis_messages_received_total',
help: 'The total number of messages the streaming server has received from redis subscriptions' help: 'The total number of messages the streaming server has received from redis subscriptions',
}); });
const messagesSent = new metrics.Counter({ const messagesSent = new metrics.Counter({
name: 'messages_sent_total', name: 'messages_sent_total',
help: 'The total number of messages the streaming server sent to clients per connection type', help: 'The total number of messages the streaming server sent to clients per connection type',
labelNames: [ 'type' ] labelNames: ['type'],
}); });
// Prime the gauges so we don't loose metrics between restarts: // Prime the gauges so we don't loose metrics between restarts:
@ -80,7 +80,7 @@ export function setupMetrics(channels, pgPool) {
connectedClients.set({ type: 'eventsource' }, 0); connectedClients.set({ type: 'eventsource' }, 0);
// For each channel, initialize the gauges at zero; There's only a finite set of channels available // For each channel, initialize the gauges at zero; There's only a finite set of channels available
channels.forEach(( channel ) => { channels.forEach((channel) => {
connectedChannels.set({ type: 'websocket', channel }, 0); connectedChannels.set({ type: 'websocket', channel }, 0);
connectedChannels.set({ type: 'eventsource', channel }, 0); connectedChannels.set({ type: 'eventsource', channel }, 0);
}); });

View file

@ -1,16 +1,6 @@
// @ts-check // @ts-check
const FALSE_VALUES = [ const FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'];
false,
0,
'0',
'f',
'F',
'false',
'FALSE',
'off',
'OFF',
];
/** /**
* @param {any} value * @param {any} value
@ -24,8 +14,10 @@ export function isTruthy(value) {
* See app/lib/ascii_folder.rb for the canon definitions * See app/lib/ascii_folder.rb for the canon definitions
* of these constants * of these constants
*/ */
const NON_ASCII_CHARS = 'ÀÁÂÃÄÅàáâãäåĀāĂ㥹ÇçĆćĈĉĊċČčÐðĎďĐđÈÉÊËèéêëĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħÌÍÎÏìíîïĨĩĪīĬĭĮįİıĴĵĶķĸĹĺĻļĽľĿŀŁłÑñŃńŅņŇňʼnŊŋÒÓÔÕÖØòóôõöøŌōŎŏŐőŔŕŖŗŘřŚśŜŝŞşŠšſŢţŤťŦŧÙÚÛÜùúûüŨũŪūŬŭŮůŰűŲųŴŵÝýÿŶŷŸŹźŻżŽž'; const NON_ASCII_CHARS =
const EQUIVALENT_ASCII_CHARS = 'AAAAAAaaaaaaAaAaAaCcCcCcCcCcDdDdDdEEEEeeeeEeEeEeEeEeGgGgGgGgHhHhIIIIiiiiIiIiIiIiIiJjKkkLlLlLlLlLlNnNnNnNnnNnOOOOOOooooooOoOoOoRrRrRrSsSsSsSssTtTtTtUUUUuuuuUuUuUuUuUuUuWwYyyYyYZzZzZz'; 'ÀÁÂÃÄÅàáâãäåĀāĂ㥹ÇçĆćĈĉĊċČčÐðĎďĐđÈÉÊËèéêëĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħÌÍÎÏìíîïĨĩĪīĬĭĮįİıĴĵĶķĸĹĺĻļĽľĿŀŁłÑñŃńŅņŇňʼnŊŋÒÓÔÕÖØòóôõöøŌōŎŏŐőŔŕŖŗŘřŚśŜŝŞşŠšſŢţŤťŦŧÙÚÛÜùúûüŨũŪūŬŭŮůŰűŲųŴŵÝýÿŶŷŸŹźŻżŽž';
const EQUIVALENT_ASCII_CHARS =
'AAAAAAaaaaaaAaAaAaCcCcCcCcCcDdDdDdEEEEeeeeEeEeEeEeEeGgGgGgGgHhHhIIIIiiiiIiIiIiIiIiJjKkkLlLlLlLlLlNnNnNnNnnNnOOOOOOooooooOoOoOoRrRrRrSsSsSsSssTtTtTtUUUUuuuuUuUuUuUuUuUuWwYyyYyYZzZzZz';
/** /**
* @param {string} str * @param {string} str
@ -34,7 +26,7 @@ const EQUIVALENT_ASCII_CHARS = 'AAAAAAaaaaaaAaAaAaCcCcCcCcCcDdDdDdEEEEeeeeEeEeEe
export function foldToASCII(str) { export function foldToASCII(str) {
const regex = new RegExp(NON_ASCII_CHARS.split('').join('|'), 'g'); const regex = new RegExp(NON_ASCII_CHARS.split('').join('|'), 'g');
return str.replace(regex, function(match) { return str.replace(regex, function (match) {
const index = NON_ASCII_CHARS.indexOf(match); const index = NON_ASCII_CHARS.indexOf(match);
return EQUIVALENT_ASCII_CHARS[index]; return EQUIVALENT_ASCII_CHARS[index];
}); });
@ -45,7 +37,10 @@ export function foldToASCII(str) {
* @returns {string} * @returns {string}
*/ */
export function normalizeHashtag(str) { export function normalizeHashtag(str) {
return foldToASCII(str.normalize('NFKC').toLowerCase()).replace(/[^\p{L}\p{N}_\u00b7\u200c]/gu, ''); return foldToASCII(str.normalize('NFKC').toLowerCase()).replace(
/[^\p{L}\p{N}_\u00b7\u200c]/gu,
'',
);
} }
/** /**