diff options
Diffstat (limited to 'src/components/structure')
| -rw-r--r-- | src/components/structure/Fonts.svelte | 19 | ||||
| -rw-r--r-- | src/components/structure/Footer.svelte | 47 | ||||
| -rw-r--r-- | src/components/structure/MetaTags.svelte | 68 | ||||
| -rw-r--r-- | src/components/structure/VisionProFooter.svelte | 142 |
4 files changed, 276 insertions, 0 deletions
diff --git a/src/components/structure/Fonts.svelte b/src/components/structure/Fonts.svelte new file mode 100644 index 0000000..63af7b6 --- /dev/null +++ b/src/components/structure/Fonts.svelte @@ -0,0 +1,19 @@ +<script lang="ts"> + import { BASE, getFontURL } from '@amp/web-apps-fonts'; + + export let language: string; + + $: fontURL = getFontURL(language); +</script> + +<svelte:head> + <link rel="preconnect" href={BASE} crossorigin="anonymous" /> + + <link + rel="stylesheet" + as="style" + href={fontURL} + type="text/css" + referrerpolicy="strict-origin-when-cross-origin" + /> +</svelte:head> diff --git a/src/components/structure/Footer.svelte b/src/components/structure/Footer.svelte new file mode 100644 index 0000000..ceabfec --- /dev/null +++ b/src/components/structure/Footer.svelte @@ -0,0 +1,47 @@ +<script lang="ts"> + import { getI18n } from '~/stores/i18n'; + import Footer, { + type Translate, + } from '@amp/web-app-components/src/components/Footer/Footer.svelte'; + import LocaleSwitcherButton from '@amp/web-app-components/src/components/buttons/LocaleSwitcherButton/LocaleSwitcherButton.svelte'; + import { items } from '~/constants/footer-items'; + import { getLocale } from '~/utils/locale'; + import { + regions, + languages, + storefrontNameTranslations, + } from '~/utils/storefront-data'; + + const i18n = getI18n(); + const locale = getLocale(); + + const translate: Translate = (key, options) => $i18n.t(key, options); +</script> + +<section class="footer-container"> + <Footer footerItems={items} translateFn={translate}> + <LocaleSwitcherButton + slot="secondary-content" + translateFn={translate} + {regions} + {languages} + {locale} + {storefrontNameTranslations} + defaultRoute="iphone/today" + /> + </Footer> +</section> + +<style lang="scss"> + @use 'ac-sasskit/modules/viewportcontent/core' as *; + @use 'amp/stylekit/core/viewports' as *; + + .footer-container { + background-color: var(--footerBg); + } + + .footer-container :global(footer) { + max-width: calc(viewport-content-for(xlarge)); + margin: 0 auto; + } +</style> diff --git a/src/components/structure/MetaTags.svelte b/src/components/structure/MetaTags.svelte new file mode 100644 index 0000000..11b9477 --- /dev/null +++ b/src/components/structure/MetaTags.svelte @@ -0,0 +1,68 @@ +<script lang="ts"> + import type { Opt } from '@jet/environment/types/optional'; + import type { Organization, WithContext } from 'schema-dts'; + import type { WebRenderablePage } from '@jet-app/app-store/api/models/web-renderable-page'; + + import MetaTags from '@amp/web-app-components/src/components/MetaTags/MetaTags.svelte'; + import type { SeoData } from '@amp/web-app-components/src/components/MetaTags/types'; + import { getLocale } from '@amp/web-app-components/src/utils/internal/locale'; + import { getPageDir } from '@amp/web-apps-localization/src'; + + import { getI18n } from '~/stores/i18n'; + + export let page: WebRenderablePage; + + const i18n = getI18n(); + const locale = getLocale(); + + const organizationSchema: WithContext<Organization> = { + '@context': 'https://schema.org', + '@id': 'https://apps.apple.com/#organization', + '@type': 'Organization', + name: 'App Store', + url: 'https://apps.apple.com', + logo: 'https://apps.apple.com/assets/app-store.png', + sameAs: [ + 'https://www.wikidata.org/wiki/Q368215', + 'https://twitter.com/AppStore', + 'https://www.instagram.com/appstore/', + 'https://www.facebook.com/appstore/', + ], + parentOrganization: { + '@type': 'Organization', + name: 'Apple', + '@id': 'https://www.apple.com/#organization', + url: 'https://www.apple.com/', + }, + }; + + // This cast of `.seoData` is technically a little risky, but our app fully + // defines this property, which should make it fairly safe. Whatever is returned + // for the page from the `SEO` dependency on the Object Graph will be the value + // reflected here. + $: seoData = (page.seoData as Opt<SeoData>) ?? undefined; + + // Provide default title for pages not yet set up with SEO data + $: defaultTitle = $i18n.t('ASE.Web.AppStore.Meta.SiteName'); + $: pageDir = getPageDir(locale.language) ?? 'ltr'; +</script> + +<MetaTags + {defaultTitle} + {locale} + {pageDir} + {seoData} + origin={'https://apps.apple.com/'} +> + <svelte:fragment slot="schemaOrganizationData"> + {#if import.meta.env.SSR} + <svelte:element + this="script" + id="organization" + type="application/ld+json" + > + {JSON.stringify(organizationSchema)} + </svelte:element> + {/if} + </svelte:fragment> +</MetaTags> diff --git a/src/components/structure/VisionProFooter.svelte b/src/components/structure/VisionProFooter.svelte new file mode 100644 index 0000000..59dcd5b --- /dev/null +++ b/src/components/structure/VisionProFooter.svelte @@ -0,0 +1,142 @@ +<script lang="ts"> + import ShelfTitle from '~/components/Shelf/Title.svelte'; + import ShelfWrapper from '~/components/Shelf/Wrapper.svelte'; + import Grid from '~/components/Grid.svelte'; + import { getI18n } from '~/stores/i18n'; + import { getLocale } from '~/utils/locale'; + + const locale = getLocale(); + const i18n = getI18n(); + + let links: Record<string, string>; + + function getAboutAppStoreUrl(storefront: string, language: string) { + let storefrontSlug = `${storefront}/`; + + if (storefront === 'us') { + storefrontSlug = ''; + } else if (storefront === 'gb') { + // The UK "About App Store" link is https://www.apple.com/uk/app-store/, not https://www.apple.com/gb/app-store/. + storefrontSlug = 'uk/'; + } else if (storefront === 'ae' && language === 'ar') { + storefrontSlug = 'ae-ar/'; + } + + return `https://www.apple.com/${storefrontSlug}app-store/`; + } + + $: storefront = locale.storefront; + $: links = { + 'ASE.Web.AppStore.VisionPro.Footer.Links.AboutAppStore': + getAboutAppStoreUrl(storefront, locale.language), + 'ASE.Web.AppStore.VisionPro.Footer.Links.AboutPurchases': `https://apps.apple.com/${storefront}/story/id1436214772`, + 'ASE.Web.AppStore.VisionPro.Footer.Links.RequestRefund': `https://www.apple.com/${storefront}/shop/goto/help/sales_refunds`, + 'ASE.Web.AppStore.VisionPro.Footer.Links.PaymentMethods': `https://support.apple.com/118429`, + }; + + $: if (storefront === 'fr') { + links[ + 'AppStore.QuickLinks.AboutFrenchAppStore.Title' + ] = `https://apps.apple.com/${storefront}/story/1700848501`; + } +</script> + +<ShelfWrapper centered={false} withBottomPadding={false}> + <section data-test-id="vision-footer"> + <p class="blurb"> + {$i18n.t('ASE.Web.AppStore.VisionPro.Footer.Blurb')} + </p> + + <article class="quick-links-container"> + <ShelfTitle + title={$i18n.t('ASE.Web.AppStore.VisionPro.Footer.LinksTitle')} + /> + + <navigation> + <Grid + items={Object.entries(links)} + gridType="FooterLink" + let:item + > + {@const [title, href] = item} + <a {href}>{$i18n.t(title)}</a> + </Grid> + </navigation> + </article> + + <article class="disclaimer-container"> + <p> + {$i18n.t('ASE.Web.AppStore.VisionPro.Footer.Disclaimer')} + </p> + </article> + </section> +</ShelfWrapper> + +<style lang="scss"> + @use 'ac-sasskit/modules/viewportcontent/core' as *; + @use 'amp/stylekit/core/viewports' as *; + + section { + font: var(--body-tall); + } + + .blurb { + flex-grow: 1; + width: 100%; + max-width: calc(viewport-content-for(xlarge) * 0.66); + margin: 40px auto 50px; + padding: 0 var(--shelfGridPaddingInline, 40px); + text-align: center; + } + + .quick-links-container { + max-width: viewport-content-for(xlarge); + margin: 50px auto; + padding: 0 var(--bodyGutter); + } + + a { + display: block; + padding: var(--grid-column-gap-medium) 0 var(--grid-column-gap-medium); + word-break: break-all; + font: var(--title-2); + color: var(--keyColor); + border-bottom: 1px solid var(--systemQuinary); + + @media (--range-xsmall-down) { + padding: var(--grid-column-gap-xsmall) 0 + var(--grid-column-gap-xsmall); + } + } + + @media (--range-medium-up) { + .quick-links-container li:nth-child(n + 4) a { + border-bottom: none; + } + } + + @media (--small) { + .quick-links-container li:nth-child(n + 5) a { + border-bottom: none; + } + } + + @media (--range-xsmall-down) { + .quick-links-container li:last-child a { + border-bottom: none; + } + } + + .disclaimer-container { + flex-grow: 1; + width: 100%; + color: var(--systemTertiary); + background-color: var(--footerBg); + } + + .disclaimer-container p { + max-width: viewport-content-for(xlarge); + margin: 0 auto; + padding: 32px var(--bodyGutter, 40px); + } +</style> |
