mirror of
https://github.com/ThatGuySam/doesitarm.git
synced 2026-05-18 06:44:46 -07:00
Clean up old Nuxt and Eleventy files
This commit is contained in:
parent
7b0aaeb413
commit
f33d2f4099
33 changed files with 0 additions and 4032 deletions
192
.eleventy.js
192
.eleventy.js
|
|
@ -1,192 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Eleventy command line debugging codes
|
||||
*
|
||||
*
|
||||
* Eleventy:Config
|
||||
* Eleventy:UserConfig
|
||||
* Eleventy:TemplateConfig
|
||||
*
|
||||
* Eleventy:CommandCheck
|
||||
*
|
||||
* Eleventy:EleventyFiles
|
||||
* Eleventy:TemplateWriter
|
||||
* Eleventy:TemplatePassthroughManager
|
||||
* Eleventy:TemplatePassthrough
|
||||
* Eleventy:TemplateData
|
||||
* Eleventy:Template
|
||||
*
|
||||
* Eleventy:ComputedData
|
||||
*
|
||||
* Eleventy:Benchmark
|
||||
*
|
||||
* EleventyAssets
|
||||
*/
|
||||
|
||||
|
||||
import fs from 'fs-extra'
|
||||
import replace_css_url from 'replace-css-url'
|
||||
import dotenv from 'dotenv'
|
||||
import { InlineCodeManager } from '@11ty/eleventy-assets'
|
||||
import { transformSync } from 'esbuild'
|
||||
|
||||
|
||||
import nuxtConfig from './nuxt.config'
|
||||
|
||||
// Setup dotenv
|
||||
dotenv.config()
|
||||
|
||||
|
||||
function getAssetFilePath(componentName) {
|
||||
return `./${componentName}`
|
||||
}
|
||||
|
||||
const inlineAssetCache = new Map()
|
||||
|
||||
const appBundles = new Map()
|
||||
|
||||
module.exports = function ( eleventyConfig ) {
|
||||
// console.log('eleventyConfig', eleventyConfig)
|
||||
|
||||
// Global Nuxt data
|
||||
eleventyConfig.addJavaScriptFunction('getNuxt', function () {
|
||||
return nuxtConfig
|
||||
})
|
||||
// eleventyConfig.addGlobalData('nuxt', () => nuxtConfig)
|
||||
|
||||
const cssManager = new InlineCodeManager()
|
||||
const jsManager = new InlineCodeManager()
|
||||
|
||||
eleventyConfig.addJavaScriptFunction('getAppVersions', async function ( bundleIdentifier ) {
|
||||
if (appBundles.size === 0) {
|
||||
const appBundlesFromJson = await fs.readJson('./static/app-bundles.json')
|
||||
appBundlesFromJson.forEach(([ jsonAppBundleIdentifier, versions ]) => {
|
||||
appBundles.set( jsonAppBundleIdentifier, versions )
|
||||
})
|
||||
}
|
||||
|
||||
if ( appBundles.has( bundleIdentifier ) ) {
|
||||
return appBundles.get( bundleIdentifier )
|
||||
}
|
||||
|
||||
// Bundle not found
|
||||
return null
|
||||
})
|
||||
|
||||
eleventyConfig.addJavaScriptFunction('usingComponent', async function ( componentName ) {
|
||||
// console.log('Getting component', componentName)
|
||||
|
||||
const assetFileName = getAssetFilePath( componentName )
|
||||
|
||||
// console.log('Cache size', inlineAssetCache)
|
||||
|
||||
if ( componentName.includes('.js') ) {
|
||||
|
||||
// If a never before seen component, add the JS code
|
||||
if( !inlineAssetCache.has( assetFileName ) ) {
|
||||
|
||||
let contents = ''
|
||||
|
||||
// if ( inlineAssetCache.has( assetFileName ) ) {
|
||||
// console.log('Reading component from cache', componentName)
|
||||
// console.log('Cache size', inlineAssetCache.size)
|
||||
|
||||
// contents = inlineAssetCache.get( assetFileName )
|
||||
|
||||
// } else {
|
||||
|
||||
// }
|
||||
|
||||
console.log('Reading component file', componentName)
|
||||
const javascriptCode = fs.readFileSync( assetFileName, { encoding: "UTF-8" })
|
||||
|
||||
console.log('Minifying code', componentName)
|
||||
const minified = await transformSync(javascriptCode, {
|
||||
// loader: 'ts',
|
||||
// bundle: true,
|
||||
minify: true,
|
||||
// format: 'iife',
|
||||
})//minify( javascriptCode )
|
||||
|
||||
// console.log('minified', minified)
|
||||
|
||||
contents = minified.code
|
||||
|
||||
inlineAssetCache.set( assetFileName, contents )
|
||||
|
||||
// console.log('Got component', componentName, componentCss)
|
||||
|
||||
jsManager.addComponentCode(componentName, contents)
|
||||
}
|
||||
|
||||
// Log usage for this url
|
||||
// this.page.url is supported on Eleventy 0.11.0 and newer
|
||||
jsManager.addComponentForUrl(componentName, this.page.url)
|
||||
} else if ( componentName.includes('.css') ) {
|
||||
// If a never before seen component, add the CSS code
|
||||
if(!cssManager.hasComponentCode(componentName)) {
|
||||
const fileContents = fs.readFileSync( assetFileName, { encoding: "UTF-8" })
|
||||
|
||||
// Replace urls in css with relative urls for dist folder
|
||||
const parsedCss = replace_css_url(
|
||||
fileContents,
|
||||
function( path ) {
|
||||
const fileName = path.split('/').pop().split('#')[0].split('?')[0]
|
||||
|
||||
return '/fonts/' + fileName
|
||||
}
|
||||
)
|
||||
|
||||
cssManager.addComponentCode(componentName, parsedCss)
|
||||
}
|
||||
|
||||
// Log usage for this url
|
||||
// this.page.url is supported on Eleventy 0.11.0 and newer
|
||||
cssManager.addComponentForUrl(componentName, this.page.url)
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
|
||||
// Copy Inter font files
|
||||
eleventyConfig.addPassthroughCopy({
|
||||
"node_modules/@fontsource/inter/**/*.woff2": "fonts"
|
||||
})
|
||||
|
||||
// This needs to be called in a Layout template.
|
||||
eleventyConfig.addJavaScriptFunction('getJs', function ( url = this.page.url ) {
|
||||
// console.log( 'jsManager.getCodeForUrl(url)', url )
|
||||
|
||||
return jsManager.getCodeForUrl(url)
|
||||
})
|
||||
|
||||
// This needs to be called in a Layout template.
|
||||
eleventyConfig.addJavaScriptFunction('getCss', function ( url = this.page.url ) {
|
||||
// console.log( 'jsManager.getCodeForUrl(url)', url )
|
||||
|
||||
return cssManager.getCodeForUrl(url)
|
||||
})
|
||||
|
||||
eleventyConfig.addJavaScriptFunction('boundComponent', function ( Component ) {
|
||||
return Component.bind(this)
|
||||
})
|
||||
|
||||
|
||||
|
||||
return {
|
||||
// https://www.11ty.dev/docs/config/#template-formats
|
||||
// Default: html,liquid,ejs,md,hbs,mustache,haml,pug,njk,11ty.js
|
||||
templateFormats: [ '11ty.js' ],
|
||||
|
||||
dir: {
|
||||
input: 'pages-eleventy',
|
||||
output: 'dist',
|
||||
jsDataFileSuffix: '.json',
|
||||
|
||||
// Relative to input directory.
|
||||
data: '../static',
|
||||
layouts: '../layouts-eleventy'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
import renderPoster from './poster.js'
|
||||
|
||||
function pill ( text ) {
|
||||
return /* html */`
|
||||
<div
|
||||
class="video-pill h-5 text-xs bg-white-2 flex justify-center items-center outline-0 rounded-full ease px-2"
|
||||
>
|
||||
${ text }
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
export default async function ( video, options = {} ) {
|
||||
const {
|
||||
width = '325px',
|
||||
classes = 'w-full flex-shrink-0 flex-grow-0 border-2 border-transparent rounded-2xl overflow-hidden'
|
||||
} = options
|
||||
|
||||
// Setup inline lazysizes
|
||||
// await this.usingComponent( 'node_modules/lazysizes/lazysizes.min.js' )
|
||||
|
||||
// console.log('video', video)
|
||||
|
||||
const posterHtml = renderPoster( video )
|
||||
|
||||
return /* html */`
|
||||
<div class="video-card ${ classes }" style="max-width: ${ width }; flex-basis: ${ width }; scroll-snap-align: start;">
|
||||
<a
|
||||
href="${video.endpoint}"
|
||||
class=""
|
||||
>
|
||||
<div class="video-card-container relative overflow-hidden bg-black">
|
||||
<div class="video-card-image ratio-wrapper">
|
||||
<div class="relative overflow-hidden w-full pb-16/9">
|
||||
${ posterHtml }
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style="--gradient-from-color: rgba(0, 0, 0, 1); --gradient-to-color: rgba(0, 0, 0, 0.7)"
|
||||
class="video-card-overlay absolute inset-0 flex justify-between items-start bg-gradient-to-tr from-black to-transparent p-4"
|
||||
>
|
||||
<div class="play-circle w-8 h-8 bg-white-2 flex justify-center items-center outline-0 rounded-full ease">
|
||||
<svg
|
||||
viewBox="0 0 18 18"
|
||||
style="width:18px;height:18px;margin-left:3px"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M15.562 8.1L3.87.225c-.818-.562-1.87 0-1.87.9v15.75c0 .9 1.052 1.462 1.87.9L15.563 9.9c.584-.45.584-1.35 0-1.8z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
${ (video.tags.includes('benchmark')) ? pill('Benchmark') : '' }
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Video Text Content -->
|
||||
<div class="video-card-content absolute inset-0 flex items-end py-4 px-6">
|
||||
<div class="w-full text-sm text-left whitespace-normal">${ video.name }</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
import renderPoster from './poster.js'
|
||||
|
||||
function renderTimestamps ( video ) {
|
||||
|
||||
if ( video.timestamps.length === 0 ) return ''
|
||||
|
||||
const timestampsForRender = video.timestamps.map( timestamp => {
|
||||
const [ minutes, seconds ] = timestamp.time.split(':')
|
||||
|
||||
return {
|
||||
...timestamp,
|
||||
inSeconds: (minutes * 60) + Number(seconds)
|
||||
}
|
||||
})
|
||||
|
||||
const timestampButtonsHtml = timestampsForRender.map( timestamp => (/* html */`
|
||||
<button
|
||||
time="${timestamp.time}"
|
||||
aria-label="Jump to ${timestamp.fullText}"
|
||||
class="inline-block text-xs rounded-lg py-1 px-2 border-2 border-white focus:outline-none border-opacity-0 neumorphic-shadow-inner">
|
||||
${ timestamp.fullText }
|
||||
</button>
|
||||
`) ).join('')
|
||||
|
||||
return /* html */`
|
||||
<div class="player-timestamps w-full max-w-4xl">
|
||||
<div class="player-timestamps-wrapper overflow-x-auto overflow-y-visible whitespace-no-wrap py-2 space-x-2">
|
||||
${ timestampButtonsHtml }
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
export default async function ( video, options = {} ) {
|
||||
const {
|
||||
coverBottomHtml = ''
|
||||
// classes = 'w-full flex-shrink-0 flex-grow-0 border-2 border-transparent rounded-2xl overflow-hidden'
|
||||
} = options
|
||||
|
||||
// Setup inline player script
|
||||
await this.usingComponent( 'node_modules/can-autoplay/build/can-autoplay.min.js' )
|
||||
|
||||
await this.usingComponent( 'helpers/lite-youtube.js' )
|
||||
|
||||
// Setup inline lazysizes
|
||||
await this.usingComponent( 'node_modules/lazysizes/lazysizes.min.js' )
|
||||
|
||||
// console.log('video', video)
|
||||
|
||||
const posterHtml = renderPoster( video )
|
||||
|
||||
const timestampsHtml = renderTimestamps( video )
|
||||
|
||||
return /* html */`
|
||||
<lite-youtube
|
||||
class="video-canvas w-screen flex flex-col justify-center items-center bg-black pt-16"
|
||||
style="left:50%;right:50%;margin-left:-50vw;margin-right:-50vw;"
|
||||
>
|
||||
<script class="video-data" type="application/json">
|
||||
${ JSON.stringify( video ) }
|
||||
</script>
|
||||
|
||||
<div class="ratio-wrapper w-full max-w-4xl">
|
||||
<div class="player-container relative overflow-hidden w-full pb-16/9">
|
||||
<div class="player-poster cursor-pointer">
|
||||
|
||||
${ posterHtml }
|
||||
|
||||
<div class="video-card-overlay absolute inset-0 flex flex-col justify-center items-center bg-gradient-to-tr from-black to-transparent p-4" style="--gradient-from-color:rgba(0, 0, 0, 1); --gradient-to-color:rgba(0, 0, 0, 0.7);">
|
||||
<div class="cover-top h-full"></div>
|
||||
<div class="play-circle bg-white-2 bg-blur flex justify-center items-center outline-0 rounded-full ease p-4">
|
||||
<svg viewBox="0 0 18 18" style="width:18px;height:18px;margin-left:3px">
|
||||
<path fill="currentColor" d="M15.562 8.1L3.87.225c-.818-.562-1.87 0-1.87.9v15.75c0 .9 1.052 1.462 1.87.9L15.563 9.9c.584-.45.584-1.35 0-1.8z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="cover-bottom h-full">${ coverBottomHtml }</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${ timestampsHtml }
|
||||
|
||||
</lite-youtube>
|
||||
`
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
|
||||
|
||||
export default function ( video ) {
|
||||
const webpSource = {
|
||||
...video.thumbnail,
|
||||
srcset: video.thumbnail.srcset.replaceAll('ytimg.com/vi/', 'ytimg.com/vi_webp/').replace(/.png|.jpg|.jpeg/g, '.webp')
|
||||
}
|
||||
|
||||
const mergedSources = {
|
||||
webp: webpSource,
|
||||
jpeg: video.thumbnail
|
||||
}
|
||||
|
||||
return /* html */`
|
||||
<picture>
|
||||
|
||||
${ Object.entries( mergedSources ).map( ([ key, source ]) => (/* html */`
|
||||
<source
|
||||
sizes="${ source.sizes }"
|
||||
data-srcset="${ source.srcset }"
|
||||
type="image/${ key }"
|
||||
>
|
||||
`) ).join('') }
|
||||
|
||||
<img
|
||||
data-src="${ video.thumbnail.src }"
|
||||
alt="${ video.name }"
|
||||
class="absolute inset-0 h-full w-full object-cover lazyload"
|
||||
>
|
||||
</picture>
|
||||
`
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
|
||||
import VideoCard from './card.js'
|
||||
import SubmitCard from './submit-card.js'
|
||||
|
||||
function getCardType ( video ) {
|
||||
const isSubmitCard = video.endpoint.includes('https://docs.google.com/forms')
|
||||
|
||||
if ( isSubmitCard ) return SubmitCard
|
||||
|
||||
return VideoCard
|
||||
}
|
||||
|
||||
export default async function ( videos, options = {} ) {
|
||||
|
||||
const {
|
||||
cardWidth = '325',
|
||||
classes = ''
|
||||
} = options
|
||||
|
||||
// Math.random should be unique because of its seeding algorithm.
|
||||
// Convert it to base 36 (numbers + letters), and grab the first 9 characters
|
||||
// after the decimal.
|
||||
const uid = Math.random().toString(36).substr(2, 9)
|
||||
const rowId = `row-${ uid }`
|
||||
|
||||
// Setup inline scroll script
|
||||
await this.usingComponent( 'helpers/scroll.js' )
|
||||
|
||||
// Setup inline lazysizes
|
||||
await this.usingComponent( 'node_modules/lazysizes/lazysizes.min.js' )
|
||||
|
||||
// console.log('video', video)
|
||||
|
||||
const renderedCards = await Promise.all(videos.map( async video => {
|
||||
const Card = getCardType( video )
|
||||
|
||||
// console.log('Card', this.boundComponent(Card)( video ) )
|
||||
|
||||
return await this.boundComponent(Card)( video )
|
||||
} ))
|
||||
|
||||
const cardsHtml = renderedCards.join('')
|
||||
|
||||
// console.log( 'cardsHtml', cardsHtml )
|
||||
|
||||
return /* html */`
|
||||
|
||||
<div class="video-row relative w-full ${ classes }">
|
||||
|
||||
<div
|
||||
id="${ rowId }"
|
||||
class="video-row-contents flex overflow-x-auto whitespace-no-wrap py-2 space-x-6"
|
||||
style="scroll-snap-type:x mandatory;"
|
||||
>
|
||||
|
||||
${ cardsHtml }
|
||||
|
||||
</div>
|
||||
<button
|
||||
class="absolute left-0 h-10 w-10 flex justify-center items-center transform -translate-y-1/2 -translate-x-1/2 bg-darker rounded-full"
|
||||
style="top:50%;"
|
||||
distance="${ cardWidth * -1 }"
|
||||
scroll-target="#${ rowId }"
|
||||
onclick="scrollHorizontalCarousel( event )"
|
||||
aria-label="Scroll to previous videos"
|
||||
>
|
||||
<svg viewBox="0 0 20 20" fill="currentColor" class="h-5 w-5 text-gray-400" style="transform: scaleX(-1);">
|
||||
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class="absolute right-0 h-10 w-10 flex justify-center items-center transform -translate-y-1/2 translate-x-1/2 bg-darker rounded-full"
|
||||
style="top:50%;"
|
||||
distance="${ cardWidth }"
|
||||
scroll-target="#${ rowId }"
|
||||
onclick="scrollHorizontalCarousel( event )"
|
||||
aria-label="Scroll to next videos"
|
||||
>
|
||||
<svg viewBox="0 0 20 20" fill="currentColor" class="h-5 w-5 text-gray-400">
|
||||
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
`
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
|
||||
export default function ( video, {
|
||||
width = '325px',
|
||||
classes = 'w-full flex-shrink-0 flex-grow-0 border-2 border-transparent rounded-2xl overflow-hidden'
|
||||
} ) {
|
||||
|
||||
// Setup inline lazysizes
|
||||
// this.usingComponent( 'node_modules/lazysizes/lazysizes.min.js' )
|
||||
|
||||
// console.log('video', video)
|
||||
|
||||
return /* html */`
|
||||
<div class="video-card ${ classes }" style="max-width: ${ width }; flex-basis: ${ width }; scroll-snap-align: start;">
|
||||
<a href="${ video.endpoint }">
|
||||
<div class="video-card-container relative overflow-hidden bg-white">
|
||||
<div class="video-card-image ratio-wrapper">
|
||||
<div class="relative overflow-hidden w-full pb-16/9"></div>
|
||||
</div>
|
||||
<div class="video-card-overlay absolute inset-0 flex justify-between items-start bg-gradient-to-tr from-black to-transparent p-4" style="--gradient-from-color:rgba(0, 0, 0, 1); --gradient-to-color:rgba(0, 0, 0, 0.7);">
|
||||
<div class="plus-circle w-8 h-8 bg-white-2 flex justify-center items-center outline-0 rounded-full ease">
|
||||
<svg viewBox="0 0 24 24" style="width: 18px; height: 18px;">
|
||||
<path fill="currentColor" d="M11 11v-11h1v11h11v1h-11v11h-1v-11h-11v-1h11z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="video-card-content absolute inset-0 flex items-end py-4 px-6">
|
||||
<div class="w-full text-sm text-left whitespace-normal">Submit Video</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="video-card ${ classes }" style="max-width: ${ width }; flex-basis: ${ width }; scroll-snap-align: start;">
|
||||
<a
|
||||
href="${video.endpoint}"
|
||||
class=""
|
||||
>
|
||||
<div class="video-card-container relative overflow-hidden bg-black">
|
||||
<div class="video-card-image ratio-wrapper">
|
||||
<div class="relative overflow-hidden w-full pb-16/9">
|
||||
<picture>
|
||||
<source
|
||||
sizes="${video.thumbnail.sizes}"
|
||||
data-srcset="${video.thumbnail.srcset}"
|
||||
type="image/jpg"
|
||||
>
|
||||
<img
|
||||
data-src="${video.thumbnail.src}"
|
||||
alt="${video.name}"
|
||||
class="lazyload absolute h-full w-full object-cover"
|
||||
>
|
||||
</picture>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style="--gradient-from-color: rgba(0, 0, 0, 1); --gradient-to-color: rgba(0, 0, 0, 0.7)"
|
||||
class="video-card-overlay absolute inset-0 flex justify-between items-start bg-gradient-to-tr from-black to-transparent p-4"
|
||||
>
|
||||
<div class="play-circle w-8 h-8 bg-white-2 flex justify-center items-center outline-0 rounded-full ease">
|
||||
<svg
|
||||
viewBox="0 0 18 18"
|
||||
style="width:18px;height:18px;margin-left:3px"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M15.562 8.1L3.87.225c-.818-.562-1.87 0-1.87.9v15.75c0 .9 1.052 1.462 1.87.9L15.563 9.9c.584-.45.584-1.35 0-1.8z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Video Text Content -->
|
||||
<div class="video-card-content absolute inset-0 flex items-end py-4 px-6">
|
||||
<div class="w-full text-sm text-left whitespace-normal">${ video.name }</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
|
@ -1,261 +0,0 @@
|
|||
import fs from 'fs'
|
||||
import { JSDOM } from 'jsdom'
|
||||
|
||||
import config from '../nuxt.config.js'
|
||||
|
||||
|
||||
console.log('Running Default Layout file')
|
||||
|
||||
|
||||
const year = new Date().getFullYear()
|
||||
|
||||
const makeTag = ( tag, tagName = 'meta') => {
|
||||
|
||||
const attributes = Object.entries(tag).map( ([ name, value ]) => `${name}="${value}"` ).join(' ')
|
||||
|
||||
return `<${tagName} ${attributes}>`
|
||||
}
|
||||
|
||||
const mapMetaTag = ( tag ) => {
|
||||
|
||||
if ( tag.hasOwnProperty('property') ) {
|
||||
return [
|
||||
`property-${tag.property}`,
|
||||
makeTag(tag)
|
||||
]
|
||||
}
|
||||
|
||||
if ( tag.hasOwnProperty('name') ) {
|
||||
return [
|
||||
`name-${tag.name}`,
|
||||
makeTag(tag)
|
||||
]
|
||||
}
|
||||
|
||||
if ( tag.hasOwnProperty('charset') ) {
|
||||
return [
|
||||
'charset',
|
||||
makeTag(tag)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const mapLinkTag = ( tag ) => {
|
||||
return [
|
||||
`type-${tag.type}`,
|
||||
makeTag(tag, 'link')
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
const getNuxtDefaultLayoutHtml = () => {
|
||||
const fileContents = fs.readFileSync('./dist/layout-template/index.html', { encoding: "UTF-8" })
|
||||
|
||||
return fileContents
|
||||
}
|
||||
|
||||
const templateVar = string => `--- template-var ${string} ---`
|
||||
|
||||
const cleanNuxtLayout = ( layout ) => {
|
||||
|
||||
const document = layout.window.document
|
||||
|
||||
// Strip out existing meta
|
||||
Array.from(document.querySelectorAll('head > meta')).forEach( domNode => {
|
||||
domNode.remove()
|
||||
})
|
||||
|
||||
// Strip out existing preloads
|
||||
Array.from(document.querySelectorAll('link[rel="preload"]')).forEach( domNode => {
|
||||
domNode.remove()
|
||||
})
|
||||
|
||||
// Strip out existing scripts
|
||||
const scriptSelector = 'script[src*=".js"]:not(.include-on-static)'
|
||||
|
||||
Array.from(document.querySelectorAll( scriptSelector )).forEach( domNode => {
|
||||
domNode.remove()
|
||||
})
|
||||
|
||||
// Convert subscribe to iframe embed
|
||||
Array.from(document.querySelectorAll('form.all-updates-subscribe')).forEach( domNode => {
|
||||
const subscribeEmbed = document.createElement('iframe')
|
||||
subscribeEmbed.setAttribute('data-src', '/embed-subscribe')
|
||||
// https://web.dev/iframe-lazy-loading/
|
||||
subscribeEmbed.setAttribute('loading', 'lazy')
|
||||
subscribeEmbed.setAttribute('title', 'Get email updates')
|
||||
subscribeEmbed.classList.add('lazyload')
|
||||
subscribeEmbed.style.width = '350px'
|
||||
subscribeEmbed.style.height = '150px'
|
||||
|
||||
domNode.parentNode.replaceChild(subscribeEmbed, domNode)
|
||||
})
|
||||
|
||||
|
||||
// Set page title
|
||||
document.title = templateVar('title')
|
||||
|
||||
// Set link tags
|
||||
document.querySelector('title').insertAdjacentHTML('afterend', templateVar('link-tags') )
|
||||
|
||||
// Add meta tags after title node
|
||||
document.querySelector('title').insertAdjacentHTML('afterend', templateVar('structured-data') )
|
||||
|
||||
// Add meta tags after title node
|
||||
document.querySelector('title').insertAdjacentHTML('afterend', templateVar('meta-tags') )
|
||||
|
||||
// Set page css
|
||||
// document.querySelector('head').insertAdjacentHTML('beforeend', this.getCss() )
|
||||
|
||||
// Set page content
|
||||
document.querySelector('.app-main').innerHTML = templateVar('main-content')//content
|
||||
|
||||
// Set js before end of body
|
||||
// `<script>${ this.getJs() }</script>`
|
||||
document.querySelector('body').insertAdjacentHTML('beforeend', templateVar('scripts') )
|
||||
|
||||
return layout
|
||||
}
|
||||
|
||||
let nuxtLayoutHtml = null
|
||||
const parseDefaultLayoutDom = () => {
|
||||
if ( nuxtLayoutHtml === null ) {
|
||||
nuxtLayoutHtml = getNuxtDefaultLayoutHtml()
|
||||
}
|
||||
// const html = getNuxtDefaultLayoutHtml()
|
||||
|
||||
// Build initial dom from the Layout
|
||||
const dom = new JSDOM( nuxtLayoutHtml )
|
||||
|
||||
const cleanedLayout = cleanNuxtLayout( dom )
|
||||
|
||||
return cleanedLayout
|
||||
}
|
||||
|
||||
// Buld data that only needs to run once
|
||||
const defaultMeta = Object.fromEntries(config.head.meta.map( mapMetaTag ))
|
||||
|
||||
const defaultLinkTags = Object.fromEntries(config.head.link.map( mapLinkTag ))
|
||||
|
||||
const masterLayoutDomString = parseDefaultLayoutDom().serialize()
|
||||
|
||||
class DefaultLayout {
|
||||
|
||||
generateMetaTags = function ( renderData ) {
|
||||
|
||||
const {
|
||||
title = null,
|
||||
description = null,
|
||||
meta: pageMeta = []
|
||||
} = renderData
|
||||
|
||||
// console.log('renderData', Object.keys(renderData))
|
||||
|
||||
const meta = {
|
||||
...defaultMeta,
|
||||
'property-twitter:url': `<meta property="twitter:url" content="${process.env.URL}${this.page.url}">`,
|
||||
...Object.fromEntries( pageMeta.map(mapMetaTag) )
|
||||
}
|
||||
|
||||
// console.log('renderData.description', renderData.description)
|
||||
|
||||
// if set
|
||||
// get description from data
|
||||
if ( description ) {
|
||||
// Set meta description
|
||||
meta['name-description'] = `<meta hid="description" name="description" content="${ description }">`
|
||||
// Set twitter description
|
||||
meta['property-twitter:description'] = `<meta hid="twitter:description" property="twitter:description" content="${ description }">`
|
||||
}
|
||||
|
||||
// if set
|
||||
// get title from data
|
||||
if ( title ) {
|
||||
// Set twitter title
|
||||
meta['property-twitter:title'] = `<meta hid="twitter:title" property="twitter:title" content="${ title }">`
|
||||
}
|
||||
|
||||
|
||||
return Object.values(meta).join('')
|
||||
}
|
||||
|
||||
generateStructuredData = function ( renderData ) {
|
||||
|
||||
const {
|
||||
structuredData = null
|
||||
} = renderData
|
||||
|
||||
// console.log('renderData', Object.keys(renderData))
|
||||
|
||||
if ( structuredData === null ) return ''
|
||||
|
||||
const structuredDataJson = JSON.stringify( structuredData )
|
||||
|
||||
return `<script type="application/ld+json">${ structuredDataJson }</script>`
|
||||
}
|
||||
|
||||
generateLinkTags = ( renderData ) => {
|
||||
|
||||
const {
|
||||
headLinkTags = []
|
||||
} = renderData
|
||||
|
||||
const linkTags = {
|
||||
...defaultLinkTags,
|
||||
...Object.fromEntries( headLinkTags.map( mapLinkTag ) )
|
||||
}
|
||||
|
||||
return Object.values( linkTags ).join('')
|
||||
}
|
||||
|
||||
|
||||
async render( data ) {
|
||||
|
||||
// return nuxtLayoutHtml
|
||||
|
||||
// console.log('Running DefaultLayout render')
|
||||
|
||||
const {
|
||||
content,
|
||||
title = null,
|
||||
// description = null
|
||||
} = data
|
||||
|
||||
const pageTitle = title || this.getNuxt().head.title
|
||||
|
||||
let workingLayoutString = masterLayoutDomString
|
||||
|
||||
// Set page title
|
||||
// pageTitle
|
||||
workingLayoutString = workingLayoutString.replace( templateVar('title'), pageTitle )
|
||||
|
||||
// Set link tags
|
||||
// this.generateLinkTags()
|
||||
workingLayoutString = workingLayoutString.replace( templateVar('link-tags'), this.generateLinkTags( data ) )
|
||||
|
||||
// Add meta tags after title node
|
||||
// this.generateMetaTags( data )
|
||||
workingLayoutString = workingLayoutString.replace( templateVar('meta-tags'), this.generateMetaTags( data ) )
|
||||
|
||||
// Add structured data
|
||||
workingLayoutString = workingLayoutString.replace( templateVar('structured-data'), this.generateStructuredData( data ) )
|
||||
|
||||
// Set page css
|
||||
// document.querySelector('head').insertAdjacentHTML('beforeend', this.getCss() )
|
||||
|
||||
// Set page content
|
||||
// content
|
||||
workingLayoutString = workingLayoutString.replace( templateVar('main-content'), content )
|
||||
|
||||
// Set js before end of body
|
||||
// `<script>${ this.getJs() }</script>`
|
||||
workingLayoutString = workingLayoutString.replace( templateVar('scripts'), `<script>${ this.getJs() }</script>` )
|
||||
|
||||
|
||||
// Return stringified html for page
|
||||
return workingLayoutString
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DefaultLayout
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# MIDDLEWARE
|
||||
|
||||
**This directory is not required, you can delete it if you don't want to use it.**
|
||||
|
||||
This directory contains your application middleware.
|
||||
The middleware lets you define custom function to be ran before rendering a page or a group of pages (layouts).
|
||||
|
||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware).
|
||||
138
nuxt.config.js
138
nuxt.config.js
|
|
@ -1,138 +0,0 @@
|
|||
import { promises as fs } from 'fs'
|
||||
|
||||
import pkg from './package.json'
|
||||
import { getSiteUrl } from '~/helpers/url.js'
|
||||
import { publicRuntimeConfig } from '~/helpers/public-runtime-config.mjs'
|
||||
|
||||
|
||||
|
||||
const siteUrl = getSiteUrl()
|
||||
|
||||
|
||||
export default {
|
||||
target: 'static',
|
||||
|
||||
publicRuntimeConfig,
|
||||
|
||||
/*
|
||||
** Hooks
|
||||
* https://nuxtjs.org/api/configuration-hooks/
|
||||
*/
|
||||
hooks: {
|
||||
// build: {
|
||||
// before: storeAppLists
|
||||
// },
|
||||
// generate: {
|
||||
// before: storeAppLists
|
||||
// }
|
||||
},
|
||||
|
||||
generate: {
|
||||
crawler: false,
|
||||
|
||||
// https://nuxtjs.org/docs/2.x/configuration-glossary/configuration-generate#cache
|
||||
cache: false,
|
||||
routes() {
|
||||
return fs.readFile('./static/nuxt-endpoints.json', 'utf-8')
|
||||
.then( endpointsJson => {
|
||||
return JSON.parse(endpointsJson)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
** Headers of the page
|
||||
*/
|
||||
head: ,
|
||||
|
||||
/*
|
||||
** Customize the progress-bar color
|
||||
*/
|
||||
loading: { color: '#fff' },
|
||||
|
||||
/*
|
||||
** Global CSS
|
||||
*/
|
||||
// css: ['~/assets/css/tailwind.css'],
|
||||
|
||||
/*
|
||||
** Plugins to load before mounting the App
|
||||
*/
|
||||
plugins: [
|
||||
'@/plugins/gtag'
|
||||
],
|
||||
|
||||
/*
|
||||
** Nuxt.js modules
|
||||
*/
|
||||
|
||||
modules: [
|
||||
'@nuxtjs/sitemap'
|
||||
],
|
||||
|
||||
sitemap: {
|
||||
hostname: 'https://doesitarm.com',
|
||||
routes: async () => {
|
||||
// Get routes from previous build
|
||||
const sitemapEndpoints = await fs.readFile('./static/sitemap-endpoints.json', 'utf-8')
|
||||
.then( endpointsJson => {
|
||||
return JSON.parse(endpointsJson)
|
||||
})
|
||||
|
||||
// console.log('Total Sitemap Endpoints', sitemapEndpoints.length)
|
||||
|
||||
return sitemapEndpoints.map( endpoint => endpoint.route )
|
||||
}
|
||||
},
|
||||
|
||||
buildModules: [
|
||||
'@nuxt/postcss8',
|
||||
'@nuxtjs/tailwindcss'
|
||||
],
|
||||
|
||||
/*
|
||||
** Build configuration
|
||||
*/
|
||||
build: {
|
||||
// parallel: true,
|
||||
// hardSource: true,
|
||||
cache: false,
|
||||
html: {
|
||||
minify: {
|
||||
minifyCSS: false,
|
||||
minifyJS: false,
|
||||
collapseWhitespace: true
|
||||
}
|
||||
},
|
||||
/*
|
||||
** You can extend webpack config here
|
||||
*/
|
||||
extend(config, ctx) {
|
||||
|
||||
// Client
|
||||
if (ctx.isClient) {
|
||||
// Push meta import rule for zip.js
|
||||
config.module.rules.push({
|
||||
test: /\.js$/,
|
||||
loader: require.resolve('@open-wc/webpack-import-meta-loader')
|
||||
})
|
||||
}
|
||||
|
||||
// Run ESLint on save
|
||||
if (ctx.isDev && ctx.isClient) {
|
||||
|
||||
config.module.rules.push({
|
||||
enforce: 'pre',
|
||||
test: /\.(js|vue)$/,
|
||||
loader: 'eslint-loader',
|
||||
exclude: /(node_modules)/
|
||||
})
|
||||
|
||||
config.node = {
|
||||
fs: "empty"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,357 +0,0 @@
|
|||
import dotenv from 'dotenv'
|
||||
|
||||
import config from '../nuxt.config.js'
|
||||
|
||||
import { getAppType, getRouteType } from '../helpers/app-derived.js'
|
||||
import { deviceSupportsApp } from '../helpers/devices.js'
|
||||
import { makeLastUpdatedFriendly } from '../helpers/parse-date.js'
|
||||
import { getStatusOfScan } from '../helpers/statuses.js'
|
||||
import { isString } from '../helpers/check-types.js'
|
||||
|
||||
|
||||
import VideoRow from '../components-eleventy/video/row.js'
|
||||
|
||||
// import VideoRow from '../components-eleventy/video/row.js'
|
||||
// import { isVideo } from '../helpers/app-derived'
|
||||
|
||||
// Setup dotenv
|
||||
dotenv.config()
|
||||
|
||||
export const makeTitle = function ( app ) {
|
||||
return `Does ${app.name} work on Apple Silicon? - ${ config.head.title }`
|
||||
}
|
||||
|
||||
export const makeDescription = function ( app ) {
|
||||
return `Latest reported support status of ${ app.name } on Apple Silicon and ${ process.env.npm_package_config_verbiage_processors } Processors.`
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/15069646/1397641
|
||||
function makeEnglishList ( array, conjunction = 'and' ) {
|
||||
const total = array.length
|
||||
|
||||
if ( total < 3 ) return array.join(` ${conjunction} `)
|
||||
|
||||
array = array.slice()
|
||||
|
||||
// Prepend conjunction to final part
|
||||
array[ total-1 ] = `${ conjunction } ${ array[ total-1 ] }`
|
||||
|
||||
return array.join(', ')
|
||||
}
|
||||
|
||||
export function renderPageLinksHtml ( links ) {
|
||||
return links.map( (link, i) => {
|
||||
|
||||
const notAppTestLink = !link.label.includes('🧪')
|
||||
|
||||
const isMainLink = (i === 0) && notAppTestLink
|
||||
|
||||
return /* html */`
|
||||
<a
|
||||
class="relative inline-flex items-center rounded-md px-4 py-2 leading-5 font-bold text-white border border-transparent focus:outline-none focus:border-indigo-600 neumorphic-shadow focus:shadow-outline-indigo bg-darker hover:bg-indigo-400 active:bg-indigo-600 transition duration-150 ease-in-out"
|
||||
href="${link.href}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
role="button"
|
||||
>${ isMainLink ? 'View' : link.label }</a>
|
||||
`
|
||||
} ).join('')
|
||||
}
|
||||
|
||||
export function supportedArchitectures ( appScan ) {
|
||||
// if ( Array.isArray(appScan['Macho Meta']) ) {
|
||||
// return appScan['Macho Meta'].map( architecture => architecture.processorType)
|
||||
// }
|
||||
|
||||
// console.log('meta', appScan['Macho Meta'])
|
||||
|
||||
if ( appScan['Macho Meta'].architectures === undefined ) return []
|
||||
|
||||
return appScan['Macho Meta'].architectures
|
||||
.map( architecture => architecture.processorType)
|
||||
.filter(processorType => Number(processorType) !== 0)
|
||||
}
|
||||
|
||||
function renderBundleDataLevel ( bundleLevelData, depth = 0 ) {
|
||||
|
||||
const levelContainerClassses = 'border rounded-lg bg-black bg-opacity-10 space-y-4 p-4 mt-4'
|
||||
const maxDepth = 2
|
||||
|
||||
if ( isString(bundleLevelData) ) {
|
||||
return bundleLevelData
|
||||
}
|
||||
|
||||
if ( depth >= maxDepth ) {
|
||||
return /* html */`<pre class="border-dashed ${ levelContainerClassses }">${ JSON.stringify(bundleLevelData, undefined, 2) }</pre>`
|
||||
}
|
||||
|
||||
if ( Array.isArray( bundleLevelData ) ) {
|
||||
const htmlList = bundleLevelData.map( ( bundleLevel ) => {
|
||||
return /* html */`<li>${ renderBundleDataLevel( bundleLevel, depth + 1 ) }</li>`
|
||||
} ).join('')
|
||||
|
||||
return /* html */`<ul class="${ levelContainerClassses }">${ htmlList }</ul>`
|
||||
}
|
||||
|
||||
if ( Object(bundleLevelData) === bundleLevelData ) {
|
||||
const htmlList = Object.entries(bundleLevelData).map( ( [key, bundleLevel] ) => {
|
||||
return /* html */`<li>${key} : ${ renderBundleDataLevel( bundleLevel, depth + 1 ) }</li>`
|
||||
} ).join('')
|
||||
|
||||
return /* html */`
|
||||
<details>
|
||||
<summary>Details</summary>
|
||||
<ul class="${ levelContainerClassses }">${ htmlList }</ul>
|
||||
</details>
|
||||
`
|
||||
}
|
||||
|
||||
return /* html */`${ JSON.stringify(bundleLevelData) }`
|
||||
}
|
||||
|
||||
export class AppTemplate {
|
||||
// or `async data() {`
|
||||
// or `get data() {`
|
||||
data () {
|
||||
return {
|
||||
layout: 'default.11ty.js',
|
||||
|
||||
pagination: {
|
||||
data: 'eleventy-endpoints',
|
||||
size: 1,
|
||||
alias: 'app',
|
||||
|
||||
before: function( data ) {
|
||||
return data.filter( entry => {
|
||||
// const [ _, routeType ] = entry.route.split('/')
|
||||
const routeType = getRouteType( entry.route )
|
||||
|
||||
return routeType === 'app'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
eleventyComputed: {
|
||||
title: ({ app: { payload: { app } } }) => {
|
||||
// console.log('data', data)
|
||||
return makeTitle( app )
|
||||
},
|
||||
description: ({ app: { payload: { app } } }) => {
|
||||
return makeDescription( app )
|
||||
},
|
||||
mainHeading: ({ app: { payload: { app } } }) => {
|
||||
return `Does ${ app.name } work on Apple Silicon?`
|
||||
},
|
||||
},
|
||||
|
||||
permalink: ({ app }) => {
|
||||
// console.log('payload', app.payload)
|
||||
return app.route.substring(1) + '/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async render( data ) {
|
||||
|
||||
const {
|
||||
app: { payload: { app, relatedVideos = [] } },
|
||||
'device-list': deviceList,
|
||||
// 'app-bundles': appBundlesFromFile
|
||||
} = data
|
||||
|
||||
|
||||
// console.log('appBundlesFromFile', appBundlesFromFile)
|
||||
|
||||
const hasRelatedVideos = relatedVideos.length > 0
|
||||
|
||||
// console.log('deviceList', deviceList)
|
||||
|
||||
// console.log('video.payload', Object.keys(video.payload))
|
||||
|
||||
const hasMultipleAliases = !!app.aliases && app.aliases.length > 1
|
||||
|
||||
const hasBundleIdentifiers = !!app.bundleIds && app.bundleIds.length > 0
|
||||
|
||||
const appDeviceSupport = deviceList.map( device => {
|
||||
const supportsApp = deviceSupportsApp( device, app )
|
||||
return {
|
||||
...device,
|
||||
emoji: supportsApp ? '✅' : '🚫',
|
||||
ariaLabel: `${app.name} has ${ supportsApp ? '' : 'not' } been reported to work on ${device.name}`
|
||||
}
|
||||
})
|
||||
|
||||
const lastUpdatedFriendly = makeLastUpdatedFriendly( app.lastUpdated )
|
||||
|
||||
const relatedLinksHtml = renderPageLinksHtml( app.relatedLinks )
|
||||
|
||||
|
||||
const relatedVideosRowHtml = hasRelatedVideos ? await this.boundComponent(VideoRow)( relatedVideos ) : null
|
||||
|
||||
const appBundles = [] //hasBundleIdentifiers ? app.bundleIds.map( bundleId => this.getAppBundles() ) : null
|
||||
|
||||
if ( hasBundleIdentifiers ) {
|
||||
for ( const bundleIdentifier of app.bundleIds ) {
|
||||
const versions = await this.getAppVersions( bundleIdentifier )
|
||||
appBundles.push( [bundleIdentifier, versions] )
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return /* html */`
|
||||
<section class="container space-y-8 py-32">
|
||||
|
||||
<div class="intro-content flex flex-col items-center text-center min-h-3/4-screen md:min-h-0 space-y-8">
|
||||
<h1 class="title text-sm md:text-2xl font-bold">
|
||||
${ data.mainHeading }
|
||||
</h1>
|
||||
<h2 class="subtitle text-2xl md:text-5xl font-bold">
|
||||
${ app.text }
|
||||
</h2>
|
||||
|
||||
${ hasMultipleAliases ? /* html */`
|
||||
<small class="text-xs opacity-75">May also be known as ${ makeEnglishList( app.aliases, 'or' ) }</small>
|
||||
` : '' }
|
||||
|
||||
<div class="subscribe">
|
||||
<iframe src="/embed-subscribe" loading="lazy" style="width: 350px; height: 150px;" class="-my-10"></iframe>
|
||||
</div>
|
||||
|
||||
<div class="links space-y-6 sm:space-x-6">
|
||||
${ relatedLinksHtml }
|
||||
</div>
|
||||
|
||||
<div class="device-support w-full">
|
||||
<h2 class="subtitle text-xl md:text-2xl font-bold mb-3">
|
||||
Device Support
|
||||
</h2>
|
||||
<div class="device-support-apps md:inline-flex md:w-full max-w-4xl overflow-x-auto overflow-y-visible md:whitespace-no-wrap border rounded-lg divide-y md:divide-y-0 md:divide-x divide-gray-700 space-y-3 md:space-y-0 py-4 px-3">
|
||||
|
||||
${ appDeviceSupport.map( device => /* html */`
|
||||
<div class="device-container w-full md:w-auto inline-flex flex-col space-y-2 px-2">
|
||||
<a
|
||||
href="${ device.endpoint }"
|
||||
role="button"
|
||||
class="device-link block rounded-md text-sm font-medium leading-5 focus:outline-none focus:text-white focus:bg-gray-700 transition duration-150 ease-in-out text-gray-300 hover:bg-darker hover:neumorphic-shadow p-2"
|
||||
aria-label="${ device.ariaLabel }"
|
||||
>${ device.emoji } ${ device.name }</a>
|
||||
|
||||
<a href="${ device.amazonUrl }" target="_blank" class="underline text-xs pb-3" rel="noopener">Check Pricing</a>
|
||||
</div>
|
||||
`).join('') }
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${ hasRelatedVideos ? /* html */`
|
||||
<div
|
||||
class="related-videos w-full"
|
||||
>
|
||||
<h2 class="subtitle text-xl md:text-2xl text-center font-bold mb-3">
|
||||
Related Videos
|
||||
</h2>
|
||||
|
||||
${ relatedVideosRowHtml }
|
||||
|
||||
</div>
|
||||
` : '' }
|
||||
|
||||
|
||||
${ hasBundleIdentifiers ? /* html */`
|
||||
<div class="app-bundles w-full">
|
||||
|
||||
<h2 class="subtitle text-xl md:text-2xl text-center font-bold mb-3">
|
||||
App Bundles
|
||||
</h2>
|
||||
|
||||
<div class="app-bundles-container border rounded-lg">
|
||||
|
||||
<div class="app-bundles-list md:inline-flex w-full overflow-x-auto overflow-y-visible md:whitespace-no-wrap divide-y md:divide-y-0 md:divide-x divide-gray-700 space-y-3 md:space-y-0 py-4 px-3">
|
||||
|
||||
${ appBundles.map( ( [bundleIdentifier, versions] ) => /* html */`
|
||||
<div class="bundle-listing-container w-full md:w-auto inline-flex flex-col space-y-2 px-2">
|
||||
<a
|
||||
href="#bundle_identifier=${ bundleIdentifier }"
|
||||
role="button"
|
||||
class="bundle-link block rounded-md text-sm font-medium leading-5 focus:outline-none focus:text-white focus:bg-gray-700 transition duration-150 ease-in-out text-gray-300 hover:bg-darker hover:neumorphic-shadow p-2"
|
||||
aria-label="${ bundleIdentifier }"
|
||||
>${ bundleIdentifier }</a>
|
||||
|
||||
</div>
|
||||
`).join('') }
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="app-bundle-detail-view space-y-12 py-6 px-5">
|
||||
${ appBundles.map( ( [bundleIdentifier, versions] ) => /* html */`
|
||||
<div id="bundle_identifier=${ bundleIdentifier }" class="bundle-detail-container w-full space-y-2 px-2">
|
||||
<h3
|
||||
class="text-2xl font-bold"
|
||||
>${ bundleIdentifier }</h3>
|
||||
|
||||
<div class="bundle-versions-container border rounded-lg bg-black bg-opacity-10">
|
||||
|
||||
<div class="app-bundles-list md:inline-flex w-full overflow-x-auto overflow-y-visible md:whitespace-no-wrap divide-y md:divide-y-0 md:divide-x divide-gray-700 space-y-3 md:space-y-0 py-4 px-3">
|
||||
|
||||
${ Object.entries(versions).sort((a, b) => new Date(b[1]['Date']) - new Date(a[1]['Date'])).map( ( [ version, report ] ) => /* html */`
|
||||
<div class="bundle-listing-container w-full md:w-auto inline-flex flex-col space-y-2 p-4">
|
||||
<div class="version-heading font-bold text-xl">v${ version }</div>
|
||||
<div class="version-body divide-y-0 py-2">
|
||||
<div class="version-status">
|
||||
${ getStatusOfScan( report, false ) }
|
||||
</div>
|
||||
<div class="version-architecture">
|
||||
🖥 Supported Architectures <span class="rounded bg-black bg-opacity-50 p-1">${ supportedArchitectures( report ).join(', ') }</span>
|
||||
</div>
|
||||
</div>
|
||||
<details>
|
||||
<summary class="text-xs">Full Info Plist</summary>
|
||||
<pre class="border-dashed border rounded-lg bg-black bg-opacity-10 space-y-4 p-4 mt-4">${ JSON.stringify(report['Info Plist'], undefined, 2) }</pre>
|
||||
</details>
|
||||
<details>
|
||||
<summary class="text-xs">Full Meta Details</summary>
|
||||
<pre class="border-dashed border rounded-lg bg-black bg-opacity-10 space-y-4 p-4 mt-4">${ JSON.stringify(report['Macho Meta'], undefined, 2) }</pre>
|
||||
</details>
|
||||
</div>
|
||||
`).join('') }
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
`).join('') }
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
` : '' }
|
||||
|
||||
<div class="report-update text-xs text-center w-full shadow-none py-24">
|
||||
${ lastUpdatedFriendly !== null ? /* html */`
|
||||
<div>
|
||||
<time
|
||||
datetime="${ app.lastUpdated.raw }"
|
||||
>
|
||||
Last Updated ${ lastUpdatedFriendly }
|
||||
</time>
|
||||
</div>
|
||||
` : '' }
|
||||
<!-- https://eric.blog/2016/01/08/prefilling-github-issues/ -->
|
||||
<a
|
||||
href="https://github.com/ThatGuySam/doesitarm/issues?q=is%3Aissue+${ app.name }"
|
||||
target="_blank"
|
||||
class="underline"
|
||||
rel="noopener"
|
||||
>Report Update</a>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AppTemplate
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
import dotenv from 'dotenv'
|
||||
|
||||
import config from '../nuxt.config.js'
|
||||
|
||||
import { getAppType, getRouteType } from '../helpers/app-derived.js'
|
||||
|
||||
import { AppTemplate } from './app.11ty.js'
|
||||
|
||||
// import VideoRow from '../components-eleventy/video/row.js'
|
||||
// import { isVideo } from '../helpers/app-derived'
|
||||
|
||||
// Setup dotenv
|
||||
dotenv.config()
|
||||
|
||||
export const makeTitle = function ( app ) {
|
||||
return `Does ${app.name} work on Apple Silicon? - ${ config.head.title }`
|
||||
}
|
||||
|
||||
export const makeDescription = function ( app ) {
|
||||
return `Latest reported support status of ${ app.name } on Apple Silicon and ${ process.env.npm_package_config_verbiage_processors } Processors when installed via Homebrew. `
|
||||
}
|
||||
|
||||
class FormulaTemplate extends AppTemplate {
|
||||
// or `async data() {`
|
||||
// or `get data() {`
|
||||
data () {
|
||||
return {
|
||||
layout: 'default.11ty.js',
|
||||
|
||||
pagination: {
|
||||
data: 'eleventy-endpoints',
|
||||
size: 1,
|
||||
alias: 'app',
|
||||
before: function( data ) {
|
||||
return data.filter( entry => {
|
||||
const routeType = getRouteType( entry.route )
|
||||
|
||||
return routeType === 'formula'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
eleventyComputed: {
|
||||
title: ({ app: { payload: { app } } }) => {
|
||||
// console.log('data', data)
|
||||
return makeTitle( app )
|
||||
},
|
||||
description: ({ app: { payload: { app } } }) => {
|
||||
return makeDescription( app )
|
||||
},
|
||||
mainHeading: ({ app: { payload: { app } } }) => {
|
||||
return `Does <code>${ app.name }</code> work on Apple Silicon when installed via Homebrew?`
|
||||
}
|
||||
},
|
||||
|
||||
permalink: ({ app }) => {
|
||||
// console.log('payload', app.payload)
|
||||
return app.route.substring(1) + '/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = FormulaTemplate
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
import dotenv from 'dotenv'
|
||||
|
||||
import config from '../nuxt.config.js'
|
||||
|
||||
import VideoPlayer from '../components-eleventy/video/player.js'
|
||||
import VideoRow from '../components-eleventy/video/row.js'
|
||||
|
||||
import { getRouteType } from '../helpers/app-derived.js'
|
||||
import { buildVideoStructuredData } from '../helpers/structured-data.js'
|
||||
|
||||
// Setup dotenv
|
||||
dotenv.config()
|
||||
|
||||
export const myChannelId = 'UCB3jOb5QVjX7lYecvyCoTqQ'
|
||||
|
||||
export const makeTitle = function ( name ) {
|
||||
// console.log('tvEntry', tvEntry)
|
||||
|
||||
return `${ name } - ${ config.head.title }`
|
||||
}
|
||||
|
||||
export const makeDescription = function ( tvEntry ) {
|
||||
if ( tvEntry.payload.featuredApps.length === 0 ) return 'Apple Silicon performance and support videos'
|
||||
|
||||
const featuredAppsString = tvEntry.payload.featuredApps.slice(0, 5).map(app => app.name).join(', ')
|
||||
|
||||
// console.log('tvEntry.payload.featuredApps', tvEntry.payload.featuredApps)
|
||||
|
||||
return `Apple Silicon performance and support videos for ${ featuredAppsString }`
|
||||
}
|
||||
|
||||
|
||||
function renderFeaturedApps ( featuredApps ) {
|
||||
return /* html */`
|
||||
<div
|
||||
class="related-apps w-full"
|
||||
>
|
||||
<h2 class="subtitle text-xl md:text-2xl font-bold mb-3">
|
||||
Related Apps
|
||||
</h2>
|
||||
<div class="featured-apps overflow-x-auto overflow-y-visible whitespace-no-wrap py-2 space-x-2">
|
||||
${ featuredApps.map( app => ( /* html */`
|
||||
<a
|
||||
href="${ app.endpoint }"
|
||||
role="button"
|
||||
class="relative inline-flex items-center rounded-md px-4 py-2 leading-5 font-bold text-white border border-transparent focus:outline-none focus:border-indigo-600 neumorphic-shadow-inner bg-darker hover:bg-indigo-400 active:bg-indigo-600 transition duration-150 ease-in-out inline-block text-xs rounded-lg py-1 px-2"
|
||||
>${ app.name }</a>
|
||||
`) ).join('') }
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
class TV {
|
||||
// or `async data() {`
|
||||
// or `get data() {`
|
||||
data () {
|
||||
return {
|
||||
layout: 'default.11ty.js',
|
||||
|
||||
pagination: {
|
||||
data: 'eleventy-endpoints',
|
||||
size: 1,
|
||||
alias: 'tvEntry',
|
||||
before: function( endpoint ) {
|
||||
// console.log('Before runs')
|
||||
|
||||
return endpoint.filter( entry => {
|
||||
const routeType = getRouteType( entry.route )
|
||||
|
||||
return routeType === 'tv'
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
// tags: [ 'tv' ],
|
||||
|
||||
eleventyComputed: {
|
||||
title: data => {
|
||||
// Declare dependencies for Eleventy
|
||||
// https://www.11ty.dev/docs/data-computed/#declaring-your-dependencies
|
||||
data.tvEntry
|
||||
|
||||
return makeTitle( data.tvEntry.payload.video.name )
|
||||
},
|
||||
description: ( data ) => {
|
||||
// Declare dependencies for Eleventy
|
||||
// https://www.11ty.dev/docs/data-computed/#declaring-your-dependencies
|
||||
data.tvEntry
|
||||
|
||||
return makeDescription( data.tvEntry )
|
||||
},
|
||||
|
||||
headLinkTags: data => {
|
||||
// Declare dependencies for Eleventy
|
||||
// https://www.11ty.dev/docs/data-computed/#declaring-your-dependencies
|
||||
data.tvEntry
|
||||
|
||||
return [
|
||||
// Preload video thumbnail
|
||||
// <link rel="preload" as="image" href="img.png" />
|
||||
{
|
||||
'rel': 'preload',
|
||||
'as': 'image',
|
||||
'href': `https://i.ytimg.com/vi_webp/${ data.tvEntry.payload.video.id }/sddefault.webp`
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
structuredData: data => {
|
||||
// Declare dependencies for Eleventy
|
||||
// https://www.11ty.dev/docs/data-computed/#declaring-your-dependencies
|
||||
data.tvEntry
|
||||
|
||||
return buildVideoStructuredData( data.tvEntry.payload.video, data.tvEntry.payload.featuredApps, {
|
||||
siteUrl: process.env.URL
|
||||
} )
|
||||
}
|
||||
},
|
||||
|
||||
permalink: ( data ) => {
|
||||
|
||||
return data.tvEntry.route.substring(1) + '/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async render( data ) {
|
||||
|
||||
const {
|
||||
tvEntry: {
|
||||
// route,
|
||||
payload: {
|
||||
video,
|
||||
relatedVideos = [],
|
||||
featuredApps = []
|
||||
}
|
||||
},
|
||||
// 'device-list': deviceList
|
||||
} = data
|
||||
|
||||
|
||||
// console.log('video.payload', Object.keys(video.payload))
|
||||
|
||||
const coverBottomHtml = /* html */`
|
||||
<div class="page-heading h-full flex items-end md:p-4">
|
||||
<h1 class="title text-xs text-left md:text-2xl font-bold">${ video.name }</h1>
|
||||
</div>
|
||||
`
|
||||
|
||||
const playerHtml = await this.boundComponent(VideoPlayer)( video, {
|
||||
coverBottomHtml
|
||||
} )
|
||||
|
||||
const hasFeaturedApps = featuredApps.length > 0
|
||||
const featuredAppsHtml = hasFeaturedApps ? renderFeaturedApps( featuredApps ) : ''
|
||||
|
||||
const rowHtml = await this.boundComponent(VideoRow)( relatedVideos )
|
||||
|
||||
// const rowHtml = renderedRow.join('')
|
||||
|
||||
return /* html */`
|
||||
<section class="container pb-16">
|
||||
<div class="flex flex-col items-center text-center space-y-6">
|
||||
|
||||
${ playerHtml }
|
||||
|
||||
<div class="md:flex w-full justify-between space-y-4 md:space-y-0 md:px-10">
|
||||
|
||||
${ video.channel.id !== myChannelId ? /* html */`
|
||||
<div
|
||||
class="channel-credit"
|
||||
>
|
||||
<a
|
||||
href="https://www.youtube.com/channel/${ video.channel.id }?sub_confirmation=1"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
role="button"
|
||||
class="relative inline-flex items-center rounded-md px-4 py-2 leading-5 font-bold text-white border border-transparent focus:outline-none focus:border-indigo-600 neumorphic-shadow focus:shadow-outline-indigo bg-darker hover:bg-indigo-400 active:bg-indigo-600 transition duration-150 ease-in-out"
|
||||
>Subscribe to ${ video.channel.name }</a>
|
||||
</div>
|
||||
` : '' }
|
||||
</div>
|
||||
|
||||
<hr class="w-full">
|
||||
|
||||
${ featuredAppsHtml }
|
||||
|
||||
<div class="related-videos w-full">
|
||||
<h2 class="subtitle text-xl md:text-2xl font-bold mb-3">Related Videos</h2>
|
||||
|
||||
${ rowHtml }
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TV
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
# PAGES
|
||||
|
||||
This directory contains your Application Views and Routes.
|
||||
The framework reads all the `*.vue` files inside this directory and create the router of your application.
|
||||
|
||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing).
|
||||
|
|
@ -1,268 +0,0 @@
|
|||
<template>
|
||||
<section class="container pb-16">
|
||||
<div class="flex flex-col items-center text-center space-y-8">
|
||||
<template
|
||||
v-if="video"
|
||||
>
|
||||
<VideoPlayer
|
||||
:video="video"
|
||||
class="pt-16"
|
||||
/>
|
||||
<ChannelCredit
|
||||
:video="video"
|
||||
class="flex w-full justify-start md:px-10"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div
|
||||
:style="{
|
||||
'left': '50%',
|
||||
'right': '50%',
|
||||
'margin-left': '-50vw',
|
||||
'margin-right': '-50vw'
|
||||
}"
|
||||
class="video-canvas w-screen flex justify-center bg-black pt-16"
|
||||
>
|
||||
<div class="ratio-wrapper w-full max-w-4xl">
|
||||
<div class="relative overflow-hidden w-full pb-16/9">
|
||||
<div class="absolute h-full w-full flex justify-center items-center">
|
||||
<div class="message text-4xl md:text-6xl font-hairline leading-tight text-center">No videos yet</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- <h1 class="title text-sm md:text-3xl font-bold">
|
||||
{{ video.name }}
|
||||
</h1> -->
|
||||
|
||||
<div class="related-videos w-full max-w-4xl">
|
||||
<h2 class="subtitle text-xl md:text-2xl font-bold mb-3">
|
||||
Benchmark Videos
|
||||
</h2>
|
||||
<!-- <pre class="text-left">{{ benchmarkVideos }}</pre> -->
|
||||
<VideoRow
|
||||
:videos="benchmarkVideos"
|
||||
:active-video-id="activeVideoId"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="performanceVideos.length !== 0"
|
||||
class="performance-videos w-full max-w-4xl"
|
||||
>
|
||||
<h2 class="subtitle text-xl md:text-2xl font-bold mb-3">
|
||||
Performance Videos
|
||||
</h2>
|
||||
<!-- <pre class="text-left">{{ performanceVideos }}</pre> -->
|
||||
<VideoRow
|
||||
:videos="performanceVideos"
|
||||
:active-video-id="activeVideoId"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="moreVideos.length !== 0"
|
||||
class="related-videos w-full max-w-4xl"
|
||||
>
|
||||
<h2 class="subtitle text-xl md:text-2xl font-bold mb-3">
|
||||
More Videos
|
||||
</h2>
|
||||
<!-- <pre class="text-left">{{ relatedVideos }}</pre> -->
|
||||
<VideoRow
|
||||
:videos="moreVideos"
|
||||
:active-video-id="activeVideoId"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- video: {{ video }} -->
|
||||
|
||||
<!-- <div class="links space-y-6 sm:space-x-6 mb-8">
|
||||
<LinkButton
|
||||
v-for="(link, i) in app.relatedLinks"
|
||||
:key="i"
|
||||
:href="link.href"
|
||||
target="_blank"
|
||||
class=""
|
||||
>{{ (i === 0) ? 'View' : link.label }}</LinkButton>
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LinkButton from '~/components/link-button.vue'
|
||||
import EmailSubscribe from '~/components/email-subscribe.vue'
|
||||
import VideoRow from '~/components/video/row.vue'
|
||||
import VideoPlayer from '~/components/video/player.vue'
|
||||
import ChannelCredit from '~/components/video/channel-credit.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LinkButton,
|
||||
EmailSubscribe,
|
||||
VideoRow,
|
||||
VideoPlayer,
|
||||
ChannelCredit
|
||||
},
|
||||
async asyncData ( data ) {
|
||||
|
||||
const {
|
||||
params: { slug },
|
||||
route
|
||||
} = data
|
||||
|
||||
let {
|
||||
payload
|
||||
} = data
|
||||
|
||||
|
||||
|
||||
// Manually get payload as fallback
|
||||
// Uncomment for dev
|
||||
// if ( payload === undefined ) {
|
||||
// // Read back the JSON we just wrote to ensure it exists
|
||||
// const { default: savedList } = await import('~/static/nuxt-endpoints.json')
|
||||
|
||||
// const endpoint = savedList.find( resource => {
|
||||
// return resource.route === route.path
|
||||
// } )
|
||||
|
||||
// payload = endpoint.payload
|
||||
// }
|
||||
|
||||
return {
|
||||
app: payload.app,
|
||||
allVideos: payload.allVideos,
|
||||
submitVideoCard: payload.submitVideoCard
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
activeVideoIndex: 0,
|
||||
benchmarkVideos: [],
|
||||
performanceVideos: [],
|
||||
moreVideos: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
video () {
|
||||
return this.allVideos[this.activeVideoIndex]
|
||||
},
|
||||
title () {
|
||||
return `${this.app.name} Benchmarks for Apple Silicon - Does It ARM`
|
||||
},
|
||||
description () {
|
||||
return `Apple Silicon benchmark, performance, and support videos for ${this.app.name}`
|
||||
},
|
||||
activeVideoId () {
|
||||
return (this.video === Object(this.video)) ? this.video.id : null
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
||||
const nonBenchmarkVideos = []
|
||||
|
||||
// console.log('benchmarkVideos.length', this.benchmarkVideos.length)
|
||||
// console.log('performanceVideos.length', this.performanceVideos.length)
|
||||
// console.log('moreVideos.length', this.moreVideos.length)
|
||||
|
||||
// Move benchmark videos out of related videos
|
||||
this.allVideos.forEach((video, index) => {
|
||||
// console.log('video.name', video.name)
|
||||
// console.log('video.tags', video.tags)
|
||||
|
||||
if (!video.tags.includes('benchmark')) {
|
||||
nonBenchmarkVideos.push(video)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Add to benchmark videos
|
||||
this.benchmarkVideos.push(video)
|
||||
})
|
||||
|
||||
// console.log('Added benchmark videos')
|
||||
// console.log('benchmarkVideos.length', this.benchmarkVideos.length)
|
||||
// console.log('performanceVideos.length', this.performanceVideos.length)
|
||||
// console.log('moreVideos.length', this.moreVideos.length)
|
||||
|
||||
// Move performance videos out of related videos
|
||||
nonBenchmarkVideos.forEach((video, index) => {
|
||||
|
||||
if (!video.tags.includes('performance')) {
|
||||
this.moreVideos.push(video)
|
||||
return
|
||||
}
|
||||
|
||||
// Add to benchmark videos
|
||||
this.performanceVideos.push(video)
|
||||
})
|
||||
|
||||
// Append submit card to end
|
||||
this.benchmarkVideos.push(this.submitVideoCard)
|
||||
|
||||
|
||||
// console.log('Added performance videos')
|
||||
// console.log('benchmarkVideos.length', this.benchmarkVideos.length)
|
||||
// console.log('performanceVideos.length', this.performanceVideos.length)
|
||||
// console.log('moreVideos.length', this.moreVideos.length)
|
||||
},
|
||||
mounted () {
|
||||
window.onhashchange = this.loadVideoFromHash
|
||||
|
||||
if (location.hash.length !== 0) this.loadVideoFromHash()
|
||||
},
|
||||
methods: {
|
||||
loadVideoFromHash () {
|
||||
// console.log('location.hash', location.hash)
|
||||
|
||||
// Separate the video id from our window hash
|
||||
const hashId = location.hash.split('#')[1]
|
||||
|
||||
// Find the index of the video with the matching hash
|
||||
const newVideoIndex = this.allVideos.findIndex(video => {
|
||||
return video.id === hashId
|
||||
})
|
||||
|
||||
console.log('newVideoIndex', newVideoIndex)
|
||||
|
||||
// Load in the index to load out video
|
||||
this.activeVideoIndex = newVideoIndex
|
||||
|
||||
window.scroll({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.title,
|
||||
meta: [
|
||||
// hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
{
|
||||
'hid': 'description',
|
||||
'name': 'description',
|
||||
'content': this.description
|
||||
},
|
||||
|
||||
// Twitter Card
|
||||
{
|
||||
'hid': 'twitter:title',
|
||||
'property': 'twitter:title',
|
||||
'content': this.title
|
||||
},
|
||||
{
|
||||
'hid': 'twitter:description',
|
||||
'property': 'twitter:description',
|
||||
'content': this.description
|
||||
},
|
||||
{
|
||||
'property': 'twitter:url',
|
||||
'content': `${process.env.URL}${this.$nuxt.$route.path}`
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
<template>
|
||||
<section class="container py-32">
|
||||
<div class="flex flex-col items-center text-center space-y-8">
|
||||
<div class="hero-heading space-y-6">
|
||||
<h1 class="title text-sm md:text-2xl font-bold">
|
||||
Does {{ app.name }} work on Apple Silicon?
|
||||
</h1>
|
||||
<h2 class="subtitle text-2xl md:text-5xl font-bold">
|
||||
{{ app.text }}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="subscribe">
|
||||
<AllUpdatesSubscribe
|
||||
:app-name="app.name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="links space-y-6 sm:space-x-6">
|
||||
<LinkButton
|
||||
v-for="(link, i) in app.relatedLinks"
|
||||
:key="i"
|
||||
:href="link.href"
|
||||
target="_blank"
|
||||
class=""
|
||||
>{{ (i === 0) ? 'View' : link.label }}</LinkButton>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="relatedVideos.length !== 0"
|
||||
class="related-videos w-full"
|
||||
>
|
||||
<h2 class="subtitle text-xl md:text-2xl font-bold mb-3">
|
||||
Related Videos
|
||||
</h2>
|
||||
<VideoRow
|
||||
:videos="relatedVideos"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="report-links py-24 text-xs shadow-none">
|
||||
<div v-if="app.lastUpdated">
|
||||
<time
|
||||
:datetime="app.lastUpdated.raw"
|
||||
>
|
||||
Last Updated {{ lastUpdatedFriendly }}
|
||||
</time>
|
||||
</div>
|
||||
<!-- https://eric.blog/2016/01/08/prefilling-github-issues/ -->
|
||||
<a
|
||||
:href="`https://github.com/ThatGuySam/doesitarm/issues?q=is%3Aissue+is%3Aopen+${app.name}`"
|
||||
target="_blank"
|
||||
class="underline"
|
||||
rel="noopener"
|
||||
>Report Update</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { makeLastUpdatedFriendly } from '~/helpers/parse-date'
|
||||
import { getAppEndpoint } from '~/helpers/app-derived.js'
|
||||
|
||||
import LinkButton from '~/components/link-button.vue'
|
||||
import AllUpdatesSubscribe from '~/components/all-updates-subscribe.vue'
|
||||
import VideoRow from '~/components/video/row.vue'
|
||||
|
||||
import appList from '~/static/app-list.json'
|
||||
// import buildAppList from '~/helpers/build-app-list'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LinkButton,
|
||||
AllUpdatesSubscribe,
|
||||
VideoRow
|
||||
},
|
||||
async asyncData ({ params: { slug } }) {
|
||||
|
||||
const { default: videoList } = await import('~/static/video-list.json')
|
||||
const { videosRelatedToApp } = await import('~/helpers/related.js')
|
||||
|
||||
const app = appList.find(app => (app.slug === slug))
|
||||
|
||||
const relatedVideos = videosRelatedToApp( app, (new Set(videoList)) )
|
||||
|
||||
// Find other videos that also feature this video's app
|
||||
// for (const video of videoList) {
|
||||
// if (!video.apps.includes(app.slug)) continue
|
||||
|
||||
// relatedVideos.push(video)
|
||||
// }
|
||||
|
||||
return {
|
||||
slug,
|
||||
app,
|
||||
relatedVideos: relatedVideos.map(video => {
|
||||
// console.log('video', video)
|
||||
return {
|
||||
...video,
|
||||
endpoint: `${getAppEndpoint(app)}/benchmarks#${video.id}`
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
lastUpdatedFriendly () {
|
||||
return makeLastUpdatedFriendly( this.app.lastUpdated )
|
||||
}
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: `Does ${this.app.name} work on Apple Silicon?`,
|
||||
meta: [
|
||||
// hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
{
|
||||
'hid': 'description',
|
||||
'name': 'description',
|
||||
'content': `Check the the latest reported support status of ${this.app.name} on Apple Silicon and ${ process.env.npm_package_config_verbiage_processors } Processors`
|
||||
},
|
||||
|
||||
// Twitter Card
|
||||
{
|
||||
'hid': 'twitter:title',
|
||||
'property': 'twitter:title',
|
||||
'content': `Does ${this.app.name} work on Apple Silicon?`
|
||||
},
|
||||
{
|
||||
'hid': 'twitter:description',
|
||||
'property': 'twitter:description',
|
||||
'content': `Check the the latest reported support status of ${this.app.name} on Apple Silicon and ${ process.env.npm_package_config_verbiage_processors } Processors`
|
||||
},
|
||||
{
|
||||
'property': 'twitter:url',
|
||||
'content': `${process.env.URL}${this.$nuxt.$route.path}`
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,253 +0,0 @@
|
|||
<template>
|
||||
<section class="container relative md:static overflow-hidden md:overflow-visible pb-16">
|
||||
<div class="flex flex-col items-center text-center space-y-12">
|
||||
<BgPlayer
|
||||
:video="video"
|
||||
class="absolute overflow-hidden w-2x-screen md:w-full pointer-events-none"
|
||||
/>
|
||||
|
||||
<div class="page-heading flex justify-start w-full">
|
||||
<h1 class="title text-2xl leading-tight mt-12 mb-6">
|
||||
Benchmarks
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="line-separator border-white border-t-2 mb-12" />
|
||||
|
||||
<a
|
||||
:href="video.endpoint"
|
||||
>
|
||||
<div
|
||||
class="relative flex flex-col w-full justify-center items-center space-y-8 py-16 md:pt-0 md:pb-12 md:px-10"
|
||||
>
|
||||
<div
|
||||
class="play-circle w-16 h-16 bg-white-2 bg-blur flex justify-center items-center outline-0 rounded-full ease"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 18 18"
|
||||
style="width:24px;height:24px;margin-left:3px"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M15.562 8.1L3.87.225c-.818-.562-1.87 0-1.87.9v15.75c0 .9 1.052 1.462 1.87.9L15.563 9.9c.584-.45.584-1.35 0-1.8z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h2 class="title text-lg md:text-2xl font-bold">
|
||||
{{ video.name }}
|
||||
</h2>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div
|
||||
class="features-apps w-full"
|
||||
>
|
||||
<hr class="w-full" >
|
||||
<div class="featured-apps overflow-x-auto overflow-y-visible whitespace-no-wrap py-2 space-x-2">
|
||||
<LinkButton
|
||||
v-for="app in featuredApps"
|
||||
:key="app.slug"
|
||||
:href="app.endpoint"
|
||||
:class="[
|
||||
'inline-block text-xs rounded-lg py-1 px-2',
|
||||
]"
|
||||
:class-groups="{
|
||||
shadow: 'neumorphic-shadow-inner'
|
||||
}"
|
||||
>{{ app.name }}</LinkButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
v-for="(row, key) in videoRows"
|
||||
:key="key"
|
||||
:class="`${key}-videos w-full max-w-4xl`"
|
||||
>
|
||||
<h2 class="subtitle text-xl md:text-2xl font-bold mb-3">
|
||||
{{ row.heading }}
|
||||
</h2>
|
||||
<!-- <pre class="text-left">{{ benchmarkVideos }}</pre> -->
|
||||
<VideoRow
|
||||
:videos="row.videos"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { getVideoEndpoint, getAppEndpoint } from '~/helpers/app-derived.js'
|
||||
|
||||
import LinkButton from '~/components/link-button.vue'
|
||||
import EmailSubscribe from '~/components/email-subscribe.vue'
|
||||
import VideoRow from '~/components/video/row.vue'
|
||||
import BgPlayer from '~/components/video/bg-player.vue'
|
||||
import ChannelCredit from '~/components/video/channel-credit.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LinkButton,
|
||||
EmailSubscribe,
|
||||
VideoRow,
|
||||
BgPlayer,
|
||||
ChannelCredit
|
||||
},
|
||||
async asyncData ({ params: { slug } }) {
|
||||
|
||||
const { appsRelatedToVideo } = await import('~/helpers/related.js')
|
||||
const { default: videoList } = await import('~/static/video-list.json')
|
||||
const { allVideoAppsList } = await import('~/helpers/get-list.js')
|
||||
|
||||
// Get featured apps
|
||||
const featuredAppsSet = new Set()
|
||||
|
||||
videoList.slice(0, 24).forEach( video => {
|
||||
appsRelatedToVideo(video, allVideoAppsList).forEach( app => {
|
||||
featuredAppsSet.add(app)
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
video: videoList[0],
|
||||
featuredApps: Array.from(featuredAppsSet).map( app => {
|
||||
return {
|
||||
...app,
|
||||
endpoint: getAppEndpoint(app) + '/benchmarks'
|
||||
}
|
||||
}),
|
||||
allVideos: videoList.map( video => {
|
||||
return {
|
||||
...video,
|
||||
endpoint: getVideoEndpoint(video)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
videoRows: {
|
||||
'video-benchmarks': {
|
||||
heading: 'Video Editing Benchmarks',
|
||||
matchesCondition: video => {
|
||||
return video.tags.includes('benchmark') && video.tags.includes('video-and-motion-tools')
|
||||
},
|
||||
videos: []
|
||||
},
|
||||
'music-and-audio-tools': {
|
||||
heading: 'Music and DAW Performance',
|
||||
matchesCondition: video => {
|
||||
return video.tags.includes('music-and-audio-tools')
|
||||
},
|
||||
videos: []
|
||||
},
|
||||
// 'science-and-research-software': {
|
||||
// heading: 'Science and Research',
|
||||
// matchesCondition: video => {
|
||||
// return video.tags.includes('science-and-research-software')
|
||||
// },
|
||||
// videos: []
|
||||
// },
|
||||
'photo-and-graphic-tools': {
|
||||
heading: 'Photography and Design Compatibility',
|
||||
matchesCondition: video => {
|
||||
return video.tags.includes('photo-and-graphic-tools')
|
||||
},
|
||||
videos: []
|
||||
},
|
||||
'games': {
|
||||
heading: 'Gaming Benchmarks',
|
||||
matchesCondition: video => {
|
||||
return video.tags.includes('benchmark') && video.tags.includes('games')
|
||||
},
|
||||
videos: []
|
||||
},
|
||||
'benchmarks': {
|
||||
heading: 'Other Benchmark Videos',
|
||||
matchesCondition: video => video.tags.includes('benchmark'),
|
||||
videos: []
|
||||
},
|
||||
'performance': {
|
||||
heading: 'Performance Videos',
|
||||
matchesCondition: video => video.tags.includes('performance'),
|
||||
videos: []
|
||||
},
|
||||
|
||||
'other': {
|
||||
heading: 'More Videos',
|
||||
// Always true
|
||||
matchesCondition: () => true,
|
||||
videos: []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title () {
|
||||
return `Benchmarks for ${ process.env.npm_package_config_verbiage_processors } Processors and Apple Silicon - Does It ARM`
|
||||
},
|
||||
description () {
|
||||
// const featuredAppsString = this.featuredApps.slice(0, 5).map(app => app.name).join(', ')
|
||||
|
||||
return `Apple Silicon benchmark, performance, and compatibility videos for Macs using the ${ process.env.npm_package_config_verbiage_processors } processors.`
|
||||
},
|
||||
activeVideoId () {
|
||||
return this.video.id
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
||||
// Move videos to relevant categories
|
||||
this.allVideos.forEach((video, index) => {
|
||||
// console.log('video.name', video.name)
|
||||
// console.log('video.tags', video.tags)
|
||||
|
||||
// Look through row conditions to see if video matches
|
||||
for (const row in this.videoRows) {
|
||||
if( this.videoRows[row].matchesCondition(video) ) {
|
||||
|
||||
// Add the matching video
|
||||
this.videoRows[row].videos.push(video)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
// console.log('lengths', Object.values(this.videoRows).map(row => [row.heading, row.videos.length]))
|
||||
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.title,
|
||||
meta: [
|
||||
// hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
{
|
||||
'hid': 'description',
|
||||
'name': 'description',
|
||||
'content': this.description
|
||||
},
|
||||
|
||||
// Twitter Card
|
||||
{
|
||||
'hid': 'twitter:title',
|
||||
'property': 'twitter:title',
|
||||
'content': this.title
|
||||
},
|
||||
{
|
||||
'hid': 'twitter:description',
|
||||
'property': 'twitter:description',
|
||||
'content': this.description
|
||||
},
|
||||
{
|
||||
'property': 'twitter:url',
|
||||
'content': `${process.env.URL}${this.$nuxt.$route.path}`
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
<template>
|
||||
<section class="container py-24">
|
||||
<div class="flex flex-col">
|
||||
<h1 class="title text-2xl leading-tight mb-6">
|
||||
Categories
|
||||
</h1>
|
||||
|
||||
<div class="line-separator border-white border-t-2 mb-12" />
|
||||
|
||||
<!-- categoryList: {{ categoryList }} -->
|
||||
|
||||
<ul class="categories-list space-y-3">
|
||||
<li
|
||||
v-for="(category, i) in categoryList"
|
||||
:key="`${category.slug}-${i}`"
|
||||
:ref="`${category.slug}-row`"
|
||||
class="relative"
|
||||
>
|
||||
<!-- category.endpoint: {{ category.endpoint }} -->
|
||||
<a
|
||||
:href="`/kind/${category.slug}`"
|
||||
class="flex justify-start items-center inset-x-0 text-3xl md:text-4xl hover:bg-darkest border-2 border-white border-opacity-0 hover:border-opacity-50 focus:outline-none focus:bg-gray-50 duration-300 ease-in-out rounded-lg space-x-3 -mx-5 px-5 md:pr-64 py-3"
|
||||
style="transition-property: border;"
|
||||
>
|
||||
<div class="font-hairline">
|
||||
<div>{{ category.label }}</div>
|
||||
<div class="text-xs opacity-75 mb-3">{{ category.appNames }}</div>
|
||||
</div>
|
||||
<div>➔</div>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Search from '~/components/search.vue'
|
||||
import LinkButton from '~/components/link-button.vue'
|
||||
|
||||
// import appList from '~/static/app-list.json'
|
||||
// import gamelist from '~/static/game-list.json'
|
||||
|
||||
export default {
|
||||
async asyncData () {
|
||||
|
||||
// const { default: appList } = await import('~/static/app-list.json')
|
||||
// const { default: gamelist } = await import('~/static/game-list.json')
|
||||
|
||||
const { allList } = await import('~/helpers/get-list.js')
|
||||
const { categories } = await import('~/helpers/categories.js')
|
||||
|
||||
// Map Categories into category list
|
||||
const categoryList = Object.fromEntries(Object.entries(categories).map( ( entry ) => {
|
||||
entry[1].appNamesList = []
|
||||
return entry
|
||||
} ))
|
||||
|
||||
// Delete no-category
|
||||
delete categoryList['no-category']
|
||||
|
||||
allList.forEach( app => {
|
||||
// Find and store all categories
|
||||
|
||||
// console.log('app.category.slug', app.category.slug)
|
||||
|
||||
if (categoryList.hasOwnProperty(app.category.slug)) {
|
||||
categoryList[app.category.slug].appNamesList.push(app.name)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
categoryList[app.category.slug] = {
|
||||
// Merg in category data from app
|
||||
...app.category,
|
||||
// Merge in category data from category file
|
||||
...categories[app.category.slug],
|
||||
appNamesList: [ app.name ]
|
||||
}
|
||||
})
|
||||
|
||||
// Add App Names Text into categoryList
|
||||
Object.keys(categoryList).map(function(key, index) {
|
||||
const category = categoryList[key]
|
||||
|
||||
categoryList[key] = {
|
||||
...category,
|
||||
appNames: category.appNamesList.slice(0, 25).join(', ') + ', etc...'
|
||||
}
|
||||
})
|
||||
|
||||
// console.log('categoryList', categoryList)
|
||||
|
||||
return {
|
||||
categoryList
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Search,
|
||||
LinkButton
|
||||
},
|
||||
data: function () {
|
||||
return {}
|
||||
},
|
||||
// computed: {
|
||||
// categoryList () {
|
||||
// return categoryList
|
||||
// }
|
||||
// },
|
||||
head() {
|
||||
return {
|
||||
title: `Categories of App Support for Apple Silicon - Does It ARM`,
|
||||
// meta: [
|
||||
// // hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
// {
|
||||
// hid: 'description',
|
||||
// name: 'description',
|
||||
// content: 'My custom description'
|
||||
// }
|
||||
// ]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
<template>
|
||||
<section class="container py-24">
|
||||
<div class="flex flex-col items-center space-y-12">
|
||||
|
||||
<div class="summary space-y-6 max-w-2xl">
|
||||
|
||||
<h1 class="title text-3xl md:text-5xl font-hairline leading-tight text-center">
|
||||
{{ device.name }}
|
||||
</h1>
|
||||
|
||||
<div
|
||||
v-if="device.description"
|
||||
class="md:text-md text-center"
|
||||
>
|
||||
{{ device.description }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="supportedAppList.length !== 0"
|
||||
class="md:text-md text-center"
|
||||
>
|
||||
Supported apps include {{ supportedAppList.join(', ') }}.
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center py-3">
|
||||
<LinkButton
|
||||
v-if="device.amazonUrl"
|
||||
:href="device.amazonUrl"
|
||||
target="_blank"
|
||||
>
|
||||
Check Pricing
|
||||
</LinkButton>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="search-apps w-full flex flex-col items-center space-y-4">
|
||||
|
||||
<h2
|
||||
class="subtitle md:text-lg font-bold text-center"
|
||||
>
|
||||
Search app support for {{ device.name }}
|
||||
</h2>
|
||||
|
||||
<Search
|
||||
:app-list="deviceAppList"
|
||||
:quick-buttons="quickButtons"
|
||||
:autofocus="false"
|
||||
:initial-limit="50"
|
||||
@update:query="query = $event"
|
||||
>
|
||||
<template v-slot:before-search>
|
||||
<div class="empty-div" />
|
||||
</template>
|
||||
</Search>
|
||||
|
||||
</div>
|
||||
|
||||
<ListEndButtons :query="query" />
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Search from '~/components/search.vue'
|
||||
import LinkButton from '~/components/link-button.vue'
|
||||
import ListEndButtons from '~/components/list-end-buttons.vue'
|
||||
|
||||
// import { categories } from '~/helpers/categories.js'
|
||||
import { deviceSupportsApp } from '~/helpers/devices.js'
|
||||
|
||||
export default {
|
||||
async asyncData ({ params: { slug } }) {
|
||||
const { default: Chance } = await import('chance')
|
||||
|
||||
const { allList } = await import('~/helpers/get-list.js')
|
||||
const { default: deviceList } = await import('~/static/device-list.json')
|
||||
|
||||
|
||||
const charCode = slug.charCodeAt( slug.length-2 )
|
||||
const shuffler = new Chance( charCode )
|
||||
|
||||
const device = deviceList.find( device => {
|
||||
return device.slug === slug
|
||||
})
|
||||
|
||||
// console.log( 'device', device )
|
||||
|
||||
const deviceAppList = allList.map( app => {
|
||||
const appIsSupported = deviceSupportsApp( device, app )
|
||||
|
||||
return {
|
||||
name: app.name,
|
||||
status: app.status,
|
||||
slug: app.slug,
|
||||
// endpoint: app.endpoint,
|
||||
text: appIsSupported ? `✅ Supported on ${device.name}` : `🚫 Not yet reported working on ${device.name}`,
|
||||
lastUpdated: app.lastUpdated,
|
||||
category: app.category,
|
||||
// searchLinks: makeAppSearchLinks( app, (new Set(videoList)) )
|
||||
}
|
||||
})
|
||||
|
||||
const supportedApps = deviceAppList.filter( app => {
|
||||
const supported = app.text.startsWith('✅')
|
||||
const hasNotAllowedCategory = ([
|
||||
'no-category',
|
||||
'homebrew',
|
||||
'games',
|
||||
]).some( categorySlug => (app.category.slug === categorySlug) )
|
||||
|
||||
// console.log('hasNonStandardCategory', app.category.slug, hasNonStandardCategory)
|
||||
|
||||
return supported && !hasNotAllowedCategory
|
||||
})
|
||||
|
||||
const featuredApps = shuffler.shuffle( supportedApps ).slice(0, 12)
|
||||
|
||||
// console.log('featuredApps', featuredApps[0])
|
||||
|
||||
return {
|
||||
slug,
|
||||
device,
|
||||
featuredApps,
|
||||
deviceAppList
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Search,
|
||||
LinkButton,
|
||||
ListEndButtons
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
query: '',
|
||||
quickButtons: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
supportedAppList () {
|
||||
return this.featuredApps.map(app => app.name)
|
||||
},
|
||||
title () {
|
||||
return `App support list for ${this.device.name}`
|
||||
},
|
||||
description () {
|
||||
return `Check the the latest reported support status of apps and software on ${this.device.name}.`
|
||||
},
|
||||
structuredData () {
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
// https://developers.google.com/search/docs/data-types/faqpage
|
||||
// https://schema.org/FAQPage
|
||||
"@type": "FAQPage",
|
||||
"mainEntity": this.deviceAppList.map( app => {
|
||||
return {
|
||||
// https://schema.org/Question
|
||||
"@type": "Question",
|
||||
"name": `Does ${app.name} work on ${ this.device.name }?`,
|
||||
"acceptedAnswer": {
|
||||
// https://schema.org/Answer
|
||||
"@type": "Answer",
|
||||
"text": app.text
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.title,
|
||||
meta: [
|
||||
// hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
{
|
||||
'hid': 'description',
|
||||
'name': 'description',
|
||||
'content': this.description
|
||||
},
|
||||
|
||||
// Twitter Card
|
||||
{
|
||||
'hid': 'twitter:title',
|
||||
'property': 'twitter:title',
|
||||
'content': this.title
|
||||
},
|
||||
{
|
||||
'hid': 'twitter:description',
|
||||
'property': 'twitter:description',
|
||||
'content': this.description
|
||||
},
|
||||
{
|
||||
'property': 'twitter:url',
|
||||
'content': `${process.env.URL}${this.$nuxt.$route.path}`
|
||||
},
|
||||
],
|
||||
__dangerouslyDisableSanitizers: ['script'],
|
||||
script: [{ innerHTML: JSON.stringify(this.structuredData), type: 'application/ld+json' }]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
<template>
|
||||
<section class="container py-24">
|
||||
<div class="flex flex-col">
|
||||
<h1 class="title text-2xl leading-tight mb-6">
|
||||
Devices
|
||||
</h1>
|
||||
|
||||
<div class="line-separator border-white border-t-2 mb-12" />
|
||||
|
||||
<!-- deviceList: {{ deviceList }} -->
|
||||
|
||||
<ul class="device-list space-y-3">
|
||||
<li
|
||||
v-for="(device, i) in deviceList"
|
||||
:key="`${device.slug}-${i}`"
|
||||
:ref="`${device.slug}-row`"
|
||||
class="relative"
|
||||
>
|
||||
<!-- device.endpoint: {{ device.endpoint }} -->
|
||||
<a
|
||||
:href="device.endpoint"
|
||||
class="flex justify-start items-center inset-x-0 text-3xl md:text-4xl hover:bg-darkest border-2 border-white border-opacity-0 hover:border-opacity-50 focus:outline-none focus:bg-gray-50 duration-300 ease-in-out rounded-lg space-x-3 -mx-5 px-5 md:pr-64 py-3"
|
||||
style="transition-property: border;"
|
||||
>
|
||||
<div class="font-hairline">
|
||||
<div>{{ device.name }}</div>
|
||||
<!-- <div class="text-xs opacity-75 mb-3">{{ device.appNames }}</div> -->
|
||||
</div>
|
||||
<div>➔</div>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LinkButton from '~/components/link-button.vue'
|
||||
|
||||
export default {
|
||||
async asyncData () {
|
||||
const { default: deviceList } = await import('~/static/device-list.json')
|
||||
|
||||
return {
|
||||
deviceList
|
||||
}
|
||||
|
||||
},
|
||||
components: {
|
||||
LinkButton
|
||||
},
|
||||
data: function () {
|
||||
return {}
|
||||
},
|
||||
// computed: {
|
||||
// deviceList () {
|
||||
// return deviceList
|
||||
// }
|
||||
// },
|
||||
head() {
|
||||
return {
|
||||
title: `Categories of App Support for Apple Silicon - Does It ARM`,
|
||||
// meta: [
|
||||
// // hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
// {
|
||||
// hid: 'description',
|
||||
// name: 'description',
|
||||
// content: 'My custom description'
|
||||
// }
|
||||
// ]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
<template>
|
||||
<AllUpdatesSubscribe
|
||||
class="w-100 h-100 absolute inset-0 flex justify-center items-center"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import AllUpdatesSubscribe from '~/components/all-updates-subscribe.vue'
|
||||
|
||||
export default {
|
||||
layout: 'embed',
|
||||
|
||||
components: {
|
||||
AllUpdatesSubscribe
|
||||
},
|
||||
|
||||
data: function () {
|
||||
return {}
|
||||
},
|
||||
|
||||
head() {
|
||||
return {
|
||||
title: 'Subscribe',
|
||||
// meta: [
|
||||
// // hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
// {
|
||||
// hid: 'description',
|
||||
// name: 'description',
|
||||
// content: 'My custom description'
|
||||
// }
|
||||
// ]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
<template>
|
||||
<VideoPlayer
|
||||
v-if="youtubeId !== null"
|
||||
:video="video"
|
||||
class="w-100 h-100 absolute inset-0 flex justify-center items-center"
|
||||
>
|
||||
<template v-slot:cover-bottom>
|
||||
<div class="page-heading h-full flex items-end md:p-4">
|
||||
<h1 class="title text-xs text-left md:text-2xl font-bold">
|
||||
{{ video.name }}
|
||||
</h1>
|
||||
</div>
|
||||
</template>
|
||||
</VideoPlayer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import VideoPlayer from '~/components/video/player.vue'
|
||||
|
||||
export default {
|
||||
layout: 'embed',
|
||||
|
||||
components: {
|
||||
VideoPlayer
|
||||
},
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
youtubeId: null,
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
video () {
|
||||
return {
|
||||
name: this.name,
|
||||
id: this.youtubeId,
|
||||
timestamps: [],
|
||||
thumbnail: {
|
||||
sizes: '(max-width: 640px) 100vw, 640px',
|
||||
srcset: `https://i.ytimg.com/vi/${this.youtubeId}/default.jpg 120w, https://i.ytimg.com/vi/${this.youtubeId}/mqdefault.jpg 320w, https://i.ytimg.com/vi/${this.youtubeId}/hqdefault.jpg 480w, https://i.ytimg.com/vi/${this.youtubeId}/sddefault.jpg 640w`,
|
||||
src: `https://i.ytimg.com/vi/${this.youtubeId}/default.jpg`
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
head() {
|
||||
return {
|
||||
title: 'Video - Does It ARM',
|
||||
// meta: [
|
||||
// // hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
// {
|
||||
// hid: 'description',
|
||||
// name: 'description',
|
||||
// content: 'My custom description'
|
||||
// }
|
||||
// ]
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
// this.youtubeId = 'NDwmqJYJq9s'
|
||||
|
||||
// console.log('window', window)
|
||||
|
||||
if ( process.client ) {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
|
||||
this.youtubeId = urlParams.get('youtube-id')
|
||||
|
||||
this.name = urlParams.get('name')
|
||||
}
|
||||
|
||||
// console.log('this.youtubeId', this.youtubeId)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
<template>
|
||||
<section class="container py-32">
|
||||
<div class="flex flex-col items-center text-center">
|
||||
<h1 class="title text-sm md:text-2xl font-bold">
|
||||
Does <code>{{ app.name }}</code> work on Apple Silicon when installed via Homebrew?
|
||||
</h1>
|
||||
<h2 class="subtitle text-2xl md:text-5xl font-bold py-6">
|
||||
{{ app.text }}
|
||||
</h2>
|
||||
|
||||
<small class="data-credit text-sm opacity-75 text-center mb-4">
|
||||
<span>According to Github</span>
|
||||
</small>
|
||||
|
||||
<div
|
||||
class="comments text-sm mb-8"
|
||||
v-html="app.content"
|
||||
/>
|
||||
|
||||
<div class="links space-y-6 sm:space-x-6 mb-8">
|
||||
<LinkButton
|
||||
v-for="(link, i) in app.relatedLinks"
|
||||
:key="i"
|
||||
:href="link.href"
|
||||
target="_blank"
|
||||
class=""
|
||||
>{{ (i === 0) ? 'View' : link.label }}</LinkButton>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LinkButton from '~/components/link-button.vue'
|
||||
// import EmailSubscribe from '~/components/email-subscribe.vue'
|
||||
|
||||
// import homebrewList from '~/static/homebrew-list.json'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LinkButton,
|
||||
// EmailSubscribe
|
||||
},
|
||||
async asyncData ({ params: { slug }, payload: { app } }) {
|
||||
|
||||
return {
|
||||
slug,
|
||||
app//: homebrewList.find(app => (app.slug === slug))
|
||||
}
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: `Does ${this.app.name} work on Apple Silicon?`,
|
||||
meta: [
|
||||
// hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
{
|
||||
'hid': 'description',
|
||||
'name': 'description',
|
||||
'content': `Check the the latest reported support status of ${this.app.name} on Apple Silicon and ${ process.env.npm_package_config_verbiage_processors } Processors when installed via Homebrew. `
|
||||
},
|
||||
|
||||
// Twitter Card
|
||||
{
|
||||
'hid': 'twitter:title',
|
||||
'property': 'twitter:title',
|
||||
'content': `Does ${this.app.name} work on Apple Silicon?`
|
||||
},
|
||||
{
|
||||
'hid': 'twitter:description',
|
||||
'property': 'twitter:description',
|
||||
'content': `Check the the latest reported support status of ${this.app.name} on Apple Silicon and ${ process.env.npm_package_config_verbiage_processors } Processors when installed via Homebrew. `
|
||||
},
|
||||
{
|
||||
'property': 'twitter:url',
|
||||
'content': `${process.env.URL}${this.$nuxt.$route.path}`
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
<template>
|
||||
<section class="container pb-16">
|
||||
<div class="flex flex-col items-center text-center space-y-8">
|
||||
<template
|
||||
v-if="video"
|
||||
>
|
||||
<VideoPlayer
|
||||
:video="video"
|
||||
class="pt-16"
|
||||
/>
|
||||
<ChannelCredit
|
||||
:video="video"
|
||||
class="flex w-full justify-start md:px-10"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div
|
||||
:style="{
|
||||
'left': '50%',
|
||||
'right': '50%',
|
||||
'margin-left': '-50vw',
|
||||
'margin-right': '-50vw'
|
||||
}"
|
||||
class="video-canvas w-screen flex justify-center bg-black pt-16"
|
||||
>
|
||||
<div class="ratio-wrapper w-full max-w-4xl">
|
||||
<div class="relative overflow-hidden w-full pb-16/9">
|
||||
<div class="absolute h-full w-full flex justify-center items-center">
|
||||
<div class="message text-4xl md:text-6xl font-hairline leading-tight text-center">No videos yet</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div
|
||||
v-for="(row, key) in videoRows"
|
||||
:key="key"
|
||||
class="w-full max-w-4xl"
|
||||
>
|
||||
<div
|
||||
v-if="row.videos.length !== 0"
|
||||
:class="`${key}-videos w-full`"
|
||||
>
|
||||
<h2 class="subtitle text-xl md:text-2xl font-bold mb-3">
|
||||
{{ row.heading }}
|
||||
</h2>
|
||||
<!-- <pre class="text-left">{{ benchmarkVideos }}</pre> -->
|
||||
<VideoRow
|
||||
:videos="row.videos"
|
||||
:active-video-id="activeVideoId"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LinkButton from '~/components/link-button.vue'
|
||||
import EmailSubscribe from '~/components/email-subscribe.vue'
|
||||
import VideoRow from '~/components/video/row.vue'
|
||||
import VideoPlayer from '~/components/video/player.vue'
|
||||
import ChannelCredit from '~/components/video/channel-credit.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LinkButton,
|
||||
EmailSubscribe,
|
||||
VideoRow,
|
||||
VideoPlayer,
|
||||
ChannelCredit
|
||||
},
|
||||
asyncData ({ params: { slug }, payload: { app, allVideos } }) {
|
||||
|
||||
// const { allVideoAppsList } = await import('~/helpers/get-list.js')
|
||||
// const { default: videoList } = await import('~/static/video-list.json')
|
||||
|
||||
// const { videosRelatedToApp } = await import('~/helpers/related.js')
|
||||
|
||||
// const app = allVideoAppsList.find(app => (app.slug === slug))
|
||||
|
||||
// // const submitVideoCard = {
|
||||
// // endpoint: `https://docs.google.com/forms/d/e/1FAIpQLSeEVGM9vE7VcfLMy6fJkfU70X2VZ60rHDyhDQLtnAN4nso0WA/viewform?usp=pp_url&entry.1018125313=${app.name}`
|
||||
// // }
|
||||
|
||||
// // const featuredApps = []
|
||||
|
||||
// const relatedVideos = videosRelatedToApp( app, videoList ).map(video => {
|
||||
// // console.log('video', video)
|
||||
// return {
|
||||
// ...video,
|
||||
// // endpoint: `#${video.id}`
|
||||
// }
|
||||
// })
|
||||
|
||||
// console.log({
|
||||
// app,
|
||||
// allVideos,
|
||||
// // submitVideoCard
|
||||
// })
|
||||
|
||||
return {
|
||||
app,
|
||||
allVideos,
|
||||
// submitVideoCard
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
activeVideoIndex: 0,
|
||||
videoRows: {
|
||||
'benchmarks': {
|
||||
heading: 'Benchmark Videos',
|
||||
matchesCondition: video => video.tags.includes('benchmark'),
|
||||
videos: []
|
||||
},
|
||||
'performance': {
|
||||
heading: 'Performance Videos',
|
||||
matchesCondition: video => video.tags.includes('performance'),
|
||||
videos: []
|
||||
},
|
||||
|
||||
'other': {
|
||||
heading: 'More Videos',
|
||||
// Always true
|
||||
matchesCondition: () => true,
|
||||
videos: []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
video () {
|
||||
return this.allVideos[this.activeVideoIndex]
|
||||
},
|
||||
title () {
|
||||
return `${this.app.name} Benchmarks for Apple Silicon - Does It ARM`
|
||||
},
|
||||
description () {
|
||||
return `Apple Silicon gaming benchmark, performance, and support videos for ${this.app.name}`
|
||||
},
|
||||
activeVideoId () {
|
||||
return (this.video === Object(this.video)) ? this.video.id : null
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
||||
// Move videos to relevant categories
|
||||
this.allVideos.forEach((video, index) => {
|
||||
// console.log('video.name', video.name)
|
||||
// console.log('video.tags', video.tags)
|
||||
|
||||
// Look through row conditions to see if video matches
|
||||
for (const row in this.videoRows) {
|
||||
if( this.videoRows[row].matchesCondition(video) ) {
|
||||
|
||||
// Add the matching video
|
||||
this.videoRows[row].videos.push(video)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
// console.log('lengths', Object.values(this.videoRows).map(row => [row.heading, row.videos.length]))
|
||||
|
||||
},
|
||||
mounted () {
|
||||
window.onhashchange = this.loadVideoFromHash
|
||||
|
||||
if (location.hash.length !== 0) this.loadVideoFromHash()
|
||||
},
|
||||
methods: {
|
||||
loadVideoFromHash () {
|
||||
// console.log('location.hash', location.hash)
|
||||
|
||||
// Separate the video id from our window hash
|
||||
const hashId = location.hash.split('#')[1]
|
||||
|
||||
// Find the index of the video with the matching hash
|
||||
const newVideoIndex = this.allVideos.findIndex(video => {
|
||||
return video.id === hashId
|
||||
})
|
||||
|
||||
console.log('newVideoIndex', newVideoIndex)
|
||||
|
||||
// Load in the index to load out video
|
||||
this.activeVideoIndex = newVideoIndex
|
||||
|
||||
window.scroll({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.title,
|
||||
meta: [
|
||||
// hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
{
|
||||
'hid': 'description',
|
||||
'name': 'description',
|
||||
'content': this.description
|
||||
},
|
||||
|
||||
// Twitter Card
|
||||
{
|
||||
'hid': 'twitter:title',
|
||||
'property': 'twitter:title',
|
||||
'content': this.title
|
||||
},
|
||||
{
|
||||
'hid': 'twitter:description',
|
||||
'property': 'twitter:description',
|
||||
'content': this.description
|
||||
},
|
||||
{
|
||||
'property': 'twitter:url',
|
||||
'content': `${process.env.URL}${this.$nuxt.$route.path}`
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
<template>
|
||||
<section class="container py-32">
|
||||
<div class="flex flex-col items-center text-center">
|
||||
|
||||
<div class="intro-content flex flex-col items-center text-center min-h-3/4-screen md:min-h-0">
|
||||
|
||||
<h1 class="title text-sm md:text-2xl font-bold">
|
||||
Does {{ app.name }} work on Apple Silicon?
|
||||
</h1>
|
||||
<h2 class="subtitle text-2xl md:text-5xl font-bold py-6">
|
||||
{{ app.text }}
|
||||
</h2>
|
||||
|
||||
<ThomasCredit />
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
v-if="relatedVideos.length !== 0"
|
||||
class="related-videos w-full"
|
||||
>
|
||||
<h2 class="subtitle text-xl md:text-2xl font-bold mb-3">
|
||||
Related Videos
|
||||
</h2>
|
||||
<VideoRow
|
||||
:videos="relatedVideos"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h2 class="subtitle text-xl md:text-2xl font-bold py-6">
|
||||
Reports
|
||||
</h2>
|
||||
|
||||
<ul class="flex flex-col md:flex-row space-x-0 space-y-4 md:space-y-0 md:space-x-4 mb-4">
|
||||
|
||||
<li
|
||||
v-for="(report, i) in app.reports"
|
||||
:key="`${app.slug}-${i}`"
|
||||
class="col-span-1 rounded-lg border w-full md:w-64"
|
||||
>
|
||||
<div class="w-full flex items-center justify-between p-6">
|
||||
<div class="flex-1">
|
||||
<div class="space-x-3">
|
||||
<h3 class="text-sm leading-5 font-bold">{{ report['Specs'] }}</h3>
|
||||
<span class="flex-shrink-0 inline-block px-2 py-0.5 text-teal-800 text-xs leading-4 font-bold bg-teal-100 rounded-full">{{ report['FPS'] }}</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm leading-5">{{ report['Notes'] }}</p>
|
||||
<p
|
||||
v-if="report['Resolution'].length !== 0"
|
||||
class="mt-1 text-sm leading-5"
|
||||
>
|
||||
🖥 {{ report['Resolution'] }}
|
||||
</p>
|
||||
<p
|
||||
v-if="report['Settings'].length !== 0"
|
||||
class="mt-1 text-sm leading-5"
|
||||
>
|
||||
⚙️ {{ report['Settings'] }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="report['Source'].includes('https://')"
|
||||
class="border-t border-gray-200"
|
||||
>
|
||||
<div class="-mt-px flex">
|
||||
<div class="w-0 flex-1 flex border-r border-gray-200">
|
||||
<a
|
||||
:href="report['Source']"
|
||||
class="relative -mr-px w-0 flex-1 inline-flex items-center justify-center py-4 text-sm leading-5 font-bold border border-transparent rounded-bl-lg hover:text-gray-500 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 transition ease-in-out duration-150"
|
||||
>
|
||||
<!-- Heroicon name: mail -->
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" />
|
||||
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" />
|
||||
</svg>
|
||||
<span class="ml-3 opacity-75">Source</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<div class="report-links py-24 shadow-none">
|
||||
<!-- https://eric.blog/2016/01/08/prefilling-github-issues/ -->
|
||||
<a
|
||||
:href="`https://forms.gle/29GWt85i1G1L7Ttj8`"
|
||||
target="_blank"
|
||||
class="text-xs"
|
||||
rel="noopener"
|
||||
>Report Update</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAppEndpoint } from '~/helpers/app-derived.js'
|
||||
|
||||
import VideoRow from '~/components/video/row.vue'
|
||||
import LinkButton from '~/components/link-button.vue'
|
||||
import ThomasCredit from '~/components/thomas-credit.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
VideoRow,
|
||||
LinkButton,
|
||||
ThomasCredit
|
||||
},
|
||||
async asyncData ({ params: { slug } }) {
|
||||
|
||||
const { default: gameList } = await import('~/static/game-list.json')
|
||||
const { default: videoList } = await import('~/static/video-list.json')
|
||||
const { videosRelatedToApp } = await import('~/helpers/related.js')
|
||||
|
||||
const app = gameList.find(app => (app.slug === slug))
|
||||
|
||||
const relatedVideos = videosRelatedToApp( app, (new Set(videoList)) )
|
||||
|
||||
// Find other videos that also feature this video's app
|
||||
// for (const video of videoList) {
|
||||
// if (!video.apps.includes(app.slug)) continue
|
||||
|
||||
// relatedVideos.push(video)
|
||||
// }
|
||||
|
||||
return {
|
||||
slug,
|
||||
app,
|
||||
relatedVideos: relatedVideos.map(video => {
|
||||
// console.log('video', video)
|
||||
return {
|
||||
...video,
|
||||
endpoint: `${getAppEndpoint(app)}/benchmarks#${video.id}`
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: `Does ${this.app.name} work on Apple Silicon?`,
|
||||
meta: [
|
||||
// hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
{
|
||||
'hid': 'description',
|
||||
'name': 'description',
|
||||
'content': `Check the the latest reported support status of ${this.app.name} on Apple Silicon and ${ process.env.npm_package_config_verbiage_processors } Processors for gaming. `
|
||||
},
|
||||
|
||||
// Twitter Card
|
||||
{
|
||||
'hid': 'twitter:title',
|
||||
'property': 'twitter:title',
|
||||
'content': `Does ${this.app.name} work on Apple Silicon?`
|
||||
},
|
||||
{
|
||||
'hid': 'twitter:description',
|
||||
'property': 'twitter:description',
|
||||
'content': `Check the the latest reported support status of ${this.app.name} on Apple Silicon and ${ process.env.npm_package_config_verbiage_processors } Processors for gaming. `
|
||||
},
|
||||
{
|
||||
'property': 'twitter:url',
|
||||
'content': `${process.env.URL}${this.$nuxt.$route.path}`
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
103
pages/games.vue
103
pages/games.vue
|
|
@ -1,103 +0,0 @@
|
|||
<template>
|
||||
<section class="container py-24">
|
||||
<div class="flex flex-col items-center">
|
||||
<h1 class="title text-4xl md:text-6xl font-hairline leading-tight text-center">
|
||||
Does It ARM?
|
||||
</h1>
|
||||
<h2 class="subtitle md:text-xl text-center">
|
||||
Games that are reported to support Apple Silicon
|
||||
</h2>
|
||||
|
||||
<ThomasCredit />
|
||||
|
||||
<Search
|
||||
:app-list="gameList"
|
||||
:quick-buttons="quickButtons"
|
||||
no-email-subscribe
|
||||
@update:query="query = $event"
|
||||
/>
|
||||
|
||||
<LinkButton
|
||||
:href="`https://forms.gle/29GWt85i1G1L7Ttj8`"
|
||||
>
|
||||
Report a Game
|
||||
</LinkButton>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Search from '~/components/search.vue'
|
||||
import LinkButton from '~/components/link-button.vue'
|
||||
import ThomasCredit from '~/components/thomas-credit.vue'
|
||||
|
||||
|
||||
import gameList from '~/static/game-list.json'
|
||||
|
||||
export default {
|
||||
async asyncData () {
|
||||
const { sortedAppList, allList, allVideoAppsList, makeAppSearchLinks } = await import('~/helpers/get-list.js')
|
||||
const { default: gameList } = await import('~/static/game-list.json')
|
||||
const { default: videoList } = await import('~/static/video-list.json')
|
||||
|
||||
return {
|
||||
// Map game list
|
||||
gameList: gameList.map( app => {
|
||||
|
||||
return {
|
||||
name: app.name,
|
||||
status: app.status,
|
||||
slug: app.slug,
|
||||
// endpoint: app.endpoint,
|
||||
text: app.text,
|
||||
lastUpdated: app.lastUpdated,
|
||||
category: app.category,
|
||||
searchLinks: makeAppSearchLinks( app, (new Set(videoList)) )
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Search,
|
||||
LinkButton,
|
||||
ThomasCredit
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
query: '',
|
||||
quickButtons: [
|
||||
{
|
||||
label: '✅ Native Support',
|
||||
query: 'status:native'
|
||||
},
|
||||
{
|
||||
label: '✳️ Rosetta',
|
||||
query: 'status:rosetta'
|
||||
},
|
||||
{
|
||||
label: '🚫 Unsupported',
|
||||
query: 'status:no'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// gameList() {
|
||||
// return gameList
|
||||
// }
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: `Games supported on Apple Silicon - Does It ARM`,
|
||||
// meta: [
|
||||
// // hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
// {
|
||||
// hid: 'description',
|
||||
// name: 'description',
|
||||
// content: 'My custom description'
|
||||
// }
|
||||
// ]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
246
pages/index.vue
246
pages/index.vue
|
|
@ -1,246 +0,0 @@
|
|||
<template>
|
||||
<section class="container py-24">
|
||||
<div class="flex flex-col items-center space-y-4">
|
||||
<div class="hero">
|
||||
<h1 class="title text-3xl md:text-6xl font-hairline leading-tight text-center">
|
||||
Does It ARM?
|
||||
</h1>
|
||||
<h2 class="subtitle md:text-xl text-center">
|
||||
Apps that are reported to support Apple Silicon
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<Search
|
||||
:app-list="allList"
|
||||
:quick-buttons="quickButtons"
|
||||
:initial-limit="100"
|
||||
@update:query="onQueryUpdate"
|
||||
>
|
||||
<template v-slot:before-search>
|
||||
<div class="list-summary-wrapper flex justify-center text-center text-sm">
|
||||
|
||||
<ListSummary
|
||||
:custom-numbers="customSummaryNumbers"
|
||||
class="max-w-4xl"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</Search>
|
||||
|
||||
<ListEndButtons :query="query" />
|
||||
|
||||
<AllUpdatesSubscribe
|
||||
:input-class-groups="{
|
||||
shadow: 'hover:neumorphic-shadow',
|
||||
bg: '',
|
||||
focus: 'bg-transparent neumorphic-shadow pl-8',
|
||||
blur: 'placeholder-white text-center border border-transparent bg-transparent opacity-50 hover:opacity-100 px-3',
|
||||
}"
|
||||
class="my-12"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
|
||||
import getListSummaryNumbers from '~/helpers/get-list-summary-numbers.js'
|
||||
|
||||
import Search from '~/components/search.vue'
|
||||
import LinkButton from '~/components/link-button.vue'
|
||||
import AllUpdatesSubscribe from '~/components/all-updates-subscribe.vue'
|
||||
import ListSummary from '~/components/list-summary.vue'
|
||||
import ListEndButtons from '~/components/list-end-buttons.vue'
|
||||
|
||||
export default {
|
||||
async asyncData () {
|
||||
// const { default: appList } = await import('~/static/app-list.json')
|
||||
// const { default: gamelist } = await import('~/static/game-list.json')
|
||||
|
||||
const { sortedAppList, allList, allVideoAppsList, makeAppSearchLinks } = await import('~/helpers/get-list.js')
|
||||
const { default: videoList } = await import('~/static/video-list.json')
|
||||
|
||||
const allAppSearchLinks = {}
|
||||
|
||||
// console.log('allVideoAppsList', allVideoAppsList)
|
||||
|
||||
allVideoAppsList.forEach( app => {
|
||||
// Make the search links
|
||||
const searchLinks = makeAppSearchLinks( app, (new Set(videoList)) )
|
||||
|
||||
// If there are more than zero
|
||||
// add them to our list
|
||||
if (searchLinks.length > 0) {
|
||||
allAppSearchLinks[app.slug] = searchLinks
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
// Filter app list to leave out data not needed for search
|
||||
initialAppList: sortedAppList.map( app => {
|
||||
|
||||
const searchLinks = allAppSearchLinks?.[app.slug] || []
|
||||
|
||||
// if (typeof allAppSearchLinks[app.slug] !== 'undefined') {
|
||||
// searchLinks = allAppSearchLinks[app.slug]
|
||||
// }
|
||||
|
||||
return {
|
||||
name: app.name,
|
||||
status: app.status,
|
||||
slug: app.slug,
|
||||
// endpoint: app.endpoint,
|
||||
text: app.text,
|
||||
lastUpdated: app.lastUpdated,
|
||||
category: app.category,
|
||||
searchLinks
|
||||
}
|
||||
}),
|
||||
allAppSearchLinks,
|
||||
customSummaryNumbers: getListSummaryNumbers(allList)
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Search,
|
||||
LinkButton,
|
||||
AllUpdatesSubscribe,
|
||||
ListSummary,
|
||||
ListEndButtons
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
query: '',
|
||||
fetchedAppList: [],
|
||||
quickButtons: [
|
||||
{
|
||||
label: '✅ Native Support',
|
||||
query: 'status:native'
|
||||
},
|
||||
{
|
||||
label: '✳️ Rosetta',
|
||||
query: 'status:rosetta'
|
||||
},
|
||||
{
|
||||
label: '🚫 Unsupported',
|
||||
query: 'status:no'
|
||||
},
|
||||
{
|
||||
label: '🎮 Games',
|
||||
query: 'Games'
|
||||
},
|
||||
{
|
||||
label: '🍺 Homebrew Formulae',
|
||||
query: 'Homebrew'
|
||||
},
|
||||
{
|
||||
label: 'Music Tools',
|
||||
query: 'Music'
|
||||
},
|
||||
{
|
||||
label: 'Developer Tools',
|
||||
query: 'Developer'
|
||||
},
|
||||
{
|
||||
label: 'Photo Tools',
|
||||
query: 'Photo'
|
||||
},
|
||||
{
|
||||
label: 'Video Tools',
|
||||
query: 'Video'
|
||||
},
|
||||
{
|
||||
label: 'Productivity Tools',
|
||||
query: 'Productivity'
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title () {
|
||||
return `Apple Silicon and ${ process.env.npm_package_config_verbiage_processors } app and game compatibility list`
|
||||
},
|
||||
description () {
|
||||
return `List of compatibility apps and games for Apple Silicon and the ${ process.env.npm_package_config_verbiage_processors } Processors including performance reports and benchmarks`
|
||||
},
|
||||
allList () {
|
||||
return [
|
||||
...this.initialAppList,
|
||||
...this.fetchedAppList
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async onQueryUpdate ( $event ) {
|
||||
// console.log('$event', $event)
|
||||
this.query = $event
|
||||
|
||||
// If fetched lists have already been loaded in
|
||||
// OR if there's no query
|
||||
// then stop
|
||||
if (this.fetchedAppList.length !== 0 || this.query.trim().length === 0) return
|
||||
|
||||
// console.log('this.allAppSearchLinks', this.allAppSearchLinks)
|
||||
|
||||
const fetchedListUrls = [
|
||||
'/game-list.json',
|
||||
'/homebrew-list.json'
|
||||
]
|
||||
|
||||
const fetchedLists = await Promise.all(fetchedListUrls.map( async listUrl => {
|
||||
// Fetch List
|
||||
const response = await axios.get(listUrl)
|
||||
// Extract apps from response data
|
||||
const fetchedApps = response.data
|
||||
|
||||
return fetchedApps
|
||||
}))
|
||||
|
||||
// console.log('fetchedLists', fetchedLists)
|
||||
|
||||
this.fetchedAppList = fetchedLists.flat(1).map( app => {
|
||||
|
||||
const searchLinks = this.allAppSearchLinks?.[app.slug] || []
|
||||
|
||||
return {
|
||||
...app,
|
||||
searchLinks
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.title,
|
||||
meta: [
|
||||
// hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
{
|
||||
'hid': 'description',
|
||||
'name': 'description',
|
||||
'content': this.description
|
||||
},
|
||||
|
||||
// Twitter Card
|
||||
{
|
||||
'hid': 'twitter:title',
|
||||
'property': 'twitter:title',
|
||||
'content': this.title
|
||||
},
|
||||
{
|
||||
'hid': 'twitter:description',
|
||||
'property': 'twitter:description',
|
||||
'content': this.description
|
||||
},
|
||||
{
|
||||
'property': 'twitter:url',
|
||||
'content': `${process.env.URL}${this.$nuxt.$route.path}`
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
<template>
|
||||
<section class="container py-24">
|
||||
<div class="flex flex-col items-center">
|
||||
<h1 class="title text-3xl md:text-5xl font-hairline leading-tight text-center pb-4">
|
||||
{{ pluralLabel }} that are reported to support Apple Silicon
|
||||
</h1>
|
||||
|
||||
<h2
|
||||
v-if="supportedAppList.length !== 0"
|
||||
class="subtitle md:text-xl text-center"
|
||||
>
|
||||
Supported apps include {{ supportedAppList.join(', ') }}.
|
||||
</h2>
|
||||
|
||||
<Search
|
||||
:app-list="categoryAppList"
|
||||
:quick-buttons="quickButtons"
|
||||
@update:query="query = $event"
|
||||
/>
|
||||
|
||||
|
||||
<div class="flex flex-col md:flex-row space-x-0 space-y-4 md:space-y-0 md:space-x-4">
|
||||
<template v-if="category.requestLinks">
|
||||
<LinkButton
|
||||
v-for="link in category.requestLinks"
|
||||
:key="link.label"
|
||||
:href="link.href"
|
||||
class="text-xs"
|
||||
>
|
||||
{{ link.label }}
|
||||
</LinkButton>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ListEndButtons :query="query" />
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Search from '~/components/search.vue'
|
||||
import LinkButton from '~/components/link-button.vue'
|
||||
import ListEndButtons from '~/components/list-end-buttons.vue'
|
||||
|
||||
import { categories, getAppCategory } from '~/helpers/categories.js'
|
||||
|
||||
export default {
|
||||
async asyncData ({ params: { slug } }) {
|
||||
const { sortedAppList, allList, allVideoAppsList, makeAppSearchLinks } = await import('~/helpers/get-list.js')
|
||||
const { default: gameList } = await import('~/static/game-list.json')
|
||||
const { default: videoList } = await import('~/static/video-list.json')
|
||||
|
||||
const filteredList = allList.filter(app => {
|
||||
return app.category.slug === slug
|
||||
})
|
||||
|
||||
return {
|
||||
slug,
|
||||
categoryAppList: filteredList.map( app => {
|
||||
return {
|
||||
name: app.name,
|
||||
status: app.status,
|
||||
slug: app.slug,
|
||||
// endpoint: app.endpoint,
|
||||
text: app.text,
|
||||
lastUpdated: app.lastUpdated,
|
||||
category: app.category,
|
||||
searchLinks: makeAppSearchLinks( app, (new Set(videoList)) )
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Search,
|
||||
LinkButton,
|
||||
ListEndButtons
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
query: '',
|
||||
quickButtons: [
|
||||
{
|
||||
label: '✅ Native Support',
|
||||
query: 'status:native'
|
||||
},
|
||||
{
|
||||
label: '✳️ Rosetta',
|
||||
query: 'status:rosetta'
|
||||
},
|
||||
{
|
||||
label: '🚫 Unsupported',
|
||||
query: 'status:no'
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
category () {
|
||||
if ( categories.hasOwnProperty( this.slug ) ) {
|
||||
return categories[this.slug]
|
||||
}
|
||||
|
||||
// Try to find the category info within the passed apps
|
||||
const appWithCategory = this.categoryAppList.find( app => {
|
||||
return app.category.slug === this.slug
|
||||
})
|
||||
|
||||
// console.log('appWithCategory', appWithCategory)
|
||||
|
||||
return appWithCategory.category
|
||||
},
|
||||
pluralLabel () {
|
||||
if ( this.category.hasOwnProperty('pluralLabel') ) {
|
||||
return this.category.pluralLabel
|
||||
}
|
||||
|
||||
return this.category.label
|
||||
},
|
||||
supportedAppList () {
|
||||
return this.categoryAppList.filter(app => {
|
||||
return app.status.includes('yes')
|
||||
}).map(app => app.name)
|
||||
},
|
||||
title () {
|
||||
return `List of ${this.pluralLabel || this.category.label} that work on Apple Silicon?`
|
||||
},
|
||||
description () {
|
||||
return `Check the the latest reported support status of ${this.pluralLabel || this.category.label} on Apple Silicon and ${ process.env.npm_package_config_verbiage_processors } Processors. `
|
||||
},
|
||||
structuredData () {
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
// https://developers.google.com/search/docs/data-types/faqpage
|
||||
// https://schema.org/FAQPage
|
||||
"@type": "FAQPage",
|
||||
"mainEntity": this.categoryAppList.map( app => {
|
||||
return {
|
||||
// https://schema.org/Question
|
||||
"@type": "Question",
|
||||
"name": `Does ${app.name} work on Apple Silicon and ${ process.env.npm_package_config_verbiage_processors } Macs?`,
|
||||
"acceptedAnswer": {
|
||||
// https://schema.org/Answer
|
||||
"@type": "Answer",
|
||||
"text": app.text
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.title,
|
||||
meta: [
|
||||
// hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
{
|
||||
'hid': 'description',
|
||||
'name': 'description',
|
||||
'content': this.description
|
||||
},
|
||||
|
||||
// Twitter Card
|
||||
{
|
||||
'hid': 'twitter:title',
|
||||
'property': 'twitter:title',
|
||||
'content': this.title
|
||||
},
|
||||
{
|
||||
'hid': 'twitter:description',
|
||||
'property': 'twitter:description',
|
||||
'content': this.description
|
||||
},
|
||||
{
|
||||
'property': 'twitter:url',
|
||||
'content': `${process.env.URL}${this.$nuxt.$route.path}`
|
||||
},
|
||||
],
|
||||
__dangerouslyDisableSanitizers: ['script'],
|
||||
script: [{ innerHTML: JSON.stringify(this.structuredData), type: 'application/ld+json' }]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<template>
|
||||
<section class="container py-24">
|
||||
<!-- __template -->
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
data: function () {
|
||||
return {}
|
||||
},
|
||||
// computed: {
|
||||
// categoryList () {
|
||||
// return categoryList
|
||||
// }
|
||||
// },
|
||||
head() {
|
||||
return {
|
||||
title: '__template',
|
||||
// meta: [
|
||||
// // hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
// {
|
||||
// hid: 'description',
|
||||
// name: 'description',
|
||||
// content: 'My custom description'
|
||||
// }
|
||||
// ]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
<template>
|
||||
<section class="container pb-16">
|
||||
<div class="flex flex-col items-center text-center space-y-6">
|
||||
<VideoPlayer
|
||||
:video="video"
|
||||
class="pt-16"
|
||||
>
|
||||
|
||||
<template v-slot:cover-bottom>
|
||||
<div class="page-heading h-full flex items-end md:p-4">
|
||||
<h1 class="title text-xs text-left md:text-2xl font-bold">
|
||||
{{ video.name }}
|
||||
</h1>
|
||||
</div>
|
||||
</template>
|
||||
</VideoPlayer>
|
||||
|
||||
<div
|
||||
class="md:flex w-full justify-between space-y-4 md:space-y-0 md:px-10"
|
||||
>
|
||||
<!-- <h1 class="title text-lg md:text-2xl font-bold">
|
||||
{{ video.name }}
|
||||
</h1> -->
|
||||
|
||||
<ChannelCredit
|
||||
:video="video"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<hr class="w-full" >
|
||||
|
||||
|
||||
<div
|
||||
v-if="featuredApps.length !== 0"
|
||||
class="related-apps w-full"
|
||||
>
|
||||
<h2 class="subtitle text-xl md:text-2xl font-bold mb-3">
|
||||
Related Apps
|
||||
</h2>
|
||||
<div class="featured-apps overflow-x-auto overflow-y-visible whitespace-no-wrap py-2 space-x-2">
|
||||
<LinkButton
|
||||
v-for="app in featuredApps"
|
||||
:key="app.slug"
|
||||
:href="getAppEndpoint(app)"
|
||||
:class="[
|
||||
'inline-block text-xs rounded-lg py-1 px-2',
|
||||
]"
|
||||
:class-groups="{
|
||||
shadow: 'neumorphic-shadow-inner'
|
||||
}"
|
||||
>{{ app.name }}</LinkButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="related-videos w-full">
|
||||
<h2 class="subtitle text-xl md:text-2xl font-bold mb-3">
|
||||
Related Videos
|
||||
</h2>
|
||||
<VideoRow
|
||||
:videos="relatedVideos"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- video: {{ video }} -->
|
||||
|
||||
<!-- <div class="links space-y-6 sm:space-x-6 mb-8">
|
||||
<LinkButton
|
||||
v-for="(link, i) in app.relatedLinks"
|
||||
:key="i"
|
||||
:href="link.href"
|
||||
target="_blank"
|
||||
class=""
|
||||
>{{ (i === 0) ? 'View' : link.label }}</LinkButton>
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { getAppEndpoint } from '~/helpers/app-derived.js'
|
||||
|
||||
import { buildVideoStructuredData } from '~/helpers/structured-data.js'
|
||||
|
||||
import LinkButton from '~/components/link-button.vue'
|
||||
import EmailSubscribe from '~/components/email-subscribe.vue'
|
||||
import VideoRow from '~/components/video/row.vue'
|
||||
import VideoPlayer from '~/components/video/player.vue'
|
||||
import ChannelCredit from '~/components/video/channel-credit.vue'
|
||||
|
||||
|
||||
function makeFeaturedAppsString ( featuredApps ) {
|
||||
return featuredApps.slice(0, 5).map(app => app.name).join(', ')
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LinkButton,
|
||||
EmailSubscribe,
|
||||
VideoRow,
|
||||
VideoPlayer,
|
||||
ChannelCredit
|
||||
},
|
||||
async asyncData ( data ) {
|
||||
|
||||
|
||||
const {
|
||||
params: { slug },
|
||||
route
|
||||
} = data
|
||||
|
||||
let {
|
||||
payload
|
||||
} = data
|
||||
|
||||
// Manually get payload as fallback
|
||||
// Uncomment for dev
|
||||
// if ( payload === undefined ) {
|
||||
// // Read back the JSON we just wrote to ensure it exists
|
||||
// const { default: savedList } = await import('~/static/nuxt-endpoints.json')
|
||||
|
||||
// const endpoint = savedList.find( resource => {
|
||||
// return resource.route === route.path
|
||||
// } )
|
||||
|
||||
// payload = endpoint.payload
|
||||
// }
|
||||
|
||||
return {
|
||||
video: payload.video,
|
||||
featuredApps: payload.featuredApps,
|
||||
relatedVideos: payload.relatedVideos
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title () {
|
||||
return `${this.video.name} - Does It ARM`
|
||||
},
|
||||
description () {
|
||||
const featuredAppsString = makeFeaturedAppsString( this.featuredApps )
|
||||
|
||||
return `Apple Silicon performance and support videos for ${featuredAppsString}`
|
||||
},
|
||||
|
||||
},
|
||||
methods: {
|
||||
getAppEndpoint
|
||||
},
|
||||
head() {
|
||||
const structuredData = buildVideoStructuredData( this.video, this.featuredApps, {
|
||||
siteUrl: this.$config.siteUrl
|
||||
} )
|
||||
|
||||
|
||||
return {
|
||||
title: this.title,
|
||||
meta: [
|
||||
// hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
{
|
||||
'hid': 'description',
|
||||
'name': 'description',
|
||||
'content': this.description
|
||||
},
|
||||
|
||||
// Twitter Card
|
||||
{
|
||||
'hid': 'twitter:title',
|
||||
'property': 'twitter:title',
|
||||
'content': this.title
|
||||
},
|
||||
{
|
||||
'hid': 'twitter:description',
|
||||
'property': 'twitter:description',
|
||||
'content': this.description
|
||||
},
|
||||
{
|
||||
'property': 'twitter:url',
|
||||
'content': `${process.env.URL}${this.$nuxt.$route.path}`
|
||||
},
|
||||
],
|
||||
|
||||
__dangerouslyDisableSanitizers: ['script'],
|
||||
script: [{ innerHTML: JSON.stringify( structuredData ), type: 'application/ld+json' }]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
# PLUGINS
|
||||
|
||||
**This directory is not required, you can delete it if you don't want to use it.**
|
||||
|
||||
This directory contains your Javascript plugins that you want to run before mounting the root Vue.js application.
|
||||
|
||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins).
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
import Vue from 'vue'
|
||||
import VueGtag from 'vue-gtag'
|
||||
|
||||
Vue.use(VueGtag, {
|
||||
config: { id: 'G-0WLH5YTTB0' }
|
||||
})
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
# STORE
|
||||
|
||||
**This directory is not required, you can delete it if you don't want to use it.**
|
||||
|
||||
This directory contains your Vuex Store files.
|
||||
Vuex Store option is implemented in the Nuxt.js framework.
|
||||
|
||||
Creating a file in this directory activate the option in the framework automatically.
|
||||
|
||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store).
|
||||
|
|
@ -4,7 +4,6 @@ module.exports = {
|
|||
'helpers/**/*.js',
|
||||
|
||||
// Nuxt
|
||||
'nuxt.config.js',
|
||||
'components/**/*.vue',
|
||||
'layouts/**/*.vue',
|
||||
'pages/**/*.vue',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue