From bce557cc2dc767628bed6aac87301a1be7c5431b Mon Sep 17 00:00:00 2001 From: rxliuli Date: Tue, 4 Nov 2025 05:03:50 +0800 Subject: init commit --- .../charts-page-redirect-intent-controller.ts | 68 ++++++++++++++++++++++ src/jet/intents/error-page-intent-controller.ts | 52 +++++++++++++++++ .../lint-metrics-event-controller.ts | 18 ++++++ .../lint-metrics-event-intent.ts | 23 ++++++++ src/jet/intents/route-url/route-url-controller.ts | 28 +++++++++ src/jet/intents/route-url/route-url-intent.ts | 48 +++++++++++++++ .../carrier-page-intent-controller.ts | 41 +++++++++++++ .../contingent-price-page-intent-controller.ts | 49 ++++++++++++++++ .../invoice-page-intent-controller.ts | 41 +++++++++++++ .../win-back-page-intent-controller.ts | 49 ++++++++++++++++ 10 files changed, 417 insertions(+) create mode 100644 src/jet/intents/charts-page-redirect-intent-controller.ts create mode 100644 src/jet/intents/error-page-intent-controller.ts create mode 100644 src/jet/intents/lint-metrics-event/lint-metrics-event-controller.ts create mode 100644 src/jet/intents/lint-metrics-event/lint-metrics-event-intent.ts create mode 100644 src/jet/intents/route-url/route-url-controller.ts create mode 100644 src/jet/intents/route-url/route-url-intent.ts create mode 100644 src/jet/intents/static-message-pages/carrier-page-intent-controller.ts create mode 100644 src/jet/intents/static-message-pages/contingent-price-page-intent-controller.ts create mode 100644 src/jet/intents/static-message-pages/invoice-page-intent-controller.ts create mode 100644 src/jet/intents/static-message-pages/win-back-page-intent-controller.ts (limited to 'src/jet/intents') diff --git a/src/jet/intents/charts-page-redirect-intent-controller.ts b/src/jet/intents/charts-page-redirect-intent-controller.ts new file mode 100644 index 0000000..06d41ce --- /dev/null +++ b/src/jet/intents/charts-page-redirect-intent-controller.ts @@ -0,0 +1,68 @@ +import type { IntentController } from '@jet/environment/dispatching'; +import type { RouteProvider } from '@jet/environment/routing'; +import type { AppStoreObjectGraph } from '@jet-app/app-store/foundation/runtime/app-store-object-graph'; +import { withActiveIntent } from '@jet-app/app-store/foundation/dependencies/active-intent'; +import { generateRoutes } from '@jet-app/app-store/common/util/generate-routes'; +import { injectWebNavigation } from '@jet-app/app-store/common/web-navigation/inject-web-navigation'; +import { makeChartsPageURL } from '@jet-app/app-store/common/charts/charts-page-url'; +import { makeChartsPageIntent } from '@jet-app/app-store/api/intents/charts-page-intent'; +import { GenericPage } from '@jet-app/app-store/api/models'; +import { isPreviewPlatform } from '@jet-app/app-store/api/models/preview-platform'; +import { notFoundError } from '@jet-app/app-store/foundation/media/network'; + +const makeIntent = (opts) => ({ + ...opts, + $kind: 'ChartsPageRedirect', +}); + +// This will catch URLs like `/charts/iphone` +const { routes: routesWithoutGenreId } = generateRoutes( + makeIntent, + '/charts/{platform}', +); + +// This will catch URLs like `/charts/iphone/utilities-apps/6002` +const { routes: routesWithGenreId } = generateRoutes( + makeIntent, + '/charts/{platform}/{slug}/{genreId}', +); + +function chartsPageRedirectRoutes(objectGraph: AppStoreObjectGraph) { + return [ + ...routesWithoutGenreId(objectGraph), + ...routesWithGenreId(objectGraph), + ]; +} + +export const ChartsPageRedirectIntentController: IntentController & + RouteProvider = { + $intentKind: 'ChartsPageRedirect', + + routes: chartsPageRedirectRoutes, + + async perform(intent, objectGraphWithoutActiveIntent: AppStoreObjectGraph) { + return await withActiveIntent( + objectGraphWithoutActiveIntent, + intent, + async (objectGraph) => { + const page = new GenericPage([]); + const chartPageIntent = makeChartsPageIntent(intent); + + if (!isPreviewPlatform(intent.platform)) { + throw notFoundError(); + } + + // Setting the `canonicalUrl` on the page to normal Charts Page URL (e.g. /{platform}/charts) + // will trigger a 301 redirect to the that page. + page.canonicalURL = makeChartsPageURL( + objectGraph, + chartPageIntent, + ); + + injectWebNavigation(objectGraph, page, intent.platform); + + return page; + }, + ); + }, +}; diff --git a/src/jet/intents/error-page-intent-controller.ts b/src/jet/intents/error-page-intent-controller.ts new file mode 100644 index 0000000..59ac1fd --- /dev/null +++ b/src/jet/intents/error-page-intent-controller.ts @@ -0,0 +1,52 @@ +import type { Intent, IntentController } from '@jet/environment/dispatching'; +import type { Opt } from '@jet/environment/types/optional'; +import type { AppStoreObjectGraph } from '@jet-app/app-store/foundation/runtime/app-store-object-graph'; +import { withActiveIntent } from '@jet-app/app-store/foundation/dependencies/active-intent'; +import { injectWebNavigation } from '@jet-app/app-store/common/web-navigation/inject-web-navigation'; + +import { ErrorPage } from '~/jet/models/error-page'; +import type { Page } from '~/jet/models/page'; +import { getRejectedIntent } from '~/jet/utils/error-metadata'; +import { isWithPlatform } from '~/jet/utils/with-platform'; + +interface ErrorPageIntent extends Intent { + $kind: 'ErrorPageIntent'; + error: Opt; +} + +export function makeErrorPageIntent( + options: Omit, +): ErrorPageIntent { + return { + ...options, + $kind: 'ErrorPageIntent', + }; +} + +export const ErrorPageIntentController: IntentController = { + $intentKind: 'ErrorPageIntent', + + async perform( + intent, + objectGraphWithoutActiveIntent: AppStoreObjectGraph, + ): Promise { + const { error } = intent; + const rejectedIntent = error ? getRejectedIntent(error) : null; + const platform = + (rejectedIntent && isWithPlatform(rejectedIntent) + ? rejectedIntent.platform + : null) ?? 'iphone'; + + return await withActiveIntent( + objectGraphWithoutActiveIntent, + { ...intent, platform }, + async (objectGraph) => { + const page = new ErrorPage({ error: intent.error }); + + injectWebNavigation(objectGraph, page, platform); + + return page; + }, + ); + }, +}; diff --git a/src/jet/intents/lint-metrics-event/lint-metrics-event-controller.ts b/src/jet/intents/lint-metrics-event/lint-metrics-event-controller.ts new file mode 100644 index 0000000..046914b --- /dev/null +++ b/src/jet/intents/lint-metrics-event/lint-metrics-event-controller.ts @@ -0,0 +1,18 @@ +import type { IntentController } from '@jet/environment/dispatching/base/intent-controller'; +import type { LintedMetricsEvent } from '@jet/environment/types/metrics'; + +import { + type LintMetricsEventIntent, + LintMetricsEventIntentKind, +} from './lint-metrics-event-intent'; + +export const LintMetricsEventIntentController: IntentController = + { + $intentKind: LintMetricsEventIntentKind.Name, + + async perform( + intent: LintMetricsEventIntent, + ): Promise { + return { fields: intent.fields }; + }, + }; diff --git a/src/jet/intents/lint-metrics-event/lint-metrics-event-intent.ts b/src/jet/intents/lint-metrics-event/lint-metrics-event-intent.ts new file mode 100644 index 0000000..a2a085e --- /dev/null +++ b/src/jet/intents/lint-metrics-event/lint-metrics-event-intent.ts @@ -0,0 +1,23 @@ +import type { Intent } from '@jet/environment/dispatching'; +import type { + LintedMetricsEvent, + MetricsFields, +} from '@jet/environment/types/metrics'; + +export const enum LintMetricsEventIntentKind { + Name = 'LintMetricsEventIntent', +} + +export interface LintMetricsEventIntent extends Intent { + $kind: LintMetricsEventIntentKind.Name; + fields: MetricsFields; +} + +export function makeLintMetricsEventIntent( + options: Omit, +): LintMetricsEventIntent { + return { + ...options, + $kind: LintMetricsEventIntentKind.Name, + }; +} diff --git a/src/jet/intents/route-url/route-url-controller.ts b/src/jet/intents/route-url/route-url-controller.ts new file mode 100644 index 0000000..8c8fdb6 --- /dev/null +++ b/src/jet/intents/route-url/route-url-controller.ts @@ -0,0 +1,28 @@ +import { isSome } from '@jet/environment/types/optional'; +import type { IntentController } from '@jet/environment/dispatching'; +import type { AppStoreObjectGraph } from '@jet-app/app-store/foundation/runtime/app-store-object-graph'; +import { isRoutableIntent } from '@jet-app/app-store/api/intents/routable-intent'; + +import type { RouteUrlIntent } from '~/jet/intents'; +import { makeFlowAction } from '~/jet/models'; + +export const RouteUrlIntentController: IntentController = { + $intentKind: 'RouteUrlIntent', + + async perform(intent: RouteUrlIntent, objectGraph: AppStoreObjectGraph) { + const targetIntent = objectGraph.router.intentFor(intent.url); + + if (isSome(targetIntent) && isRoutableIntent(targetIntent)) { + return { + // intent needed for SSR + intent: targetIntent, + // only ever used by client; only clients have actions + action: makeFlowAction(targetIntent), + storefront: targetIntent.storefront, + language: targetIntent.language, + }; + } + + return null; + }, +}; diff --git a/src/jet/intents/route-url/route-url-intent.ts b/src/jet/intents/route-url/route-url-intent.ts new file mode 100644 index 0000000..841bd25 --- /dev/null +++ b/src/jet/intents/route-url/route-url-intent.ts @@ -0,0 +1,48 @@ +import type { Optional } from '@jet/environment/types/optional'; +import type { Intent } from '@jet/environment/dispatching'; +import type { FlowAction } from '@jet-app/app-store/api/models'; + +import type { + NormalizedStorefront, + NormalizedLanguage, +} from '@jet-app/app-store/api/locale'; + +/** + * A response from the router given an incoming (deeplink) URL. + */ +export interface RouterResponse { + /** + * The intent to dispatch to get the view model for this URL. + */ + intent: Intent; + + /** + * action to navigate to a new page of the app. + */ + action: FlowAction; + + storefront: NormalizedStorefront; + + language: NormalizedLanguage; +} + +export interface RouteUrlIntent extends Intent> { + $kind: 'RouteUrlIntent'; + + /** + * The URL to route (ex. "https://podcasts.apple.com/us/show/serial/id123"). + */ + url: string; +} + +export function isRouteUrlIntent( + intent: Intent, +): intent is RouteUrlIntent { + return intent.$kind === 'RouteUrlIntent'; +} + +export function makeRouteUrlIntent( + options: Omit, +): RouteUrlIntent { + return { $kind: 'RouteUrlIntent', ...options }; +} diff --git a/src/jet/intents/static-message-pages/carrier-page-intent-controller.ts b/src/jet/intents/static-message-pages/carrier-page-intent-controller.ts new file mode 100644 index 0000000..a1b049c --- /dev/null +++ b/src/jet/intents/static-message-pages/carrier-page-intent-controller.ts @@ -0,0 +1,41 @@ +import type { IntentController } from '@jet/environment/dispatching'; +import type { RouteProvider } from '@jet/environment/routing'; +import type { AppStoreObjectGraph } from '@jet-app/app-store/foundation/runtime/app-store-object-graph'; +import { withActiveIntent } from '@jet-app/app-store/foundation/dependencies/active-intent'; +import { generateRoutes } from '@jet-app/app-store/common/util/generate-routes'; +import { injectWebNavigation } from '@jet-app/app-store/common/web-navigation/inject-web-navigation'; + +import { StaticMessagePage } from '~/jet/models/static-message-page'; + +const { routes, makeCanonicalUrl } = generateRoutes( + (opts) => ({ + ...opts, + $kind: 'CarrierPageIntent', + }), + '/carrier', +); + +export const CarrierPageIntentController: IntentController & + RouteProvider = { + $intentKind: 'CarrierPageIntent', + + routes, + + async perform(intent, objectGraphWithoutActiveIntent: AppStoreObjectGraph) { + return await withActiveIntent( + objectGraphWithoutActiveIntent, + intent, + async (objectGraph) => { + const page = new StaticMessagePage({ + titleLocKey: 'ASE.Web.AppStore.Carrier.Title', + contentType: 'carrier', + }); + + page.canonicalURL = makeCanonicalUrl(objectGraph, intent); + + injectWebNavigation(objectGraph, page, intent.platform); + return page; + }, + ); + }, +}; diff --git a/src/jet/intents/static-message-pages/contingent-price-page-intent-controller.ts b/src/jet/intents/static-message-pages/contingent-price-page-intent-controller.ts new file mode 100644 index 0000000..ba2babd --- /dev/null +++ b/src/jet/intents/static-message-pages/contingent-price-page-intent-controller.ts @@ -0,0 +1,49 @@ +import type { IntentController } from '@jet/environment/dispatching'; +import type { RouteProvider } from '@jet/environment/routing'; +import type { AppStoreObjectGraph } from '@jet-app/app-store/foundation/runtime/app-store-object-graph'; +import { withActiveIntent } from '@jet-app/app-store/foundation/dependencies/active-intent'; +import { generateRoutes } from '@jet-app/app-store/common/util/generate-routes'; +import { injectWebNavigation } from '@jet-app/app-store/common/web-navigation/inject-web-navigation'; + +import { StaticMessagePage } from '~/jet/models/static-message-page'; + +const { routes, makeCanonicalUrl } = generateRoutes( + (opts) => ({ + ...opts, + $kind: 'ContingentPriceIntent', + }), + '/contingent-price/{offerId}', + [], + { + extraRules: [ + { + regex: [/(?:\/[a-z]{2})?\/contingent-price/], + }, + ], + }, +); + +export const ContingentPricingIntentController: IntentController & + RouteProvider = { + $intentKind: 'ContingentPriceIntent', + + routes, + + async perform(intent, objectGraphWithoutActiveIntent: AppStoreObjectGraph) { + return await withActiveIntent( + objectGraphWithoutActiveIntent, + intent, + async (objectGraph) => { + const page = new StaticMessagePage({ + titleLocKey: 'ASE.Web.AppStore.WinBack.Title', + contentType: 'contingent-price', + }); + + page.canonicalURL = makeCanonicalUrl(objectGraph, intent); + + injectWebNavigation(objectGraph, page, intent.platform); + return page; + }, + ); + }, +}; diff --git a/src/jet/intents/static-message-pages/invoice-page-intent-controller.ts b/src/jet/intents/static-message-pages/invoice-page-intent-controller.ts new file mode 100644 index 0000000..caa02f4 --- /dev/null +++ b/src/jet/intents/static-message-pages/invoice-page-intent-controller.ts @@ -0,0 +1,41 @@ +import type { IntentController } from '@jet/environment/dispatching'; +import type { RouteProvider } from '@jet/environment/routing'; +import type { AppStoreObjectGraph } from '@jet-app/app-store/foundation/runtime/app-store-object-graph'; +import { withActiveIntent } from '@jet-app/app-store/foundation/dependencies/active-intent'; +import { generateRoutes } from '@jet-app/app-store/common/util/generate-routes'; +import { injectWebNavigation } from '@jet-app/app-store/common/web-navigation/inject-web-navigation'; + +import { StaticMessagePage } from '~/jet/models/static-message-page'; + +const { routes, makeCanonicalUrl } = generateRoutes( + (opts) => ({ + ...opts, + $kind: 'InvoicePageIntent', + }), + '/invoice', +); + +export const InvoicePageIntentController: IntentController & + RouteProvider = { + $intentKind: 'InvoicePageIntent', + + routes, + + async perform(intent, objectGraphWithoutActiveIntent: AppStoreObjectGraph) { + return await withActiveIntent( + objectGraphWithoutActiveIntent, + intent, + async (objectGraph) => { + const page = new StaticMessagePage({ + titleLocKey: 'ASE.Web.AppStore.Invoice.Title', + contentType: 'invoice', + }); + + page.canonicalURL = makeCanonicalUrl(objectGraph, intent); + + injectWebNavigation(objectGraph, page, intent.platform); + return page; + }, + ); + }, +}; diff --git a/src/jet/intents/static-message-pages/win-back-page-intent-controller.ts b/src/jet/intents/static-message-pages/win-back-page-intent-controller.ts new file mode 100644 index 0000000..2b78ba0 --- /dev/null +++ b/src/jet/intents/static-message-pages/win-back-page-intent-controller.ts @@ -0,0 +1,49 @@ +import type { IntentController } from '@jet/environment/dispatching'; +import type { RouteProvider } from '@jet/environment/routing'; +import type { AppStoreObjectGraph } from '@jet-app/app-store/foundation/runtime/app-store-object-graph'; +import { withActiveIntent } from '@jet-app/app-store/foundation/dependencies/active-intent'; +import { generateRoutes } from '@jet-app/app-store/common/util/generate-routes'; +import { injectWebNavigation } from '@jet-app/app-store/common/web-navigation/inject-web-navigation'; + +import { StaticMessagePage } from '~/jet/models/static-message-page'; + +const { routes, makeCanonicalUrl } = generateRoutes( + (opts) => ({ + ...opts, + $kind: 'WinBackPageIntent', + }), + '/win-back/{offerId}', + [], + { + extraRules: [ + { + regex: [/(?:\/[a-z]{2})?\/win-back/], + }, + ], + }, +); + +export const WinBackPageIntentController: IntentController & + RouteProvider = { + $intentKind: 'WinBackPageIntent', + + routes, + + async perform(intent, objectGraphWithoutActiveIntent: AppStoreObjectGraph) { + return await withActiveIntent( + objectGraphWithoutActiveIntent, + intent, + async (objectGraph) => { + const page = new StaticMessagePage({ + titleLocKey: 'ASE.Web.AppStore.WinBack.Title', + contentType: 'win-back', + }); + + page.canonicalURL = makeCanonicalUrl(objectGraph, intent); + + injectWebNavigation(objectGraph, page, intent.platform); + return page; + }, + ); + }, +}; -- cgit v1.2.3