mirror of
https://github.com/glitch-soc/mastodon.git
synced 2025-12-12 23:38:20 +00:00
Create new entrypoint for sharable Wrapstodon (#37121)
This commit is contained in:
16
app/helpers/wrapstodon_helper.rb
Normal file
16
app/helpers/wrapstodon_helper.rb
Normal file
@@ -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
|
||||||
58
app/javascript/entrypoints/wrapstodon.tsx
Normal file
58
app/javascript/entrypoints/wrapstodon.tsx
Normal file
@@ -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(
|
||||||
|
<IntlProvider>
|
||||||
|
<ReduxProvider store={store}>
|
||||||
|
<Router>
|
||||||
|
<WrapstodonShare />
|
||||||
|
</Router>
|
||||||
|
</ReduxProvider>
|
||||||
|
</IntlProvider>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPolyfills()
|
||||||
|
.then(loadLocale)
|
||||||
|
.then(() => ready(loaded))
|
||||||
|
.catch((err: unknown) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import logo from '@/images/logo.svg';
|
import logo from '@/images/logo.svg';
|
||||||
|
|
||||||
export const WordmarkLogo: React.FC = () => (
|
export const WordmarkLogo: React.FC = () => (
|
||||||
@@ -7,8 +9,12 @@ export const WordmarkLogo: React.FC = () => (
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const IconLogo: React.FC = () => (
|
export const IconLogo: React.FC<{ className?: string }> = ({ className }) => (
|
||||||
<svg viewBox='0 0 79 79' className='logo logo--icon' role='img'>
|
<svg
|
||||||
|
viewBox='0 0 79 79'
|
||||||
|
className={classNames('logo logo--icon', className)}
|
||||||
|
role='img'
|
||||||
|
>
|
||||||
<title>Mastodon</title>
|
<title>Mastodon</title>
|
||||||
<use xlinkHref='#logo-symbol-icon' />
|
<use xlinkHref='#logo-symbol-icon' />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
18
app/javascript/mastodon/features/annual_report/share.tsx
Normal file
18
app/javascript/mastodon/features/annual_report/share.tsx
Normal file
@@ -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 (
|
||||||
|
<main className={classes.wrapper}>
|
||||||
|
<AnnualReport share={false} />
|
||||||
|
<footer className={classes.footer}>
|
||||||
|
<IconLogo className={classes.logo} />
|
||||||
|
Generated with ♥ by the Mastodon team
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -29,7 +30,12 @@ interface AnnualReportState {
|
|||||||
const annualReportSlice = createSlice({
|
const annualReportSlice = createSlice({
|
||||||
name: 'annualReport',
|
name: 'annualReport',
|
||||||
initialState: {} as AnnualReportState,
|
initialState: {} as AnnualReportState,
|
||||||
reducers: {},
|
reducers: {
|
||||||
|
setReport(state, action: PayloadAction<AnnualReport>) {
|
||||||
|
state.report = action.payload;
|
||||||
|
state.state = 'available';
|
||||||
|
},
|
||||||
|
},
|
||||||
extraReducers(builder) {
|
extraReducers(builder) {
|
||||||
builder
|
builder
|
||||||
.addCase(fetchReportState.fulfilled, (state, action) => {
|
.addCase(fetchReportState.fulfilled, (state, action) => {
|
||||||
@@ -47,6 +53,7 @@ const annualReportSlice = createSlice({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const annualReport = annualReportSlice.reducer;
|
export const annualReport = annualReportSlice.reducer;
|
||||||
|
export const { setReport } = annualReportSlice.actions;
|
||||||
|
|
||||||
export const selectWrapstodonYear = createAppSelector(
|
export const selectWrapstodonYear = createAppSelector(
|
||||||
[(state) => state.server.getIn(['server', 'wrapstodon'])],
|
[(state) => state.server.getIn(['server', 'wrapstodon'])],
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class AnnualReport
|
|||||||
return unless Mastodon::Feature.wrapstodon_enabled?
|
return unless Mastodon::Feature.wrapstodon_enabled?
|
||||||
|
|
||||||
datetime = Time.now.utc
|
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
|
end
|
||||||
|
|
||||||
def initialize(account, year)
|
def initialize(account, year)
|
||||||
|
|||||||
@@ -6,4 +6,7 @@
|
|||||||
= opengraph 'og:site_name', site_title
|
= opengraph 'og:site_name', site_title
|
||||||
= opengraph 'profile:username', acct(@account)[1..]
|
= 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
|
||||||
|
|||||||
Reference in New Issue
Block a user