refactor: remove unbody service
This commit is contained in:
parent
047f378e4d
commit
6d4f93f71c
2
.env
2
.env
|
@ -1,5 +1,3 @@
|
|||
UNBODY_API_KEY=
|
||||
UNBODY_PROJECT_ID=
|
||||
SIMPLECAST_ACCESS_TOKEN=
|
||||
REVALIDATE_WEBHOOK_TOKEN=
|
||||
DISCORD_LOGS_WEBHOOK_URL=
|
||||
|
|
|
@ -39,3 +39,4 @@ next-env.d.ts
|
|||
public/rss.xml
|
||||
public/atom*.xml
|
||||
public/images/placeholders/*
|
||||
!public/images/placeholders/.gitkeep
|
||||
|
|
|
@ -7,10 +7,9 @@ ARG PORT=3000
|
|||
EXPOSE ${PORT}
|
||||
|
||||
# Credentials
|
||||
ARG UNBODY_PROJECT_ID
|
||||
ARG UNBODY_API_KEY
|
||||
ARG SIMPLECAST_ACCESS_TOKEN
|
||||
ARG REVALIDATE_WEBHOOK_TOKEN
|
||||
ARG STRAPI_API_KEY
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
|
|
@ -43,7 +43,7 @@ pipeline {
|
|||
]) {
|
||||
image = docker.build(
|
||||
"${IMAGE_NAME}:${GIT_COMMIT.take(8)}",
|
||||
["--build-arg='UNBODY_PROJECT_ID=${env.UNBODY_PROJECT_ID}'",
|
||||
["--build-arg='STRAPI_API_KEY=${env.UNBODY_PROJECT_ID}'",
|
||||
"--build-arg='UNBODY_API_KEY=${env.UNBODY_API_KEY}'",
|
||||
"--build-arg='SIMPLECAST_ACCESS_TOKEN=${SIMPLECAST_ACCESS_TOKEN}'",
|
||||
"--build-arg='REVALIDATE_WEBHOOK_TOKEN=${REVALIDATE_WEBHOOK_TOKEN}'",
|
||||
|
|
|
@ -12,7 +12,7 @@ The repository for [press.logos.co](https://press.logos.co/) website.
|
|||
|
||||
- Emotion: CSS-in-JS
|
||||
|
||||
- [Unbody](https://unbody.io/) : CMS
|
||||
- [Strapi](https://strapi.io/) : CMS
|
||||
|
||||
|
||||
## Environment Variables
|
||||
|
@ -20,8 +20,6 @@ The repository for [press.logos.co](https://press.logos.co/) website.
|
|||
Please check the environment values in `.env` located in the root directory.
|
||||
|
||||
```
|
||||
UNBODY_API_KEY=
|
||||
UNBODY_PROJECT_ID=
|
||||
SIMPLECAST_ACCESS_TOKEN=
|
||||
REVALIDATE_WEBHOOK_TOKEN=
|
||||
NEXT_PUBLIC_SITE_URL=https://press.logos.co
|
||||
|
@ -30,8 +28,6 @@ FATHOM_SITE_ID=
|
|||
|
||||
This is a template for `.env.local`, which is included in `.gitignore`.
|
||||
|
||||
You can obtain an Unbody API key and project ID through your [Unbody project](https://app.unbody.io/).
|
||||
|
||||
To find the Simplecast access token, follow these steps on the Simplecast dashboard:
|
||||
|
||||
1. Click the gear button in the top-right corner.
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
type TocItem {
|
||||
level: Int!
|
||||
tag: String!
|
||||
href: String!
|
||||
title: String!
|
||||
blockIndex: Int!
|
||||
}
|
||||
|
||||
type Mention {
|
||||
name: String!
|
||||
emailAddress: String!
|
||||
}
|
||||
|
||||
type Footnote {
|
||||
index: Int!
|
||||
id: String!
|
||||
refId: String!
|
||||
refValue: String!
|
||||
valueHTML: String!
|
||||
valueText: String!
|
||||
}
|
||||
|
||||
extend type GoogleDoc {
|
||||
mentionsObj: [Mention]!
|
||||
tocObj: [TocItem]!
|
||||
}
|
||||
|
||||
extend type TextBlock {
|
||||
footnotesObj: [Footnote]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -27,12 +27,6 @@ Page.getLayout = function getLayout(page: React.ReactNode) {
|
|||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps<PageProps> = async () => {
|
||||
// const { data: tags = [] } = await unbodyApi.getTopics(true)
|
||||
// const { data: highlighted } = await unbodyApi.getHighlightedPosts()
|
||||
// const { data: latest } = await unbodyApi.getRecentPosts({
|
||||
// skip: 0,
|
||||
// limit: 15,
|
||||
// })
|
||||
const { data: latest } = await strapiApi.getRecentPosts({
|
||||
highlighted: 'exclude',
|
||||
limit: 15,
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
import { LPE } from '../../../types/lpe.types'
|
||||
import { calcReadingTime } from '../../../utils/string.utils'
|
||||
import { UnbodyResGoogleDocData } from '../unbody.types'
|
||||
import { UnbodyDataTypeConfig } from './types'
|
||||
|
||||
export const ArticleDataType: UnbodyDataTypeConfig<
|
||||
UnbodyResGoogleDocData,
|
||||
LPE.Article.Data
|
||||
> = {
|
||||
key: 'ArticleDocument',
|
||||
objectType: 'GoogleDoc',
|
||||
classes: ['article', 'podcast', 'show', 'episode', 'document'],
|
||||
|
||||
isMatch: (helpers, data, original, root) =>
|
||||
data.pathString.includes('/Articles/') ||
|
||||
data.pathString.includes('/Podcasts/'),
|
||||
|
||||
transform: async (helpers, data) => {
|
||||
const textBlock = helpers.dataTypes.get({
|
||||
objectType: 'TextBlock',
|
||||
})
|
||||
const imageBlock = helpers.dataTypes.get({
|
||||
objectType: 'ImageBlock',
|
||||
})
|
||||
|
||||
const blocks =
|
||||
await helpers.dataTypes.transformMany<LPE.Article.ContentBlock>(
|
||||
[...textBlock, ...imageBlock],
|
||||
[...(data.blocks || [])]
|
||||
.filter(
|
||||
(block) =>
|
||||
block.__typename === 'ImageBlock' || !!block.html || !!block.text,
|
||||
)
|
||||
.sort((a, b) => a.order - b.order),
|
||||
data,
|
||||
)
|
||||
|
||||
const readingTime = calcReadingTime(
|
||||
(data.blocks || [])
|
||||
.filter((block) => block.__typename === 'TextBlock')
|
||||
.map((block) => ('text' in block ? block.text : ''))
|
||||
.join(' '),
|
||||
)
|
||||
|
||||
return {
|
||||
id: data._additional.id,
|
||||
slug: data.slug,
|
||||
tags: data.tags ?? [],
|
||||
title: data.title,
|
||||
subtitle: data.subtitle || '',
|
||||
summary: data.summary || '',
|
||||
authors: data.mentionsObj ?? [],
|
||||
createdAt: data.createdAt || null,
|
||||
modifiedAt: data.modifiedAt || null,
|
||||
content: blocks,
|
||||
coverImage:
|
||||
(blocks.find((block) =>
|
||||
(block.labels || []).includes(
|
||||
LPE.Article.ContentBlockLabels.CoverImage,
|
||||
),
|
||||
) as LPE.Article.ImageBlock) || null,
|
||||
readingTime,
|
||||
toc: data.tocObj ?? [],
|
||||
featured: data.path.includes('featured'),
|
||||
highlighted: data.path.includes('highlighted'),
|
||||
isDraft: data.path.includes('draft'),
|
||||
type: LPE.PostTypes.Article,
|
||||
} as any
|
||||
},
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
import { ArticleBlocksOrders } from '../../../configs/data.configs'
|
||||
import { LPE } from '../../../types/lpe.types'
|
||||
import {
|
||||
UnbodyResGoogleDocData,
|
||||
UnbodyResImageBlockData,
|
||||
} from '../unbody.types'
|
||||
import { UnbodyDataTypeConfig } from './types'
|
||||
|
||||
export const ArticleImageBlockDataType: UnbodyDataTypeConfig<
|
||||
LPE.Article.ImageBlock,
|
||||
LPE.Article.ImageBlock,
|
||||
UnbodyResImageBlockData,
|
||||
UnbodyResGoogleDocData
|
||||
> = {
|
||||
key: 'ArticleImageBlock',
|
||||
objectType: 'ImageBlock',
|
||||
classes: ['article'],
|
||||
|
||||
isMatch: (helpers, data, original, root) =>
|
||||
data.type === 'image' && (root?.path || []).includes('Articles'),
|
||||
|
||||
transform: (helpers, data, original, root) => {
|
||||
if (!root) return data
|
||||
|
||||
if (data.order === ArticleBlocksOrders.cover)
|
||||
return {
|
||||
...data,
|
||||
labels: [...data.labels, LPE.Article.ContentBlockLabels.CoverImage],
|
||||
}
|
||||
|
||||
return data
|
||||
},
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
import { LPE } from '../../../types/lpe.types'
|
||||
import { UnbodyHelpers } from '../unbody.helpers'
|
||||
import {
|
||||
UnbodyResSearchGoogleDocData,
|
||||
UnbodyResSearchResultImageBlockData,
|
||||
UnbodyResSearchResultTextBlockData,
|
||||
} from '../unbody.types'
|
||||
import { UnbodyDataTypeConfig } from './types'
|
||||
|
||||
export const ArticleSearchResultItemDataType: UnbodyDataTypeConfig<
|
||||
| UnbodyResSearchGoogleDocData
|
||||
| UnbodyResSearchResultTextBlockData
|
||||
| UnbodyResSearchResultImageBlockData,
|
||||
LPE.Search.ResultItem,
|
||||
| UnbodyResSearchGoogleDocData
|
||||
| UnbodyResSearchResultTextBlockData
|
||||
| UnbodyResSearchResultImageBlockData,
|
||||
undefined,
|
||||
{
|
||||
query: string
|
||||
tags: string[]
|
||||
shows: LPE.Podcast.Show[]
|
||||
}
|
||||
> = {
|
||||
key: 'ArticleSearchResultItem',
|
||||
objectType: 'GoogleDoc',
|
||||
classes: ['article', 'search'],
|
||||
|
||||
isMatch: (helpers, data, original, root) =>
|
||||
data.__typename === 'GoogleDoc'
|
||||
? !root &&
|
||||
(data.pathString.includes('/Articles/') ||
|
||||
data.pathString.includes('/Podcasts/'))
|
||||
: data.__typename === 'ImageBlock' || data.__typename === 'TextBlock',
|
||||
|
||||
transform: async (helpers, data, original, root, context) => {
|
||||
const { query = '', tags = [] } = context ?? {}
|
||||
|
||||
if (data.__typename === 'GoogleDoc') {
|
||||
const score = UnbodyHelpers.resolveScore(data._additional)
|
||||
|
||||
const transformers = helpers.dataTypes.get({ objectType: 'GoogleDoc' })
|
||||
const transformed = await helpers.dataTypes.transform<LPE.Post.Document>(
|
||||
transformers,
|
||||
data,
|
||||
undefined,
|
||||
{ ...context },
|
||||
)
|
||||
|
||||
return {
|
||||
score,
|
||||
data: transformed,
|
||||
type: transformed.type,
|
||||
}
|
||||
}
|
||||
|
||||
const score =
|
||||
query.length > 0 || tags.length > 0
|
||||
? UnbodyHelpers.resolveScore(data._additional)
|
||||
: 0
|
||||
|
||||
const transformers = helpers.dataTypes.get({ objectType: 'GoogleDoc' })
|
||||
const document = await helpers.dataTypes.transform<LPE.Post.Document>(
|
||||
transformers,
|
||||
'document' in data && data.document?.[0],
|
||||
undefined,
|
||||
{
|
||||
...context,
|
||||
},
|
||||
)
|
||||
|
||||
const transformed = await helpers.dataTypes.transform<
|
||||
LPE.Post.ContentBlock<any>
|
||||
>(
|
||||
[
|
||||
helpers.dataTypes.getOne({
|
||||
objectType: 'TextBlock',
|
||||
classes: 'article',
|
||||
})!,
|
||||
helpers.dataTypes.getOne({
|
||||
objectType: 'ImageBlock',
|
||||
classes: 'article',
|
||||
})!,
|
||||
],
|
||||
data,
|
||||
)
|
||||
|
||||
return {
|
||||
score,
|
||||
data: { ...transformed, document } as any,
|
||||
type: transformed.type,
|
||||
}
|
||||
},
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
import { LPE } from '../../../types/lpe.types'
|
||||
import { similarity } from '../../../utils/string.utils'
|
||||
import { UnbodyResGoogleDocData, UnbodyResTextBlockData } from '../unbody.types'
|
||||
import { UnbodyDataTypeConfig } from './types'
|
||||
|
||||
export const ArticleTextBlockDataType: UnbodyDataTypeConfig<
|
||||
LPE.Article.TextBlock,
|
||||
LPE.Article.TextBlock,
|
||||
UnbodyResTextBlockData,
|
||||
UnbodyResGoogleDocData
|
||||
> = {
|
||||
key: 'ArticleTextBlock',
|
||||
objectType: 'TextBlock',
|
||||
classes: ['article'],
|
||||
|
||||
isMatch: (helpers, data, original, root) =>
|
||||
data.type === 'text' && (root?.path || []).includes('Articles'),
|
||||
|
||||
transform: (helpers, data, original, root) => {
|
||||
if (!root) return data
|
||||
|
||||
const { text = '', html = '', classNames = [] } = original
|
||||
const { summary, tags = [], mentionsObj: mentions = [] } = root
|
||||
|
||||
const labels: LPE.Article.ContentBlockLabel[] = []
|
||||
|
||||
const isTitle = classNames.includes('title')
|
||||
const isSubtitle = classNames.includes('subtitle')
|
||||
|
||||
const isAuthor =
|
||||
similarity(text, mentions.map((m) => m.name).join('')) > 0.8
|
||||
|
||||
const isSummary = summary === text
|
||||
|
||||
const isTag = similarity(text, tags.map((t) => `#${t}`).join(' ')) > 0.8
|
||||
|
||||
//TODO this is a hack to remove the footnotes from the body
|
||||
// we should find a better way to do this
|
||||
const isFootnotes = html.match(`<a href="#ftnt_ref`)?.length
|
||||
|
||||
isTitle && labels.push(LPE.Article.ContentBlockLabels.Title)
|
||||
isSubtitle && labels.push(LPE.Article.ContentBlockLabels.Subtitle)
|
||||
isAuthor && labels.push(LPE.Article.ContentBlockLabels.Authors)
|
||||
isSummary && labels.push(LPE.Article.ContentBlockLabels.Summary)
|
||||
isTag && labels.push(LPE.Article.ContentBlockLabels.Tags)
|
||||
isFootnotes && labels.push(LPE.Article.ContentBlockLabels.Footnote)
|
||||
|
||||
return {
|
||||
...data,
|
||||
labels: [...data.labels, ...labels],
|
||||
}
|
||||
},
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
import { LPE } from '../../../types/lpe.types'
|
||||
import {
|
||||
UnbodyResGoogleDocData,
|
||||
UnbodyResImageBlockData,
|
||||
} from '../unbody.types'
|
||||
import { UnbodyDataTypeConfig } from './types'
|
||||
|
||||
export const ImageBlockDataType: UnbodyDataTypeConfig<
|
||||
UnbodyResImageBlockData,
|
||||
LPE.Article.ImageBlock,
|
||||
UnbodyResImageBlockData,
|
||||
UnbodyResGoogleDocData
|
||||
> = {
|
||||
key: 'ImageBlock',
|
||||
objectType: 'ImageBlock',
|
||||
classes: ['article'],
|
||||
|
||||
isMatch: (helpers, data, original, root) => data.__typename === 'ImageBlock',
|
||||
|
||||
transform: (helpers, data, original, root) => {
|
||||
return {
|
||||
id: data?._additional?.id || `${data.order}`,
|
||||
type: 'image',
|
||||
alt: data.alt,
|
||||
url: data.url,
|
||||
height: data.height,
|
||||
order: data.order,
|
||||
width: data.width,
|
||||
labels: [],
|
||||
}
|
||||
},
|
||||
}
|
|
@ -1,256 +0,0 @@
|
|||
import parseISO from 'date-fns/parseISO'
|
||||
import { LPE } from '../../../types/lpe.types'
|
||||
import { settle } from '../../../utils/promise.utils'
|
||||
import { simplecastApi } from '../../simplecast.service'
|
||||
import { UnbodyResGoogleDocData } from '../unbody.types'
|
||||
import { UnbodyDataTypeConfig } from './types'
|
||||
|
||||
export const PodcastEpisodeDataType: UnbodyDataTypeConfig<
|
||||
LPE.Article.Data,
|
||||
LPE.Podcast.Document,
|
||||
UnbodyResGoogleDocData,
|
||||
any,
|
||||
| {
|
||||
shows: LPE.Podcast.Show[]
|
||||
parseContent?: boolean
|
||||
}
|
||||
| undefined
|
||||
> = {
|
||||
key: 'PodcastEpisodeDocument',
|
||||
objectType: 'GoogleDoc',
|
||||
classes: ['podcast', 'episode', 'document'],
|
||||
|
||||
isMatch: (helpers, data, original) =>
|
||||
original
|
||||
? original.pathString.includes('/Podcasts/') &&
|
||||
/^ep\d+-\d{8}-.*/.test(original.slug)
|
||||
: false,
|
||||
|
||||
transform: async (helpers, data, original, root, context) => {
|
||||
const { shows = [] } = context ?? {}
|
||||
|
||||
const show = shows.find((show) => show.slug === original.path[2])
|
||||
|
||||
const name = original.path[original.path.length - 1]
|
||||
const [ep, date] = name.split('-')
|
||||
const slug = original.slug.slice(`${ep}-${date}-`.length)
|
||||
|
||||
const publishedAt = parseISO(date)
|
||||
const episodeNumber = parseInt(ep.slice(2), 10)
|
||||
|
||||
const coverImage = data.content.find(
|
||||
(block) => block.type === 'image' && block.order < 20,
|
||||
) as LPE.Podcast.Metadata['coverImage']
|
||||
|
||||
const channels: LPE.Podcast.Content['channels'] = []
|
||||
const credits: LPE.Podcast.Content['credits'] = []
|
||||
const transcription: LPE.Podcast.Content['transcription'] = []
|
||||
const content: LPE.Podcast.Content['content'] = []
|
||||
|
||||
const allBlocks = data.content.filter((block) => {
|
||||
if (
|
||||
((block.type === 'text' && block.html) || '').match(
|
||||
`<a href="#ftnt_ref`,
|
||||
)?.length
|
||||
)
|
||||
return false
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
if (context?.parseContent) {
|
||||
const sections = findSections(
|
||||
['Credits', 'Content', 'Timestamps', 'Transcription'],
|
||||
allBlocks,
|
||||
)
|
||||
|
||||
const textBlocks = allBlocks.filter(
|
||||
(block) => block.type === 'text',
|
||||
) as LPE.Post.TextBlock[]
|
||||
|
||||
sections.forEach((section) => {
|
||||
switch (section.name) {
|
||||
case 'Credits': {
|
||||
credits.push(
|
||||
...(section.blocks.filter(
|
||||
(block) => block.type === 'text',
|
||||
) as LPE.Post.TextBlock[]),
|
||||
)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'Content':
|
||||
case 'Timestamps':
|
||||
case 'Transcription': {
|
||||
content.push(...section.blocks)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'Transcriptions': {
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
channels.push(...(await getDistributionChannels(textBlocks)))
|
||||
}
|
||||
|
||||
return {
|
||||
id: data.id,
|
||||
slug,
|
||||
title: data.title,
|
||||
authors: data.authors,
|
||||
description: data.summary,
|
||||
modifiedAt: data.modifiedAt || '',
|
||||
publishedAt: publishedAt.toJSON(),
|
||||
episodeNumber,
|
||||
tags: data.tags,
|
||||
credits,
|
||||
content,
|
||||
transcription,
|
||||
channels,
|
||||
...(show ? { show } : {}),
|
||||
...(show ? { showId: show.id } : {}),
|
||||
...(coverImage ? { coverImage } : {}),
|
||||
highlighted: data.highlighted,
|
||||
isDraft: data.isDraft,
|
||||
type: LPE.PostTypes.Podcast,
|
||||
} as any
|
||||
},
|
||||
}
|
||||
|
||||
const getDistributionChannels = async (blocks: LPE.Post.TextBlock[]) => {
|
||||
const channels: LPE.Podcast.Channel[] = []
|
||||
|
||||
{
|
||||
const youtubeUrlRegex =
|
||||
/(https?\:\/\/)?((www\.)?youtube\.com|youtu\.?be)\/[^ "]+/gi
|
||||
|
||||
for (const block of blocks) {
|
||||
const match = (block.html || '').match(youtubeUrlRegex)
|
||||
if (match && match.length) {
|
||||
const url = match[0]
|
||||
|
||||
channels.push({
|
||||
name: LPE.Podcast.ChannelNames.Youtube,
|
||||
url,
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const linkBlocks = blocks.filter((block) =>
|
||||
/^(https):\/\/[^ "]+$/.test((block.text || '').trim()),
|
||||
)
|
||||
|
||||
for (const block of linkBlocks) {
|
||||
const { text } = block
|
||||
const url = text
|
||||
|
||||
const name = [
|
||||
[/spotify\.com/i, LPE.Podcast.ChannelNames.Spotify],
|
||||
[/podcasts\.google\.com/i, LPE.Podcast.ChannelNames.GooglePodcasts],
|
||||
[/podcasts\.apple\.com/i, LPE.Podcast.ChannelNames.ApplePodcasts],
|
||||
[/simplecast\.com/i, LPE.Podcast.ChannelNames.Simplecast],
|
||||
].find(
|
||||
([reg, name]) => url.match(reg)?.length,
|
||||
)?.[1] as LPE.Podcast.ChannelName
|
||||
|
||||
if (!name) continue
|
||||
|
||||
switch (name) {
|
||||
case LPE.Podcast.ChannelNames.Simplecast: {
|
||||
if (!simplecastApi.isValidPlayerUrl(url)) {
|
||||
console.error('invalid Simplecast player url!')
|
||||
continue
|
||||
}
|
||||
|
||||
const episodeId = simplecastApi.extractEpisodeIdFromUrl(url)
|
||||
if (!episodeId) {
|
||||
console.error('invalid Simplecast player url!')
|
||||
continue
|
||||
}
|
||||
|
||||
const [res, err] = await settle(() =>
|
||||
simplecastApi.getEpisode({ id: episodeId }),
|
||||
)
|
||||
|
||||
if (err) {
|
||||
console.error('failed to fetch Simplecast episode ', url)
|
||||
console.error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
const data: LPE.Podcast.SimplecastChannelData = {
|
||||
duration: res.duration,
|
||||
audioFileUrl: res.ad_free_audio_file_url ?? res.audio_file?.url,
|
||||
}
|
||||
|
||||
channels.push({
|
||||
name,
|
||||
url,
|
||||
data,
|
||||
})
|
||||
|
||||
break
|
||||
}
|
||||
default: {
|
||||
channels.push({
|
||||
name,
|
||||
url,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return channels
|
||||
}
|
||||
|
||||
const findSections = (
|
||||
names: string[],
|
||||
blocks: LPE.Post.ContentBlock[],
|
||||
): {
|
||||
name: string
|
||||
start: number
|
||||
end: number
|
||||
blocks: LPE.Post.ContentBlock[]
|
||||
}[] => {
|
||||
let sections: {
|
||||
name: string
|
||||
start: number
|
||||
end: number
|
||||
blocks: LPE.Post.ContentBlock[]
|
||||
}[] = names.map((name) => ({ name, start: -1, end: -1, blocks: [] }))
|
||||
|
||||
blocks.forEach((block, index) => {
|
||||
const { type, ...rest } = block
|
||||
if (block.type === 'text') {
|
||||
const sectionIndex = sections.findIndex(
|
||||
({ name }) => block.text.trim() === `[${name}]`,
|
||||
)
|
||||
|
||||
if (sectionIndex === -1) return
|
||||
|
||||
const section = sections[sectionIndex]
|
||||
section.start = index
|
||||
}
|
||||
})
|
||||
|
||||
sections = [...sections]
|
||||
.sort((a, b) => (a.start < b.start ? -1 : 1))
|
||||
.filter((section) => section.start > -1)
|
||||
|
||||
for (let i = 0; i < sections.length; i++) {
|
||||
const section = sections[i]
|
||||
const nextSection = sections[i + 1]
|
||||
section.end = nextSection ? nextSection.start - 1 : blocks.length - 1
|
||||
|
||||
section.blocks = blocks.slice(section.start + 1, section.end + 1)
|
||||
}
|
||||
|
||||
return sections
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
import { LPE } from '../../../types/lpe.types'
|
||||
import { getPostLink } from '../../../utils/route.utils'
|
||||
import { UnbodyResGoogleDocData } from '../unbody.types'
|
||||
import { UnbodyDataTypeConfig } from './types'
|
||||
|
||||
export const PodcastShowDataType: UnbodyDataTypeConfig<
|
||||
LPE.Article.Data,
|
||||
LPE.Podcast.Show,
|
||||
UnbodyResGoogleDocData,
|
||||
any,
|
||||
| {
|
||||
numberOfEpisodes: number
|
||||
}
|
||||
| undefined
|
||||
> = {
|
||||
key: 'PodcastShowDocument',
|
||||
objectType: 'GoogleDoc',
|
||||
classes: ['podcast', 'show', 'document'],
|
||||
|
||||
isMatch: (helpers, data, original) =>
|
||||
original
|
||||
? original.pathString.includes('/Podcasts/') && original.slug === 'index'
|
||||
: false,
|
||||
|
||||
transform: async (helpers, data, original, root, context) => {
|
||||
if (!original) return data as any
|
||||
|
||||
const description = data.content.find(
|
||||
(block) =>
|
||||
block.labels.length === 0 && block.type === 'text' && block.order > 2,
|
||||
)
|
||||
|
||||
const showSlug = original.path[2]
|
||||
|
||||
return {
|
||||
id: data.id,
|
||||
slug: showSlug,
|
||||
title: data.title,
|
||||
numberOfEpisodes: context?.numberOfEpisodes || 0,
|
||||
hosts: data.authors,
|
||||
url: getPostLink('podcast', { showSlug }),
|
||||
description: (description?.type === 'text' && description.html) || '',
|
||||
descriptionText: (description?.type === 'text' && description.text) || '',
|
||||
logo: {
|
||||
alt: data.title,
|
||||
width: 24,
|
||||
height: 24,
|
||||
url: `/podcasts/${showSlug}-logo.svg`,
|
||||
},
|
||||
episodes: [],
|
||||
}
|
||||
},
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
import { LPE } from '../../../types/lpe.types'
|
||||
import { UnbodyResGoogleDocData } from '../unbody.types'
|
||||
import { UnbodyDataTypeConfig } from './types'
|
||||
|
||||
export const StaticPageDataType: UnbodyDataTypeConfig<
|
||||
UnbodyResGoogleDocData,
|
||||
LPE.StaticPage.Document
|
||||
> = {
|
||||
key: 'StaticPageDocument',
|
||||
objectType: 'GoogleDoc',
|
||||
classes: ['static-page', 'document'],
|
||||
|
||||
isMatch: (helpers, data) =>
|
||||
!!data?.pathString && data.pathString.includes('/Static pages/'),
|
||||
|
||||
transform: async (helpers, data) => {
|
||||
const textBlock = helpers.dataTypes.get({
|
||||
objectType: 'TextBlock',
|
||||
})
|
||||
const imageBlock = helpers.dataTypes.get({
|
||||
objectType: 'ImageBlock',
|
||||
})
|
||||
|
||||
const blocks =
|
||||
await helpers.dataTypes.transformMany<LPE.Article.ContentBlock>(
|
||||
[...textBlock, ...imageBlock],
|
||||
[...(data.blocks || [])].sort((a, b) => a.order - b.order),
|
||||
data,
|
||||
)
|
||||
|
||||
return {
|
||||
id: data._additional.id,
|
||||
slug: data.slug,
|
||||
title: data.title,
|
||||
subtitle: data.subtitle || '',
|
||||
summary: data.summary || '',
|
||||
createdAt: data.createdAt || null,
|
||||
modifiedAt: data.modifiedAt || null,
|
||||
content: blocks,
|
||||
type: 'static_page',
|
||||
isDraft: data.pathString.includes('/draft/'),
|
||||
}
|
||||
},
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
import { LPE } from '../../../types/lpe.types'
|
||||
import { setAttributeOnHTML } from '../../../utils/html.utils'
|
||||
import { convertToIframe } from '../../../utils/string.utils'
|
||||
import { UnbodyResGoogleDocData, UnbodyResTextBlockData } from '../unbody.types'
|
||||
import { UnbodyDataTypeConfig } from './types'
|
||||
|
||||
export const TextBlockDataType: UnbodyDataTypeConfig<
|
||||
UnbodyResTextBlockData,
|
||||
LPE.Article.TextBlock,
|
||||
UnbodyResTextBlockData,
|
||||
UnbodyResGoogleDocData
|
||||
> = {
|
||||
key: 'TextBlock',
|
||||
objectType: 'TextBlock',
|
||||
classes: ['article'],
|
||||
|
||||
isMatch: (helpers, data, original, root) => data.__typename === 'TextBlock',
|
||||
|
||||
transform: (helpers, data, original, root) => {
|
||||
let { text = '', html = '' } = data
|
||||
const labels: LPE.Post.ContentBlockLabel[] = []
|
||||
let embed: LPE.Post.TextBlockEmbed | null = null
|
||||
|
||||
if (text.length > 0 || html.length > 0) {
|
||||
const isLink =
|
||||
/^<p[^>]*>(<span[^>]*>[\s]*<\/span>)*<span[^>]*><a[^>]*href="(https):\/\/[^ "]+"[^>]*>.*<\/a><\/span>(<span[^>]*>(\s|(<br(\/)?>))*<\/span>)*<\/p>$/.test(
|
||||
html,
|
||||
)
|
||||
const isIframe = /<iframe[^>]*>(?:<\/iframe>|[^]*?<\/iframe>)/.test(text)
|
||||
|
||||
if (isLink) {
|
||||
labels.push(LPE.Post.ContentBlockLabels.LinkOnly)
|
||||
|
||||
const youtube = html.match(
|
||||
/(https?\:\/\/)?((www\.)?youtube\.com|youtu\.?be)\/[^ "]+/gi,
|
||||
)
|
||||
|
||||
const simplecast = html.match(
|
||||
/(https?\:\/\/)?((player\.)?simplecast\.com)\/[^ "]+/gi,
|
||||
)
|
||||
|
||||
const label = youtube?.[0]
|
||||
? LPE.Post.ContentBlockLabels.YoutubeEmbed
|
||||
: LPE.Post.ContentBlockLabels.SimplecastEmbed
|
||||
const src = youtube?.[0] || simplecast?.[0]
|
||||
if (src && src.length > 0) {
|
||||
labels.push(label)
|
||||
labels.push(LPE.Post.ContentBlockLabels.Embed)
|
||||
|
||||
embed = {
|
||||
src,
|
||||
html: convertToIframe(src),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isIframe) {
|
||||
const src = text.match(/(?<=src=").*?(?=[\?"])/g)?.[0]
|
||||
|
||||
if (src) {
|
||||
labels.push(LPE.Post.ContentBlockLabels.Embed)
|
||||
|
||||
embed = {
|
||||
html: text,
|
||||
src,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set target="_blank" on anchor elements
|
||||
{
|
||||
const matches = Array.from(
|
||||
html.matchAll(/<a[^>]*href="http[^>]*"[^>]*>/gi),
|
||||
)
|
||||
|
||||
for (const match of matches) {
|
||||
const [anchorHTML] = match
|
||||
const newAnchorHTML = setAttributeOnHTML(anchorHTML, 'target', '_blank')
|
||||
html = html.replace(anchorHTML, newAnchorHTML)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: data?._additional?.id || `${data.order}`,
|
||||
type: 'text',
|
||||
html,
|
||||
text: data.text || '',
|
||||
classNames: data.classNames,
|
||||
footnotes: data.footnotesObj,
|
||||
order: data.order,
|
||||
tagName: data.tagName,
|
||||
labels,
|
||||
...(embed ? { embed } : {}),
|
||||
}
|
||||
},
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
import {
|
||||
UnbodyDataTypeClass,
|
||||
UnbodyDataTypeConfig,
|
||||
UnbodyDataTypeConfigHelpers,
|
||||
UnbodyDataTypeKey,
|
||||
} from './types'
|
||||
|
||||
export class UnbodyDataTypes {
|
||||
private dataTypes: UnbodyDataTypeConfig[] = []
|
||||
private helpers: UnbodyDataTypeConfigHelpers
|
||||
|
||||
constructor(dataTypes: UnbodyDataTypeConfig<any>[]) {
|
||||
this.dataTypes = dataTypes
|
||||
|
||||
this.helpers = {
|
||||
dataTypes: this,
|
||||
}
|
||||
}
|
||||
|
||||
getOne = ({
|
||||
key,
|
||||
classes,
|
||||
objectType,
|
||||
}: {
|
||||
key?: UnbodyDataTypeKey
|
||||
classes?: UnbodyDataTypeClass | UnbodyDataTypeClass[]
|
||||
objectType?: UnbodyDataTypeConfig['objectType']
|
||||
}) => {
|
||||
let dataTypes = this.dataTypes
|
||||
|
||||
if (key) {
|
||||
return dataTypes.find((doc) => doc.key === key)
|
||||
}
|
||||
|
||||
return this.get({ classes, objectType })[0]
|
||||
}
|
||||
|
||||
get = ({
|
||||
classes: _classes,
|
||||
objectType,
|
||||
}: {
|
||||
classes?: UnbodyDataTypeClass | UnbodyDataTypeClass[]
|
||||
objectType?: UnbodyDataTypeConfig['objectType']
|
||||
}) => {
|
||||
let dataTypes = this.dataTypes
|
||||
|
||||
if (objectType)
|
||||
dataTypes = dataTypes.filter(
|
||||
(dataType) => dataType.objectType === objectType,
|
||||
)
|
||||
|
||||
const classes = !_classes
|
||||
? []
|
||||
: Array.isArray(_classes)
|
||||
? _classes
|
||||
: [_classes]
|
||||
if (classes.length > 0)
|
||||
dataTypes = dataTypes.filter((dataType) =>
|
||||
classes.every((cls) => dataType.classes.includes(cls)),
|
||||
)
|
||||
|
||||
return dataTypes
|
||||
}
|
||||
|
||||
transform = async <O = any, T = any>(
|
||||
pipeline: UnbodyDataTypeConfig[],
|
||||
data: T,
|
||||
root?: any,
|
||||
context?: any,
|
||||
): Promise<O> => {
|
||||
let obj = data
|
||||
|
||||
for (const dataType of pipeline) {
|
||||
if (dataType.isMatch(this.helpers, obj, data, root, context)) {
|
||||
obj = await dataType.transform(this.helpers, obj, data, root, context)
|
||||
}
|
||||
}
|
||||
|
||||
return obj as O | Promise<O>
|
||||
}
|
||||
|
||||
transformMany = async <O = any, T = any>(
|
||||
pipeline: UnbodyDataTypeConfig[],
|
||||
data: T[],
|
||||
root?: any,
|
||||
context?: any,
|
||||
): Promise<O[]> => {
|
||||
return Promise.all(
|
||||
data.map((d) => this.transform<O, T>(pipeline, d, root, context)),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
import { ArticleDataType } from './ArticleDocument.dataType'
|
||||
import { ArticleImageBlockDataType } from './ArticleImageBlock.dataType'
|
||||
import { ArticleSearchResultItemDataType } from './ArticleSearchResultItem.dataType'
|
||||
import { ArticleTextBlockDataType } from './ArticleTextBlock.dataType'
|
||||
import { ImageBlockDataType } from './ImageBlock.dataType'
|
||||
import { PodcastEpisodeDataType } from './PodcastEpisodeDocument.dataType'
|
||||
import { PodcastShowDataType } from './PodcastShowDocument.dataType'
|
||||
import { StaticPageDataType } from './StaticPageDocument.dataType'
|
||||
import { TextBlockDataType } from './TextBlock.dataType'
|
||||
import { UnbodyDataTypes } from './UnbodyDataTypes'
|
||||
|
||||
export const unbodyDataTypes = new UnbodyDataTypes([
|
||||
ArticleDataType,
|
||||
TextBlockDataType,
|
||||
ImageBlockDataType,
|
||||
ArticleTextBlockDataType,
|
||||
ArticleImageBlockDataType,
|
||||
ArticleSearchResultItemDataType,
|
||||
PodcastShowDataType,
|
||||
PodcastEpisodeDataType,
|
||||
StaticPageDataType,
|
||||
])
|
|
@ -1,7 +0,0 @@
|
|||
export * from './ArticleDocument.dataType'
|
||||
export * from './ArticleImageBlock.dataType'
|
||||
export * from './ArticleTextBlock.dataType'
|
||||
export * from './TextBlock.dataType'
|
||||
export * from './UnbodyDataTypes'
|
||||
export * from './dataTypes'
|
||||
export * from './types'
|
|
@ -1,69 +0,0 @@
|
|||
import {
|
||||
GoogleDoc,
|
||||
ImageBlock,
|
||||
TextBlock,
|
||||
} from '../../../lib/unbody/unbody.generated'
|
||||
import { UnbodyDataTypes } from './UnbodyDataTypes'
|
||||
|
||||
export type UnbodyDataTypeConfig<
|
||||
D = any,
|
||||
T = any,
|
||||
O = any,
|
||||
R = any,
|
||||
C = any,
|
||||
> = {
|
||||
key: UnbodyDataTypeKey
|
||||
classes: UnbodyDataTypeClass[]
|
||||
objectType:
|
||||
| GoogleDoc['__typename']
|
||||
| TextBlock['__typename']
|
||||
| ImageBlock['__typename']
|
||||
|
||||
isMatch: (
|
||||
helpers: UnbodyDataTypeConfigHelpers,
|
||||
object: D,
|
||||
original: O,
|
||||
root: R | undefined,
|
||||
context: C,
|
||||
) => boolean
|
||||
|
||||
transform: (
|
||||
helpers: UnbodyDataTypeConfigHelpers,
|
||||
object: D,
|
||||
original: O,
|
||||
root: R | undefined,
|
||||
context: C,
|
||||
) => T | Promise<T>
|
||||
}
|
||||
|
||||
export type UnbodyDataTypeConfigHelpers = {
|
||||
dataTypes: UnbodyDataTypes
|
||||
}
|
||||
|
||||
export const UnbodyDataTypeKeys = {
|
||||
TextBlock: 'TextBlock',
|
||||
ImageBlock: 'ImageBlock',
|
||||
ArticleDocument: 'ArticleDocument',
|
||||
ArticleTextBlock: 'ArticleTextBlock',
|
||||
ArticleImageBlock: 'ArticleImageBlock',
|
||||
ArticleSearchResultItem: 'ArticleSearchResultItem',
|
||||
PodcastShowDocument: 'PodcastShowDocument',
|
||||
PodcastEpisodeDocument: 'PodcastEpisodeDocument',
|
||||
StaticPageDocument: 'StaticPageDocument',
|
||||
} as const
|
||||
|
||||
export type UnbodyDataTypeKey =
|
||||
(typeof UnbodyDataTypeKeys)[keyof typeof UnbodyDataTypeKeys]
|
||||
|
||||
export const UnbodyDataTypeClasses = {
|
||||
Article: 'article',
|
||||
Podcast: 'podcast',
|
||||
Show: 'show',
|
||||
Episode: 'episode',
|
||||
Document: 'document',
|
||||
Search: 'search',
|
||||
StaticPage: 'static-page',
|
||||
} as const
|
||||
|
||||
export type UnbodyDataTypeClass =
|
||||
(typeof UnbodyDataTypeClasses)[keyof typeof UnbodyDataTypeClasses]
|
|
@ -1 +0,0 @@
|
|||
export * from './unbody.service'
|
|
@ -1,151 +0,0 @@
|
|||
import { gql } from 'graphql-request'
|
||||
|
||||
export const TEXT_BLOCK_FRAGMENT_COMMON = gql`
|
||||
fragment TextBlockCommon on TextBlock {
|
||||
footnotes
|
||||
footnotesObj @client(always: true) {
|
||||
index
|
||||
id
|
||||
refId
|
||||
refValue
|
||||
valueHTML
|
||||
valueText
|
||||
}
|
||||
html
|
||||
order
|
||||
text
|
||||
tagName
|
||||
classNames
|
||||
__typename
|
||||
_additional {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const IMAGE_BLOCK_FRAGMENT_COMMON = gql`
|
||||
fragment ImageBlockCommon on ImageBlock {
|
||||
url
|
||||
alt
|
||||
order
|
||||
width
|
||||
height
|
||||
__typename
|
||||
_additional {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const GOOGLE_DOC_FRAGMENT_COMMON = gql`
|
||||
fragment GoogleDocCommon on GoogleDoc {
|
||||
sourceId
|
||||
title
|
||||
subtitle
|
||||
summary
|
||||
tags
|
||||
createdAt
|
||||
modifiedAt
|
||||
slug
|
||||
path
|
||||
pathString
|
||||
_additional {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const GOOGLE_DOC_FRAGMENT_MENTIONS = gql`
|
||||
fragment GoogleDocMentions on GoogleDoc {
|
||||
mentions
|
||||
mentionsObj @client(always: true) {
|
||||
name
|
||||
emailAddress
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const GOOGLE_DOC_FRAGMENT_TOC = gql`
|
||||
fragment GoogleDocTOC on GoogleDoc {
|
||||
toc
|
||||
tocObj @client(always: true) {
|
||||
level
|
||||
tag
|
||||
href
|
||||
title
|
||||
blockIndex
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const SEARCH_IMAGE_BLOCK_FRAGMENT = gql`
|
||||
fragment SearchImageBlock on ImageBlock {
|
||||
__typename
|
||||
|
||||
...ImageBlockCommon
|
||||
|
||||
_additional {
|
||||
id
|
||||
score
|
||||
certainty
|
||||
}
|
||||
|
||||
document {
|
||||
... on GoogleDoc {
|
||||
...GoogleDocCommon
|
||||
...GoogleDocMentions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${IMAGE_BLOCK_FRAGMENT_COMMON}
|
||||
${GOOGLE_DOC_FRAGMENT_COMMON}
|
||||
${GOOGLE_DOC_FRAGMENT_MENTIONS}
|
||||
`
|
||||
|
||||
export const SEARCH_TEXT_BLOCK_FRAGMENT = gql`
|
||||
fragment SearchTextBlock on TextBlock {
|
||||
...TextBlockCommon
|
||||
|
||||
_additional {
|
||||
id
|
||||
score
|
||||
certainty
|
||||
}
|
||||
|
||||
document {
|
||||
... on GoogleDoc {
|
||||
...GoogleDocCommon
|
||||
...GoogleDocMentions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${TEXT_BLOCK_FRAGMENT_COMMON}
|
||||
${GOOGLE_DOC_FRAGMENT_COMMON}
|
||||
${GOOGLE_DOC_FRAGMENT_MENTIONS}
|
||||
`
|
||||
|
||||
export const SEARCH_GOOGLE_DOC_FRAGMENT = gql`
|
||||
fragment SearchGoogleDoc on GoogleDoc {
|
||||
...GoogleDocCommon
|
||||
...GoogleDocMentions
|
||||
|
||||
blocks {
|
||||
... on ImageBlock {
|
||||
...ImageBlockCommon
|
||||
}
|
||||
}
|
||||
|
||||
_additional {
|
||||
id
|
||||
score
|
||||
certainty
|
||||
}
|
||||
}
|
||||
|
||||
${TEXT_BLOCK_FRAGMENT_COMMON}
|
||||
${IMAGE_BLOCK_FRAGMENT_COMMON}
|
||||
${GOOGLE_DOC_FRAGMENT_COMMON}
|
||||
${GOOGLE_DOC_FRAGMENT_MENTIONS}
|
||||
`
|
|
@ -1,111 +0,0 @@
|
|||
import {
|
||||
GetObjectsGoogleDocWhereInpObj,
|
||||
GetObjectsGoogleDocWhereOperandsInpObj,
|
||||
GoogleDocAdditional,
|
||||
ImageBlockAdditional,
|
||||
TextBlockAdditional,
|
||||
} from '../../lib/unbody/unbody.generated'
|
||||
|
||||
export class UnbodyHelpers {
|
||||
static resolveScore = (
|
||||
_additional: Partial<
|
||||
GoogleDocAdditional | ImageBlockAdditional | TextBlockAdditional
|
||||
>,
|
||||
): number =>
|
||||
_additional?.certainty ||
|
||||
(_additional?.score && parseFloat(_additional.score)) ||
|
||||
0
|
||||
|
||||
static args = {
|
||||
limit: (value: number): any => String(value),
|
||||
skip: (value: number): any => String(value),
|
||||
page: (skip: number, limit: number = 10) => ({
|
||||
limit: this.args.limit(limit),
|
||||
skip: this.args.skip(skip),
|
||||
}),
|
||||
wherePath: (
|
||||
path: Array<string | null | undefined | false>,
|
||||
key: string[] = ['path'],
|
||||
) => {
|
||||
const input = path.filter((p) => p && typeof p === 'string') as string[]
|
||||
const paths: string[] = []
|
||||
const or: string[] = []
|
||||
const exclude: string[] = []
|
||||
|
||||
for (const p of input) {
|
||||
if (p.startsWith('!')) exclude.push(p.slice(1))
|
||||
else if (p.includes('|'))
|
||||
or.push(...p.split('|').filter((s) => s.length > 0))
|
||||
else paths.push(p)
|
||||
}
|
||||
|
||||
return {
|
||||
operator: 'And',
|
||||
operands: [
|
||||
...paths.map(
|
||||
(p) =>
|
||||
({
|
||||
operator: 'Equal',
|
||||
path: key,
|
||||
valueString: p,
|
||||
} as GetObjectsGoogleDocWhereInpObj),
|
||||
),
|
||||
...exclude.map(
|
||||
(p) =>
|
||||
({
|
||||
operator: 'NotEqual',
|
||||
path: key,
|
||||
valueString: p,
|
||||
} as GetObjectsGoogleDocWhereInpObj),
|
||||
),
|
||||
...(or.length
|
||||
? [
|
||||
{
|
||||
operator: 'Or',
|
||||
operands: or.map(
|
||||
(p) =>
|
||||
({
|
||||
operator: 'Equal',
|
||||
path: key,
|
||||
valueString: p,
|
||||
} as GetObjectsGoogleDocWhereInpObj),
|
||||
),
|
||||
} as GetObjectsGoogleDocWhereInpObj,
|
||||
]
|
||||
: []),
|
||||
],
|
||||
} as GetObjectsGoogleDocWhereInpObj
|
||||
},
|
||||
wherePublished: (value: boolean, path: string[] = ['pathString']) =>
|
||||
({
|
||||
operator: 'Or',
|
||||
operands: value
|
||||
? [
|
||||
{
|
||||
path,
|
||||
operator: 'Like',
|
||||
valueString: '/Articles/published/*',
|
||||
},
|
||||
]
|
||||
: [],
|
||||
} as GetObjectsGoogleDocWhereOperandsInpObj),
|
||||
whereSlugIs: (slug: string, path = ['slug']) =>
|
||||
({
|
||||
operator: 'Equal',
|
||||
path,
|
||||
valueString: slug,
|
||||
} as GetObjectsGoogleDocWhereOperandsInpObj),
|
||||
whereSlugIsNot: (slug: string, path = ['slug']) =>
|
||||
({
|
||||
operator: 'NotEqual',
|
||||
path: path,
|
||||
valueString: slug,
|
||||
} as GetObjectsGoogleDocWhereOperandsInpObj),
|
||||
whereIdIsNot: (id: string) =>
|
||||
({
|
||||
operator: 'NotEqual',
|
||||
path: ['id'],
|
||||
valueString: id,
|
||||
} as GetObjectsGoogleDocWhereOperandsInpObj),
|
||||
}
|
||||
}
|
|
@ -1,237 +0,0 @@
|
|||
import { gql } from '@apollo/client'
|
||||
|
||||
export const COUNT_DOCUMENTS_QUERY = gql`
|
||||
query CountDocuments($filter: AggregateObjectsGoogleDocWhereInpObj) {
|
||||
Aggregate {
|
||||
GoogleDoc(where: $filter) {
|
||||
meta {
|
||||
count
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
export const GET_POSTS_QUERY = gql`
|
||||
query GetPosts(
|
||||
$filter: GetObjectsGoogleDocWhereInpObj
|
||||
$sort: [GetObjectsGoogleDocSortInpObj]
|
||||
$searchResult: Boolean = false
|
||||
$nearText: Txt2VecC11yGetObjectsGoogleDocNearTextInpObj
|
||||
$hybrid: GetObjectsGoogleDocHybridInpObj
|
||||
$nearObject: GetObjectsGoogleDocNearObjectInpObj
|
||||
$skip: Int = 0
|
||||
$limit: Int = 10
|
||||
$toc: Boolean = false
|
||||
$mentions: Boolean = false
|
||||
$textBlocks: Boolean = false
|
||||
$imageBlocks: Boolean = false
|
||||
$remoteId: Boolean = false
|
||||
) {
|
||||
Get {
|
||||
GoogleDoc(
|
||||
where: $filter
|
||||
hybrid: $hybrid
|
||||
nearText: $nearText
|
||||
nearObject: $nearObject
|
||||
sort: $sort
|
||||
offset: $skip
|
||||
limit: $limit
|
||||
) {
|
||||
_additional {
|
||||
id
|
||||
score @include(if: $searchResult)
|
||||
distance @include(if: $searchResult)
|
||||
certainty @include(if: $searchResult)
|
||||
}
|
||||
title
|
||||
subtitle
|
||||
summary
|
||||
slug
|
||||
tags
|
||||
path
|
||||
createdAt
|
||||
modifiedAt
|
||||
pathString
|
||||
remoteId @include(if: $remoteId)
|
||||
mentions @include(if: $mentions)
|
||||
mentionsObj @client(always: true) @include(if: $mentions) {
|
||||
name
|
||||
emailAddress
|
||||
}
|
||||
toc @include(if: $toc)
|
||||
tocObj @client(always: true) @include(if: $toc) {
|
||||
level
|
||||
tag
|
||||
href
|
||||
title
|
||||
blockIndex
|
||||
}
|
||||
blocks {
|
||||
... on ImageBlock @include(if: $imageBlocks) {
|
||||
url
|
||||
alt
|
||||
order
|
||||
width
|
||||
height
|
||||
__typename
|
||||
_additional {
|
||||
id
|
||||
}
|
||||
}
|
||||
... on TextBlock @include(if: $textBlocks) {
|
||||
footnotes
|
||||
footnotesObj @client(always: true) {
|
||||
index
|
||||
id
|
||||
refId
|
||||
refValue
|
||||
valueHTML
|
||||
valueText
|
||||
}
|
||||
html
|
||||
order
|
||||
text
|
||||
tagName
|
||||
classNames
|
||||
__typename
|
||||
_additional {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const GET_ALL_TOPICS_QUERY = gql`
|
||||
query GetAllTopics($filter: AggregateObjectsGoogleDocWhereInpObj) {
|
||||
Aggregate {
|
||||
GoogleDoc(where: $filter, groupBy: "tags") {
|
||||
groupedBy {
|
||||
value
|
||||
}
|
||||
tags {
|
||||
topOccurrences {
|
||||
value
|
||||
occurs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const SEARCH_BLOCKS_QUERY = gql`
|
||||
query SearchBlocks(
|
||||
$limit: Int
|
||||
$skip: Int
|
||||
$textNearText: Txt2VecC11yGetObjectsTextBlockNearTextInpObj
|
||||
$imageNearText: Txt2VecC11yGetObjectsImageBlockNearTextInpObj
|
||||
$textFilter: GetObjectsTextBlockWhereInpObj
|
||||
$imageFilter: GetObjectsImageBlockWhereInpObj
|
||||
$textHybrid: GetObjectsTextBlockHybridInpObj
|
||||
$imageHybrid: GetObjectsImageBlockHybridInpObj
|
||||
$text: Boolean = true
|
||||
$image: Boolean = true
|
||||
) {
|
||||
Get {
|
||||
TextBlock(
|
||||
where: $textFilter
|
||||
nearText: $textNearText
|
||||
hybrid: $textHybrid
|
||||
limit: $limit
|
||||
offset: $skip
|
||||
) @include(if: $text) {
|
||||
footnotes
|
||||
footnotesObj @client(always: true) {
|
||||
index
|
||||
id
|
||||
refId
|
||||
refValue
|
||||
valueHTML
|
||||
valueText
|
||||
}
|
||||
html
|
||||
order
|
||||
text
|
||||
tagName
|
||||
classNames
|
||||
__typename
|
||||
|
||||
document {
|
||||
... on GoogleDoc {
|
||||
sourceId
|
||||
title
|
||||
subtitle
|
||||
summary
|
||||
tags
|
||||
createdAt
|
||||
modifiedAt
|
||||
slug
|
||||
path
|
||||
pathString
|
||||
_additional {
|
||||
id
|
||||
}
|
||||
mentions
|
||||
mentionsObj @client(always: true) {
|
||||
name
|
||||
emailAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_additional {
|
||||
certainty
|
||||
score
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
ImageBlock(
|
||||
where: $imageFilter
|
||||
nearText: $imageNearText
|
||||
hybrid: $imageHybrid
|
||||
limit: $limit
|
||||
offset: $skip
|
||||
) @include(if: $image) {
|
||||
url
|
||||
alt
|
||||
order
|
||||
width
|
||||
height
|
||||
__typename
|
||||
|
||||
document {
|
||||
... on GoogleDoc {
|
||||
sourceId
|
||||
title
|
||||
subtitle
|
||||
summary
|
||||
tags
|
||||
createdAt
|
||||
modifiedAt
|
||||
slug
|
||||
path
|
||||
pathString
|
||||
_additional {
|
||||
id
|
||||
}
|
||||
mentions
|
||||
mentionsObj @client(always: true) {
|
||||
name
|
||||
emailAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_additional {
|
||||
certainty
|
||||
score
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
File diff suppressed because it is too large
Load Diff
|
@ -1,36 +0,0 @@
|
|||
import {
|
||||
GoogleDocCommonFragment,
|
||||
GoogleDocMentionsFragment,
|
||||
GoogleDocTocFragment,
|
||||
ImageBlockCommonFragment,
|
||||
SearchGoogleDocFragment,
|
||||
SearchImageBlockFragment,
|
||||
SearchTextBlockFragment,
|
||||
TextBlockCommonFragment,
|
||||
} from '../../lib/unbody/unbody.generated'
|
||||
|
||||
export type UnbodyResTextBlockData = TextBlockCommonFragment
|
||||
export type UnbodyResImageBlockData = ImageBlockCommonFragment
|
||||
export type UnbodyResGoogleDocData = GoogleDocCommonFragment &
|
||||
GoogleDocMentionsFragment &
|
||||
GoogleDocTocFragment & {
|
||||
blocks: Array<UnbodyResTextBlockData | UnbodyResImageBlockData>
|
||||
}
|
||||
|
||||
export type UnbodyResRelatedPostData = GoogleDocCommonFragment &
|
||||
GoogleDocMentionsFragment
|
||||
|
||||
export type UnbodyResPostData = {
|
||||
data: UnbodyResGoogleDocData
|
||||
relatedArticles: UnbodyResRelatedPostData[]
|
||||
articlesFromSameAuthors: UnbodyResRelatedPostData[]
|
||||
}
|
||||
|
||||
export type UnbodyResSearchGoogleDocData = SearchGoogleDocFragment
|
||||
export type UnbodyResSearchResultTextBlockData = SearchTextBlockFragment
|
||||
export type UnbodyResSearchResultImageBlockData = SearchImageBlockFragment
|
||||
|
||||
export type ApiSearchResultItem =
|
||||
| UnbodyResSearchGoogleDocData
|
||||
| UnbodyResSearchResultTextBlockData
|
||||
| UnbodyResSearchResultImageBlockData
|
|
@ -31,11 +31,7 @@ export type SearchHookDataPayload = {
|
|||
export type SearchResults = {
|
||||
articles: SearchHook<LPE.Article.ContentBlock>
|
||||
blocks: SearchHook<LPE.Article.ContentBlock>
|
||||
search: (
|
||||
query: string,
|
||||
tags: string[],
|
||||
docType: any, // TODO: @refactor UnbodyGraphQl.UnbodyDocumentTypeNames
|
||||
) => Promise<void>
|
||||
search: (query: string, tags: string[], docType: any) => Promise<void>
|
||||
reset: (initialData: SearchHookDataPayload) => void
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue