Merge remote-tracking branch 'soapbox/develop' into events-

environments/review-events-5jp5it/deployments/1372
marcin mikołajczak 2 years ago
commit b7c89e502c

@ -148,6 +148,8 @@ docker:
image: docker:20.10.17
services:
- docker:20.10.17-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
script:
- echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

@ -6,9 +6,10 @@ import Bundle from 'soapbox/features/ui/components/bundle';
import { MediaGallery } from 'soapbox/features/ui/util/async-components';
import type { List as ImmutableList } from 'immutable';
import type { Attachment } from 'soapbox/types/entities';
interface IAttachmentThumbs {
media: ImmutableList<Immutable.Record<any>>
media: ImmutableList<Attachment>
onClick?(): void
sensitive?: boolean
}
@ -18,7 +19,7 @@ const AttachmentThumbs = (props: IAttachmentThumbs) => {
const dispatch = useDispatch();
const renderLoading = () => <div className='media-gallery--compact' />;
const onOpenMedia = (media: Immutable.Record<any>, index: number) => dispatch(openModal('MEDIA', { media, index }));
const onOpenMedia = (media: ImmutableList<Attachment>, index: number) => dispatch(openModal('MEDIA', { media, index }));
return (
<div className='attachment-thumbs'>
@ -30,6 +31,7 @@ const AttachmentThumbs = (props: IAttachmentThumbs) => {
height={50}
compact
sensitive={sensitive}
visible
/>
)}
</Bundle>

@ -263,14 +263,13 @@ const Item: React.FC<IItem> = ({
interface IMediaGallery {
sensitive?: boolean,
media: ImmutableList<Attachment>,
size: number,
height: number,
onOpenMedia: (media: ImmutableList<Attachment>, index: number) => void,
defaultWidth: number,
cacheWidth: (width: number) => void,
defaultWidth?: number,
cacheWidth?: (width: number) => void,
visible?: boolean,
onToggleVisibility?: () => void,
displayMedia: string,
displayMedia?: string,
compact: boolean,
}
@ -278,7 +277,7 @@ const MediaGallery: React.FC<IMediaGallery> = (props) => {
const {
media,
sensitive = false,
defaultWidth,
defaultWidth = 0,
onToggleVisibility,
onOpenMedia,
cacheWidth,

@ -1,6 +1,6 @@
import classNames from 'clsx';
import React, { useEffect, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { useIntl, FormattedMessage } from 'react-intl';
import { usePopper } from 'react-popper';
import { useHistory } from 'react-router-dom';
@ -15,9 +15,10 @@ import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
import { UserPanel } from 'soapbox/features/ui/util/async-components';
import { useAppSelector, useAppDispatch } from 'soapbox/hooks';
import { makeGetAccount } from 'soapbox/selectors';
import { isLocal } from 'soapbox/utils/accounts';
import { showProfileHoverCard } from './hover_ref_wrapper';
import { Card, CardBody, Stack, Text } from './ui';
import { Card, CardBody, HStack, Icon, Stack, Text } from './ui';
import type { AppDispatch } from 'soapbox/store';
import type { Account } from 'soapbox/types/entities';
@ -60,6 +61,7 @@ interface IProfileHoverCard {
export const ProfileHoverCard: React.FC<IProfileHoverCard> = ({ visible = true }) => {
const dispatch = useAppDispatch();
const history = useHistory();
const intl = useIntl();
const [popperElement, setPopperElement] = useState<HTMLElement | null>(null);
@ -88,6 +90,7 @@ export const ProfileHoverCard: React.FC<IProfileHoverCard> = ({ visible = true }
if (!account) return null;
const accountBio = { __html: account.note_emojified };
const memberSinceDate = intl.formatDate(account.created_at, { month: 'long', year: 'numeric' });
const followedBy = me !== account.id && account.relationship?.followed_by === true;
return (
@ -116,6 +119,23 @@ export const ProfileHoverCard: React.FC<IProfileHoverCard> = ({ visible = true }
)}
</BundleContainer>
{isLocal(account) ? (
<HStack alignItems='center' space={0.5}>
<Icon
src={require('@tabler/icons/calendar.svg')}
className='w-4 h-4 text-gray-800 dark:text-gray-200'
/>
<Text size='sm'>
<FormattedMessage
id='account.member_since' defaultMessage='Joined {date}' values={{
date: memberSinceDate,
}}
/>
</Text>
</HStack>
) : null}
{account.source.get('note', '').length > 0 && (
<Text size='sm' dangerouslySetInnerHTML={accountBio} />
)}

@ -376,7 +376,7 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
menu.push({
text: intl.formatMessage(status.pinned ? messages.unpin : messages.pin),
action: handlePinClick,
icon: mutingConversation ? require('@tabler/icons/pinned-off.svg') : require('@tabler/icons/pin.svg'),
icon: status.pinned ? require('@tabler/icons/pinned-off.svg') : require('@tabler/icons/pin.svg'),
});
} else {
if (status.visibility === 'private') {

@ -178,6 +178,7 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chatId, chatMessageIds, a
media={ImmutableList([attachment])}
height={120}
onOpenMedia={onOpenMedia}
visible
/>
)}
</Bundle>

@ -48,6 +48,7 @@ const messages = defineMessages({
promoPanelIconsLink: { id: 'soapbox_config.hints.promo_panel_icons.link', defaultMessage: 'Soapbox Icons List' },
authenticatedProfileLabel: { id: 'soapbox_config.authenticated_profile_label', defaultMessage: 'Profiles require authentication' },
authenticatedProfileHint: { id: 'soapbox_config.authenticated_profile_hint', defaultMessage: 'Users must be logged-in to view replies and media on user profiles.' },
displayCtaLabel: { id: 'soapbox_config.cta_label', defaultMessage: 'Display call to action panels if not authenticated' },
singleUserModeLabel: { id: 'soapbox_config.single_user_mode_label', defaultMessage: 'Single user mode' },
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' },
@ -261,6 +262,13 @@ const SoapboxConfig: React.FC = () => {
/>
</ListItem>
<ListItem label={intl.formatMessage(messages.displayCtaLabel)}>
<Toggle
checked={soapbox.displayCta === true}
onChange={handleChange(['displayCta'], (e) => e.target.checked)}
/>
</ListItem>
<ListItem
label={intl.formatMessage(messages.authenticatedProfileLabel)}
hint={intl.formatMessage(messages.authenticatedProfileHint)}

@ -2,12 +2,15 @@ import React from 'react';
import { FormattedMessage } from 'react-intl';
import { Card, CardTitle, Text, Stack, Button } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks';
import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks';
/** Prompts logged-out users to log in when viewing a thread. */
const ThreadLoginCta: React.FC = () => {
const { displayCta } = useSoapboxConfig();
const siteTitle = useAppSelector(state => state.instance.title);
if (!displayCta) return null;
return (
<Card className='px-6 py-12 space-y-6 text-center' variant='rounded'>
<Stack>

@ -5,11 +5,11 @@ import { Banner, Button, HStack, Stack, Text } from 'soapbox/components/ui';
import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks';
const CtaBanner = () => {
const { singleUserMode } = useSoapboxConfig();
const { displayCta, singleUserMode } = useSoapboxConfig();
const siteTitle = useAppSelector((state) => state.instance.title);
const me = useAppSelector((state) => state.me);
if (me || singleUserMode) return null;
if (me || !displayCta || singleUserMode) return null;
return (
<div data-testid='cta-banner' className='hidden lg:block'>

@ -1,125 +0,0 @@
import classNames from 'clsx';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import { changeUploadCompose } from '../../../actions/compose';
import { getPointerPosition } from '../../video';
import ImageLoader from './image_loader';
const mapStateToProps = (state, { id }) => ({
media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
});
const mapDispatchToProps = (dispatch, { id }) => ({
onSave: (x, y) => {
dispatch(changeUploadCompose(id, { focus: `${x.toFixed(2)},${y.toFixed(2)}` }));
},
});
export default @connect(mapStateToProps, mapDispatchToProps)
class FocalPointModal extends ImmutablePureComponent {
static propTypes = {
media: ImmutablePropTypes.map.isRequired,
};
state = {
x: 0,
y: 0,
focusX: 0,
focusY: 0,
dragging: false,
};
componentDidMount() {
this.updatePositionFromMedia(this.props.media);
}
componentDidUpdate(prevProps) {
const { media } = this.props;
if (prevProps.media.get('id') !== media.get('id')) {
this.updatePositionFromMedia(media);
}
}
componentWillUnmount() {
document.removeEventListener('mousemove', this.handleMouseMove);
document.removeEventListener('mouseup', this.handleMouseUp);
}
handleMouseDown = e => {
document.addEventListener('mousemove', this.handleMouseMove);
document.addEventListener('mouseup', this.handleMouseUp);
this.updatePosition(e);
this.setState({ dragging: true });
}
handleMouseMove = e => {
this.updatePosition(e);
}
handleMouseUp = () => {
document.removeEventListener('mousemove', this.handleMouseMove);
document.removeEventListener('mouseup', this.handleMouseUp);
this.setState({ dragging: false });
this.props.onSave(this.state.focusX, this.state.focusY);
}
updatePosition = e => {
const { x, y } = getPointerPosition(this.node, e);
const focusX = (x - .5) * 2;
const focusY = (y - .5) * -2;
this.setState({ x, y, focusX, focusY });
}
updatePositionFromMedia = media => {
const focusX = media.getIn(['meta', 'focus', 'x']);
const focusY = media.getIn(['meta', 'focus', 'y']);
if (focusX && focusY) {
const x = (focusX / 2) + .5;
const y = (focusY / -2) + .5;
this.setState({ x, y, focusX, focusY });
} else {
this.setState({ x: 0.5, y: 0.5, focusX: 0, focusY: 0 });
}
}
setRef = c => {
this.node = c;
}
render() {
const { media } = this.props;
const { x, y, dragging } = this.state;
const width = media.getIn(['meta', 'original', 'width']) || null;
const height = media.getIn(['meta', 'original', 'height']) || null;
return (
<div className='modal-root__modal video-modal focal-point-modal'>
<div className={classNames('focal-point', { dragging })} ref={this.setRef}>
<ImageLoader
previewSrc={media.get('preview_url')}
src={media.get('url')}
width={width}
height={height}
/>
<div className='focal-point__reticle' style={{ top: `${y * 100}%`, left: `${x * 100}%` }} />
<div className='focal-point__overlay' onMouseDown={this.handleMouseDown} />
</div>
</div>
);
}
}

@ -15,7 +15,6 @@ import {
ListAdder,
MissingDescriptionModal,
ActionsModal,
FocalPointModal,
HotkeysModal,
ComposeModal,
ReplyMentionsModal,
@ -55,7 +54,6 @@ const MODAL_COMPONENTS = {
'ACTIONS': ActionsModal,
'EMBED': EmbedModal,
'LIST_EDITOR': ListEditor,
'FOCAL_POINT': FocalPointModal,
'LIST_ADDER': ListAdder,
'HOTKEYS': HotkeysModal,
'COMPOSE': ComposeModal,

@ -146,10 +146,6 @@ export function ActionsModal() {
return import(/* webpackChunkName: "features/ui" */'../components/actions_modal');
}
export function FocalPointModal() {
return import(/* webpackChunkName: "features/ui" */'../components/focal_point_modal');
}
export function HotkeysModal() {
return import(/* webpackChunkName: "features/ui" */'../components/hotkeys_modal');
}

@ -49,6 +49,7 @@
"account.requested": "Oczekująca prośba, kliknij aby anulować",
"account.requested_small": "Oczekująca prośba",
"account.search": "Szukaj wpisów @{name}",
"account.search_self": "Szukaj własnych wpisów",
"account.share": "Udostępnij profil @{name}",
"account.show_reblogs": "Pokazuj podbicia od @{name}",
"account.subscribe": "Subskrybuj wpisy @{name}",
@ -67,6 +68,18 @@
"account.verified": "Zweryfikowane konto",
"account.welcome": "Welcome",
"account_gallery.none": "Brak zawartości multimedialnej do wyświetlenia.",
"account_moderation_modal.admin_fe": "Otwórz w AdminFE",
"account_moderation_modal.fields.account_role": "Poziom uprawnień",
"account_moderation_modal.fields.badges": "Niestandaradowe odznaki",
"account_moderation_modal.fields.deactivate": "Dezaktywuj konto",
"account_moderation_modal.fields.delete": "Usuń konto",
"account_moderation_modal.fields.suggested": "Proponuj obserwację tego konta",
"account_moderation_modal.fields.verified": "Zweryfikowane konto",
"account_moderation_modal.info.id": "ID: {id}",
"account_moderation_modal.roles.admin": "Administrator",
"account_moderation_modal.roles.moderator": "Moderator",
"account_moderation_modal.roles.user": "Użytkownik",
"account_moderation_modal.title": "Moderuj @{acct}",
"account_note.hint": "Możesz pozostawić dla siebie notatkę o tym użytkowniku (tylko ty ją zobaczysz):",
"account_note.placeholder": "Nie wprowadzono opisu",
"account_note.save": "Zapisz",
@ -125,6 +138,7 @@
"admin.users.actions.unsuggest_user": "Przestań polecać @{name}",
"admin.users.actions.unverify_user": "Cofnij weryfikację @{name}",
"admin.users.actions.verify_user": "Weryfikuj @{name}",
"admin.users.badges_saved_message": "Zaktualizowano niestandardowe odznaki.",
"admin.users.remove_donor_message": "Usunięto @{acct} ze wspierających",
"admin.users.set_donor_message": "Ustawiono @{acct} jako wspierającego",
"admin.users.user_deactivated_message": "Zdezaktywowano @{acct}",
@ -173,6 +187,7 @@
"backups.empty_message": "Nie znaleziono kopii zapasowych. {action}",
"backups.empty_message.action": "Chcesz utworzyć?",
"backups.pending": "Oczekująca",
"badge_input.placeholder": "Wprowadź odznakę…",
"beta.also_available": "Dostępne w językach:",
"birthday_panel.title": "Urodziny",
"birthdays_modal.empty": "Żaden z Twoich znajomych nie ma dziś urodzin.",
@ -736,8 +751,10 @@
"migration.fields.acct.placeholder": "konto@domena",
"migration.fields.confirm_password.label": "Obecne hasło",
"migration.hint": "Ta opcja przeniesie Twoich obserwujących na nowe konto. Żadne inne dane nie zostaną przeniesione. Aby dokonać migracji, musisz najpierw {link} na swoim nowym koncie.",
"migration.hint.cooldown_period": "Jeżeli przemigrujesz swoje konto, nie będziesz móc wykonać kolejnej migracji przez {cooldownPeriod, plural, one {jeden dzień} other {kolejne # dni}}.",
"migration.hint.link": "utworzyć alias konta",
"migration.move_account.fail": "Przenoszenie konta nie powiodło się.",
"migration.move_account.fail.cooldown_period": "Niedawno migrowałeś(-aś) swoje konto. Spróbuj ponownie później.",
"migration.move_account.success": "Pomyślnie przeniesiono konto.",
"migration.submit": "Przenieś obserwujących",
"missing_description_modal.cancel": "Anuluj",
@ -747,6 +764,9 @@
"missing_indicator.label": "Nie znaleziono",
"missing_indicator.sublabel": "Nie można odnaleźć tego zasobu",
"mobile.also_available": "Dostępne w językach:",
"moderation_overlay.contact": "Kontakt",
"moderation_overlay.hide": "Ukryj",
"moderation_overlay.show": "Wyświetl",
"morefollows.followers_label": "…i {count} więcej {count, plural, one {obserwujący(-a)} few {obserwujących} many {obserwujących} other {obserwujących}} na zdalnych stronach.",
"morefollows.following_label": "…i {count} więcej {count, plural, one {obserwowany(-a)} few {obserwowanych} many {obserwowanych} other {obserwowanych}} na zdalnych stronach.",
"mute_modal.hide_notifications": "Chcesz ukryć powiadomienia od tego użytkownika?",
@ -843,6 +863,10 @@
"onboarding.display_name.subtitle": "Możesz ją zawsze zmienić później.",
"onboarding.display_name.title": "Wybierz wyświetlaną nazwę",
"onboarding.done": "Gotowe",
"onboarding.fediverse.its_you": "Oto Twoje konto! Inni ludzie mogą Cię obserwować z innych serwerów używając pełnej @nazwy.",
"onboarding.fediverse.next": "Dalej",
"onboarding.fediverse.title": "{siteTitle} to tylko jedna z części Fediwersum",
"onboarding.fediverse.other_instances": "Kiedy przeglądasz oś czasum, zwróć uwagę na pełną nazwę użytkownika po znaku @, aby wiedzieć z którego serwera pochodzi wpis.",
"onboarding.finished.message": "Cieszymy się, że możemy powitać Cię w naszej społeczności! Naciśnij poniższy przycisk, aby rozpocząć.",
"onboarding.finished.title": "Wprowadzenie ukończone",
"onboarding.header.subtitle": "Będzie widoczny w górnej części Twojego profilu",
@ -1008,6 +1032,7 @@
"report.target": "Zgłaszanie {target}",
"reset_password.fail": "Token wygasł, spróbuj ponownie.",
"reset_password.header": "Ustaw nowe hasło",
"save": "Zapisz",
"schedule.post_time": "Data/godzina publikacji",
"schedule.remove": "Usuń zaplanowany wpis",
"schedule_button.add_schedule": "Zaplanuj wpis na później",
@ -1128,7 +1153,7 @@
"sponsored.info.title": "Dlaczego widzę tę reklamę?",
"sponsored.subtitle": "Wpis sponsorowany",
"status.actions.more": "Więcej",
"status.admin_account": "Otwórz interfejs moderacyjny dla @{name}",
"status.admin_account": "Moderuj @{name}",
"status.admin_status": "Otwórz ten wpis w interfejsie moderacyjnym",
"status.block": "Zablokuj @{name}",
"status.bookmark": "Dodaj do zakładek",

@ -5,6 +5,8 @@ import {
fromJS,
} from 'immutable';
import { normalizeAttachment } from 'soapbox/normalizers/attachment';
import type { Attachment, Card, Emoji } from 'soapbox/types/entities';
export const ChatMessageRecord = ImmutableRecord({
@ -22,8 +24,14 @@ export const ChatMessageRecord = ImmutableRecord({
pending: false,
});
const normalizeMedia = (status: ImmutableMap<string, any>) => {
return status.update('attachment', null, normalizeAttachment);
};
export const normalizeChatMessage = (chatMessage: Record<string, any>) => {
return ChatMessageRecord(
ImmutableMap(fromJS(chatMessage)),
ImmutableMap(fromJS(chatMessage)).withMutations(chatMessage => {
normalizeMedia(chatMessage);
}),
);
};

@ -112,6 +112,7 @@ export const SoapboxConfigRecord = ImmutableRecord({
singleUserModeProfile: '',
linkFooterMessage: '',
links: ImmutableMap<string, string>(),
displayCta: true,
}, 'SoapboxConfig');
type SoapboxConfigMap = ImmutableMap<string, any>;

@ -39,7 +39,7 @@ const DefaultPage: React.FC = ({ children }) => {
{Component => <Component limit={3} key='trends-panel' />}
</BundleContainer>
)}
{features.suggestions && (
{me && features.suggestions && (
<BundleContainer fetchComponent={WhoToFollowPanel}>
{Component => <Component limit={3} key='wtf-panel' />}
</BundleContainer>

@ -103,7 +103,7 @@ const HomePage: React.FC = ({ children }) => {
{Component => <Component limit={10} />}
</BundleContainer>
)}
{features.suggestions && (
{me && features.suggestions && (
<BundleContainer fetchComponent={WhoToFollowPanel}>
{Component => <Component limit={3} />}
</BundleContainer>

@ -137,7 +137,7 @@ const ProfilePage: React.FC<IProfilePage> = ({ params, children }) => {
<BundleContainer fetchComponent={PinnedAccountsPanel}>
{Component => <Component account={account} limit={5} key='pinned-accounts-panel' />}
</BundleContainer>
) : features.suggestions && (
) : me && features.suggestions && (
<BundleContainer fetchComponent={WhoToFollowPanel}>
{Component => <Component limit={3} key='wtf-panel' />}
</BundleContainer>

@ -43,7 +43,7 @@ const StatusPage: React.FC<IStatusPage> = ({ children }) => {
{Component => <Component limit={3} key='trends-panel' />}
</BundleContainer>
)}
{features.suggestions && (
{me && features.suggestions && (
<BundleContainer fetchComponent={WhoToFollowPanel}>
{Component => <Component limit={3} key='wtf-panel' />}
</BundleContainer>

@ -242,39 +242,3 @@
@apply block shadow-md;
}
}
.focal-point {
position: relative;
cursor: pointer;
overflow: hidden;
&.dragging {
cursor: move;
}
img {
max-width: 80vw;
max-height: 80vh;
width: auto;
height: auto;
margin: auto;
}
&__reticle {
position: absolute;
width: 100px;
height: 100px;
transform: translate(-50%, -50%);
background: url('../images/reticle.png') no-repeat 0 0;
border-radius: 50%;
box-shadow: 0 0 0 9999em rgba($base-shadow-color, 0.35);
}
&__overlay {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
}

@ -387,12 +387,6 @@
}
}
.focal-point-modal {
max-width: 80vw;
max-height: 80vh;
position: relative;
}
.column-inline-form {
padding: 7px 15px;
padding-right: 5px;

Loading…
Cancel
Save