Merge remote-tracking branch 'origin/main' into fix-classnames

fix-error-messages
danidfra 2024-10-27 19:34:50 -03:00
commit de2e62746d
10 zmienionych plików z 131 dodań i 82 usunięć

Wyświetl plik

@ -137,7 +137,6 @@
"react-redux": "^9.0.4",
"react-router-dom": "^5.3.0",
"react-router-dom-v5-compat": "^6.6.2",
"react-router-scroll-4": "^1.0.0-beta.2",
"react-simple-pull-to-refresh": "^1.3.3",
"react-sparklines": "^1.7.0",
"react-sticky-box": "^2.0.0",

Wyświetl plik

@ -0,0 +1,24 @@
import React, { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { usePrevious } from 'soapbox/hooks';
export type Location<T> = ReturnType<typeof useLocation<T>>;
interface IScrollContext {
shouldUpdateScroll(prevLocation: Location<any> | undefined, location: Location<any>): boolean;
children: React.ReactNode;
}
export const ScrollContext: React.FC<IScrollContext> = ({ shouldUpdateScroll, children }) => {
const location = useLocation();
const prevLocation = usePrevious(location);
useEffect(() => {
if (prevLocation && shouldUpdateScroll(prevLocation, location)) {
window.scrollTo(0, 0);
}
}, [location, shouldUpdateScroll]);
return children;
};

Wyświetl plik

@ -1,6 +1,6 @@
import clsx from 'clsx';
import React from 'react';
import { NavLink } from 'react-router-dom';
import { NavLink, useLocation } from 'react-router-dom';
import { Icon, Text } from './ui';
@ -24,7 +24,9 @@ interface ISidebarNavigationLink {
/** Desktop sidebar navigation link. */
const SidebarNavigationLink = React.forwardRef((props: ISidebarNavigationLink, ref: React.ForwardedRef<HTMLAnchorElement>): JSX.Element => {
const { icon, activeIcon, text, to = '', count, countMax, onClick } = props;
const isActive = location.pathname === to;
const { pathname } = useLocation();
const isActive = pathname === to;
const handleClick: React.EventHandler<React.MouseEvent> = (e) => {
if (onClick) {

Wyświetl plik

@ -4,7 +4,7 @@ import throttle from 'lodash/throttle';
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import Icon from 'soapbox/components/icon';
import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
import { formatTime, getPointerPosition } from 'soapbox/features/video';
import Visualizer from './visualizer';
@ -64,10 +64,11 @@ const Audio: React.FC<IAudio> = (props) => {
const [duration, setDuration] = useState<number | undefined>(undefined);
const [paused, setPaused] = useState(true);
const [muted, setMuted] = useState(false);
const [preVolume, setPreVolume] = useState(0);
const [volume, setVolume] = useState(0.5);
const [dragging, setDragging] = useState(false);
const [hovered, setHovered] = useState(false);
const [seekHovered, setSeekHovered] = useState(false);
const visualizer = useRef<Visualizer>(new Visualizer(TICK_SIZE));
const audioContext = useRef<AudioContext | null>(null);
@ -150,12 +151,20 @@ const Audio: React.FC<IAudio> = (props) => {
};
const toggleMute = () => {
const nextMuted = !muted;
setMuted(nextMuted);
if (audio.current) {
audio.current.muted = nextMuted;
const muted = !audio.current.muted;
setMuted(muted);
audio.current.muted = muted;
if (muted) {
setPreVolume(audio.current.volume);
audio.current.volume = 0;
setVolume(0);
} else {
audio.current.volume = preVolume;
setVolume(preVolume);
}
}
};
@ -259,6 +268,14 @@ const Audio: React.FC<IAudio> = (props) => {
setHovered(false);
};
const handleSeekEnter = () => {
setSeekHovered(true);
};
const handleSeekLeave = () => {
setSeekHovered(false);
};
const handleLoadedData = () => {
if (audio.current) {
setDuration(audio.current.duration);
@ -438,7 +455,8 @@ const Audio: React.FC<IAudio> = (props) => {
return (
<div
className={clsx('audio-player', { editable })}
role='menuitem'
className={clsx('relative box-border overflow-hidden rounded-[10px] bg-black pb-11', { 'rounded-none h-full': editable })}
ref={player}
style={{
backgroundColor: _getBackgroundColor(),
@ -446,8 +464,6 @@ const Audio: React.FC<IAudio> = (props) => {
width: '100%',
height: fullscreen ? '100%' : (height || props.height),
}}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
tabIndex={0}
onKeyDown={handleKeyDown}
onClick={e => e.stopPropagation()}
@ -466,7 +482,7 @@ const Audio: React.FC<IAudio> = (props) => {
<canvas
role='button'
tabIndex={0}
className='audio-player__canvas absolute left-0 top-0 w-full'
className='absolute left-0 top-0 w-full'
width={width}
height={height}
ref={canvas}
@ -490,86 +506,95 @@ const Audio: React.FC<IAudio> = (props) => {
/>
)}
<div className='video-player__seek' onMouseDown={handleMouseDown} ref={seek}>
<div className='relative h-6 cursor-pointer' onMouseDown={handleMouseDown} onMouseEnter={handleSeekEnter} onMouseLeave={handleSeekLeave} ref={seek}>
<div className='video-player__seek__buffer' style={{ width: `${buffer}%` }} />
<div className='absolute top-0 block h-1 rounded-md bg-white/20' style={{ width: `${buffer}%` }} />
<div
className='video-player__seek__progress'
className='absolute top-0 block h-1 rounded-md bg-accent-500'
style={{ width: `${progress}%`, backgroundColor: accentColor }}
/>
<span
className={clsx('video-player__seek__handle', { active: dragging })}
className={clsx('absolute -top-1 z-30 -ml-1.5 size-3 rounded-full bg-accent-500 opacity-0 shadow-[1px_2px_6px_rgba(0,0,0,0.3)] transition-opacity duration-100', { 'opacity-100': dragging || seekHovered })}
tabIndex={0}
style={{ left: `${progress}%`, backgroundColor: accentColor }}
onKeyDown={handleAudioKeyDown}
/>
</div>
<div className='video-player__controls active'>
<div className='video-player__buttons-bar'>
<div className='video-player__buttons left'>
<div className={clsx('absolute inset-x-0 bottom-0 z-20 box-border bg-gradient-to-t from-black/70 to-transparent px-[10px] opacity-100 transition-opacity duration-100 ease-linear')}>
<div className='my-[-5px] flex justify-between pb-3.5'>
<div className='flex w-full flex-auto items-center truncate text-[16px]'>
<button
type='button'
title={intl.formatMessage(paused ? messages.play : messages.pause)}
aria-label={intl.formatMessage(paused ? messages.play : messages.pause)}
className='player-button'
className={clsx('inline-block flex-none border-0 bg-transparent px-[6px] py-[5px] text-[16px] text-white/75 opacity-75 outline-none hover:text-white hover:opacity-100 active:text-white active:opacity-100 ')}
onClick={togglePlay}
>
<Icon src={paused ? require('@tabler/icons/outline/player-play.svg') : require('@tabler/icons/outline/player-pause.svg')} />
<SvgIcon className='w-5' src={paused ? require('@tabler/icons/outline/player-play.svg') : require('@tabler/icons/outline/player-pause.svg')} />
</button>
<button
type='button'
title={intl.formatMessage(muted ? messages.unmute : messages.mute)}
aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)}
className='player-button'
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
className={clsx('inline-block flex-none border-0 bg-transparent px-[6px] py-[5px] text-[16px] text-white/75 opacity-75 outline-none hover:text-white hover:opacity-100 active:text-white active:opacity-100')}
onClick={toggleMute}
>
<Icon src={muted ? require('@tabler/icons/outline/volume-3.svg') : require('@tabler/icons/outline/volume.svg')} />
<SvgIcon className='w-5' src={muted ? require('@tabler/icons/outline/volume-3.svg') : require('@tabler/icons/outline/volume.svg')} />
</button>
<div
className={clsx('video-player__volume', { active: hovered })}
ref={slider}
onMouseDown={handleVolumeMouseDown}
className={clsx('relative inline-flex h-6 flex-none cursor-pointer overflow-hidden transition-all duration-100 ease-linear', { 'overflow-visible w-[50px] mr-[16px]': hovered })} onMouseDown={handleVolumeMouseDown} ref={slider}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<div
className='video-player__volume__current'
style={{
width: `${volume * 100}%`,
backgroundColor: _getAccentColor(),
content: '',
width: '50px',
background: 'rgba(255, 255, 255, 0.35)',
borderRadius: '4px',
display: 'block',
position: 'absolute',
height: '4px',
left: '0',
top: '50%',
transform: 'translateY(-50%)',
}}
/>
<div className={clsx('absolute left-0 top-1/2 block h-1 -translate-y-1/2 rounded-md bg-accent-500')} style={{ width: `${volume * 100}%` }} />
<span
className='video-player__volume__handle'
className={clsx('absolute left-0 top-1/2 z-30 -ml-1.5 size-3 -translate-y-1/2 rounded-full bg-accent-500 opacity-0 shadow-[1px_2px_6px_rgba(0,0,0,0.3)] transition-opacity duration-100', { 'opacity-100': hovered })}
tabIndex={0}
style={{ left: `${volume * 100}%`, backgroundColor: _getAccentColor() }}
style={{ left: `${volume * 100}%` }}
/>
</div>
<span className='video-player__time'>
<span className='video-player__time-current'>{formatTime(Math.floor(currentTime))}</span>
<span className='mx-[5px] inline-flex flex-[0_1_auto] overflow-hidden text-ellipsis'>
<span className='text-sm font-medium text-white/75'>{formatTime(Math.floor(currentTime))}</span>
{getDuration() && (<>
<span className='video-player__time-sep'>/</span> {/* eslint-disable-line formatjs/no-literal-string-in-jsx */}
<span className='video-player__time-total'>{formatTime(Math.floor(getDuration()))}</span>
<span className='mx-1.5 inline-block text-sm font-medium text-white/75'>/</span>{/* eslint-disable-line formatjs/no-literal-string-in-jsx */}
<span className='text-sm font-medium text-white/75'>{formatTime(Math.floor(getDuration()))}</span>
</>)}
</span>
</div>
<div className='video-player__buttons right'>
<div className='flex min-w-[30px] flex-auto items-center truncate text-[16px]'>
<a
title={intl.formatMessage(messages.download)}
aria-label={intl.formatMessage(messages.download)}
className='video-player__download__icon player-button'
className={clsx('inline-block flex-none border-0 bg-transparent px-[6px] py-[5px] text-[16px] text-white/75 opacity-75 outline-none hover:text-white hover:opacity-100 active:text-white active:opacity-100 ')}
href={src}
download
target='_blank'
>
<Icon src={require('@tabler/icons/outline/download.svg')} />
<SvgIcon className='w-5' src={require('@tabler/icons/outline/download.svg')} />
</a>
</div>
</div>

Wyświetl plik

@ -15,20 +15,38 @@ const NostrExtensionIndicator: React.FC = () => {
dispatch(closeModal());
};
function renderBody(): React.ReactNode {
if (window.nostr && window.nostr.nip44) {
return (
<FormattedMessage
id='nostr_extension.found'
defaultMessage='<link>Sign in</link> with browser extension.'
values={{
link: (node) => <button type='button' className='underline' onClick={onClick}>{node}</button>,
}}
/>
);
} else if (window.nostr) {
return (
<FormattedMessage
id='nostr_extension.not_supported'
defaultMessage='Browser extension not supported. Please upgrade to the latest version.'
/>
);
} else {
return (
<FormattedMessage
id='nostr_extension.not_found'
defaultMessage='Browser extension not found.'
/>
);
}
}
return (
<Stack space={2} className='flex items-center rounded-lg bg-gray-100 p-2 dark:bg-gray-800'>
<Text size='xs'>
{window.nostr ? (
<FormattedMessage
id='nostr_extension.found'
defaultMessage='<link>Sign in</link> with browser extension.'
values={{
link: (node) => <button type='button' className='underline' onClick={onClick}>{node}</button>,
}}
/>
) : (
<FormattedMessage id='nostr_extension.not_found' defaultMessage='Browser extension not found.' />
)}
{renderBody()}
</Text>
</Stack>
);

Wyświetl plik

@ -400,7 +400,7 @@ const Video: React.FC<IVideo> = ({
const toggleMute = () => {
if (video.current) {
const muted = !video.current.muted;
setMuted(!muted);
setMuted(muted);
video.current.muted = muted;
if (muted) {

Wyświetl plik

@ -2,12 +2,11 @@ import React, { Suspense, useEffect } from 'react';
import { Toaster } from 'react-hot-toast';
import { BrowserRouter, Switch, Redirect, Route } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
// @ts-ignore: it doesn't have types
import { ScrollContext } from 'react-router-scroll-4';
import { openModal } from 'soapbox/actions/modals';
import * as BuildConfig from 'soapbox/build-config';
import LoadingScreen from 'soapbox/components/loading-screen';
import { Location, ScrollContext } from 'soapbox/components/scroll-context';
import SiteErrorBoundary from 'soapbox/components/site-error-boundary';
import {
ModalContainer,
@ -25,6 +24,10 @@ const GdprBanner = React.lazy(() => import('soapbox/components/gdpr-banner'));
const EmbeddedStatus = React.lazy(() => import('soapbox/features/embedded-status'));
const UI = React.lazy(() => import('soapbox/features/ui'));
interface LocationState {
soapboxModalKey?: string;
}
/** Highest level node with the Redux store. */
const SoapboxMount = () => {
useCachedLocationHandler();
@ -51,10 +54,9 @@ const SoapboxMount = () => {
const { redirectRootNoLogin, gdpr } = soapboxConfig;
// @ts-ignore: I don't actually know what these should be, lol
const shouldUpdateScroll = (prevRouterProps, { location }) => {
return !(location.state?.soapboxModalKey && location.state?.soapboxModalKey !== prevRouterProps?.location?.state?.soapboxModalKey);
};
function shouldUpdateScroll<T extends LocationState>(prev: Location<T> | undefined, location: Location<T>): boolean {
return !(location.state?.soapboxModalKey && location.state?.soapboxModalKey !== prev?.state?.soapboxModalKey);
}
return (
<SiteErrorBoundary>

Wyświetl plik

@ -1167,6 +1167,7 @@
"new_group_panel.title": "Create Group",
"nostr_extension.found": "<link>Sign in</link> with browser extension.",
"nostr_extension.not_found": "Browser extension not found.",
"nostr_extension.not_supported": "Browser extension not supported. Please upgrade to the latest version.",
"nostr_login.siwe.action": "Log in with extension",
"nostr_login.siwe.alt": "Log in with key",
"nostr_login.siwe.sign_up": "Sign Up",

Wyświetl plik

@ -988,6 +988,7 @@ const getInstanceFeatures = (instance: InstanceV1 | InstanceV2) => {
v.software === ICESHRIMP,
v.software === MASTODON && gte(v.version, '2.8.0'),
v.software === PLEROMA && gte(v.version, '1.0.0'),
v.software === DITTO,
]),
/**

Wyświetl plik

@ -5516,13 +5516,6 @@ intl-pluralrules@^2.0.0:
resolved "https://registry.yarnpkg.com/intl-pluralrules/-/intl-pluralrules-2.0.1.tgz#de16c3df1e09437635829725e88ea70c9ad79569"
integrity sha512-astxTLzIdXPeN0K9Rumi6LfMpm3rvNO0iJE+h/k8Kr/is+wPbRe4ikyDjlLr6VTh/mEfNv8RjN+gu3KwDiuhqg==
invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
dependencies:
loose-envify "^1.0.0"
is-array-buffer@^3.0.1, is-array-buffer@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe"
@ -7363,14 +7356,6 @@ react-router-dom@^5.3.0:
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-router-scroll-4@^1.0.0-beta.2:
version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/react-router-scroll-4/-/react-router-scroll-4-1.0.0-beta.2.tgz#d887063ec0f66124aaf450158dd158ff7d3dc279"
integrity sha512-K67Dnm75naSBs/WYc2CDNxqU+eE8iA3I0wSCArgGSHb0xR/7AUcgUEXtCxrQYVTogXvjVK60gmwYvOyRQ6fuBA==
dependencies:
scroll-behavior "^0.9.1"
warning "^3.0.0"
react-router@5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.1.tgz#4d2e4e9d5ae9425091845b8dbc6d9d276239774d"
@ -7817,14 +7802,6 @@ schema-utils@^4.0.0:
ajv-formats "^2.1.1"
ajv-keywords "^5.0.0"
scroll-behavior@^0.9.1:
version "0.9.12"
resolved "https://registry.yarnpkg.com/scroll-behavior/-/scroll-behavior-0.9.12.tgz#1c22d273ec4ce6cd4714a443fead50227da9424c"
integrity sha512-18sirtyq1P/VsBX6O/vgw20Np+ngduFXEMO4/NDFXabdOKBL2kjPVUpz1y0+jm99EWwFJafxf5/tCyMeXt9Xyg==
dependencies:
dom-helpers "^3.4.0"
invariant "^2.2.4"
semver-compare@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"