diff --git a/app/helpers/wrapstodon_helper.rb b/app/helpers/wrapstodon_helper.rb new file mode 100644 index 0000000000..da3b0d6fad --- /dev/null +++ b/app/helpers/wrapstodon_helper.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module WrapstodonHelper + def render_wrapstodon_share_data(report) + json = ActiveModelSerializers::SerializableResource.new( + AnnualReportsPresenter.new([report]), + serializer: REST::AnnualReportsSerializer, + scope: nil, + scope_name: :current_user + ).to_json + + # rubocop:disable Rails/OutputSafety + content_tag(:script, json_escape(json).html_safe, type: 'application/json', id: 'wrapstodon-data') + # rubocop:enable Rails/OutputSafety + end +end diff --git a/app/javascript/entrypoints/wrapstodon.tsx b/app/javascript/entrypoints/wrapstodon.tsx new file mode 100644 index 0000000000..e1eebcce57 --- /dev/null +++ b/app/javascript/entrypoints/wrapstodon.tsx @@ -0,0 +1,58 @@ +import { createRoot } from 'react-dom/client'; + +import { Provider as ReduxProvider } from 'react-redux'; + +import { + importFetchedAccounts, + importFetchedStatuses, +} from '@/mastodon/actions/importer'; +import type { ApiAnnualReportResponse } from '@/mastodon/api/annual_report'; +import { Router } from '@/mastodon/components/router'; +import { WrapstodonShare } from '@/mastodon/features/annual_report/share'; +import { IntlProvider, loadLocale } from '@/mastodon/locales'; +import { loadPolyfills } from '@/mastodon/polyfills'; +import ready from '@/mastodon/ready'; +import { setReport } from '@/mastodon/reducers/slices/annual_report'; +import { store } from '@/mastodon/store'; + +function loaded() { + const mountNode = document.getElementById('wrapstodon'); + if (!mountNode) { + throw new Error('Mount node not found'); + } + const propsNode = document.getElementById('wrapstodon-data'); + if (!propsNode) { + throw new Error('Initial state prop not found'); + } + + const initialState = JSON.parse( + propsNode.textContent, + ) as ApiAnnualReportResponse; + + const report = initialState.annual_reports[0]; + if (!report) { + throw new Error('Initial state report not found'); + } + store.dispatch(importFetchedAccounts(initialState.accounts)); + store.dispatch(importFetchedStatuses(initialState.statuses)); + + store.dispatch(setReport(report)); + + const root = createRoot(mountNode); + root.render( + + + + + + + , + ); +} + +loadPolyfills() + .then(loadLocale) + .then(() => ready(loaded)) + .catch((err: unknown) => { + console.error(err); + }); diff --git a/app/javascript/mastodon/components/logo.tsx b/app/javascript/mastodon/components/logo.tsx index fe9680d0e3..c0ed4d78a7 100644 --- a/app/javascript/mastodon/components/logo.tsx +++ b/app/javascript/mastodon/components/logo.tsx @@ -1,3 +1,5 @@ +import classNames from 'classnames'; + import logo from '@/images/logo.svg'; export const WordmarkLogo: React.FC = () => ( @@ -7,8 +9,12 @@ export const WordmarkLogo: React.FC = () => ( ); -export const IconLogo: React.FC = () => ( - +export const IconLogo: React.FC<{ className?: string }> = ({ className }) => ( + Mastodon diff --git a/app/javascript/mastodon/features/annual_report/share.module.css b/app/javascript/mastodon/features/annual_report/share.module.css new file mode 100644 index 0000000000..e4d01ff3e3 --- /dev/null +++ b/app/javascript/mastodon/features/annual_report/share.module.css @@ -0,0 +1,19 @@ +.wrapper { + max-width: 40rem; + margin: 0 auto; +} + +.footer { + text-align: center; + margin-top: 1rem; + display: flex; + flex-direction: column; + gap: 0.75rem; + align-items: center; + color: var(--color-text-secondary); +} + +.logo { + width: 2rem; + opacity: 0.6; +} diff --git a/app/javascript/mastodon/features/annual_report/share.tsx b/app/javascript/mastodon/features/annual_report/share.tsx new file mode 100644 index 0000000000..25b3ee9885 --- /dev/null +++ b/app/javascript/mastodon/features/annual_report/share.tsx @@ -0,0 +1,18 @@ +import type { FC } from 'react'; + +import { IconLogo } from '@/mastodon/components/logo'; + +import { AnnualReport } from './index'; +import classes from './share.module.css'; + +export const WrapstodonShare: FC = () => { + return ( +
+ +
+ + Generated with ♥ by the Mastodon team +
+
+ ); +}; diff --git a/app/javascript/mastodon/reducers/slices/annual_report.ts b/app/javascript/mastodon/reducers/slices/annual_report.ts index 4d5db41655..c01b8f7995 100644 --- a/app/javascript/mastodon/reducers/slices/annual_report.ts +++ b/app/javascript/mastodon/reducers/slices/annual_report.ts @@ -1,3 +1,4 @@ +import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; import { @@ -29,7 +30,12 @@ interface AnnualReportState { const annualReportSlice = createSlice({ name: 'annualReport', initialState: {} as AnnualReportState, - reducers: {}, + reducers: { + setReport(state, action: PayloadAction) { + state.report = action.payload; + state.state = 'available'; + }, + }, extraReducers(builder) { builder .addCase(fetchReportState.fulfilled, (state, action) => { @@ -47,6 +53,7 @@ const annualReportSlice = createSlice({ }); export const annualReport = annualReportSlice.reducer; +export const { setReport } = annualReportSlice.actions; export const selectWrapstodonYear = createAppSelector( [(state) => state.server.getIn(['server', 'wrapstodon'])], diff --git a/app/lib/annual_report.rb b/app/lib/annual_report.rb index 12d69dfba8..0acd51eb17 100644 --- a/app/lib/annual_report.rb +++ b/app/lib/annual_report.rb @@ -22,7 +22,7 @@ class AnnualReport return unless Mastodon::Feature.wrapstodon_enabled? datetime = Time.now.utc - datetime.year if datetime.month == 12 && (10..31).cover?(datetime.day) + datetime.year if datetime.month == 12 && (1..31).cover?(datetime.day) end def initialize(account, year) diff --git a/app/views/wrapstodon/show.html.haml b/app/views/wrapstodon/show.html.haml index 6978f231f9..70c8c77b19 100644 --- a/app/views/wrapstodon/show.html.haml +++ b/app/views/wrapstodon/show.html.haml @@ -6,4 +6,7 @@ = opengraph 'og:site_name', site_title = opengraph 'profile:username', acct(@account)[1..] -= render 'shared/web_app' + = vite_typescript_tag 'wrapstodon.tsx', crossorigin: 'anonymous' + +#wrapstodon += render_wrapstodon_share_data @generated_annual_report