From 7a3bbfde10d40d09f8149d9644d01d065338c557 Mon Sep 17 00:00:00 2001 From: Djordje Vlaisavljevic Date: Thu, 25 Apr 2024 09:44:29 +0100 Subject: [PATCH] Added ActivityPub playground (#20081) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref MOM61 - Adds admin-x react app we’ll use as ActivityPub playground to the sidebar nav behind the feature flag. - Wired up routing to Ember - Setup the project as `admin-x-activitypub` --------- Co-authored-by: Ronald Langeveld --- .github/scripts/dev.js | 4 +- apps/admin-x-activitypub/.eslintignore | 1 + apps/admin-x-activitypub/.eslintrc.cjs | 56 ++++++++++++++++ apps/admin-x-activitypub/.gitignore | 3 + apps/admin-x-activitypub/index.html | 13 ++++ apps/admin-x-activitypub/package.json | 67 +++++++++++++++++++ .../admin-x-activitypub/playwright.config.mjs | 3 + apps/admin-x-activitypub/postcss.config.cjs | 1 + apps/admin-x-activitypub/src/App.tsx | 23 +++++++ .../src/components/ListIndex.tsx | 26 +++++++ apps/admin-x-activitypub/src/index.tsx | 6 ++ apps/admin-x-activitypub/src/standalone.tsx | 5 ++ apps/admin-x-activitypub/src/styles/index.css | 1 + apps/admin-x-activitypub/tailwind.config.cjs | 6 ++ apps/admin-x-activitypub/test/.eslintrc.cjs | 6 ++ .../test/acceptance/app.test.ts | 10 +++ .../test/unit/ListIndex.test.tsx | 10 +++ apps/admin-x-activitypub/tsconfig.json | 23 +++++++ apps/admin-x-activitypub/vite.config.mjs | 10 +++ .../app/components/admin-x/activitypub.hbs | 1 + .../app/components/admin-x/activitypub.js | 8 +++ .../admin/app/components/gh-nav-menu/main.hbs | 5 ++ ghost/admin/app/controllers/activitypub-x.js | 6 ++ ghost/admin/app/router.js | 4 ++ ghost/admin/app/routes/activitypub-x.js | 3 + ghost/admin/app/services/feature.js | 1 + ghost/admin/app/templates/activitypub-x.hbs | 1 + ghost/admin/lib/asset-delivery/index.js | 2 +- ghost/admin/package.json | 6 +- ghost/core/core/shared/labs.js | 1 + 30 files changed, 307 insertions(+), 5 deletions(-) create mode 100644 apps/admin-x-activitypub/.eslintignore create mode 100644 apps/admin-x-activitypub/.eslintrc.cjs create mode 100644 apps/admin-x-activitypub/.gitignore create mode 100644 apps/admin-x-activitypub/index.html create mode 100644 apps/admin-x-activitypub/package.json create mode 100644 apps/admin-x-activitypub/playwright.config.mjs create mode 100644 apps/admin-x-activitypub/postcss.config.cjs create mode 100644 apps/admin-x-activitypub/src/App.tsx create mode 100644 apps/admin-x-activitypub/src/components/ListIndex.tsx create mode 100644 apps/admin-x-activitypub/src/index.tsx create mode 100644 apps/admin-x-activitypub/src/standalone.tsx create mode 100644 apps/admin-x-activitypub/src/styles/index.css create mode 100644 apps/admin-x-activitypub/tailwind.config.cjs create mode 100644 apps/admin-x-activitypub/test/.eslintrc.cjs create mode 100644 apps/admin-x-activitypub/test/acceptance/app.test.ts create mode 100644 apps/admin-x-activitypub/test/unit/ListIndex.test.tsx create mode 100644 apps/admin-x-activitypub/tsconfig.json create mode 100644 apps/admin-x-activitypub/vite.config.mjs create mode 100644 ghost/admin/app/components/admin-x/activitypub.hbs create mode 100644 ghost/admin/app/components/admin-x/activitypub.js create mode 100644 ghost/admin/app/controllers/activitypub-x.js create mode 100644 ghost/admin/app/routes/activitypub-x.js create mode 100644 ghost/admin/app/templates/activitypub-x.hbs diff --git a/.github/scripts/dev.js b/.github/scripts/dev.js index ad6f3b02af..209e86d708 100644 --- a/.github/scripts/dev.js +++ b/.github/scripts/dev.js @@ -66,7 +66,7 @@ const COMMAND_TYPESCRIPT = { env: {} }; -const adminXApps = '@tryghost/admin-x-demo,@tryghost/admin-x-settings'; +const adminXApps = '@tryghost/admin-x-demo,@tryghost/admin-x-settings,@tryghost/admin-x-activitypub'; const COMMANDS_ADMINX = [{ name: 'adminXDeps', @@ -77,7 +77,7 @@ const COMMANDS_ADMINX = [{ }, { name: 'adminX', command: `nx run-many --projects=${adminXApps} --parallel=${adminXApps.length} --targets=dev`, - cwd: path.resolve(__dirname, '../../apps/admin-x-settings'), + cwd: path.resolve(__dirname, '../../apps/admin-x-settings', '../../apps/admin-x-activitypub'), prefixColor: '#C35831', env: {} }]; diff --git a/apps/admin-x-activitypub/.eslintignore b/apps/admin-x-activitypub/.eslintignore new file mode 100644 index 0000000000..9944eccea2 --- /dev/null +++ b/apps/admin-x-activitypub/.eslintignore @@ -0,0 +1 @@ +tailwind.config.cjs diff --git a/apps/admin-x-activitypub/.eslintrc.cjs b/apps/admin-x-activitypub/.eslintrc.cjs new file mode 100644 index 0000000000..919b0f2cdf --- /dev/null +++ b/apps/admin-x-activitypub/.eslintrc.cjs @@ -0,0 +1,56 @@ +/* eslint-env node */ +module.exports = { + root: true, + extends: [ + 'plugin:ghost/ts', + 'plugin:react/recommended', + 'plugin:react-hooks/recommended' + ], + plugins: [ + 'ghost', + 'react-refresh', + 'tailwindcss' + ], + settings: { + react: { + version: 'detect' + } + }, + rules: { + // sort multiple import lines into alphabetical groups + 'ghost/sort-imports-es6-autofix/sort-imports-es6': ['error', { + memberSyntaxSortOrder: ['none', 'all', 'single', 'multiple'] + }], + + // TODO: re-enable this (maybe fixed fast refresh?) + 'react-refresh/only-export-components': 'off', + + // suppress errors for missing 'import React' in JSX files, as we don't need it + 'react/react-in-jsx-scope': 'off', + // ignore prop-types for now + 'react/prop-types': 'off', + + // TODO: re-enable these if deemed useful + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-empty-function': 'off', + + // custom react rules + 'react/jsx-sort-props': ['error', { + reservedFirst: true, + callbacksLast: true, + shorthandLast: true, + locale: 'en' + }], + 'react/button-has-type': 'error', + 'react/no-array-index-key': 'error', + 'react/jsx-key': 'off', + + 'tailwindcss/classnames-order': ['error', {config: 'tailwind.config.cjs'}], + 'tailwindcss/enforces-negative-arbitrary-values': ['warn', {config: 'tailwind.config.cjs'}], + 'tailwindcss/enforces-shorthand': ['warn', {config: 'tailwind.config.cjs'}], + 'tailwindcss/migration-from-tailwind-2': ['warn', {config: 'tailwind.config.cjs'}], + 'tailwindcss/no-arbitrary-value': 'off', + 'tailwindcss/no-custom-classname': 'off', + 'tailwindcss/no-contradicting-classname': ['error', {config: 'tailwind.config.cjs'}] + } +}; diff --git a/apps/admin-x-activitypub/.gitignore b/apps/admin-x-activitypub/.gitignore new file mode 100644 index 0000000000..68565785a7 --- /dev/null +++ b/apps/admin-x-activitypub/.gitignore @@ -0,0 +1,3 @@ +dist +playwright-report +test-results diff --git a/apps/admin-x-activitypub/index.html b/apps/admin-x-activitypub/index.html new file mode 100644 index 0000000000..60bd860b4a --- /dev/null +++ b/apps/admin-x-activitypub/index.html @@ -0,0 +1,13 @@ + + + + + + + AdminX Standalone + + +
+ + + diff --git a/apps/admin-x-activitypub/package.json b/apps/admin-x-activitypub/package.json new file mode 100644 index 0000000000..435ba50b73 --- /dev/null +++ b/apps/admin-x-activitypub/package.json @@ -0,0 +1,67 @@ +{ + "name": "@tryghost/admin-x-activitypub", + "version": "0.0.1", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/TryGhost/Ghost/tree/main/apps/admin-x-activitypub" + }, + "author": "Ghost Foundation", + "files": [ + "LICENSE", + "README.md", + "dist/" + ], + "main": "./dist/admin-x-activitypub.umd.cjs", + "module": "./dist/admin-x-activitypub.js", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "scripts": { + "dev": "vite build --watch", + "dev:start": "vite", + "build": "concurrently \"tsc\" \"vite build\"", + "lint": "yarn run lint:code && yarn run lint:test", + "lint:code": "eslint --ext .js,.ts,.cjs,.tsx --cache src", + "lint:test": "eslint -c test/.eslintrc.cjs --ext .js,.ts,.cjs,.tsx --cache test", + "test:unit": "vitest run", + "test:acceptance": "NODE_OPTIONS='--experimental-specifier-resolution=node --no-warnings' VITE_TEST=true playwright test", + "test:acceptance:slowmo": "TIMEOUT=100000 PLAYWRIGHT_SLOWMO=100 yarn test:acceptance --headed", + "test:acceptance:full": "ALL_BROWSERS=1 yarn test:acceptance", + "preview": "vite preview" + }, + "devDependencies": { + "@testing-library/react": "14.1.0", + "@tryghost/admin-x-design-system": "0.0.0", + "@tryghost/admin-x-framework": "0.0.0", + "@types/react": "18.2.48", + "@types/react-dom": "18.2.18", + "react": "18.2.0", + "react-dom": "18.2.0" + }, + "nx": { + "targets": { + "build": { + "dependsOn": [ + "build", + {"projects": ["@tryghost/admin-x-design-system", "@tryghost/admin-x-framework"], "target": "build"} + ] + }, + "test:unit": { + "dependsOn": [ + "build", + "test:unit", + {"projects": ["@tryghost/admin-x-design-system", "@tryghost/admin-x-framework"], "target": "build"} + ] + }, + "test:acceptance": { + "dependsOn": [ + "build", + "test:acceptance", + {"projects": ["@tryghost/admin-x-design-system", "@tryghost/admin-x-framework"], "target": "build"} + ] + } + } + } +} diff --git a/apps/admin-x-activitypub/playwright.config.mjs b/apps/admin-x-activitypub/playwright.config.mjs new file mode 100644 index 0000000000..8fa59553e5 --- /dev/null +++ b/apps/admin-x-activitypub/playwright.config.mjs @@ -0,0 +1,3 @@ +import {adminXPlaywrightConfig} from '@tryghost/admin-x-framework/playwright'; + +export default adminXPlaywrightConfig(); diff --git a/apps/admin-x-activitypub/postcss.config.cjs b/apps/admin-x-activitypub/postcss.config.cjs new file mode 100644 index 0000000000..8799f4acf8 --- /dev/null +++ b/apps/admin-x-activitypub/postcss.config.cjs @@ -0,0 +1 @@ +module.exports = require('@tryghost/admin-x-design-system/postcss.config.cjs'); diff --git a/apps/admin-x-activitypub/src/App.tsx b/apps/admin-x-activitypub/src/App.tsx new file mode 100644 index 0000000000..efb1b2fdd2 --- /dev/null +++ b/apps/admin-x-activitypub/src/App.tsx @@ -0,0 +1,23 @@ +import ListIndex from './components/ListIndex'; +import {DesignSystemApp, DesignSystemAppProps} from '@tryghost/admin-x-design-system'; +import {FrameworkProvider, TopLevelFrameworkProps} from '@tryghost/admin-x-framework'; +import {RoutingProvider} from '@tryghost/admin-x-framework/routing'; + +interface AppProps { + framework: TopLevelFrameworkProps; + designSystem: DesignSystemAppProps; +} + +const App: React.FC = ({framework, designSystem}) => { + return ( + + + + + + + + ); +}; + +export default App; diff --git a/apps/admin-x-activitypub/src/components/ListIndex.tsx b/apps/admin-x-activitypub/src/components/ListIndex.tsx new file mode 100644 index 0000000000..7156dba145 --- /dev/null +++ b/apps/admin-x-activitypub/src/components/ListIndex.tsx @@ -0,0 +1,26 @@ +const ListIndex = () => { + return ( +
+

ActivityPub Demo

+
+
+

This is a post title

+

This is some very short post content

+

Publish McPublisher

+
+
+

This is a post title

+

This is some very short post content

+

Publish McPublisher

+
+
+

This is a post title

+

This is some very short post content

+

Publish McPublisher

+
+
+
+ ); +}; + +export default ListIndex; diff --git a/apps/admin-x-activitypub/src/index.tsx b/apps/admin-x-activitypub/src/index.tsx new file mode 100644 index 0000000000..cb20f2b589 --- /dev/null +++ b/apps/admin-x-activitypub/src/index.tsx @@ -0,0 +1,6 @@ +import './styles/index.css'; +import App from './App'; + +export { + App as AdminXApp +}; diff --git a/apps/admin-x-activitypub/src/standalone.tsx b/apps/admin-x-activitypub/src/standalone.tsx new file mode 100644 index 0000000000..dd723c833c --- /dev/null +++ b/apps/admin-x-activitypub/src/standalone.tsx @@ -0,0 +1,5 @@ +import './styles/index.css'; +import App from './App.tsx'; +import renderStandaloneApp from '@tryghost/admin-x-framework/test/render'; + +renderStandaloneApp(App, {}); diff --git a/apps/admin-x-activitypub/src/styles/index.css b/apps/admin-x-activitypub/src/styles/index.css new file mode 100644 index 0000000000..d1f1f198ed --- /dev/null +++ b/apps/admin-x-activitypub/src/styles/index.css @@ -0,0 +1 @@ +@import '@tryghost/admin-x-design-system/styles.css'; diff --git a/apps/admin-x-activitypub/tailwind.config.cjs b/apps/admin-x-activitypub/tailwind.config.cjs new file mode 100644 index 0000000000..c85c4230dd --- /dev/null +++ b/apps/admin-x-activitypub/tailwind.config.cjs @@ -0,0 +1,6 @@ +const adminXPreset = require('@tryghost/admin-x-design-system/tailwind.cjs'); + +module.exports = { + presets: [adminXPreset('.admin-x-activitypub')], + content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}', '../../node_modules/@tryghost/admin-x-design-system/es/**/*.{js,ts,jsx,tsx}'] +}; \ No newline at end of file diff --git a/apps/admin-x-activitypub/test/.eslintrc.cjs b/apps/admin-x-activitypub/test/.eslintrc.cjs new file mode 100644 index 0000000000..42f8e77355 --- /dev/null +++ b/apps/admin-x-activitypub/test/.eslintrc.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: ['ghost'], + extends: [ + 'plugin:ghost/ts-test' + ] +}; diff --git a/apps/admin-x-activitypub/test/acceptance/app.test.ts b/apps/admin-x-activitypub/test/acceptance/app.test.ts new file mode 100644 index 0000000000..9e84a05428 --- /dev/null +++ b/apps/admin-x-activitypub/test/acceptance/app.test.ts @@ -0,0 +1,10 @@ +import {expect, test} from '@playwright/test'; +// import {mockApi, responseFixtures} from '@tryghost/admin-x-framework/test/acceptance'; + +test.describe('Demo', async () => { + test('Renders the list page', async ({page}) => { + await page.goto('/'); + + await expect(page.locator('body')).toContainText('ActivityPub Demo'); + }); +}); diff --git a/apps/admin-x-activitypub/test/unit/ListIndex.test.tsx b/apps/admin-x-activitypub/test/unit/ListIndex.test.tsx new file mode 100644 index 0000000000..50459c8584 --- /dev/null +++ b/apps/admin-x-activitypub/test/unit/ListIndex.test.tsx @@ -0,0 +1,10 @@ +import ListIndex from '../../src/components/ListIndex'; +import {render, screen} from '@testing-library/react'; + +describe('Demo', function () { + it('renders a component', async function () { + render(); + + expect(screen.getAllByRole('heading')[0].textContent).toEqual('ActivityPub Demo'); + }); +}); diff --git a/apps/admin-x-activitypub/tsconfig.json b/apps/admin-x-activitypub/tsconfig.json new file mode 100644 index 0000000000..621d5ec558 --- /dev/null +++ b/apps/admin-x-activitypub/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src", "test"] +} diff --git a/apps/admin-x-activitypub/vite.config.mjs b/apps/admin-x-activitypub/vite.config.mjs new file mode 100644 index 0000000000..ae5b996d87 --- /dev/null +++ b/apps/admin-x-activitypub/vite.config.mjs @@ -0,0 +1,10 @@ +import adminXViteConfig from '@tryghost/admin-x-framework/vite'; +import pkg from './package.json'; +import {resolve} from 'path'; + +export default (function viteConfig() { + return adminXViteConfig({ + packageName: pkg.name, + entry: resolve(__dirname, 'src/index.tsx') + }); +}); diff --git a/ghost/admin/app/components/admin-x/activitypub.hbs b/ghost/admin/app/components/admin-x/activitypub.hbs new file mode 100644 index 0000000000..ed901509b3 --- /dev/null +++ b/ghost/admin/app/components/admin-x/activitypub.hbs @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/ghost/admin/app/components/admin-x/activitypub.js b/ghost/admin/app/components/admin-x/activitypub.js new file mode 100644 index 0000000000..0f7edb5422 --- /dev/null +++ b/ghost/admin/app/components/admin-x/activitypub.js @@ -0,0 +1,8 @@ +import AdminXComponent from './admin-x-component'; +import {inject as service} from '@ember/service'; + +export default class AdminXActivityPub extends AdminXComponent { + @service upgradeStatus; + + static packageName = '@tryghost/admin-x-activitypub'; +} diff --git a/ghost/admin/app/components/gh-nav-menu/main.hbs b/ghost/admin/app/components/gh-nav-menu/main.hbs index 36acc26874..58f9ecc8f7 100644 --- a/ghost/admin/app/components/gh-nav-menu/main.hbs +++ b/ghost/admin/app/components/gh-nav-menu/main.hbs @@ -123,6 +123,11 @@ {{svg-jar "star"}}AdminX Demo {{/if}} + {{#if (feature "ActivityPub")}} +
  • + {{svg-jar "star"}}ActivityPub Demo +
  • + {{/if}} {{#if this.session.user.isOwnerOnly}} diff --git a/ghost/admin/app/controllers/activitypub-x.js b/ghost/admin/app/controllers/activitypub-x.js new file mode 100644 index 0000000000..dd48c0620a --- /dev/null +++ b/ghost/admin/app/controllers/activitypub-x.js @@ -0,0 +1,6 @@ +import Controller from '@ember/controller'; +import {inject as service} from '@ember/service'; + +export default class ActivitypubXController extends Controller { + @service upgradeStatus; +} diff --git a/ghost/admin/app/router.js b/ghost/admin/app/router.js index e0f5c4e960..07ba4f1da8 100644 --- a/ghost/admin/app/router.js +++ b/ghost/admin/app/router.js @@ -56,6 +56,10 @@ Router.map(function () { this.route('settings-x', {path: '/*sub'}); }); + this.route('activitypub-x',{path: '/activitypub'}, function () { + this.route('activitypub-x', {path: '/*sub'}); + }); + // testing websockets this.route('websockets'); diff --git a/ghost/admin/app/routes/activitypub-x.js b/ghost/admin/app/routes/activitypub-x.js new file mode 100644 index 0000000000..72481e7df1 --- /dev/null +++ b/ghost/admin/app/routes/activitypub-x.js @@ -0,0 +1,3 @@ +import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; + +export default class AcitvitypubXRoute extends AuthenticatedRoute {} diff --git a/ghost/admin/app/services/feature.js b/ghost/admin/app/services/feature.js index 3b34bb5b88..abd2502824 100644 --- a/ghost/admin/app/services/feature.js +++ b/ghost/admin/app/services/feature.js @@ -82,6 +82,7 @@ export default class FeatureService extends Service { @feature('portalImprovements') portalImprovements; @feature('onboardingChecklist') onboardingChecklist; @feature('membersSpamPrevention') membersSpamPrevention; + @feature('ActivityPub') ActivityPub; @feature('internalLinking') internalLinking; _user = null; diff --git a/ghost/admin/app/templates/activitypub-x.hbs b/ghost/admin/app/templates/activitypub-x.hbs new file mode 100644 index 0000000000..ff79cd26b7 --- /dev/null +++ b/ghost/admin/app/templates/activitypub-x.hbs @@ -0,0 +1 @@ + diff --git a/ghost/admin/lib/asset-delivery/index.js b/ghost/admin/lib/asset-delivery/index.js index 8a8a529d1b..d48581a34f 100644 --- a/ghost/admin/lib/asset-delivery/index.js +++ b/ghost/admin/lib/asset-delivery/index.js @@ -6,7 +6,7 @@ const fs = require('fs'); const path = require('path'); const camelCase = require('lodash/camelCase'); -const adminXApps = ['admin-x-demo', 'admin-x-settings']; +const adminXApps = ['admin-x-demo', 'admin-x-settings', 'admin-x-activitypub']; function generateHash(filePath) { const fileContents = fs.readFileSync(filePath, 'utf8'); diff --git a/ghost/admin/package.json b/ghost/admin/package.json index fe34d9c2e3..18ab5617b8 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -181,7 +181,8 @@ { "projects": [ "@tryghost/admin-x-demo", - "@tryghost/admin-x-settings" + "@tryghost/admin-x-settings", + "@tryghost/admin-x-activitypub" ], "target": "build" } @@ -193,7 +194,8 @@ { "projects": [ "@tryghost/admin-x-demo", - "@tryghost/admin-x-settings" + "@tryghost/admin-x-settings", + "@tryghost/admin-x-activitypub" ], "target": "build" } diff --git a/ghost/core/core/shared/labs.js b/ghost/core/core/shared/labs.js index f766e82ac6..0d56291a5f 100644 --- a/ghost/core/core/shared/labs.js +++ b/ghost/core/core/shared/labs.js @@ -53,6 +53,7 @@ const ALPHA_FEATURES = [ // 'adminXOffers', 'adminXDemo', 'membersSpamPrevention', + 'ActivityPub', 'internalLinking' ];