From b67c4541444b673cf236c30be09d8a712ffe3561 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 31 Aug 2022 10:18:13 -0500 Subject: [PATCH 01/36] findAccountByUsername: safely check account?.acct --- app/soapbox/selectors/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/soapbox/selectors/index.ts b/app/soapbox/selectors/index.ts index 2a026afe1..84165fc98 100644 --- a/app/soapbox/selectors/index.ts +++ b/app/soapbox/selectors/index.ts @@ -59,7 +59,7 @@ const findAccountsByUsername = (state: RootState, username: string) => { const accounts = state.accounts; return accounts.filter(account => { - return username.toLowerCase() === account.acct.toLowerCase(); + return username.toLowerCase() === account?.acct.toLowerCase(); }); }; From 38b6f87a8310237bfa46a083c09b72c4e44366ad Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 31 Aug 2022 17:01:19 -0500 Subject: [PATCH 02/36] RelativeTimestamp: convert to TSX --- app/soapbox/components/account.tsx | 2 +- ...ve_timestamp.js => relative_timestamp.tsx} | 62 +++++++++++-------- app/soapbox/components/ui/text/text.tsx | 9 ++- .../normalizers/__tests__/poll.test.ts | 1 - .../normalizers/__tests__/status.test.ts | 1 - app/soapbox/normalizers/account.ts | 4 +- app/soapbox/normalizers/poll.ts | 2 +- app/soapbox/normalizers/status.ts | 4 +- 8 files changed, 49 insertions(+), 36 deletions(-) rename app/soapbox/components/{relative_timestamp.js => relative_timestamp.tsx} (78%) diff --git a/app/soapbox/components/account.tsx b/app/soapbox/components/account.tsx index 281e1aee2..eeb970518 100644 --- a/app/soapbox/components/account.tsx +++ b/app/soapbox/components/account.tsx @@ -54,7 +54,7 @@ interface IAccount { id?: string, onActionClick?: (account: any) => void, showProfileHoverCard?: boolean, - timestamp?: string | Date, + timestamp?: string, timestampUrl?: string, futureTimestamp?: boolean, withAccountNote?: boolean, diff --git a/app/soapbox/components/relative_timestamp.js b/app/soapbox/components/relative_timestamp.tsx similarity index 78% rename from app/soapbox/components/relative_timestamp.js rename to app/soapbox/components/relative_timestamp.tsx index 1e64f9807..d530051d8 100644 --- a/app/soapbox/components/relative_timestamp.js +++ b/app/soapbox/components/relative_timestamp.tsx @@ -1,8 +1,7 @@ -import PropTypes from 'prop-types'; import React from 'react'; -import { injectIntl, defineMessages } from 'react-intl'; +import { injectIntl, defineMessages, IntlShape, FormatDateOptions } from 'react-intl'; -import { Text } from './ui'; +import Text, { IText } from './ui/text/text'; const messages = defineMessages({ just_now: { id: 'relative_time.just_now', defaultMessage: 'now' }, @@ -17,7 +16,7 @@ const messages = defineMessages({ days_remaining: { id: 'time_remaining.days', defaultMessage: '{number, plural, one {# day} other {# days}} left' }, }); -const dateFormatOptions = { +const dateFormatOptions: FormatDateOptions = { hour12: false, year: 'numeric', month: 'short', @@ -26,7 +25,7 @@ const dateFormatOptions = { minute: '2-digit', }; -const shortDateFormatOptions = { +const shortDateFormatOptions: FormatDateOptions = { month: 'short', day: 'numeric', }; @@ -38,7 +37,7 @@ const DAY = 1000 * 60 * 60 * 24; const MAX_DELAY = 2147483647; -const selectUnits = delta => { +const selectUnits = (delta: number) => { const absDelta = Math.abs(delta); if (absDelta < MINUTE) { @@ -52,7 +51,7 @@ const selectUnits = delta => { return 'day'; }; -const getUnitDelay = units => { +const getUnitDelay = (units: string) => { switch (units) { case 'second': return SECOND; @@ -67,7 +66,7 @@ const getUnitDelay = units => { } }; -export const timeAgoString = (intl, date, now, year) => { +export const timeAgoString = (intl: IntlShape, date: Date, now: number, year: number) => { const delta = now - date.getTime(); let relativeTime; @@ -93,7 +92,7 @@ export const timeAgoString = (intl, date, now, year) => { return relativeTime; }; -const timeRemainingString = (intl, date, now) => { +const timeRemainingString = (intl: IntlShape, date: Date, now: number) => { const delta = date.getTime() - now; let relativeTime; @@ -113,16 +112,21 @@ const timeRemainingString = (intl, date, now) => { return relativeTime; }; -export default @injectIntl -class RelativeTimestamp extends React.Component { +interface RelativeTimestampProps extends IText { + intl: IntlShape, + timestamp: string, + year?: number, + futureDate?: boolean, +} - static propTypes = { - intl: PropTypes.object.isRequired, - timestamp: PropTypes.string.isRequired, - year: PropTypes.number.isRequired, - theme: PropTypes.string, - futureDate: PropTypes.bool, - }; +interface RelativeTimestampState { + now: number, +} + +/** Displays a timestamp compared to the current time, eg "1m" for one minute ago. */ +class RelativeTimestamp extends React.Component { + + _timer: NodeJS.Timeout | undefined; state = { now: Date.now(), @@ -130,10 +134,10 @@ class RelativeTimestamp extends React.Component { static defaultProps = { year: (new Date()).getFullYear(), - theme: 'inherit', + theme: 'inherit' as const, }; - shouldComponentUpdate(nextProps, nextState) { + shouldComponentUpdate(nextProps: RelativeTimestampProps, nextState: RelativeTimestampState) { // As of right now the locale doesn't change without a new page load, // but we might as well check in case that ever changes. return this.props.timestamp !== nextProps.timestamp || @@ -141,14 +145,14 @@ class RelativeTimestamp extends React.Component { this.state.now !== nextState.now; } - UNSAFE_componentWillReceiveProps(prevProps) { + UNSAFE_componentWillReceiveProps(prevProps: RelativeTimestampProps) { if (this.props.timestamp !== prevProps.timestamp) { this.setState({ now: Date.now() }); } } componentDidMount() { - this._scheduleNextUpdate(this.props, this.state); + this._scheduleNextUpdate(); } UNSAFE_componentWillUpdate() { @@ -156,11 +160,15 @@ class RelativeTimestamp extends React.Component { } componentWillUnmount() { - clearTimeout(this._timer); + if (this._timer) { + clearTimeout(this._timer); + } } _scheduleNextUpdate() { - clearTimeout(this._timer); + if (this._timer) { + clearTimeout(this._timer); + } const { timestamp } = this.props; const delta = (new Date(timestamp)).getTime() - this.state.now; @@ -177,8 +185,8 @@ class RelativeTimestamp extends React.Component { render() { const { timestamp, intl, year, futureDate, theme, ...textProps } = this.props; - const date = new Date(timestamp); - const relativeTime = futureDate ? timeRemainingString(intl, date, this.state.now) : timeAgoString(intl, date, this.state.now, year); + const date = new Date(timestamp); + const relativeTime = futureDate ? timeRemainingString(intl, date, this.state.now) : timeAgoString(intl, date, this.state.now, year!); return ( @@ -188,3 +196,5 @@ class RelativeTimestamp extends React.Component { } } + +export default injectIntl(RelativeTimestamp); diff --git a/app/soapbox/components/ui/text/text.tsx b/app/soapbox/components/ui/text/text.tsx index 933ac7a76..d25554972 100644 --- a/app/soapbox/components/ui/text/text.tsx +++ b/app/soapbox/components/ui/text/text.tsx @@ -84,7 +84,9 @@ interface IText extends Pick, 'danger /** Whether to truncate the text if its container is too small. */ truncate?: boolean, /** Font weight of the text. */ - weight?: Weights + weight?: Weights, + /** Tooltip title. */ + title?: string, } /** UI-friendly text container with dark mode support. */ @@ -133,4 +135,7 @@ const Text: React.FC = React.forwardRef( }, ); -export default Text; +export { + Text as default, + IText, +}; diff --git a/app/soapbox/normalizers/__tests__/poll.test.ts b/app/soapbox/normalizers/__tests__/poll.test.ts index 8acf2ece4..b7ba0a46f 100644 --- a/app/soapbox/normalizers/__tests__/poll.test.ts +++ b/app/soapbox/normalizers/__tests__/poll.test.ts @@ -21,7 +21,6 @@ describe('normalizePoll()', () => { expect(ImmutableRecord.isRecord(result)).toBe(true); expect(ImmutableRecord.isRecord(result.options.get(0))).toBe(true); expect(result.toJS()).toMatchObject(expected); - expect(result.expires_at instanceof Date).toBe(true); }); it('normalizes a Pleroma logged-out poll', () => { diff --git a/app/soapbox/normalizers/__tests__/status.test.ts b/app/soapbox/normalizers/__tests__/status.test.ts index 43336d00f..b60373975 100644 --- a/app/soapbox/normalizers/__tests__/status.test.ts +++ b/app/soapbox/normalizers/__tests__/status.test.ts @@ -164,7 +164,6 @@ describe('normalizeStatus()', () => { expect(ImmutableRecord.isRecord(poll)).toBe(true); expect(ImmutableRecord.isRecord(poll.options.get(0))).toBe(true); expect(poll.toJS()).toMatchObject(expected); - expect(poll.expires_at instanceof Date).toBe(true); }); it('normalizes a Pleroma logged-out poll', () => { diff --git a/app/soapbox/normalizers/account.ts b/app/soapbox/normalizers/account.ts index 1a519b8a8..cfaf92a1c 100644 --- a/app/soapbox/normalizers/account.ts +++ b/app/soapbox/normalizers/account.ts @@ -26,7 +26,7 @@ export const AccountRecord = ImmutableRecord({ avatar_static: '', birthday: '', bot: false, - created_at: new Date(), + created_at: '', discoverable: false, display_name: '', emojis: ImmutableList(), @@ -38,7 +38,7 @@ export const AccountRecord = ImmutableRecord({ header: '', header_static: '', id: '', - last_status_at: new Date(), + last_status_at: '', location: '', locked: false, moved: null as EmbeddedEntity, diff --git a/app/soapbox/normalizers/poll.ts b/app/soapbox/normalizers/poll.ts index fb0f786ed..efae796c3 100644 --- a/app/soapbox/normalizers/poll.ts +++ b/app/soapbox/normalizers/poll.ts @@ -21,7 +21,7 @@ import type { Emoji, PollOption } from 'soapbox/types/entities'; export const PollRecord = ImmutableRecord({ emojis: ImmutableList(), expired: false, - expires_at: new Date(), + expires_at: '', id: '', multiple: false, options: ImmutableList(), diff --git a/app/soapbox/normalizers/status.ts b/app/soapbox/normalizers/status.ts index 6f35a3900..4788758d3 100644 --- a/app/soapbox/normalizers/status.ts +++ b/app/soapbox/normalizers/status.ts @@ -28,8 +28,8 @@ export const StatusRecord = ImmutableRecord({ bookmarked: false, card: null as Card | null, content: '', - created_at: new Date(), - edited_at: null as Date | null, + created_at: '', + edited_at: null as string | null, emojis: ImmutableList(), favourited: false, favourites_count: 0, From 5f1d9ac56a4bf39acb58c23dfcec9dd0fd2cf096 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 31 Aug 2022 17:01:58 -0500 Subject: [PATCH 03/36] relative_timestamp --> relative-timestamp --- app/soapbox/components/account.tsx | 2 +- app/soapbox/components/display-name.tsx | 2 +- app/soapbox/components/polls/poll-footer.tsx | 2 +- .../{relative_timestamp.tsx => relative-timestamp.tsx} | 0 app/soapbox/features/directory/components/account_card.tsx | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename app/soapbox/components/{relative_timestamp.tsx => relative-timestamp.tsx} (100%) diff --git a/app/soapbox/components/account.tsx b/app/soapbox/components/account.tsx index eeb970518..438d2f0f4 100644 --- a/app/soapbox/components/account.tsx +++ b/app/soapbox/components/account.tsx @@ -8,7 +8,7 @@ import { useAppSelector, useOnScreen } from 'soapbox/hooks'; import { getAcct } from 'soapbox/utils/accounts'; import { displayFqn } from 'soapbox/utils/state'; -import RelativeTimestamp from './relative_timestamp'; +import RelativeTimestamp from './relative-timestamp'; import { Avatar, Emoji, HStack, Icon, IconButton, Stack, Text } from './ui'; import type { Account as AccountEntity } from 'soapbox/types/entities'; diff --git a/app/soapbox/components/display-name.tsx b/app/soapbox/components/display-name.tsx index 1bb72a319..63028ccfe 100644 --- a/app/soapbox/components/display-name.tsx +++ b/app/soapbox/components/display-name.tsx @@ -6,7 +6,7 @@ import { useSoapboxConfig } from 'soapbox/hooks'; import { getAcct } from '../utils/accounts'; import Icon from './icon'; -import RelativeTimestamp from './relative_timestamp'; +import RelativeTimestamp from './relative-timestamp'; import VerificationBadge from './verification_badge'; import type { Account } from 'soapbox/types/entities'; diff --git a/app/soapbox/components/polls/poll-footer.tsx b/app/soapbox/components/polls/poll-footer.tsx index 366f34d1c..ef4ca2276 100644 --- a/app/soapbox/components/polls/poll-footer.tsx +++ b/app/soapbox/components/polls/poll-footer.tsx @@ -4,7 +4,7 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { fetchPoll, vote } from 'soapbox/actions/polls'; import { useAppDispatch } from 'soapbox/hooks'; -import RelativeTimestamp from '../relative_timestamp'; +import RelativeTimestamp from '../relative-timestamp'; import { Button, HStack, Stack, Text, Tooltip } from '../ui'; import type { Selected } from './poll'; diff --git a/app/soapbox/components/relative_timestamp.tsx b/app/soapbox/components/relative-timestamp.tsx similarity index 100% rename from app/soapbox/components/relative_timestamp.tsx rename to app/soapbox/components/relative-timestamp.tsx diff --git a/app/soapbox/features/directory/components/account_card.tsx b/app/soapbox/features/directory/components/account_card.tsx index ca3e852f1..12ac5e7a7 100644 --- a/app/soapbox/features/directory/components/account_card.tsx +++ b/app/soapbox/features/directory/components/account_card.tsx @@ -6,7 +6,7 @@ import { getSettings } from 'soapbox/actions/settings'; import Avatar from 'soapbox/components/avatar'; import DisplayName from 'soapbox/components/display-name'; import Permalink from 'soapbox/components/permalink'; -import RelativeTimestamp from 'soapbox/components/relative_timestamp'; +import RelativeTimestamp from 'soapbox/components/relative-timestamp'; import { Text } from 'soapbox/components/ui'; import ActionButton from 'soapbox/features/ui/components/action-button'; import { useAppSelector } from 'soapbox/hooks'; From babac13493f9112ca047d8acb38aee38a509b59e Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 31 Aug 2022 17:22:37 -0500 Subject: [PATCH 04/36] Bundle: convert to TSX --- .../ui/components/{bundle.js => bundle.tsx} | 42 +++++++++++-------- ...ndle_container.js => bundle_container.tsx} | 6 ++- 2 files changed, 29 insertions(+), 19 deletions(-) rename app/soapbox/features/ui/components/{bundle.js => bundle.tsx} (68%) rename app/soapbox/features/ui/containers/{bundle_container.js => bundle_container.tsx} (74%) diff --git a/app/soapbox/features/ui/components/bundle.js b/app/soapbox/features/ui/components/bundle.tsx similarity index 68% rename from app/soapbox/features/ui/components/bundle.js rename to app/soapbox/features/ui/components/bundle.tsx index 11622ec19..55f6478bc 100644 --- a/app/soapbox/features/ui/components/bundle.js +++ b/app/soapbox/features/ui/components/bundle.tsx @@ -1,21 +1,29 @@ -import PropTypes from 'prop-types'; import React from 'react'; const emptyComponent = () => null; const noop = () => { }; -class Bundle extends React.PureComponent { - - static propTypes = { - fetchComponent: PropTypes.func.isRequired, - loading: PropTypes.func, - error: PropTypes.func, - children: PropTypes.func.isRequired, - renderDelay: PropTypes.number, - onFetch: PropTypes.func, - onFetchSuccess: PropTypes.func, - onFetchFail: PropTypes.func, - } +interface BundleProps { + fetchComponent: () => Promise, + loading: React.ComponentType, + error: React.ComponentType<{ onRetry: (props: BundleProps) => void }>, + children: (mod: any) => React.ReactNode, + renderDelay?: number, + onFetch: () => void, + onFetchSuccess: () => void, + onFetchFail: (error: any) => void, +} + +interface BundleState { + mod: any, + forceRender: boolean, +} + +/** Fetches and renders an async component. */ +class Bundle extends React.PureComponent { + + timeout: NodeJS.Timeout | undefined; + timestamp: Date | undefined; static defaultProps = { loading: emptyComponent, @@ -37,7 +45,7 @@ class Bundle extends React.PureComponent { this.load(this.props); } - componentWillReceiveProps(nextProps) { + componentWillReceiveProps(nextProps: BundleProps) { if (nextProps.fetchComponent !== this.props.fetchComponent) { this.load(nextProps); } @@ -49,7 +57,7 @@ class Bundle extends React.PureComponent { } } - load = (props) => { + load = (props: BundleProps) => { const { fetchComponent, onFetch, onFetchSuccess, onFetchFail, renderDelay } = props || this.props; const cachedMod = Bundle.cache.get(fetchComponent); @@ -88,10 +96,10 @@ class Bundle extends React.PureComponent { render() { const { loading: Loading, error: Error, children, renderDelay } = this.props; const { mod, forceRender } = this.state; - const elapsed = this.timestamp ? (new Date() - this.timestamp) : renderDelay; + const elapsed = this.timestamp ? ((new Date()).getTime() - this.timestamp.getTime()) : renderDelay!; if (mod === undefined) { - return (elapsed >= renderDelay || forceRender) ? : null; + return (elapsed >= renderDelay! || forceRender) ? : null; } if (mod === null) { diff --git a/app/soapbox/features/ui/containers/bundle_container.js b/app/soapbox/features/ui/containers/bundle_container.tsx similarity index 74% rename from app/soapbox/features/ui/containers/bundle_container.js rename to app/soapbox/features/ui/containers/bundle_container.tsx index b12e29a43..12e4b3787 100644 --- a/app/soapbox/features/ui/containers/bundle_container.js +++ b/app/soapbox/features/ui/containers/bundle_container.tsx @@ -3,14 +3,16 @@ import { connect } from 'react-redux'; import { fetchBundleRequest, fetchBundleSuccess, fetchBundleFail } from '../../../actions/bundles'; import Bundle from '../components/bundle'; -const mapDispatchToProps = dispatch => ({ +import type { AppDispatch } from 'soapbox/store'; + +const mapDispatchToProps = (dispatch: AppDispatch) => ({ onFetch() { dispatch(fetchBundleRequest()); }, onFetchSuccess() { dispatch(fetchBundleSuccess()); }, - onFetchFail(error) { + onFetchFail(error: any) { dispatch(fetchBundleFail(error)); }, }); From 84b794cac9882cb17a74d53492d648430ad95e62 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 1 Sep 2022 16:57:39 -0500 Subject: [PATCH 05/36] Add preliminary Dockerfile --- .dockerignore | 32 ++++++++++++++++++++++++++++++ Dockerfile | 12 +++++++++++ installation/docker.conf | 43 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 installation/docker.conf diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..92e9362d8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,32 @@ +/node_modules/ +/tmp/ +/build/ +/coverage/ +/.coverage/ +/.eslintcache +/.env +/deploy.sh +/.vs/ +yarn-error.log* +/junit.xml + +/static/ +/static-test/ +/public/ +/dist/ + +.idea +.DS_Store + +# Custom build files +/custom/**/* +!/custom/* +/custom/*.* +!/custom/.gitkeep +!/custom/**/.gitkeep + +# surge.sh +/CNAME +/AUTH +/CORS +/ROUTER diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..f93c5fdbe --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM node:18 as build +WORKDIR /app +COPY package.json . +COPY yarn.lock . +RUN yarn +COPY . . +ARG NODE_ENV=production +RUN yarn build + +FROM nginx:stable-alpine +COPY installation/docker.conf /etc/nginx/conf.d/default.conf +COPY --from=build /app/static /usr/share/nginx/html diff --git a/installation/docker.conf b/installation/docker.conf new file mode 100644 index 000000000..cdd5699ae --- /dev/null +++ b/installation/docker.conf @@ -0,0 +1,43 @@ +# Soapbox Nginx for Docker. +server { + keepalive_timeout 70; + sendfile on; + client_max_body_size 80m; + + root /usr/share/nginx/html; + + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml image/x-icon; + + add_header Strict-Transport-Security "max-age=31536000" always; + + # SPA. + # Try static files, then fall back to index.html. + location / { + try_files $uri /index.html; + } + + # Build files. + # New builds produce hashed filenames, so these should be cached heavily. + location /packs { + add_header Cache-Control "public, max-age=31536000, immutable"; + add_header Strict-Transport-Security "max-age=31536000" always; + } + + # Return 404 on API routes so Soapbox knows what to do. + location /api { + add_header Content-Type "application/json"; + return 404 '{"error": "Not implemented"}'; + } + + # ServiceWorker: don't cache. + location = /sw.js { + add_header Cache-Control "public, max-age=0"; + add_header Strict-Transport-Security "max-age=31536000" always; + } +} From ce397ba7aa8794154c9b84fb8cce3caace29cf85 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 1 Sep 2022 16:59:44 -0500 Subject: [PATCH 06/36] Add app.json --- app.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 app.json diff --git a/app.json b/app.json new file mode 100644 index 000000000..bd168fb5a --- /dev/null +++ b/app.json @@ -0,0 +1,7 @@ +{ + "name": "Soapbox", + "description": "Software for the next generation of social media.", + "keywords": ["fediverse"], + "website": "https://soapbox.pub", + "stack": "container" +} From 5aa9db26853b308da16b49005b6d5be7085c2a4a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 1 Sep 2022 17:21:05 -0500 Subject: [PATCH 07/36] Add heroku.yml so it uses the Dockerfile --- heroku.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 heroku.yml diff --git a/heroku.yml b/heroku.yml new file mode 100644 index 000000000..8eec25b9c --- /dev/null +++ b/heroku.yml @@ -0,0 +1,3 @@ +build: + docker: + web: Dockerfile From a50a760f52653f3d0d0c469331d76f80b4f14fb6 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 1 Sep 2022 18:10:41 -0500 Subject: [PATCH 08/36] Dockerfile: support the PORT variable, expose 5000 by default --- Dockerfile | 4 +++- installation/docker.conf | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f93c5fdbe..a15e8aad9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,5 +8,7 @@ ARG NODE_ENV=production RUN yarn build FROM nginx:stable-alpine -COPY installation/docker.conf /etc/nginx/conf.d/default.conf +EXPOSE 5000 +ENV PORT=5000 +COPY installation/docker.conf /etc/nginx/templates/default.conf.template COPY --from=build /app/static /usr/share/nginx/html diff --git a/installation/docker.conf b/installation/docker.conf index cdd5699ae..072283fc7 100644 --- a/installation/docker.conf +++ b/installation/docker.conf @@ -1,5 +1,7 @@ # Soapbox Nginx for Docker. server { + listen ${PORT}; + keepalive_timeout 70; sendfile on; client_max_body_size 80m; From bb680be366934d13c0aadaf7e536f8542825635c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 1 Sep 2022 19:49:21 -0500 Subject: [PATCH 09/36] docker.conf --> docker.conf.template --- Dockerfile | 2 +- installation/{docker.conf => docker.conf.template} | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) rename installation/{docker.conf => docker.conf.template} (89%) diff --git a/Dockerfile b/Dockerfile index a15e8aad9..efe959b6d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,5 +10,5 @@ RUN yarn build FROM nginx:stable-alpine EXPOSE 5000 ENV PORT=5000 -COPY installation/docker.conf /etc/nginx/templates/default.conf.template +COPY installation/docker.conf.template /etc/nginx/templates/default.conf.template COPY --from=build /app/static /usr/share/nginx/html diff --git a/installation/docker.conf b/installation/docker.conf.template similarity index 89% rename from installation/docker.conf rename to installation/docker.conf.template index 072283fc7..d4393ad35 100644 --- a/installation/docker.conf +++ b/installation/docker.conf.template @@ -1,4 +1,7 @@ # Soapbox Nginx for Docker. +# It's intended to be used by the official nginx image, which has templating functionality. +# Mount at: `/etc/nginx/templates/default.conf.template` + server { listen ${PORT}; From 65df16ab77d9b929d6667d74531be4dd2f1d74a9 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 1 Sep 2022 22:08:12 -0500 Subject: [PATCH 10/36] vscode: recognize *.conf.template files --- .vscode/settings.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..7533f01f8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "*.conf.template": "properties" + } +} From af0c47c46f3f5fa336c6f8c2b13e4b1667616823 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 1 Sep 2022 22:18:46 -0500 Subject: [PATCH 11/36] vscode: make settings match .editorconfig --- .vscode/settings.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7533f01f8..4a7155a74 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,9 @@ { + "editor.insertSpaces": true, + "editor.tabSize": 2, "files.associations": { "*.conf.template": "properties" - } + }, + "files.eol": "\n", + "files.insertFinalNewline": false } From 0f6528e0613cc828b3eccae0ff45b26ee965ad85 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 1 Sep 2022 22:23:42 -0500 Subject: [PATCH 12/36] vscode: remove editorconfig extension --- .vscode/extensions.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 527dfacad..57a35ab4f 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,5 @@ { "recommendations": [ - "editorconfig.editorconfig", "dbaeumer.vscode-eslint", "bradlc.vscode-tailwindcss", "stylelint.vscode-stylelint", From 591aba83d9dbf4e345d653ad9996af0b99d66a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 3 Sep 2022 16:13:41 +0200 Subject: [PATCH 13/36] Use content_type from backend when editing a status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/actions/statuses.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/soapbox/actions/statuses.ts b/app/soapbox/actions/statuses.ts index db15e7a21..b1bced1f0 100644 --- a/app/soapbox/actions/statuses.ts +++ b/app/soapbox/actions/statuses.ts @@ -101,7 +101,7 @@ const editStatus = (id: string) => (dispatch: AppDispatch, getState: () => RootS api(getState).get(`/api/v1/statuses/${id}/source`).then(response => { dispatch({ type: STATUS_FETCH_SOURCE_SUCCESS }); - dispatch(setComposeToStatus(status, response.data.text, response.data.spoiler_text, false)); + dispatch(setComposeToStatus(status, response.data.text, response.data.spoiler_text, response.data.content_type, false)); dispatch(openModal('COMPOSE')); }).catch(error => { dispatch({ type: STATUS_FETCH_SOURCE_FAIL, error }); From cd93399569b60c7867cc4a68bd4e98839b0bb510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 3 Sep 2022 17:15:21 +0200 Subject: [PATCH 14/36] Update Polish translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/components/polls/poll.tsx | 2 +- .../report-modal/steps/other-actions-step.tsx | 2 +- app/soapbox/locales/pl.json | 14 ++++++++------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/soapbox/components/polls/poll.tsx b/app/soapbox/components/polls/poll.tsx index a30348678..2b6f99392 100644 --- a/app/soapbox/components/polls/poll.tsx +++ b/app/soapbox/components/polls/poll.tsx @@ -18,7 +18,7 @@ interface IPoll { } const messages = defineMessages({ - multiple: { id: 'poll.chooseMultiple', defaultMessage: 'Choose as many as you\'d like.' }, + multiple: { id: 'poll.choose_multiple', defaultMessage: 'Choose as many as you\'d like.' }, }); const Poll: React.FC = ({ id, status }): JSX.Element | null => { diff --git a/app/soapbox/features/ui/components/modals/report-modal/steps/other-actions-step.tsx b/app/soapbox/features/ui/components/modals/report-modal/steps/other-actions-step.tsx index e26cbbf44..e50a90778 100644 --- a/app/soapbox/features/ui/components/modals/report-modal/steps/other-actions-step.tsx +++ b/app/soapbox/features/ui/components/modals/report-modal/steps/other-actions-step.tsx @@ -14,7 +14,7 @@ import { isRemote, getDomain } from 'soapbox/utils/accounts'; import type { ReducerAccount } from 'soapbox/reducers/accounts'; const messages = defineMessages({ - addAdditionalStatuses: { id: 'report.otherActions.addAdditionl', defaultMessage: 'Would you like to add additional statuses to this report?' }, + addAdditionalStatuses: { id: 'report.otherActions.addAdditional', defaultMessage: 'Would you like to add additional statuses to this report?' }, addMore: { id: 'report.otherActions.addMore', defaultMessage: 'Add more' }, furtherActions: { id: 'report.otherActions.furtherActions', defaultMessage: 'Further actions:' }, hideAdditonalStatuses: { id: 'report.otherActions.hideAdditional', defaultMessage: 'Hide additional statuses' }, diff --git a/app/soapbox/locales/pl.json b/app/soapbox/locales/pl.json index bd359fbc9..0acbe3acd 100644 --- a/app/soapbox/locales/pl.json +++ b/app/soapbox/locales/pl.json @@ -564,8 +564,8 @@ "forms.copy": "Kopiuj", "forms.hide_password": "Ukryj hasło", "forms.show_password": "Pokaż hasło", - "gdpr.accept": "Aceptuj", - "gdpr.learn_more": "Dowiedz się więcej", + "gdpr.accept": "Akceptuj", + "gdpr.learn_more": "Dowiedz się więcej", "gdpr.message": "{siteTitle} korzysta z ciasteczek sesji, które są niezbędne dla działania strony.", "gdpr.title": "{siteTitle} korzysta z ciasteczek", "getting_started.open_source_notice": "{code_name} jest oprogramowaniem o otwartym źródle. Możesz pomóc w rozwoju lub zgłaszać błędy na GitLabie tutaj: {code_link} (v{code_version}).", @@ -859,8 +859,10 @@ "patron.title": "Cel wsparcia", "pinned_accounts.title": "Polecani przez {name}", "pinned_statuses.none": "Brak przypięć do pokazania.", - "poll.chooseMultiple": "Wybierz tyle, ile potrzebujesz.", + "poll.choose_multiple": "Wybierz tyle, ile potrzebujesz.", "poll.closed": "Zamknięte", + "poll.non_anonymous": "Publiczne głosowanie", + "poll.non_anonymous.label": "Inne instancje mogą wyświetlać, które odpowiedzi wybrałeś(-aś)", "poll.refresh": "Odśwież", "poll.total_people": "{count, plural, one {# osoba} few {# osoby} many {# osób} other {# osób}}", "poll.total_votes": "{count, plural, one {# głos} few {# głosy} many {# głosów} other {# głosów}}", @@ -889,7 +891,7 @@ "preferences.fields.reduce_motion_label": "Ogranicz ruch w animacjach", "preferences.fields.system_font_label": "Używaj domyślnej czcionki systemu", "preferences.fields.theme": "Motyw", - "preferences.fields.underline_links_label": "Zawsze podkreślaj odnośniki we wpisach", + "preferences.fields.underline_links_label": "Zawsze podkreślaj odnośniki we wpisach", "preferences.fields.unfollow_modal_label": "Pokazuj prośbę o potwierdzenie przed cofnięciem obserwacji", "preferences.hints.feed": "Na stronie głównej", "preferences.notifications.advanced": "Pokazuj wszystkie kategorie powiadomień", @@ -988,7 +990,7 @@ "report.forward": "Przekaż na {target}", "report.forward_hint": "To konto znajduje się na innej instancji. Czy chcesz wysłać anonimową kopię zgłoszenia rnież na nią?", "report.next": "Dalej", - "report.otherActions.addAdditionl": "Czy chcesz uwzględnić inne wpisy w tym zgłoszeniu?", + "report.otherActions.addAdditional": "Czy chcesz uwzględnić inne wpisy w tym zgłoszeniu?", "report.otherActions.addMore": "Dodaj więcej", "report.otherActions.furtherActions": "Dodatkowe działania:", "report.otherActions.hideAdditional": "Ukryj dodatkowe wpisy", @@ -1186,7 +1188,7 @@ "streamfield.add": "Dodaj", "streamfield.remove": "Usuń", "suggestions.dismiss": "Odrzuć sugestię", - "sw.update": "Aktualizacja", + "sw.update": "Aktualizacja", "sw.update_text": "Dostępna jest aktualizacja.", "tabs_bar.all": "Wszystkie", "tabs_bar.apps": "Aplikacje", From 08702248b91d769e37c45f65a567449949d2624e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 3 Sep 2022 17:27:27 +0200 Subject: [PATCH 15/36] Update Polish translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/locales/pl.json | 38 +++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/app/soapbox/locales/pl.json b/app/soapbox/locales/pl.json index 0acbe3acd..70a7b3902 100644 --- a/app/soapbox/locales/pl.json +++ b/app/soapbox/locales/pl.json @@ -73,6 +73,8 @@ "account_note.target": "Notatka o @{target}", "account_search.placeholder": "Szukaj konta", "account_timeline.column_settings.show_pinned": "Show pinned posts", + "actualStatus.edited": "Edytowano {date}", + "actualStatuses.quote_tombstone": "Wpis jest niedostępny", "admin.awaiting_approval.approved_message": "Przyjęto {acct}!", "admin.awaiting_approval.empty_message": "Nikt nie oczekuje przyjęcia. Gdy zarejestruje się nowy użytkownik, możesz zatwierdzić go tutaj.", "admin.awaiting_approval.rejected_message": "Odrzucono {acct}!", @@ -134,8 +136,8 @@ "admin_nav.awaiting_approval": "Oczekujące zgłoszenia", "admin_nav.dashboard": "Panel administracyjny", "admin_nav.reports": "Zgłoszenia", - "age_verification.header": "Wprowadź datę urodzenia", "age_verification.fail": "Musisz mieć przynajmniej {ageMinimum, plural, one {# rok} few {# lata} many {# lat} other {# lat}}.", + "age_verification.header": "Wprowadź datę urodzenia", "alert.unexpected.body": "Przepraszamy za niedogodności. Jeżeli problem nie ustanie, skontaktuj się z naszym wsparciem technicznym. Możesz też spróbować {clearCookies} (zostaniesz wylogowany(-a)).", "alert.unexpected.browser": "Przeglądarka", "alert.unexpected.clear_cookies": "wyczyścić pliki cookies i dane przeglądarki", @@ -164,16 +166,16 @@ "app_create.scopes_placeholder": "np. „read write follow”", "app_create.submit": "Utwórz aplikację", "app_create.website_label": "Strona", - "auth_layout.register": "Utwórz konto", "auth.invalid_credentials": "Nieprawidłowa nazwa użytkownika lub hasło", "auth.logged_out": "Wylogowano.", + "auth_layout.register": "Utwórz konto", "backups.actions.create": "Utwórz kopię zapasową", "backups.empty_message": "Nie znaleziono kopii zapasowych. {action}", "backups.empty_message.action": "Chcesz utworzyć?", "backups.pending": "Oczekująca", "beta.also_available": "Dostępne w językach:", - "birthdays_modal.empty": "Żaden z Twoich znajomych nie ma dziś urodzin.", "birthday_panel.title": "Urodziny", + "birthdays_modal.empty": "Żaden z Twoich znajomych nie ma dziś urodzin.", "boost_modal.combo": "Naciśnij {combo}, aby pominąć to następnym razem", "bundle_column_error.body": "Coś poszło nie tak podczas ładowania tego składnika.", "bundle_column_error.retry": "Spróbuj ponownie", @@ -283,8 +285,8 @@ "community.column_settings.title": "Ustawienia lokalnej osi czasu", "compare_history_modal.header": "Historia edycji", "compose.character_counter.title": "Wykorzystano {chars} z {maxChars} znaków", - "compose.invalid_schedule": "Musisz zaplanować wpis przynajmniej 5 minut wcześniej.", "compose.edit_success": "Twój wpis został zedytowany", + "compose.invalid_schedule": "Musisz zaplanować wpis przynajmniej 5 minut wcześniej.", "compose.submit_success": "Twój wpis został wysłany", "compose_form.direct_message_warning": "Ten wpis będzie widoczny tylko dla wszystkich wspomnianych użytkowników.", "compose_form.hashtag_warning": "Ten wpis nie będzie widoczny pod podanymi hashtagami, ponieważ jest oznaczony jako niewidoczny. Tylko publiczne wpisy mogą zostać znalezione z użyciem hashtagów.", @@ -342,6 +344,7 @@ "confirmations.block.confirm": "Zablokuj", "confirmations.block.heading": "Zablokuj @{name}", "confirmations.block.message": "Czy na pewno chcesz zablokować {name}?", + "confirmations.cancel_editing.confirm": "Anuluj edycję", "confirmations.cancel_editing.heading": "Anuluj edycję wpisu", "confirmations.cancel_editing.message": "Czy na pewno chcesz anulować edytowanie wpisu? Niezapisane zmiany zostaną utracone.", "confirmations.delete.confirm": "Usuń", @@ -432,9 +435,9 @@ "edit_profile.fields.location_label": "Lokalizacja", "edit_profile.fields.location_placeholder": "Lokalizacja", "edit_profile.fields.locked_label": "Zablokuj konto", - "edit_profile.fields.meta_fields_label": "Pola profilu", "edit_profile.fields.meta_fields.content_placeholder": "Treść", "edit_profile.fields.meta_fields.label_placeholder": "Podpis", + "edit_profile.fields.meta_fields_label": "Pola profilu", "edit_profile.fields.stranger_notifications_label": "Blokuj powiadomienia od nieznajomych", "edit_profile.fields.website_label": "Strona internetowa", "edit_profile.fields.website_placeholder": "Wyświetl link", @@ -446,7 +449,7 @@ "edit_profile.hints.header": "PNG, GIF lub JPG. Zostanie zmniejszony do {size}", "edit_profile.hints.hide_network": "To, kogo obserwujesz i kto Cię obserwuje nie będzie wyświetlane na Twoim profilu", "edit_profile.hints.locked": "Wymaga ręcznego zatwierdzania obserwacji", - "edit_profile.hints.meta_fields": "Możesz ustawić {count, plural, one {# niestandardowe pole} few {# niestandardowe pola} many {# niestandardowych pól} wyświetlanych na Twoim profilu.", + "edit_profile.hints.meta_fields": "Możesz ustawić {count, plural, one {# niestandardowe pole wyświetlane} few {# niestandardowe pola wyświetlane} many {# niestandardowych pól wyświetlanych}} na Twoim profilu.", "edit_profile.hints.stranger_notifications": "Wyświetlaj tylko powiadomienia od osób, które obserwujesz", "edit_profile.save": "Zapisz", "edit_profile.success": "Zapisano profil!", @@ -568,6 +571,7 @@ "gdpr.learn_more": "Dowiedz się więcej", "gdpr.message": "{siteTitle} korzysta z ciasteczek sesji, które są niezbędne dla działania strony.", "gdpr.title": "{siteTitle} korzysta z ciasteczek", + "generic.saved": "Zapisano", "getting_started.open_source_notice": "{code_name} jest oprogramowaniem o otwartym źródle. Możesz pomóc w rozwoju lub zgłaszać błędy na GitLabie tutaj: {code_link} (v{code_version}).", "group.detail.archived_group": "Archived group", "group.members.empty": "Ta grupa nie ma żadnych członków.", @@ -625,6 +629,7 @@ "import_data.success.blocks": "Pomyślnie zaimportowano zablokowane konta", "import_data.success.followers": "Pomyślnie zaimportowano obserwowane konta", "import_data.success.mutes": "Pomyślnie zaimportowano wyciszone konta", + "input.copy": "Kopiuj", "input.password.hide_password": "Ukryj hasło", "input.password.show_password": "Pokazuj hasło", "intervals.full.days": "{number, plural, one {# dzień} few {# dni} many {# dni} other {# dni}}", @@ -831,8 +836,8 @@ "notifications.filter.statuses": "Nowe wpisy osób, które subskrybujesz", "notifications.group": "{count, number} {count, plural, one {powiadomienie} few {powiadomienia} many {powiadomień} more {powiadomień}}", "notifications.queue_label": "Naciśnij aby zobaczyć {count} {count, plural, one {nowe powiadomienie} few {nowe powiadomienia} many {nowych powiadomień} other {nowe powiadomienia}}", - "oauth_consumers.title": "Inne opcje logowania", "oauth_consumer.tooltip": "Zaloguj się używając {provider}", + "oauth_consumers.title": "Inne opcje logowania", "onboarding.avatar.subtitle": "Just have fun with it.", "onboarding.avatar.title": "Wybierz zdjęcie profilowe", "onboarding.display_name.subtitle": "Możesz ją zawsze zmienić później.", @@ -893,6 +898,7 @@ "preferences.fields.theme": "Motyw", "preferences.fields.underline_links_label": "Zawsze podkreślaj odnośniki we wpisach", "preferences.fields.unfollow_modal_label": "Pokazuj prośbę o potwierdzenie przed cofnięciem obserwacji", + "preferences.hints.demetricator": "Ogranicz skutki uzależnienia od mediów społecznościowych, ukrywając wyświetlane liczby.", "preferences.hints.feed": "Na stronie głównej", "preferences.notifications.advanced": "Pokazuj wszystkie kategorie powiadomień", "preferences.options.content_type_markdown": "Markdown", @@ -920,12 +926,6 @@ "regeneration_indicator.sublabel": "Twoja oś czasu jest przygotowywana!", "register_invite.lead": "Wypełnij poniższy formularz, aby utworzyć konto.", "register_invite.title": "Otrzymałeś(-aś) zaproszenie na {siteTitle}!", - "registrations.create_account": "Utwórz konto", - "registrations.error": "Nie udało się zarejestrować konta.", - "registrations.get_started": "Rozpocznijmy!", - "registrations.success": "Witamy na {siteTitle}!", - "registrations.tagline": "Media społecznościowe, które nie wykluczają", - "registrations.unprocessable_entity": "Ta nazwa użytkownika jest już zajęta.", "registration.acceptance": "Rejestrując się, wyrażasz zgodę na {terms} i {privacy}.", "registration.agreement": "Akceptuję {tos}.", "registration.captcha.hint": "Naciśnij na obrazek, aby uzyskać nową captchę", @@ -950,6 +950,12 @@ "registration.validation.capital_letter": "1 wielka litera", "registration.validation.lowercase_letter": "1 mała litera", "registration.validation.minimum_characters": "8 znaków", + "registrations.create_account": "Utwórz konto", + "registrations.error": "Nie udało się zarejestrować konta.", + "registrations.get_started": "Rozpocznijmy!", + "registrations.success": "Witamy na {siteTitle}!", + "registrations.tagline": "Media społecznościowe, które nie wykluczają", + "registrations.unprocessable_entity": "Ta nazwa użytkownika jest już zajęta.", "relative_time.days": "{number} dni", "relative_time.hours": "{number} godz.", "relative_time.just_now": "teraz", @@ -1118,8 +1124,8 @@ "soapbox_config.single_user_mode_profile_hint": "@nazwa", "soapbox_config.single_user_mode_profile_label": "Nazwa głównego użytkownika", "soapbox_config.verified_can_edit_name_label": "Pozwól zweryfikowanym użytkownikom na zmianę swojej nazwy wyświetlanej.", - "sponsored.info.title": "Dlaczego widzę tę reklamę?", "sponsored.info.message": "{siteTitle} wyświetla reklamy, aby utrzymać naszą usługę.", + "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}", @@ -1139,6 +1145,10 @@ "status.embed": "Osadź", "status.favourite": "Zareaguj", "status.filtered": "Filtrowany(-a)", + "status.in_review_summary.contact": "Jeżeli uważasz że to błąd, {link}.", + "status.in_review_summary.link": "skontaktuj się z działem pomocy", + "status.in_review_summary.summary": "Ten wpis został wysłany do weryfikacji moderatorom i jest widoczny tylko dla Ciebie.", + "status.in_review_warning": "Treści w trakcie weryfikacji", "status.load_more": "Załaduj więcej", "status.media_hidden": "Zawartość multimedialna ukryta", "status.mention": "Wspomnij o @{name}", From 005f317a93dfa2d05a3664037456019374857426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 3 Sep 2022 23:19:30 +0200 Subject: [PATCH 16/36] Change active tab when navigated to another profile page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/pages/profile_page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/soapbox/pages/profile_page.tsx b/app/soapbox/pages/profile_page.tsx index 9c5a4a55e..f4ff974e0 100644 --- a/app/soapbox/pages/profile_page.tsx +++ b/app/soapbox/pages/profile_page.tsx @@ -105,7 +105,7 @@ const ProfilePage: React.FC = ({ params, children }) => { {account && showTabs && ( - + )} {children} From 6bd4bc7bdd7c7049968ee925e22b553ccd88e4b2 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 4 Sep 2022 11:45:45 -0500 Subject: [PATCH 17/36] Docker: configurable BACKEND_URL --- Dockerfile | 1 + installation/docker.conf.template | 49 ++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index efe959b6d..4c47eae54 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,5 +10,6 @@ RUN yarn build FROM nginx:stable-alpine EXPOSE 5000 ENV PORT=5000 +ENV BACKEND_URL=http://localhost COPY installation/docker.conf.template /etc/nginx/templates/default.conf.template COPY --from=build /app/static /usr/share/nginx/html diff --git a/installation/docker.conf.template b/installation/docker.conf.template index d4393ad35..06fdc5096 100644 --- a/installation/docker.conf.template +++ b/installation/docker.conf.template @@ -2,6 +2,22 @@ # It's intended to be used by the official nginx image, which has templating functionality. # Mount at: `/etc/nginx/templates/default.conf.template` +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g; + +server { + server_name localhost; + + location / { + add_header Content-Type "application/json" always; + return 404 '{"error": "Not implemented"}'; + } +} + server { listen ${PORT}; @@ -34,10 +50,9 @@ server { add_header Strict-Transport-Security "max-age=31536000" always; } - # Return 404 on API routes so Soapbox knows what to do. - location /api { - add_header Content-Type "application/json"; - return 404 '{"error": "Not implemented"}'; + # Backend routes + location ~ ^/(api|oauth|auth|admin|pghero|sidekiq|manifest.json|media|nodeinfo|unsubscribe|.well-known/(webfinger|host-meta|nodeinfo|change-password)|@(.+)/embed$) { + try_files /dev/null @backend; } # ServiceWorker: don't cache. @@ -45,4 +60,30 @@ server { add_header Cache-Control "public, max-age=0"; add_header Strict-Transport-Security "max-age=31536000" always; } + + # Proxy to the backend. + location @backend { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Proxy ""; + proxy_pass_header Server; + + proxy_pass ${BACKEND_URL}; + proxy_buffering on; + proxy_redirect off; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + proxy_cache CACHE; + proxy_cache_valid 200 7d; + proxy_cache_valid 410 24h; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; + add_header X-Cached $upstream_cache_status; + add_header Strict-Transport-Security "max-age=31536000" always; + + tcp_nodelay on; + } } From 6d07c45903885670b0cb8b0d1b9a146f6ba8568a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 4 Sep 2022 12:54:41 -0500 Subject: [PATCH 18/36] dockerignore: .git --- .dockerignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.dockerignore b/.dockerignore index 92e9362d8..41b87855a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,5 @@ +.git + /node_modules/ /tmp/ /build/ From 9821a1f639cbd06b23b34537adce1f1e194536e5 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 4 Sep 2022 13:58:13 -0500 Subject: [PATCH 19/36] Docker: adopt mastodon.conf --- installation/docker.conf.template | 47 ++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/installation/docker.conf.template b/installation/docker.conf.template index 06fdc5096..0c13a26a3 100644 --- a/installation/docker.conf.template +++ b/installation/docker.conf.template @@ -2,11 +2,20 @@ # It's intended to be used by the official nginx image, which has templating functionality. # Mount at: `/etc/nginx/templates/default.conf.template` +map_hash_bucket_size 128; + map $http_upgrade $connection_upgrade { default upgrade; '' close; } +# ActivityPub routing. +map $http_accept $activitypub_location { + default @soapbox; + "application/activity+json" @backend; + 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' @backend; +} + proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g; server { @@ -20,6 +29,7 @@ server { server { listen ${PORT}; + listen [::]:${PORT}; keepalive_timeout 70; sendfile on; @@ -28,6 +38,7 @@ server { root /usr/share/nginx/html; gzip on; + gzip_disable "msie6"; gzip_vary on; gzip_proxied any; gzip_comp_level 6; @@ -37,30 +48,46 @@ server { add_header Strict-Transport-Security "max-age=31536000" always; - # SPA. - # Try static files, then fall back to index.html. + # Content Security Policy (CSP) + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy + add_header Content-Security-Policy "base-uri 'none'; default-src 'none'; font-src 'self'; img-src 'self' https: data: blob:; style-src 'self' 'unsafe-inline'; media-src 'self' https: data:; frame-src 'self' https:; manifest-src 'self'; connect-src 'self' data: blob:; script-src 'self'; child-src 'self'; worker-src 'self';"; + + # Fallback route. + # Try static files, then fall back to the SPA. location / { - try_files $uri /index.html; + try_files $uri @soapbox; + } + + # Backend routes. + # These are routes to the backend's API and important rendered pages. + location ~ ^/(api|oauth|auth|admin|pghero|sidekiq|manifest.json|media|nodeinfo|unsubscribe|.well-known/(webfinger|host-meta|nodeinfo|change-password)|@(.+)/embed$) { + try_files /dev/null @backend; } - # Build files. + # Backend ActivityPub routes. + # Conditionally send to the backend by Accept header. + location ~ ^/(inbox|users|@(.+)) { + try_files /dev/null $activitypub_location; + } + + # Soapbox build files. # New builds produce hashed filenames, so these should be cached heavily. location /packs { add_header Cache-Control "public, max-age=31536000, immutable"; add_header Strict-Transport-Security "max-age=31536000" always; } - # Backend routes - location ~ ^/(api|oauth|auth|admin|pghero|sidekiq|manifest.json|media|nodeinfo|unsubscribe|.well-known/(webfinger|host-meta|nodeinfo|change-password)|@(.+)/embed$) { - try_files /dev/null @backend; - } - - # ServiceWorker: don't cache. + # Soapbox ServiceWorker. location = /sw.js { add_header Cache-Control "public, max-age=0"; add_header Strict-Transport-Security "max-age=31536000" always; } + # Soapbox SPA (Single Page App). + location @soapbox { + try_files /index.html /dev/null; + } + # Proxy to the backend. location @backend { proxy_set_header Host $host; From 06afd122130f7cc41e0e2050e75a2d3abc59738b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 4 Sep 2022 14:46:29 -0500 Subject: [PATCH 20/36] Docker: make CSP configurable --- Dockerfile | 1 + installation/docker.conf.template | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4c47eae54..c7068a768 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,5 +11,6 @@ FROM nginx:stable-alpine EXPOSE 5000 ENV PORT=5000 ENV BACKEND_URL=http://localhost +ENV CSP= COPY installation/docker.conf.template /etc/nginx/templates/default.conf.template COPY --from=build /app/static /usr/share/nginx/html diff --git a/installation/docker.conf.template b/installation/docker.conf.template index 0c13a26a3..2759f765a 100644 --- a/installation/docker.conf.template +++ b/installation/docker.conf.template @@ -50,7 +50,7 @@ server { # Content Security Policy (CSP) # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy - add_header Content-Security-Policy "base-uri 'none'; default-src 'none'; font-src 'self'; img-src 'self' https: data: blob:; style-src 'self' 'unsafe-inline'; media-src 'self' https: data:; frame-src 'self' https:; manifest-src 'self'; connect-src 'self' data: blob:; script-src 'self'; child-src 'self'; worker-src 'self';"; + add_header Content-Security-Policy "${CSP}"; # Fallback route. # Try static files, then fall back to the SPA. @@ -97,7 +97,7 @@ server { proxy_set_header Proxy ""; proxy_pass_header Server; - proxy_pass ${BACKEND_URL}; + proxy_pass "${BACKEND_URL}"; proxy_buffering on; proxy_redirect off; proxy_http_version 1.1; From 8403615eb0346067fa4a22520ae619a9a2f418bd Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 4 Sep 2022 15:02:12 -0500 Subject: [PATCH 21/36] Docker: open fallback backend at :4444 --- Dockerfile | 3 ++- installation/docker.conf.template | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index c7068a768..bfb7c2e48 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,8 @@ RUN yarn build FROM nginx:stable-alpine EXPOSE 5000 ENV PORT=5000 -ENV BACKEND_URL=http://localhost +ENV FALLBACK_PORT=4444 +ENV BACKEND_URL=http://localhost:4444 ENV CSP= COPY installation/docker.conf.template /etc/nginx/templates/default.conf.template COPY --from=build /app/static /usr/share/nginx/html diff --git a/installation/docker.conf.template b/installation/docker.conf.template index 2759f765a..0938b756e 100644 --- a/installation/docker.conf.template +++ b/installation/docker.conf.template @@ -18,8 +18,10 @@ map $http_accept $activitypub_location { proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g; +# Fake backend for when BACKEND_URL isn't defined. server { - server_name localhost; + listen ${FALLBACK_PORT}; + listen [::]:${FALLBACK_PORT}; location / { add_header Content-Type "application/json" always; From d9d9c4521fca441f5d1fa049bc8777cf0f13fae8 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 4 Sep 2022 15:15:54 -0500 Subject: [PATCH 22/36] soapbox-pub/soapbox-fe --> soapbox-pub/soapbox --- .vscode/soapbox.code-snippets | 2 +- CHANGELOG.md | 8 ++++---- README.md | 12 ++++++------ app/instance/about.example/index.html | 5 ++--- app/soapbox/actions/importer/index.ts | 4 ++-- app/soapbox/normalizers/account.ts | 2 +- app/soapbox/reducers/custom_emojis.ts | 2 +- app/soapbox/reducers/notifications.js | 2 +- app/soapbox/reducers/timelines.ts | 2 +- app/soapbox/utils/features.ts | 4 ++-- app/soapbox/utils/status.ts | 2 +- docs/administration/deploy-at-scale.md | 4 ++-- docs/administration/install-subdomain.md | 2 +- docs/administration/install-yunohost.md | 2 +- docs/administration/mastodon.md | 4 ++-- docs/administration/updating.md | 4 ++-- docs/contributing.md | 8 ++++---- docs/development/developing-backend.md | 4 ++-- docs/development/how-it-works.md | 2 +- docs/development/running-locally.md | 4 ++-- docs/installing.md | 2 +- package.json | 6 +++--- 22 files changed, 43 insertions(+), 44 deletions(-) diff --git a/.vscode/soapbox.code-snippets b/.vscode/soapbox.code-snippets index 66da1a25b..b31d50ff5 100644 --- a/.vscode/soapbox.code-snippets +++ b/.vscode/soapbox.code-snippets @@ -1,5 +1,5 @@ { - // Place your soapbox-fe workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and + // Place your Soapbox workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is // used to trigger the snippet and the body will be expanded and inserted. Possible variables are: diff --git a/CHANGELOG.md b/CHANGELOG.md index b6971b861..6a3850599 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -211,7 +211,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Initial beta release. -[Unreleased]: https://gitlab.com/soapbox-pub/soapbox-fe/-/compare/v1.0.0...develop -[Unreleased patch]: https://gitlab.com/soapbox-pub/soapbox-fe/-/compare/v1.0.0...stable/1.0.x -[1.0.0]: https://gitlab.com/soapbox-pub/soapbox-fe/-/compare/v0.9.0...v1.0.0 -[0.9.0]: https://gitlab.com/soapbox-pub/soapbox-fe/-/tags/v0.9.0 +[Unreleased]: https://gitlab.com/soapbox-pub/soapbox/-/compare/v1.0.0...develop +[Unreleased patch]: https://gitlab.com/soapbox-pub/soapbox/-/compare/v1.0.0...stable/1.0.x +[1.0.0]: https://gitlab.com/soapbox-pub/soapbox/-/compare/v0.9.0...v1.0.0 +[0.9.0]: https://gitlab.com/soapbox-pub/soapbox/-/tags/v0.9.0 diff --git a/README.md b/README.md index c8ddf4480..07ba0d7a7 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,13 @@ Installing Soapbox on an existing Pleroma server is extremely easy. Just ssh into the server and download a .zip of the latest build: ```sh -curl -L https://gitlab.com/soapbox-pub/soapbox-fe/-/jobs/artifacts/develop/download?job=build-production -o soapbox-fe.zip +curl -L https://gitlab.com/soapbox-pub/soapbox/-/jobs/artifacts/develop/download?job=build-production -o soapbox.zip ``` Then unpack it into Pleroma's `instance` directory: ```sh -busybox unzip soapbox-fe.zip -o -d /opt/pleroma/instance +busybox unzip soapbox.zip -o -d /opt/pleroma/instance ``` **That's it!** :tada: @@ -54,7 +54,7 @@ location / { } ``` -(See [`mastodon.conf`](https://gitlab.com/soapbox-pub/soapbox-fe/-/blob/develop/installation/mastodon.conf) for a full example.) +(See [`mastodon.conf`](https://gitlab.com/soapbox-pub/soapbox/-/blob/develop/installation/mastodon.conf) for a full example.) Soapbox incorporates much of the [Mastodon API](https://docs.joinmastodon.org/methods/), [Pleroma API](https://api.pleroma.social/), and more. It detects features supported by the backend to provide the right experience for the backend. @@ -64,8 +64,8 @@ It detects features supported by the backend to provide the right experience for To get it running, just clone the repo: ```sh -git clone https://gitlab.com/soapbox-pub/soapbox-fe.git -cd soapbox-fe +git clone https://gitlab.com/soapbox-pub/soapbox.git +cd soapbox ``` Ensure that Node.js and Yarn are installed, then install dependencies: @@ -101,7 +101,7 @@ Try again. ### Troubleshooting: it's not working! -Run `node -V` and compare your Node.js version with the version in [`.tool-versions`](https://gitlab.com/soapbox-pub/soapbox-fe/-/blob/develop/.tool-versions). +Run `node -V` and compare your Node.js version with the version in [`.tool-versions`](https://gitlab.com/soapbox-pub/soapbox/-/blob/develop/.tool-versions). If they don't match, try installing [asdf](https://asdf-vm.com/). ## Local Dev Configuration diff --git a/app/instance/about.example/index.html b/app/instance/about.example/index.html index 5efb11fc9..6af826f85 100644 --- a/app/instance/about.example/index.html +++ b/app/instance/about.example/index.html @@ -23,6 +23,5 @@

Open Source Software

-

Soapbox is free and open source (FOSS) software that runs atop a Pleroma server

-

The Soapbox repository can be found at Soapbox-fe

-

The Pleroma server repository can be found at Pleroma-be

+

Soapbox is free and open source (FOSS) software.

+

The Soapbox repository can be found at Soapbox

diff --git a/app/soapbox/actions/importer/index.ts b/app/soapbox/actions/importer/index.ts index 20041180b..a5d86c9b0 100644 --- a/app/soapbox/actions/importer/index.ts +++ b/app/soapbox/actions/importer/index.ts @@ -106,10 +106,10 @@ export function importFetchedStatus(status: APIEntity, idempotencyKey?: string) const isBroken = (status: APIEntity) => { try { // Skip empty accounts - // https://gitlab.com/soapbox-pub/soapbox-fe/-/issues/424 + // https://gitlab.com/soapbox-pub/soapbox/-/issues/424 if (!status.account.id) return true; // Skip broken reposts - // https://gitlab.com/soapbox-pub/soapbox/-/issues/28 + // https://gitlab.com/soapbox-pub/rebased/-/issues/28 if (status.reblog && !status.reblog.account.id) return true; return false; } catch (e) { diff --git a/app/soapbox/normalizers/account.ts b/app/soapbox/normalizers/account.ts index cfaf92a1c..37f42ab8f 100644 --- a/app/soapbox/normalizers/account.ts +++ b/app/soapbox/normalizers/account.ts @@ -78,7 +78,7 @@ export const FieldRecord = ImmutableRecord({ value_plain: '', }); -// https://gitlab.com/soapbox-pub/soapbox-fe/-/issues/549 +// https://gitlab.com/soapbox-pub/soapbox/-/issues/549 const normalizePleromaLegacyFields = (account: ImmutableMap) => { return account.update('pleroma', ImmutableMap(), (pleroma: ImmutableMap) => { return pleroma.withMutations(pleroma => { diff --git a/app/soapbox/reducers/custom_emojis.ts b/app/soapbox/reducers/custom_emojis.ts index 477e7cce9..38b54a673 100644 --- a/app/soapbox/reducers/custom_emojis.ts +++ b/app/soapbox/reducers/custom_emojis.ts @@ -20,7 +20,7 @@ const importEmojis = (customEmojis: APIEntity[]) => { const emojis = (fromJS(customEmojis) as ImmutableList>).filter((emoji) => { // If a custom emoji has the shortcode of a Unicode emoji, skip it. // Otherwise it breaks EmojiMart. - // https://gitlab.com/soapbox-pub/soapbox-fe/-/issues/610 + // https://gitlab.com/soapbox-pub/soapbox/-/issues/610 const shortcode = emoji.get('shortcode', '').toLowerCase(); return !emojiData[shortcode]; }); diff --git a/app/soapbox/reducers/notifications.js b/app/soapbox/reducers/notifications.js index 08b87e4f0..d7697d4dd 100644 --- a/app/soapbox/reducers/notifications.js +++ b/app/soapbox/reducers/notifications.js @@ -67,7 +67,7 @@ const fixNotification = notification => { const isValid = notification => { try { - // https://gitlab.com/soapbox-pub/soapbox-fe/-/issues/424 + // https://gitlab.com/soapbox-pub/soapbox/-/issues/424 if (!notification.account.id) { return false; } diff --git a/app/soapbox/reducers/timelines.ts b/app/soapbox/reducers/timelines.ts index cd24d9df3..def257407 100644 --- a/app/soapbox/reducers/timelines.ts +++ b/app/soapbox/reducers/timelines.ts @@ -242,7 +242,7 @@ const timelineDisconnect = (state: State, timelineId: string) => { if (items.isEmpty()) return; // This is causing problems. Disable for now. - // https://gitlab.com/soapbox-pub/soapbox-fe/-/issues/716 + // https://gitlab.com/soapbox-pub/soapbox/-/issues/716 // timeline.set('items', addStatusId(items, null)); })); }; diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts index c9eac3688..78908b04f 100644 --- a/app/soapbox/utils/features.ts +++ b/app/soapbox/utils/features.ts @@ -46,8 +46,8 @@ export const PIXELFED = 'Pixelfed'; export const TRUTHSOCIAL = 'TruthSocial'; /** - * Soapbox BE, the recommended Pleroma fork for Soapbox. - * @see {@link https://gitlab.com/soapbox-pub/soapbox-be} + * Rebased, the recommended backend for Soapbox. + * @see {@link https://gitlab.com/soapbox-pub/rebased} */ export const SOAPBOX = 'soapbox'; diff --git a/app/soapbox/utils/status.ts b/app/soapbox/utils/status.ts index 6f03b1ce0..66e380b5a 100644 --- a/app/soapbox/utils/status.ts +++ b/app/soapbox/utils/status.ts @@ -35,7 +35,7 @@ export const shouldHaveCard = (status: StatusEntity): boolean => { }; /** Whether the media IDs on this status have integer IDs (opposed to FlakeIds). */ -// https://gitlab.com/soapbox-pub/soapbox-fe/-/merge_requests/1087 +// https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/1087 export const hasIntegerMediaIds = (status: StatusEntity): boolean => { return status.media_attachments.some(({ id }) => isIntegerId(id)); }; diff --git a/docs/administration/deploy-at-scale.md b/docs/administration/deploy-at-scale.md index 9d413fb0a..40e878a0a 100644 --- a/docs/administration/deploy-at-scale.md +++ b/docs/administration/deploy-at-scale.md @@ -11,7 +11,7 @@ The best way to get Soapbox builds is from a GitLab CI job. The official build URL is here: ``` -https://gitlab.com/soapbox-pub/soapbox-fe/-/jobs/artifacts/develop/download?job=build-production +https://gitlab.com/soapbox-pub/soapbox/-/jobs/artifacts/develop/download?job=build-production ``` (Note that `develop` in that URL can be replaced with any git ref, eg `v2.0.0`, and thus will be updated with the latest zip whenever a new commit is pushed to `develop`.) @@ -44,7 +44,7 @@ location ~ ^/(api|oauth|admin) { } ``` -We recommend trying [`mastodon.conf`](https://gitlab.com/soapbox-pub/soapbox-fe/-/blob/develop/installation/mastodon.conf) as a starting point. +We recommend trying [`mastodon.conf`](https://gitlab.com/soapbox-pub/soapbox/-/blob/develop/installation/mastodon.conf) as a starting point. It is fine-tuned, includes support for federation, and should work with any backend. ## The ServiceWorker diff --git a/docs/administration/install-subdomain.md b/docs/administration/install-subdomain.md index 513d8dd93..34a8cb37b 100644 --- a/docs/administration/install-subdomain.md +++ b/docs/administration/install-subdomain.md @@ -13,7 +13,7 @@ mkdir -p /opt/soapbox Fetch the build. ```sh -curl -L https://gitlab.com/soapbox-pub/soapbox-fe/-/jobs/artifacts/v1.3.0/download?job=build-production -o /tmp/soapbox-fe.zip +curl -L https://gitlab.com/soapbox-pub/soapbox/-/jobs/artifacts/v1.3.0/download?job=build-production -o /tmp/soapbox-fe.zip ``` Unzip the build. diff --git a/docs/administration/install-yunohost.md b/docs/administration/install-yunohost.md index d5cba3ba6..af99231b4 100644 --- a/docs/administration/install-yunohost.md +++ b/docs/administration/install-yunohost.md @@ -7,7 +7,7 @@ If you want to install Soapbox to a Pleroma instance installed using [YunoHost]( First, download the latest build of Soapbox from GitLab. ```sh -curl -L https://gitlab.com/soapbox-pub/soapbox-fe/-/jobs/artifacts/v1.3.0/download?job=build-production -o soapbox-fe.zip +curl -L https://gitlab.com/soapbox-pub/soapbox/-/jobs/artifacts/v1.3.0/download?job=build-production -o soapbox-fe.zip ``` ## 2. Unzip the build diff --git a/docs/administration/mastodon.md b/docs/administration/mastodon.md index d8261d9de..345408ad1 100644 --- a/docs/administration/mastodon.md +++ b/docs/administration/mastodon.md @@ -8,7 +8,7 @@ To do so, shell into your server and unpack Soapbox: ```sh mkdir -p /opt/soapbox -curl -L https://gitlab.com/soapbox-pub/soapbox-fe/-/jobs/artifacts/develop/download?job=build-production -o soapbox-fe.zip +curl -L https://gitlab.com/soapbox-pub/soapbox/-/jobs/artifacts/develop/download?job=build-production -o soapbox-fe.zip busybox unzip soapbox-fe.zip -o -d /opt/soapbox ``` @@ -17,7 +17,7 @@ Now create an Nginx file for Soapbox with Mastodon. If you already have one, replace it: ```sh -curl https://gitlab.com/soapbox-pub/soapbox-fe/-/raw/develop/installation/mastodon.conf > /etc/nginx/sites-available/mastodon +curl https://gitlab.com/soapbox-pub/soapbox/-/raw/develop/installation/mastodon.conf > /etc/nginx/sites-available/mastodon ``` Edit this file and replace all occurrences of `example.com` with your domain name. diff --git a/docs/administration/updating.md b/docs/administration/updating.md index ddfb62e08..6e5252efa 100644 --- a/docs/administration/updating.md +++ b/docs/administration/updating.md @@ -1,6 +1,6 @@ # Updating Soapbox -You should always check the [release notes/changelog](https://gitlab.com/soapbox-pub/soapbox-fe/-/blob/develop/CHANGELOG.md) in case there are deprecations, special update changes, etc. +You should always check the [release notes/changelog](https://gitlab.com/soapbox-pub/soapbox/-/blob/develop/CHANGELOG.md) in case there are deprecations, special update changes, etc. Besides that, it's relatively pretty easy to update Soapbox. There's two ways to go about it: with the command line or with an unofficial script. @@ -10,7 +10,7 @@ To update Soapbox via the command line, do the following: ``` # Download the build. -curl -L https://gitlab.com/soapbox-pub/soapbox-fe/-/jobs/artifacts/develop/download?job=build-production -o soapbox-fe.zip +curl -L https://gitlab.com/soapbox-pub/soapbox/-/jobs/artifacts/develop/download?job=build-production -o soapbox-fe.zip # Remove all the current Soapbox build in Pleroma's instance directory. rm -R /opt/pleroma/instance/static/packs diff --git a/docs/contributing.md b/docs/contributing.md index 47a7b747d..bb59effc7 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -2,20 +2,20 @@ Thank you for your interest in Soapbox! -When contributing to Soapbox, please first discuss the change you wish to make by [opening an issue](https://gitlab.com/soapbox-pub/soapbox-fe/-/issues). +When contributing to Soapbox, please first discuss the change you wish to make by [opening an issue](https://gitlab.com/soapbox-pub/soapbox/-/issues). ## Opening an MR (merge request) 1. Smash that "fork" button on GitLab to make a copy of the repo. 2. Clone the repo locally, then begin work on a new branch (eg not `develop`). 3. Push your branch to your fork. -4. Once pushed, GitLab should provide you with a URL to open a new merge request right in your terminal. If not, do it [manually](https://gitlab.com/soapbox-pub/soapbox-fe/-/merge_requests/new). +4. Once pushed, GitLab should provide you with a URL to open a new merge request right in your terminal. If not, do it [manually](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/new). ### Ensuring the CI pipeline succeeds When you push to a branch, the CI pipeline will run. -[Soapbox uses GitLab CI](https://gitlab.com/soapbox-pub/soapbox-fe/-/blob/develop/.gitlab-ci.yml) to lint, run tests, and verify changes. +[Soapbox uses GitLab CI](https://gitlab.com/soapbox-pub/soapbox/-/blob/develop/.gitlab-ci.yml) to lint, run tests, and verify changes. It's important this pipeline passes, otherwise we cannot merge the change. New users of gitlab.com may see a "detatched pipeline" error. @@ -31,4 +31,4 @@ We recommend developing Soapbox with [VSCodium](https://vscodium.com/) (or its p This will help give you feedback about your changes _in the editor itself_ before GitLab CI performs linting, etc. When this project is opened in Code it will automatically recommend extensions. -See [`.vscode/extensions.json`](https://gitlab.com/soapbox-pub/soapbox-fe/-/blob/develop/.vscode/extensions.json) for the full list. +See [`.vscode/extensions.json`](https://gitlab.com/soapbox-pub/soapbox/-/blob/develop/.vscode/extensions.json) for the full list. diff --git a/docs/development/developing-backend.md b/docs/development/developing-backend.md index af4400e9f..723a28002 100644 --- a/docs/development/developing-backend.md +++ b/docs/development/developing-backend.md @@ -48,7 +48,7 @@ Typically checks are done against `BACKEND_NAME` and `VERSION`. The version string is similar in purpose to a [User-Agent](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) string. The format was first invented by Pleroma, but is now widely used, including by Pixelfed, Mitra, and Soapbox BE. -See [`features.ts`](https://gitlab.com/soapbox-pub/soapbox-fe/-/blob/develop/app/soapbox/utils/features.ts) for the complete list of features. +See [`features.ts`](https://gitlab.com/soapbox-pub/soapbox/-/blob/develop/app/soapbox/utils/features.ts) for the complete list of features. ## Forks of other software @@ -73,4 +73,4 @@ For Pleroma forks, the fork name should be in the compat section (eg Soapbox BE) ## Adding support for a new backend -If the backend conforms to the above format, please modify [`features.ts`](https://gitlab.com/soapbox-pub/soapbox-fe/-/blob/develop/app/soapbox/utils/features.ts) and submit a merge request to enable features for your backend! +If the backend conforms to the above format, please modify [`features.ts`](https://gitlab.com/soapbox-pub/soapbox/-/blob/develop/app/soapbox/utils/features.ts) and submit a merge request to enable features for your backend! diff --git a/docs/development/how-it-works.md b/docs/development/how-it-works.md index 68aa0e5ee..52a326d8a 100644 --- a/docs/development/how-it-works.md +++ b/docs/development/how-it-works.md @@ -18,7 +18,7 @@ location / { } ``` -(See [`mastodon.conf`](https://gitlab.com/soapbox-pub/soapbox-fe/-/blob/develop/installation/mastodon.conf) for a full example.) +(See [`mastodon.conf`](https://gitlab.com/soapbox-pub/soapbox/-/blob/develop/installation/mastodon.conf) for a full example.) Soapbox incorporates much of the [Mastodon API](https://docs.joinmastodon.org/methods/), [Pleroma API](https://api.pleroma.social/), and more. It detects features supported by the backend to provide the right experience for the backend. diff --git a/docs/development/running-locally.md b/docs/development/running-locally.md index d11c59396..7cd1164a6 100644 --- a/docs/development/running-locally.md +++ b/docs/development/running-locally.md @@ -3,7 +3,7 @@ To get it running, just clone the repo: ``` -git clone https://gitlab.com/soapbox-pub/soapbox-fe.git +git clone https://gitlab.com/soapbox-pub/soapbox.git cd soapbox-fe ``` @@ -40,5 +40,5 @@ Try again. ## Troubleshooting: it's not working! -Run `node -V` and compare your Node.js version with the version in [`.tool-versions`](https://gitlab.com/soapbox-pub/soapbox-fe/-/blob/develop/.tool-versions). +Run `node -V` and compare your Node.js version with the version in [`.tool-versions`](https://gitlab.com/soapbox-pub/soapbox/-/blob/develop/.tool-versions). If they don't match, try installing [asdf](https://asdf-vm.com/). diff --git a/docs/installing.md b/docs/installing.md index fb659e751..37c9c36e5 100644 --- a/docs/installing.md +++ b/docs/installing.md @@ -10,7 +10,7 @@ First, follow the instructions to [install Pleroma](https://docs-develop.pleroma The Soapbox frontend is the main component of Soapbox. Once you've installed Pleroma, installing Soapbox is a breeze. -First, ssh into the server and download a .zip of the latest build: ``curl -L https://gitlab.com/soapbox-pub/soapbox-fe/-/jobs/artifacts/develop/download?job=build-production -o soapbox-fe.zip`` +First, ssh into the server and download a .zip of the latest build: ``curl -L https://gitlab.com/soapbox-pub/soapbox/-/jobs/artifacts/develop/download?job=build-production -o soapbox-fe.zip`` Then unpack it into Pleroma's ``instance`` directory: ``busybox unzip soapbox-fe.zip -o -d /opt/pleroma/instance`` diff --git a/package.json b/package.json index 8cb1e8a4b..d5647cdec 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,19 @@ { - "name": "soapbox-fe", + "name": "soapbox", "displayName": "Soapbox", "version": "3.0.0", "description": "Soapbox frontend for the Fediverse.", "homepage": "https://soapbox.pub/", "repository": { "type": "git", - "url": "https://gitlab.com/soapbox-pub/soapbox-fe" + "url": "https://gitlab.com/soapbox-pub/soapbox" }, "keywords": [ "fediverse", "pleroma" ], "bugs": { - "url": "https://gitlab.com/soapbox-pub/soapbox-fe/-/issues" + "url": "https://gitlab.com/soapbox-pub/soapbox/-/issues" }, "scripts": { "start": "npx webpack-dev-server", From 06646a01df32779e39d6a420e6920e55b352330e Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 4 Sep 2022 15:37:34 -0500 Subject: [PATCH 23/36] GitLab CI: build a docker image --- .gitlab-ci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 90c856e40..edd3700ae 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -142,3 +142,16 @@ pages: only: refs: - develop + +docker: + image: docker:20.10.17 + services: + - docker:20.10.17-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 + - docker build -t $CI_REGISTRY_IMAGE . + - docker push $CI_REGISTRY_IMAGE + # only: + # refs: + # - develop \ No newline at end of file From 53be32de34c64bb40c9bf05a25ffc3c4d085e249 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 4 Sep 2022 15:58:48 -0500 Subject: [PATCH 24/36] GitLab CI: only publish image on develop --- .gitlab-ci.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index edd3700ae..aaca197c3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -144,14 +144,15 @@ pages: - develop docker: + stage: deploy image: docker:20.10.17 services: - - docker:20.10.17-dind + - docker:20.10.17-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 - docker build -t $CI_REGISTRY_IMAGE . - docker push $CI_REGISTRY_IMAGE - # only: - # refs: - # - develop \ No newline at end of file + only: + refs: + - develop \ No newline at end of file From d074553e21b7ae63523231a1e75b78f8479ad97f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 5 Sep 2022 15:18:15 +0200 Subject: [PATCH 25/36] Wrap HStack in some places MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/components/polls/poll-footer.tsx | 2 +- app/soapbox/components/ui/hstack/hstack.tsx | 5 ++++- app/soapbox/features/status/components/detailed-status.tsx | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/soapbox/components/polls/poll-footer.tsx b/app/soapbox/components/polls/poll-footer.tsx index ef4ca2276..dfa91e663 100644 --- a/app/soapbox/components/polls/poll-footer.tsx +++ b/app/soapbox/components/polls/poll-footer.tsx @@ -54,7 +54,7 @@ const PollFooter: React.FC = ({ poll, showResults, selected }): JSX )} - + {poll.pleroma.get('non_anonymous') && ( <> diff --git a/app/soapbox/components/ui/hstack/hstack.tsx b/app/soapbox/components/ui/hstack/hstack.tsx index 3eef11055..f959cdd51 100644 --- a/app/soapbox/components/ui/hstack/hstack.tsx +++ b/app/soapbox/components/ui/hstack/hstack.tsx @@ -42,11 +42,13 @@ interface IHStack { grow?: boolean, /** Extra CSS styles for the
*/ style?: React.CSSProperties + /** Whether to let the flexbox wrap onto multiple lines. */ + wrap?: boolean, } /** Horizontal row of child elements. */ const HStack = forwardRef((props, ref) => { - const { space, alignItems, grow, justifyContent, className, ...filteredProps } = props; + const { space, alignItems, grow, justifyContent, wrap, className, ...filteredProps } = props; return (
((props, ref) => { // @ts-ignore [spaces[space]]: typeof space !== 'undefined', 'flex-grow': grow, + 'flex-wrap': wrap, }, className)} /> ); diff --git a/app/soapbox/features/status/components/detailed-status.tsx b/app/soapbox/features/status/components/detailed-status.tsx index b8fed9255..f484be4cb 100644 --- a/app/soapbox/features/status/components/detailed-status.tsx +++ b/app/soapbox/features/status/components/detailed-status.tsx @@ -99,7 +99,7 @@ const DetailedStatus: React.FC = ({ {quote} - + From d00a50bf7c97d03008a278df27fed149a4de2144 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 5 Sep 2022 17:33:08 -0500 Subject: [PATCH 26/36] sw.js: whitelist Phoenix LiveDashboard --- webpack/production.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webpack/production.js b/webpack/production.js index 9bd16e045..e1d833abc 100644 --- a/webpack/production.js +++ b/webpack/production.js @@ -113,6 +113,7 @@ module.exports = merge(sharedConfig, { '/objects', '/ostatus_subscribe', '/pghero', + '/phoenix', '/pleroma', '/proxy', '/relay', From 57b2527b55c957720750f77e6892edcea579d0bc Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 6 Sep 2022 13:09:31 -0500 Subject: [PATCH 27/36] StatusActionBar: copy status.url --> status.uri --- app/soapbox/components/status-action-bar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/soapbox/components/status-action-bar.tsx b/app/soapbox/components/status-action-bar.tsx index c07bf818d..5bce513a5 100644 --- a/app/soapbox/components/status-action-bar.tsx +++ b/app/soapbox/components/status-action-bar.tsx @@ -301,12 +301,12 @@ const StatusActionBar: React.FC = ({ }; const handleCopy: React.EventHandler = (e) => { - const { url } = status; + const { uri } = status; const textarea = document.createElement('textarea'); e.stopPropagation(); - textarea.textContent = url; + textarea.textContent = uri; textarea.style.position = 'fixed'; document.body.appendChild(textarea); From 99e5e4492a732843f8993d2701764fb222ff5426 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 7 Sep 2022 08:09:32 -0400 Subject: [PATCH 28/36] Extend Divider with 'textSize' prop --- app/soapbox/components/ui/divider/divider.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/soapbox/components/ui/divider/divider.tsx b/app/soapbox/components/ui/divider/divider.tsx index 033f139ae..343a1b948 100644 --- a/app/soapbox/components/ui/divider/divider.tsx +++ b/app/soapbox/components/ui/divider/divider.tsx @@ -1,11 +1,16 @@ import React from 'react'; +import Text from '../text/text'; + +import type { Sizes as TextSizes } from '../text/text'; + interface IDivider { text?: string + textSize?: TextSizes } /** Divider */ -const Divider = ({ text }: IDivider) => ( +const Divider = ({ text, textSize = 'md' }: IDivider) => (