Merge remote-tracking branch 'soapbox/develop' into cleanup
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
commit
08a47bed5d
12 changed files with 644 additions and 537 deletions
|
@ -146,9 +146,9 @@ pages:
|
|||
|
||||
docker:
|
||||
stage: deploy
|
||||
image: docker:20.10.17
|
||||
image: docker:20.10.22
|
||||
services:
|
||||
- docker:20.10.17-dind
|
||||
- docker:20.10.22-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 18.2.0
|
||||
nodejs 18.12.1
|
||||
|
|
|
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Added
|
||||
- Compatibility: rudimentary support for Takahē.
|
||||
- UI: added backdrop blur behind modals.
|
||||
- Admin: let admins configure media preview for attachment thumbnails.
|
||||
|
||||
### Changed
|
||||
- Posts: letterbox images to 19:6 again.
|
||||
|
|
|
@ -5,7 +5,7 @@ import Blurhash from 'soapbox/components/blurhash';
|
|||
import Icon from 'soapbox/components/icon';
|
||||
import StillImage from 'soapbox/components/still-image';
|
||||
import { MIMETYPE_ICONS } from 'soapbox/components/upload';
|
||||
import { useSettings } from 'soapbox/hooks';
|
||||
import { useSettings, useSoapboxConfig } from 'soapbox/hooks';
|
||||
import { Attachment } from 'soapbox/types/entities';
|
||||
import { truncateFilename } from 'soapbox/utils/media';
|
||||
|
||||
|
@ -72,6 +72,7 @@ const Item: React.FC<IItem> = ({
|
|||
}) => {
|
||||
const settings = useSettings();
|
||||
const autoPlayGif = settings.get('autoPlayGif') === true;
|
||||
const { mediaPreview } = useSoapboxConfig();
|
||||
|
||||
const handleMouseEnter: React.MouseEventHandler<HTMLVideoElement> = ({ currentTarget: video }) => {
|
||||
if (hoverToPlay()) {
|
||||
|
@ -171,7 +172,7 @@ const Item: React.FC<IItem> = ({
|
|||
>
|
||||
<StillImage
|
||||
className='w-full h-full'
|
||||
src={attachment.url}
|
||||
src={mediaPreview ? attachment.preview_url : attachment.url}
|
||||
alt={attachment.description}
|
||||
letterboxed={letterboxed}
|
||||
showExt
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from 'clsx';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { replaceHomeTimeline } from 'soapbox/actions/timelines';
|
||||
|
@ -11,7 +11,7 @@ import PlaceholderAvatar from '../placeholder/components/placeholder-avatar';
|
|||
|
||||
const CarouselItem = React.forwardRef((
|
||||
{ avatar, seen, onViewed, onPinned }: { avatar: Avatar, seen: boolean, onViewed: (account_id: string) => void, onPinned?: (avatar: null | Avatar) => void },
|
||||
ref: any,
|
||||
ref: React.ForwardedRef<HTMLDivElement>,
|
||||
) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
|
@ -40,8 +40,11 @@ const CarouselItem = React.forwardRef((
|
|||
onPinned(avatar);
|
||||
}
|
||||
|
||||
onViewed(avatar.account_id);
|
||||
markAsSeen.mutate(avatar.account_id);
|
||||
if (!seen) {
|
||||
onViewed(avatar.account_id);
|
||||
markAsSeen.mutate(avatar.account_id);
|
||||
}
|
||||
|
||||
dispatch(replaceHomeTimeline(avatar.account_id, { maxId: null }, () => setLoading(false)));
|
||||
}
|
||||
};
|
||||
|
@ -51,7 +54,7 @@ const CarouselItem = React.forwardRef((
|
|||
ref={ref}
|
||||
aria-disabled={isFetching}
|
||||
onClick={handleClick}
|
||||
className='cursor-pointer snap-start py-4'
|
||||
className='cursor-pointer py-4'
|
||||
role='filter-feed-by-user'
|
||||
data-testid='carousel-item'
|
||||
>
|
||||
|
@ -87,6 +90,7 @@ const FeedCarousel = () => {
|
|||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [_ref, setContainerRef, { width }] = useDimensions();
|
||||
const carouselItemRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [seenAccountIds, setSeenAccountIds] = useState<string[]>([]);
|
||||
const [pageSize, setPageSize] = useState<number>(0);
|
||||
|
@ -151,18 +155,18 @@ const FeedCarousel = () => {
|
|||
data-testid='feed-carousel'
|
||||
>
|
||||
<HStack alignItems='stretch'>
|
||||
<div className='z-10 rounded-l-xl bg-white dark:bg-gray-900 w-8 flex self-stretch items-center justify-center'>
|
||||
<div className='z-10 rounded-l-xl bg-white dark:bg-primary-900 w-8 flex self-stretch items-center justify-center'>
|
||||
<button
|
||||
data-testid='prev-page'
|
||||
onClick={handlePrevPage}
|
||||
className='h-7 w-7 flex items-center justify-center disabled:opacity-25 transition-opacity duration-500'
|
||||
className='w-7 flex items-center justify-center disabled:opacity-25 transition-opacity duration-500'
|
||||
disabled={!hasPrevPage}
|
||||
>
|
||||
<Icon src={require('@tabler/icons/chevron-left.svg')} className='text-black dark:text-white h-5 w-5' />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='overflow-hidden relative'>
|
||||
<div className='overflow-hidden relative w-full'>
|
||||
{pinnedAvatar ? (
|
||||
<div
|
||||
className='z-10 flex items-center justify-center absolute left-0 top-0 bottom-0 bg-white dark:bg-primary-900'
|
||||
|
@ -175,6 +179,7 @@ const FeedCarousel = () => {
|
|||
seen={seenAccountIds?.includes(pinnedAvatar.account_id)}
|
||||
onViewed={markAsSeen}
|
||||
onPinned={(avatar) => setPinnedAvatar(avatar)}
|
||||
ref={carouselItemRef}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
@ -203,7 +208,11 @@ const FeedCarousel = () => {
|
|||
}}
|
||||
>
|
||||
{avatar === null ? (
|
||||
<Stack className='w-14 snap-start py-4 h-auto' space={3}>
|
||||
<Stack
|
||||
className='w-14 py-4 h-auto'
|
||||
space={3}
|
||||
style={{ height: carouselItemRef.current?.clientHeight }}
|
||||
>
|
||||
<div className='block mx-auto relative w-16 h-16 rounded-full'>
|
||||
<div className='w-16 h-16' />
|
||||
</div>
|
||||
|
@ -227,11 +236,11 @@ const FeedCarousel = () => {
|
|||
</HStack>
|
||||
</div>
|
||||
|
||||
<div className='z-10 rounded-r-xl bg-white dark:bg-gray-900 w-8 self-stretch flex items-center justify-center'>
|
||||
<div className='z-10 rounded-r-xl bg-white dark:bg-primary-900 w-8 self-stretch flex items-center justify-center'>
|
||||
<button
|
||||
data-testid='next-page'
|
||||
onClick={handleNextPage}
|
||||
className='h-7 w-7 flex items-center justify-center disabled:opacity-25 transition-opacity duration-500'
|
||||
className='w-7 flex items-center justify-center disabled:opacity-25 transition-opacity duration-500'
|
||||
disabled={!hasNextPage}
|
||||
>
|
||||
<Icon src={require('@tabler/icons/chevron-right.svg')} className='text-black dark:text-white h-5 w-5' />
|
||||
|
|
|
@ -50,6 +50,8 @@ const messages = defineMessages({
|
|||
singleUserModeHint: { id: 'soapbox_config.single_user_mode_hint', defaultMessage: 'Front page will redirect to a given user profile.' },
|
||||
singleUserModeProfileLabel: { id: 'soapbox_config.single_user_mode_profile_label', defaultMessage: 'Main user handle' },
|
||||
singleUserModeProfileHint: { id: 'soapbox_config.single_user_mode_profile_hint', defaultMessage: '@handle' },
|
||||
mediaPreviewLabel: { id: 'soapbox_config.media_preview_label', defaultMessage: 'Prefer preview media for thumbnails' },
|
||||
mediaPreviewHint: { id: 'soapbox_config.media_preview_hint', defaultMessage: 'Some backends provide an optimized version of media for display in timelines. However, these preview images may be too small without additional configuration.' },
|
||||
feedInjectionLabel: { id: 'soapbox_config.feed_injection_label', defaultMessage: 'Feed injection' },
|
||||
feedInjectionHint: { id: 'soapbox_config.feed_injection_hint', defaultMessage: 'Inject the feed with additional content, such as suggested profiles.' },
|
||||
tileServerLabel: { id: 'soapbox_config.tile_server_label', defaultMessage: 'Map tile server' },
|
||||
|
@ -250,6 +252,16 @@ const SoapboxConfig: React.FC = () => {
|
|||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
label={intl.formatMessage(messages.mediaPreviewLabel)}
|
||||
hint={intl.formatMessage(messages.mediaPreviewHint)}
|
||||
>
|
||||
<Toggle
|
||||
checked={soapbox.mediaPreview === true}
|
||||
onChange={handleChange(['mediaPreview'], (e) => e.target.checked)}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem label={intl.formatMessage(messages.displayCtaLabel)}>
|
||||
<Toggle
|
||||
checked={soapbox.displayCta === true}
|
||||
|
|
|
@ -1191,6 +1191,8 @@
|
|||
"soapbox_config.hints.promo_panel_icons.link": "Soapbox Icons List",
|
||||
"soapbox_config.home_footer.meta_fields.label_placeholder": "Label",
|
||||
"soapbox_config.home_footer.meta_fields.url_placeholder": "URL",
|
||||
"soapbox_config.media_preview_hint": "Some backends provide an optimized version of media for display in timelines. However, these preview images may be too small without additional configuration.",
|
||||
"soapbox_config.media_preview_label": "Prefer preview media for thumbnails",
|
||||
"soapbox_config.promo_panel.meta_fields.icon_placeholder": "Icon",
|
||||
"soapbox_config.promo_panel.meta_fields.label_placeholder": "Label",
|
||||
"soapbox_config.promo_panel.meta_fields.url_placeholder": "URL",
|
||||
|
|
|
@ -115,6 +115,11 @@ export const SoapboxConfigRecord = ImmutableRecord({
|
|||
feedInjection: true,
|
||||
tileServer: '',
|
||||
tileServerAttribution: '',
|
||||
/**
|
||||
* Whether to use the preview URL for media thumbnails.
|
||||
* On some platforms this can be too blurry without additional configuration.
|
||||
*/
|
||||
mediaPreview: false,
|
||||
}, 'SoapboxConfig');
|
||||
|
||||
type SoapboxConfigMap = ImmutableMap<string, any>;
|
||||
|
|
14
package.json
14
package.json
|
@ -52,7 +52,7 @@
|
|||
"@fontsource/inter": "^4.5.1",
|
||||
"@fontsource/roboto": "^4.5.0",
|
||||
"@gamestdio/websocket": "^0.3.2",
|
||||
"@jest/globals": "^28.1.2",
|
||||
"@jest/globals": "^29.0.0",
|
||||
"@lcdp/offline-plugin": "^5.1.0",
|
||||
"@metamask/providers": "^9.0.0",
|
||||
"@popperjs/core": "^2.11.5",
|
||||
|
@ -75,7 +75,7 @@
|
|||
"@testing-library/react": "^12.1.4",
|
||||
"@types/escape-html": "^1.0.1",
|
||||
"@types/http-link-header": "^1.0.3",
|
||||
"@types/jest": "^28.1.4",
|
||||
"@types/jest": "^29.0.0",
|
||||
"@types/leaflet": "^1.8.0",
|
||||
"@types/lodash": "^4.14.180",
|
||||
"@types/object-assign": "^4.0.30",
|
||||
|
@ -209,13 +209,14 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@jedmao/redux-mock-store": "^3.0.5",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
"@testing-library/user-event": "^14.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.15.0",
|
||||
"@typescript-eslint/parser": "^5.15.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-jest": "^28.1.2",
|
||||
"babel-jest": "^29.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"danger": "^11.0.7",
|
||||
"eslint": "^7.0.0",
|
||||
|
@ -228,17 +229,18 @@
|
|||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"fake-indexeddb": "^4.0.0",
|
||||
"husky": "^7.0.2",
|
||||
"jest": "^28.1.2",
|
||||
"jest-environment-jsdom": "^28.1.2",
|
||||
"jest": "^29.0.0",
|
||||
"jest-environment-jsdom": "^29.0.0",
|
||||
"jest-junit": "^15.0.0",
|
||||
"lint-staged": ">=10",
|
||||
"raf": "^3.4.1",
|
||||
"react-intl-translations-manager": "^5.0.3",
|
||||
"react-refresh": "^0.14.0",
|
||||
"stylelint": "^13.7.2",
|
||||
"stylelint-config-standard": "^22.0.0",
|
||||
"stylelint-scss": "^3.18.0",
|
||||
"tailwindcss": "^3.2.1",
|
||||
"ts-jest": "^28.0.5",
|
||||
"ts-jest": "^29.0.0",
|
||||
"webpack-dev-server": "^4.9.1",
|
||||
"yargs": "^17.6.2"
|
||||
},
|
||||
|
|
|
@ -3,6 +3,7 @@ console.log('Running in development mode'); // eslint-disable-line no-console
|
|||
|
||||
import { join } from 'path';
|
||||
|
||||
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
|
||||
import { merge } from 'webpack-merge';
|
||||
|
||||
import sharedConfig from './shared';
|
||||
|
@ -78,7 +79,7 @@ const devServer: DevServerConfiguration = {
|
|||
host: devServerUrl.hostname,
|
||||
port: devServerUrl.port,
|
||||
https: devServerUrl.protocol === 'https:',
|
||||
hot: false,
|
||||
hot: true,
|
||||
allowedHosts: 'all',
|
||||
historyApiFallback: {
|
||||
disableDotRule: true,
|
||||
|
@ -116,6 +117,10 @@ const configuration: Configuration = {
|
|||
watchOptions,
|
||||
),
|
||||
|
||||
plugins: [
|
||||
new ReactRefreshWebpackPlugin(),
|
||||
],
|
||||
|
||||
devServer,
|
||||
};
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ import { env, settings } from '../configuration';
|
|||
|
||||
import type { RuleSetRule } from 'webpack';
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
|
||||
const rule: RuleSetRule = {
|
||||
test: /\.(js|jsx|mjs|ts|tsx)$/,
|
||||
include: [
|
||||
|
@ -25,6 +27,7 @@ const rule: RuleSetRule = {
|
|||
cacheDirectory: join(settings.cache_path, 'babel-loader'),
|
||||
cacheCompression: env.NODE_ENV === 'production',
|
||||
compact: env.NODE_ENV === 'production',
|
||||
plugins: isDevelopment ? ['react-refresh/babel'] : [],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
Loading…
Reference in a new issue