Merge remote-tracking branch 'origin/main' into renovate/html-react-parser-5.x
This commit is contained in:
commit
b7f18a5dcc
47 changed files with 1875 additions and 1251 deletions
|
@ -61,7 +61,7 @@ module.exports = {
|
|||
'URLSearchParams', // core-js
|
||||
],
|
||||
tailwindcss: {
|
||||
config: 'tailwind.config.cjs',
|
||||
config: 'tailwind.config.ts',
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -268,7 +268,7 @@ module.exports = {
|
|||
'error',
|
||||
{
|
||||
classRegex: '^(base|container|icon|item|list|outer|wrapper)?[c|C]lass(Name)?$',
|
||||
config: 'tailwind.config.cjs',
|
||||
config: 'tailwind.config.ts',
|
||||
},
|
||||
],
|
||||
'tailwindcss/migration-from-tailwind-2': 'error',
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
image: node:20
|
||||
image: node:21
|
||||
|
||||
variables:
|
||||
NODE_ENV: test
|
||||
|
@ -111,9 +111,9 @@ pages:
|
|||
|
||||
docker:
|
||||
stage: deploy
|
||||
image: docker:24.0.6
|
||||
image: docker:24.0.7
|
||||
services:
|
||||
- docker:24.0.6-dind
|
||||
- docker:24.0.7-dind
|
||||
tags:
|
||||
- dind
|
||||
# https://medium.com/devops-with-valentine/how-to-build-a-docker-image-and-push-it-to-the-gitlab-container-registry-from-a-gitlab-ci-pipeline-acac0d1f26df
|
||||
|
|
|
@ -1 +1 @@
|
|||
nodejs 20.0.0
|
||||
nodejs 21.4.0
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM node:20 as build
|
||||
FROM node:21 as build
|
||||
WORKDIR /app
|
||||
COPY package.json .
|
||||
COPY yarn.lock .
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM node:20
|
||||
FROM node:21
|
||||
|
||||
RUN apt-get update &&\
|
||||
apt-get install -y inotify-tools &&\
|
||||
|
|
28
package.json
28
package.json
|
@ -62,14 +62,14 @@
|
|||
"@reach/popover": "^0.18.0",
|
||||
"@reach/rect": "^0.18.0",
|
||||
"@reach/tabs": "^0.18.0",
|
||||
"@reduxjs/toolkit": "^1.8.1",
|
||||
"@reduxjs/toolkit": "^2.0.1",
|
||||
"@sentry/browser": "^7.74.1",
|
||||
"@sentry/react": "^7.74.1",
|
||||
"@soapbox.pub/wasmboy": "^0.8.0",
|
||||
"@tabler/icons": "^2.0.0",
|
||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@tanstack/react-query": "^5.0.0",
|
||||
"@types/escape-html": "^1.0.1",
|
||||
"@types/http-link-header": "^1.0.3",
|
||||
|
@ -86,7 +86,7 @@
|
|||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-sparklines": "^1.7.2",
|
||||
"@types/react-swipeable-views": "^0.13.1",
|
||||
"@types/redux-mock-store": "^1.0.3",
|
||||
"@types/redux-mock-store": "^1.0.6",
|
||||
"@types/semver": "^7.3.9",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"@vitejs/plugin-react": "^4.0.4",
|
||||
|
@ -145,7 +145,7 @@
|
|||
"react-motion": "^0.5.2",
|
||||
"react-overlays": "^0.9.0",
|
||||
"react-popper": "^2.3.0",
|
||||
"react-redux": "^8.0.0",
|
||||
"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",
|
||||
|
@ -154,12 +154,12 @@
|
|||
"react-sticky-box": "^2.0.0",
|
||||
"react-swipeable-views": "^0.14.0",
|
||||
"react-virtuoso": "^4.3.11",
|
||||
"redux": "^4.1.1",
|
||||
"redux": "^5.0.0",
|
||||
"redux-immutable": "^4.0.0",
|
||||
"redux-thunk": "^2.2.0",
|
||||
"reselect": "^4.0.0",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"reselect": "^5.0.0",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"sass": "^1.66.1",
|
||||
"sass": "^1.69.5",
|
||||
"semver": "^7.3.8",
|
||||
"stringz": "^2.0.0",
|
||||
"substring-trie": "^1.0.2",
|
||||
|
@ -170,11 +170,11 @@
|
|||
"typescript": "^5.1.3",
|
||||
"util": "^0.12.4",
|
||||
"uuid": "^9.0.0",
|
||||
"vite": "^4.4.9",
|
||||
"vite": "^5.0.10",
|
||||
"vite-plugin-compile-time": "^0.2.1",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vite-plugin-require": "^1.1.10",
|
||||
"vite-plugin-static-copy": "^0.17.0",
|
||||
"vite-plugin-static-copy": "^1.0.0",
|
||||
"wicg-inert": "^3.1.1",
|
||||
"zod": "^3.21.4"
|
||||
},
|
||||
|
@ -202,17 +202,17 @@
|
|||
"eslint-plugin-tailwindcss": "^3.13.0",
|
||||
"fake-indexeddb": "^5.0.0",
|
||||
"husky": "^8.0.0",
|
||||
"jsdom": "^22.1.0",
|
||||
"jsdom": "^23.0.0",
|
||||
"lint-staged": ">=10",
|
||||
"react-intl-translations-manager": "^5.0.3",
|
||||
"react-refresh": "^0.14.0",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"stylelint": "^15.10.3",
|
||||
"stylelint-config-standard-scss": "^11.0.0",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"tailwindcss": "^3.4.0",
|
||||
"vite-plugin-checker": "^0.6.2",
|
||||
"vite-plugin-pwa": "^0.17.0",
|
||||
"vitest": "^0.34.4"
|
||||
"vitest": "^1.0.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "^18.0.26",
|
||||
|
|
|
@ -41,7 +41,7 @@ function useGroupValidation(name: string = '') {
|
|||
...queryInfo,
|
||||
data: {
|
||||
...queryInfo.data,
|
||||
isValid: !queryInfo.data?.error ?? true,
|
||||
isValid: !queryInfo.data?.error,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ const BigCard: React.FC<IBigCard> = ({ title, subtitle, children }) => {
|
|||
return (
|
||||
<Card variant='rounded' size='xl'>
|
||||
<CardBody>
|
||||
<div className='-mx-4 mb-4 border-b border-solid border-gray-200 pb-4 dark:border-gray-800 sm:-mx-10 sm:pb-10'>
|
||||
<div className='-mx-4 mb-4 border-b border-solid border-gray-200 pb-4 sm:-mx-10 sm:pb-10 dark:border-gray-800'>
|
||||
<Stack space={2}>
|
||||
<Text size='2xl' align='center' weight='bold'>{title}</Text>
|
||||
{subtitle && <Text theme='muted' align='center'>{subtitle}</Text>}
|
||||
|
|
|
@ -28,7 +28,7 @@ const GdprBanner: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Banner theme='opaque' className={clsx('transition-transform', { 'translate-y-full': slideout })}>
|
||||
<div className='flex flex-col space-y-4 rtl:space-x-reverse lg:flex-row lg:items-center lg:justify-between lg:space-x-4 lg:space-y-0'>
|
||||
<div className='flex flex-col space-y-4 lg:flex-row lg:items-center lg:justify-between lg:space-x-4 lg:space-y-0 rtl:space-x-reverse'>
|
||||
<Stack space={2}>
|
||||
<Text size='xl' weight='bold'>
|
||||
<FormattedMessage id='gdpr.title' defaultMessage='{siteTitle} uses cookies' values={{ siteTitle: instance.title }} />
|
||||
|
|
|
@ -84,7 +84,7 @@ const SiteErrorBoundary: React.FC<ISiteErrorBoundary> = ({ children }) => {
|
|||
|
||||
<div className='py-8'>
|
||||
<div className='mx-auto max-w-xl space-y-2 text-center'>
|
||||
<h1 className='text-3xl font-extrabold tracking-tight text-gray-900 dark:text-gray-500 sm:text-4xl'>
|
||||
<h1 className='text-3xl font-extrabold tracking-tight text-gray-900 sm:text-4xl dark:text-gray-500'>
|
||||
<FormattedMessage id='alert.unexpected.message' defaultMessage='Something went wrong.' />
|
||||
</h1>
|
||||
<p className='text-lg text-gray-700 dark:text-gray-600'>
|
||||
|
|
|
@ -13,7 +13,7 @@ const Select = React.forwardRef<HTMLSelectElement, ISelect>((props, ref) => {
|
|||
<select
|
||||
ref={ref}
|
||||
className={clsx(
|
||||
'w-full truncate rounded-md border-gray-300 py-2 pl-3 pr-10 text-base focus:border-primary-500 focus:outline-none focus:ring-primary-500 disabled:opacity-50 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-1 dark:ring-gray-800 dark:focus:border-primary-500 dark:focus:ring-primary-500 sm:text-sm',
|
||||
'w-full truncate rounded-md border-gray-300 py-2 pl-3 pr-10 text-base focus:border-primary-500 focus:outline-none focus:ring-primary-500 disabled:opacity-50 sm:text-sm dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-1 dark:ring-gray-800 dark:focus:border-primary-500 dark:focus:ring-primary-500',
|
||||
className,
|
||||
)}
|
||||
{...filteredProps}
|
||||
|
|
|
@ -45,7 +45,7 @@ const TagInput: React.FC<ITagInput> = ({ tags, onChange, placeholder }) => {
|
|||
return (
|
||||
<div className='relative mt-1 grow shadow-sm'>
|
||||
<HStack
|
||||
className='block w-full rounded-md border-gray-400 bg-white p-2 pb-0 text-gray-900 placeholder:text-gray-600 focus:border-primary-500 focus:ring-primary-500 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-1 dark:ring-gray-800 dark:placeholder:text-gray-600 dark:focus:border-primary-500 dark:focus:ring-primary-500 sm:text-sm'
|
||||
className='block w-full rounded-md border-gray-400 bg-white p-2 pb-0 text-gray-900 placeholder:text-gray-600 focus:border-primary-500 focus:ring-primary-500 sm:text-sm dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-1 dark:ring-gray-800 dark:placeholder:text-gray-600 dark:focus:border-primary-500 dark:focus:ring-primary-500'
|
||||
space={2}
|
||||
wrap
|
||||
>
|
||||
|
|
|
@ -91,7 +91,7 @@ const Textarea = React.forwardRef(({
|
|||
ref={ref}
|
||||
rows={rows}
|
||||
onChange={handleChange}
|
||||
className={clsx('block w-full rounded-md text-gray-900 placeholder:text-gray-600 dark:text-gray-100 dark:placeholder:text-gray-600 sm:text-sm', {
|
||||
className={clsx('block w-full rounded-md text-gray-900 placeholder:text-gray-600 sm:text-sm dark:text-gray-100 dark:placeholder:text-gray-600', {
|
||||
'bg-white dark:bg-transparent shadow-sm border-gray-400 dark:border-gray-800 dark:ring-1 dark:ring-gray-800 focus:ring-primary-500 focus:border-primary-500 dark:focus:ring-primary-500 dark:focus:border-primary-500':
|
||||
theme === 'default',
|
||||
'bg-transparent border-0 focus:border-0 focus:ring-0': theme === 'transparent',
|
||||
|
|
|
@ -110,7 +110,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
|||
return (
|
||||
<div className='-mx-4 -mt-4 sm:-mx-6 sm:-mt-6'>
|
||||
<div>
|
||||
<div className='relative h-32 w-full bg-gray-200 dark:bg-gray-900/50 md:rounded-t-xl lg:h-48' />
|
||||
<div className='relative h-32 w-full bg-gray-200 md:rounded-t-xl lg:h-48 dark:bg-gray-900/50' />
|
||||
</div>
|
||||
|
||||
<div className='px-4 sm:px-6'>
|
||||
|
@ -620,7 +620,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
|||
)}
|
||||
|
||||
<div>
|
||||
<div className='relative isolate flex h-32 w-full flex-col justify-center overflow-hidden bg-gray-200 dark:bg-gray-900/50 md:rounded-t-xl lg:h-48'>
|
||||
<div className='relative isolate flex h-32 w-full flex-col justify-center overflow-hidden bg-gray-200 md:rounded-t-xl lg:h-48 dark:bg-gray-900/50'>
|
||||
{renderHeader()}
|
||||
|
||||
<div className='absolute left-2 top-2'>
|
||||
|
|
|
@ -44,7 +44,7 @@ const Search: React.FC = () => {
|
|||
<span style={{ display: 'none' }}>{intl.formatMessage(messages.search)}</span>
|
||||
|
||||
<input
|
||||
className='block w-full rounded-full focus:border-primary-500 focus:ring-primary-500 dark:bg-gray-800 dark:text-white dark:placeholder:text-gray-500 sm:text-sm'
|
||||
className='block w-full rounded-full focus:border-primary-500 focus:ring-primary-500 sm:text-sm dark:bg-gray-800 dark:text-white dark:placeholder:text-gray-500'
|
||||
type='text'
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
|
|
|
@ -16,7 +16,7 @@ const ConsumersList: React.FC<IConsumersList> = () => {
|
|||
|
||||
if (providers.length > 0) {
|
||||
return (
|
||||
<Card className='bg-gray-50 p-4 dark:bg-primary-800 sm:rounded-xl'>
|
||||
<Card className='bg-gray-50 p-4 sm:rounded-xl dark:bg-primary-800'>
|
||||
<Text size='xs' theme='muted'>
|
||||
<FormattedMessage id='oauth_consumers.title' defaultMessage='Other ways to sign in' />
|
||||
</Text>
|
||||
|
|
|
@ -60,7 +60,7 @@ const ChatPage: React.FC<IChatPage> = ({ chatId }) => {
|
|||
<div
|
||||
ref={containerRef}
|
||||
style={{ height }}
|
||||
className='h-screen overflow-hidden bg-white text-gray-900 shadow-lg dark:bg-primary-900 dark:text-gray-100 dark:shadow-none sm:rounded-t-xl'
|
||||
className='h-screen overflow-hidden bg-white text-gray-900 shadow-lg sm:rounded-t-xl dark:bg-primary-900 dark:text-gray-100 dark:shadow-none'
|
||||
>
|
||||
{isOnboarded ? (
|
||||
<div
|
||||
|
@ -68,7 +68,7 @@ const ChatPage: React.FC<IChatPage> = ({ chatId }) => {
|
|||
data-testid='chat-page'
|
||||
>
|
||||
<Stack
|
||||
className={clsx('dark:inset col-span-9 overflow-hidden bg-gradient-to-r from-white to-gray-100 dark:bg-gray-900 dark:bg-none sm:col-span-3', {
|
||||
className={clsx('dark:inset col-span-9 overflow-hidden bg-gradient-to-r from-white to-gray-100 sm:col-span-3 dark:bg-gray-900 dark:bg-none', {
|
||||
'hidden sm:block': isSidebarHidden,
|
||||
})}
|
||||
>
|
||||
|
|
|
@ -121,7 +121,7 @@ const ChatPageMain = () => {
|
|||
<HStack alignItems='center'>
|
||||
<IconButton
|
||||
src={require('@tabler/icons/arrow-left.svg')}
|
||||
className='mr-2 h-7 w-7 rtl:rotate-180 sm:mr-0 sm:hidden'
|
||||
className='mr-2 h-7 w-7 sm:mr-0 sm:hidden rtl:rotate-180'
|
||||
onClick={() => history.push('/chats')}
|
||||
/>
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ const ChatPageNew: React.FC<IChatPageNew> = () => {
|
|||
<HStack alignItems='center'>
|
||||
<IconButton
|
||||
src={require('@tabler/icons/arrow-left.svg')}
|
||||
className='mr-2 h-7 w-7 rtl:rotate-180 sm:mr-0 sm:hidden'
|
||||
className='mr-2 h-7 w-7 sm:mr-0 sm:hidden rtl:rotate-180'
|
||||
onClick={() => history.push('/chats')}
|
||||
/>
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ const ChatPageSettings = () => {
|
|||
<HStack alignItems='center'>
|
||||
<IconButton
|
||||
src={require('@tabler/icons/arrow-left.svg')}
|
||||
className='mr-2 h-7 w-7 rtl:rotate-180 sm:mr-0 sm:hidden'
|
||||
className='mr-2 h-7 w-7 sm:mr-0 sm:hidden rtl:rotate-180'
|
||||
onClick={() => history.push('/chats')}
|
||||
/>
|
||||
|
||||
|
|
|
@ -39,9 +39,9 @@ const ChatTextarea: React.FC<IChatTextarea> = React.forwardRef(({
|
|||
bg-white text-gray-900
|
||||
shadow-sm placeholder:text-gray-600
|
||||
focus-within:border-primary-500
|
||||
focus-within:ring-1 focus-within:ring-primary-500 dark:border-gray-800 dark:bg-gray-800
|
||||
dark:text-gray-100 dark:ring-1 dark:ring-gray-800 dark:placeholder:text-gray-600 dark:focus-within:border-primary-500
|
||||
dark:focus-within:ring-primary-500 sm:text-sm
|
||||
focus-within:ring-1 focus-within:ring-primary-500 sm:text-sm dark:border-gray-800
|
||||
dark:bg-gray-800 dark:text-gray-100 dark:ring-1 dark:ring-gray-800 dark:placeholder:text-gray-600
|
||||
dark:focus-within:border-primary-500 dark:focus-within:ring-primary-500
|
||||
`}
|
||||
>
|
||||
{(!!attachments?.length || isUploading) && (
|
||||
|
|
|
@ -36,7 +36,7 @@ const HeaderPicker = React.forwardRef<HTMLInputElement, IMediaInput>(({ src, onC
|
|||
<label
|
||||
ref={picker}
|
||||
className={clsx(
|
||||
'dark:sm:shadow-inset relative h-24 w-full cursor-pointer overflow-hidden rounded-lg bg-primary-100 text-primary-500 dark:bg-gray-800 dark:text-accent-blue sm:h-36 sm:shadow',
|
||||
'dark:sm:shadow-inset relative h-24 w-full cursor-pointer overflow-hidden rounded-lg bg-primary-100 text-primary-500 sm:h-36 sm:shadow dark:bg-gray-800 dark:text-accent-blue',
|
||||
{
|
||||
'border-2 border-primary-600 border-dashed !z-[99]': isDragging,
|
||||
'ring-2 ring-offset-2 ring-primary-600': isDraggedOver,
|
||||
|
|
|
@ -80,7 +80,7 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
|
|||
return (
|
||||
<>
|
||||
<div className='-mx-4 -mt-4'>
|
||||
<div className='relative h-32 w-full bg-gray-200 dark:bg-gray-900/50 md:rounded-t-xl lg:h-48' />
|
||||
<div className='relative h-32 w-full bg-gray-200 md:rounded-t-xl lg:h-48 dark:bg-gray-900/50' />
|
||||
</div>
|
||||
|
||||
<PlaceholderEventHeader />
|
||||
|
@ -364,7 +364,7 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
|
|||
return (
|
||||
<>
|
||||
<div className='-mx-4 -mt-4'>
|
||||
<div className='relative h-32 w-full bg-gray-200 dark:bg-gray-900/50 md:rounded-t-xl lg:h-48'>
|
||||
<div className='relative h-32 w-full bg-gray-200 md:rounded-t-xl lg:h-48 dark:bg-gray-900/50'>
|
||||
{banner && (
|
||||
<a href={banner.url} onClick={handleHeaderClick} target='_blank'>
|
||||
<StillImage
|
||||
|
|
|
@ -24,7 +24,7 @@ const SuggestionItem: React.FC<ISuggestionItem> = ({ accountId }) => {
|
|||
if (!account) return null;
|
||||
|
||||
return (
|
||||
<Stack space={3} className='w-52 shrink-0 rounded-md border border-solid border-gray-300 p-4 dark:border-gray-800 md:w-full md:shrink md:border-transparent md:p-0 dark:md:border-transparent'>
|
||||
<Stack space={3} className='w-52 shrink-0 rounded-md border border-solid border-gray-300 p-4 md:w-full md:shrink md:border-transparent md:p-0 dark:border-gray-800 dark:md:border-transparent'>
|
||||
<Link
|
||||
to={`/@${account.acct}`}
|
||||
title={account.acct}
|
||||
|
|
|
@ -36,7 +36,7 @@ const GroupHeader: React.FC<IGroupHeader> = ({ group }) => {
|
|||
return (
|
||||
<div className='-mx-4 -mt-4 sm:-mx-6 sm:-mt-6' data-testid='group-header-missing'>
|
||||
<div>
|
||||
<div className='relative h-32 w-full bg-gray-200 dark:bg-gray-900/50 md:rounded-t-xl lg:h-48' />
|
||||
<div className='relative h-32 w-full bg-gray-200 md:rounded-t-xl lg:h-48 dark:bg-gray-900/50' />
|
||||
</div>
|
||||
|
||||
<div className='px-4 sm:px-6'>
|
||||
|
@ -92,7 +92,7 @@ const GroupHeader: React.FC<IGroupHeader> = ({ group }) => {
|
|||
<StillImage
|
||||
src={group.header}
|
||||
alt={intl.formatMessage(messages.header)}
|
||||
className='relative h-32 w-full bg-gray-200 object-center dark:bg-gray-900/50 md:rounded-t-xl lg:h-52'
|
||||
className='relative h-32 w-full bg-gray-200 object-center md:rounded-t-xl lg:h-52 dark:bg-gray-900/50'
|
||||
onError={() => setIsHeaderMissing(true)}
|
||||
/>
|
||||
);
|
||||
|
@ -109,7 +109,7 @@ const GroupHeader: React.FC<IGroupHeader> = ({ group }) => {
|
|||
return (
|
||||
<div
|
||||
data-testid='group-header-image'
|
||||
className='flex h-32 w-full items-center justify-center bg-gray-200 dark:bg-gray-800/30 md:rounded-t-xl lg:h-52'
|
||||
className='flex h-32 w-full items-center justify-center bg-gray-200 md:rounded-t-xl lg:h-52 dark:bg-gray-800/30'
|
||||
>
|
||||
{isHeaderMissing ? (
|
||||
<Icon src={require('@tabler/icons/photo-off.svg')} className='h-6 w-6 text-gray-500 dark:text-gray-700' />
|
||||
|
|
|
@ -26,7 +26,7 @@ const FediverseStep = ({ onNext }: { onNext: () => void }) => {
|
|||
</Text>
|
||||
|
||||
<Stack space={4}>
|
||||
<div className='border-b border-solid border-gray-200 pb-2 dark:border-gray-800 sm:pb-5'>
|
||||
<div className='border-b border-solid border-gray-200 pb-2 sm:pb-5 dark:border-gray-800'>
|
||||
<Stack space={4}>
|
||||
<Text theme='muted'>
|
||||
<FormattedMessage
|
||||
|
|
|
@ -8,7 +8,7 @@ import PlaceholderStatusContent from './placeholder-status-content';
|
|||
|
||||
/** Fake notification to display while data is loading. */
|
||||
const PlaceholderNotification = () => (
|
||||
<div className='bg-white px-4 py-6 dark:bg-primary-900 sm:p-6'>
|
||||
<div className='bg-white px-4 py-6 sm:p-6 dark:bg-primary-900'>
|
||||
<div className='w-full animate-pulse'>
|
||||
<div className='mb-2'>
|
||||
<PlaceholderStatusContent minLength={20} maxLength={20} />
|
||||
|
|
|
@ -209,7 +209,7 @@ const ComposeEventModal: React.FC<IComposeEventModal> = ({ onClose }) => {
|
|||
<FormGroup
|
||||
labelText={<FormattedMessage id='compose_event.fields.banner_label' defaultMessage='Event banner' />}
|
||||
>
|
||||
<div className='dark:sm:shadow-inset relative flex h-24 items-center justify-center overflow-hidden rounded-lg bg-primary-100 text-primary-500 dark:bg-gray-800 dark:text-white sm:h-32 sm:shadow'>
|
||||
<div className='dark:sm:shadow-inset relative flex h-24 items-center justify-center overflow-hidden rounded-lg bg-primary-100 text-primary-500 sm:h-32 sm:shadow dark:bg-gray-800 dark:text-white'>
|
||||
{banner ? (
|
||||
<>
|
||||
<img className='h-full w-full object-cover' src={banner.url} alt='' />
|
||||
|
@ -234,7 +234,7 @@ const ComposeEventModal: React.FC<IComposeEventModal> = ({ onClose }) => {
|
|||
labelText={<FormattedMessage id='compose_event.fields.description_label' defaultMessage='Event description' />}
|
||||
>
|
||||
<ComposeEditor
|
||||
className='block w-full rounded-md border border-gray-400 bg-white px-3 py-2 text-base text-gray-900 ring-1 placeholder:text-gray-600 focus-within:border-primary-500 focus-within:ring-primary-500 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-gray-800 dark:placeholder:text-gray-600 dark:focus-within:border-primary-500 dark:focus-within:ring-primary-500 sm:text-sm'
|
||||
className='block w-full rounded-md border border-gray-400 bg-white px-3 py-2 text-base text-gray-900 ring-1 placeholder:text-gray-600 focus-within:border-primary-500 focus-within:ring-primary-500 sm:text-sm dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-gray-800 dark:placeholder:text-gray-600 dark:focus-within:border-primary-500 dark:focus-within:ring-primary-500'
|
||||
placeholderClassName='pt-2'
|
||||
composeId='compose-event-modal'
|
||||
placeholder={intl.formatMessage(messages.eventDescriptionPlaceholder)}
|
||||
|
|
|
@ -42,7 +42,7 @@ const ConfirmationStep: React.FC<IConfirmationStep> = ({ group }) => {
|
|||
<Stack space={3}>
|
||||
<Stack>
|
||||
<label
|
||||
className='dark:sm:shadow-inset relative h-24 w-full cursor-pointer overflow-hidden rounded-lg bg-primary-100 text-primary-500 dark:bg-gray-800 dark:text-accent-blue sm:h-36 sm:shadow'
|
||||
className='dark:sm:shadow-inset relative h-24 w-full cursor-pointer overflow-hidden rounded-lg bg-primary-100 text-primary-500 sm:h-36 sm:shadow dark:bg-gray-800 dark:text-accent-blue'
|
||||
>
|
||||
{group.header && <img className='h-full w-full object-cover' src={group.header} alt='' />}
|
||||
</label>
|
||||
|
|
|
@ -76,7 +76,7 @@ const Navbar = () => {
|
|||
<div className='mx-auto max-w-7xl px-2 sm:px-6 lg:px-8'>
|
||||
<div className='relative flex h-12 justify-between lg:h-16'>
|
||||
{account && (
|
||||
<div className='absolute inset-y-0 left-0 flex items-center rtl:left-auto rtl:right-0 lg:hidden'>
|
||||
<div className='absolute inset-y-0 left-0 flex items-center lg:hidden rtl:left-auto rtl:right-0'>
|
||||
<button onClick={onOpenSidebar}>
|
||||
<Avatar src={account.avatar} size={34} />
|
||||
</button>
|
||||
|
@ -125,7 +125,7 @@ const Navbar = () => {
|
|||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<Form className='hidden items-center space-x-2 rtl:space-x-reverse xl:flex' onSubmit={handleSubmit}>
|
||||
<Form className='hidden items-center space-x-2 xl:flex rtl:space-x-reverse' onSubmit={handleSubmit}>
|
||||
<Input
|
||||
required
|
||||
value={username}
|
||||
|
|
|
@ -494,7 +494,7 @@ const UI: React.FC<IUI> = ({ children }) => {
|
|||
</Layout>
|
||||
|
||||
{(me && !shouldHideFAB()) && (
|
||||
<div className='fixed bottom-24 right-4 z-40 transition-all rtl:left-4 rtl:right-auto lg:hidden'>
|
||||
<div className='fixed bottom-24 right-4 z-40 transition-all lg:hidden rtl:left-4 rtl:right-auto'>
|
||||
<FloatingActionButton />
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -9,7 +9,7 @@ import { IntlProvider } from 'react-intl';
|
|||
import { Provider } from 'react-redux';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { Action, applyMiddleware, createStore } from 'redux';
|
||||
import thunk from 'redux-thunk';
|
||||
import { thunk } from 'redux-thunk';
|
||||
|
||||
import { ChatProvider } from 'soapbox/contexts/chat-context';
|
||||
import { StatProvider } from 'soapbox/contexts/stat-context';
|
||||
|
|
1884
src/locales/id.json
1884
src/locales/id.json
File diff suppressed because it is too large
Load diff
1
src/locales/jv.json
Normal file
1
src/locales/jv.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -198,6 +198,9 @@
|
|||
"birthdays_modal.empty": "Żaden z Twoich znajomych nie ma dziś urodzin.",
|
||||
"boost_modal.combo": "Naciśnij {combo}, aby pominąć to następnym razem",
|
||||
"boost_modal.title": "Repost?",
|
||||
"bundle_column_error.body": "Coś poszło nie tak podczas ładowania tej strony.",
|
||||
"bundle_column_error.retry": "Spróbuj ponownie",
|
||||
"bundle_column_error.title": "Błąd sieci",
|
||||
"card.back.label": "Wstecz",
|
||||
"chat.actions.send": "Wyślij",
|
||||
"chat.failed_to_send": "Nie udało się wysłać wiadomości.",
|
||||
|
@ -1369,6 +1372,8 @@
|
|||
"soapbox_config.redirect_root_no_login_label": "Przekieruj stronę główną",
|
||||
"soapbox_config.save": "Zapisz",
|
||||
"soapbox_config.saved": "Zapisano konfigurację Soapbox!",
|
||||
"soapbox_config.sentry_dsn_hint": "Adres URL DSN do zgłaszania błędów. Działa z Sentry i GlitchTip.",
|
||||
"soapbox_config.sentry_dsn_label": "DSN Sentry",
|
||||
"soapbox_config.tile_server_attribution_label": "Atrybucja kafelków map",
|
||||
"soapbox_config.tile_server_label": "Serwer kafelków map",
|
||||
"soapbox_config.verified_can_edit_name_label": "Pozwól zweryfikowanym użytkownikom na zmianę swojej nazwy wyświetlanej.",
|
||||
|
|
|
@ -198,6 +198,9 @@
|
|||
"birthdays_modal.empty": "今天您的朋友中无人过生日。",
|
||||
"boost_modal.combo": "下次按住 {combo} 即可跳过此提示",
|
||||
"boost_modal.title": "转发?",
|
||||
"bundle_column_error.body": "载入页面时发生错误。",
|
||||
"bundle_column_error.retry": "重试",
|
||||
"bundle_column_error.title": "网络错误",
|
||||
"card.back.label": "返回",
|
||||
"chat.actions.send": "发送",
|
||||
"chat.failed_to_send": "消息发送失败。",
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import toast from 'soapbox/toast';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
import type { ThunkMiddleware } from 'redux-thunk';
|
||||
import type { AnyAction, Middleware } from 'redux';
|
||||
|
||||
/** Whether the action is considered a failure. */
|
||||
const isFailType = (type: string): boolean => type.endsWith('_FAIL');
|
||||
|
@ -21,8 +20,9 @@ const shouldShowError = ({ type, skipAlert, error }: AnyAction): boolean => {
|
|||
};
|
||||
|
||||
/** Middleware to display Redux errors to the user. */
|
||||
const errorsMiddleware = (): ThunkMiddleware =>
|
||||
() => next => action => {
|
||||
const errorsMiddleware = (): Middleware =>
|
||||
() => next => anyAction => {
|
||||
const action = anyAction as AnyAction;
|
||||
if (shouldShowError(action)) {
|
||||
toast.showAlertForError(action.error);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { AnyAction } from 'redux';
|
||||
|
||||
import { play, soundCache } from 'soapbox/utils/sounds';
|
||||
|
||||
import type { ThunkMiddleware } from 'redux-thunk';
|
||||
import type { AnyAction, Middleware } from 'redux';
|
||||
import type { Sounds } from 'soapbox/utils/sounds';
|
||||
|
||||
interface Action extends AnyAction {
|
||||
|
@ -12,8 +11,9 @@ interface Action extends AnyAction {
|
|||
}
|
||||
|
||||
/** Middleware to play sounds in response to certain Redux actions. */
|
||||
export default function soundsMiddleware(): ThunkMiddleware {
|
||||
return () => next => (action: Action) => {
|
||||
export default function soundsMiddleware(): Middleware {
|
||||
return () => next => anyAction => {
|
||||
const action = anyAction as Action;
|
||||
if (action.meta?.sound && soundCache[action.meta.sound]) {
|
||||
play(soundCache[action.meta.sound]);
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ const HomePage: React.FC<IHomePage> = ({ children }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Layout.Main className='space-y-3 pt-3 dark:divide-gray-800 sm:pt-0'>
|
||||
<Layout.Main className='space-y-3 pt-3 sm:pt-0 dark:divide-gray-800'>
|
||||
{me && (
|
||||
<Card
|
||||
className={clsx('relative z-[1] transition', {
|
||||
|
|
|
@ -20,7 +20,7 @@ const LandingPage: React.FC<ILandingPage> = ({ children }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Layout.Main className='space-y-3 pt-3 dark:divide-gray-800 sm:pt-0'>
|
||||
<Layout.Main className='space-y-3 pt-3 sm:pt-0 dark:divide-gray-800'>
|
||||
{children}
|
||||
|
||||
{!me && (
|
||||
|
|
|
@ -39,7 +39,7 @@ async function startSentry(dsn: string): Promise<void> {
|
|||
/^moz-extension:\/\//i,
|
||||
],
|
||||
|
||||
tracesSampleRate: 1.0,
|
||||
tracesSampleRate: .1,
|
||||
});
|
||||
|
||||
Sentry.setContext('soapbox', sourceCode);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import thunk, { ThunkDispatch } from 'redux-thunk';
|
||||
import { configureStore, Tuple } from '@reduxjs/toolkit';
|
||||
import { thunk, type ThunkDispatch } from 'redux-thunk';
|
||||
|
||||
import errorsMiddleware from './middleware/errors';
|
||||
import soundsMiddleware from './middleware/sounds';
|
||||
|
@ -9,11 +9,11 @@ import type { AnyAction } from 'redux';
|
|||
|
||||
export const store = configureStore({
|
||||
reducer: appReducer,
|
||||
middleware: [
|
||||
middleware: () => new Tuple(
|
||||
thunk,
|
||||
errorsMiddleware(),
|
||||
soundsMiddleware(),
|
||||
],
|
||||
),
|
||||
devTools: true,
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
const { parseColorMatrix } = require('./tailwind/colors.cjs');
|
||||
import aspectRatioPlugin from '@tailwindcss/aspect-ratio';
|
||||
import formsPlugin from '@tailwindcss/forms';
|
||||
import typographyPlugin from '@tailwindcss/typography';
|
||||
import { type Config } from 'tailwindcss';
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
import { parseColorMatrix } from './tailwind/colors';
|
||||
|
||||
const config: Config = {
|
||||
content: ['./src/**/*.{html,js,ts,tsx}', './custom/instance/**/*.html', './index.html'],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
|
@ -98,8 +102,10 @@ module.exports = {
|
|||
},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/forms'),
|
||||
require('@tailwindcss/typography'),
|
||||
require('@tailwindcss/aspect-ratio'),
|
||||
aspectRatioPlugin,
|
||||
formsPlugin,
|
||||
typographyPlugin,
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
|
@ -1,46 +0,0 @@
|
|||
// https://tailwindcss.com/docs/customizing-colors#using-css-variables
|
||||
function withOpacityValue(variable) {
|
||||
return ({ opacityValue }) => {
|
||||
if (opacityValue === undefined) {
|
||||
return `rgb(var(${variable}))`;
|
||||
}
|
||||
return `rgb(var(${variable}) / ${opacityValue})`;
|
||||
};
|
||||
}
|
||||
|
||||
// Parse a single color as a CSS variable
|
||||
const toColorVariable = (colorName, tint = null) => {
|
||||
const suffix = tint ? `-${tint}` : '';
|
||||
const variable = `--color-${colorName}${suffix}`;
|
||||
|
||||
return withOpacityValue(variable);
|
||||
};
|
||||
|
||||
// Parse list of tints into Tailwind function with CSS variables
|
||||
const parseTints = (colorName, tints) => {
|
||||
return tints.reduce((colorObj, tint) => {
|
||||
colorObj[tint] = toColorVariable(colorName, tint);
|
||||
return colorObj;
|
||||
}, {});
|
||||
};
|
||||
|
||||
// Parse color matrix into Tailwind color palette
|
||||
const parseColorMatrix = colorMatrix => {
|
||||
return Object.entries(colorMatrix).reduce((palette, colorData) => {
|
||||
const [colorName, tints] = colorData;
|
||||
|
||||
// Conditionally parse array or single-tint colors
|
||||
if (Array.isArray(tints)) {
|
||||
palette[colorName] = parseTints(colorName, tints);
|
||||
} else if (tints === true) {
|
||||
palette[colorName] = toColorVariable(colorName);
|
||||
}
|
||||
|
||||
return palette;
|
||||
}, {});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
withOpacityValue,
|
||||
parseColorMatrix,
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
import {
|
||||
withOpacityValue,
|
||||
parseColorMatrix,
|
||||
} from './colors.cjs';
|
||||
} from './colors';
|
||||
|
||||
describe('withOpacityValue()', () => {
|
||||
it('returns a Tailwind color function with alpha support', () => {
|
||||
|
@ -11,8 +11,7 @@ describe('withOpacityValue()', () => {
|
|||
expect(typeof result).toBe('function');
|
||||
|
||||
// Test calling the function
|
||||
expect(result({})).toBe('rgb(var(--color-primary-500))');
|
||||
expect(result({ opacityValue: .5 })).toBe('rgb(var(--color-primary-500) / 0.5)');
|
||||
expect(result).toBe('rgb(var(--color-primary-500) / <alpha-value>)');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -29,8 +28,8 @@ describe('parseColorMatrix()', () => {
|
|||
const result = parseColorMatrix(colorMatrix);
|
||||
|
||||
// Colors are mapped to functions which return CSS values
|
||||
expect(result.primary[500]({})).toEqual('rgb(var(--color-primary-500))');
|
||||
expect(result.accent[300]({ opacityValue: .3 })).toEqual('rgb(var(--color-accent-300) / 0.3)');
|
||||
// @ts-ignore
|
||||
expect(result.accent['300']).toEqual('rgb(var(--color-accent-300) / <alpha-value>)');
|
||||
});
|
||||
|
||||
it('parses single-tint values', () => {
|
||||
|
@ -46,6 +45,6 @@ describe('parseColorMatrix()', () => {
|
|||
|
||||
const result = parseColorMatrix(colorMatrix);
|
||||
|
||||
expect(result['gradient-start']({ opacityValue: .7 })).toEqual('rgb(var(--color-gradient-start) / 0.7)');
|
||||
expect(result['gradient-start']).toEqual('rgb(var(--color-gradient-start) / <alpha-value>)');
|
||||
});
|
||||
});
|
47
tailwind/colors.ts
Normal file
47
tailwind/colors.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { type RecursiveKeyValuePair } from 'tailwindcss/types/config';
|
||||
|
||||
/** https://tailwindcss.com/docs/customizing-colors#using-css-variables */
|
||||
function withOpacityValue(variable: string): string {
|
||||
return `rgb(var(${variable}) / <alpha-value>)`;
|
||||
}
|
||||
|
||||
/** Parse a single color as a CSS variable. */
|
||||
const toColorVariable = (colorName: string, tint: number | null = null): string => {
|
||||
const suffix = tint ? `-${tint}` : '';
|
||||
const variable = `--color-${colorName}${suffix}`;
|
||||
|
||||
return withOpacityValue(variable);
|
||||
};
|
||||
|
||||
/** Parse list of tints into Tailwind function with CSS variables. */
|
||||
const parseTints = (colorName: string, tints: number[]): RecursiveKeyValuePair => {
|
||||
return tints.reduce<Record<string, string>>((colorObj, tint) => {
|
||||
colorObj[tint] = toColorVariable(colorName, tint);
|
||||
return colorObj;
|
||||
}, {});
|
||||
};
|
||||
|
||||
interface ColorMatrix {
|
||||
[colorName: string]: number[] | boolean;
|
||||
}
|
||||
|
||||
/** Parse color matrix into Tailwind color palette. */
|
||||
const parseColorMatrix = (colorMatrix: ColorMatrix): RecursiveKeyValuePair => {
|
||||
return Object.entries(colorMatrix).reduce<RecursiveKeyValuePair>((palette, colorData) => {
|
||||
const [colorName, tints] = colorData;
|
||||
|
||||
// Conditionally parse array or single-tint colors
|
||||
if (Array.isArray(tints)) {
|
||||
palette[colorName] = parseTints(colorName, tints);
|
||||
} else if (tints === true) {
|
||||
palette[colorName] = toColorVariable(colorName);
|
||||
}
|
||||
|
||||
return palette;
|
||||
}, {});
|
||||
};
|
||||
|
||||
export {
|
||||
withOpacityValue,
|
||||
parseColorMatrix,
|
||||
};
|
Loading…
Reference in a new issue