diff --git a/apps/admin-x-settings/package.json b/apps/admin-x-settings/package.json index 5f0dbef6be..d1a8bc0da8 100644 --- a/apps/admin-x-settings/package.json +++ b/apps/admin-x-settings/package.json @@ -39,6 +39,7 @@ "dependencies": { "@codemirror/lang-html": "^6.4.5", "@tryghost/color-utils": "0.2.0", + "@tryghost/kg-unsplash-selector": "^0.1.11", "@tryghost/limit-service": "^1.2.10", "@tryghost/nql": "0.12.1", "@tryghost/timezone-data": "0.4.1", diff --git a/apps/admin-x-settings/src/components/selectors/UnsplashSelector.tsx b/apps/admin-x-settings/src/components/selectors/UnsplashSelector.tsx new file mode 100644 index 0000000000..0e6665b912 --- /dev/null +++ b/apps/admin-x-settings/src/components/selectors/UnsplashSelector.tsx @@ -0,0 +1,24 @@ +import '@tryghost/kg-unsplash-selector/dist/style.css'; +import Portal from '../../utils/portal'; +import React from 'react'; +import {DefaultHeaderTypes, PhotoType, UnsplashSearchModal} from '@tryghost/kg-unsplash-selector'; + +type UnsplashSelectorModalProps = { + onClose: () => void; + onImageInsert: (image: PhotoType) => void; + unsplashProviderConfig: DefaultHeaderTypes | null; +}; + +const UnsplashSelector : React.FC = ({unsplashProviderConfig, onClose, onImageInsert}) => { + return ( + + + + ); +}; + +export default UnsplashSelector; diff --git a/apps/admin-x-settings/src/components/settings/site/designAndBranding/BrandSettings.tsx b/apps/admin-x-settings/src/components/settings/site/designAndBranding/BrandSettings.tsx index 2be76a8b76..05a9a735f9 100644 --- a/apps/admin-x-settings/src/components/settings/site/designAndBranding/BrandSettings.tsx +++ b/apps/admin-x-settings/src/components/settings/site/designAndBranding/BrandSettings.tsx @@ -1,5 +1,5 @@ import React, {useRef, useState} from 'react'; -import UnsplashSearchModal from '../../../../unsplash/UnsplashSearchModal'; +import UnsplashSelector from '../../../selectors/UnsplashSelector'; import usePinturaEditor from '../../../../hooks/usePinturaEditor'; import {ColorPickerField, Heading, Hint, ImageUpload, SettingGroupContent, TextField, debounce} from '@tryghost/admin-x-design-system'; import {SettingValue, getSettingValues} from '@tryghost/admin-x-framework/api/settings'; @@ -144,10 +144,8 @@ const BrandSettings: React.FC<{ values: BrandSettingValues, updateSetting: (key: { showUnsplash && unsplashConfig && unsplashEnabled && ( - { setShowUnsplash(false); }} diff --git a/apps/admin-x-settings/src/unsplash/UnsplashSearchModal.tsx b/apps/admin-x-settings/src/unsplash/UnsplashSearchModal.tsx deleted file mode 100644 index 86c00216c1..0000000000 --- a/apps/admin-x-settings/src/unsplash/UnsplashSearchModal.tsx +++ /dev/null @@ -1,192 +0,0 @@ -import MasonryService from './masonry/MasonryService'; -import Portal from './portal'; -import React, {useMemo, useRef, useState} from 'react'; -import UnsplashGallery from './ui/UnsplashGallery'; -import UnsplashSelector from './ui/UnsplashSelector'; -import {DefaultHeaderTypes, Photo} from './UnsplashTypes'; -import {PhotoUseCases} from './photo/PhotoUseCase'; -import {UnsplashProvider} from './api/UnsplashProvider'; -import {UnsplashService} from './UnsplashService'; - -interface UnsplashModalProps { - onClose: () => void; - onImageInsert: (image: Photo) => void; - unsplashConf: { - defaultHeaders: DefaultHeaderTypes; - }; - } - -const UnsplashSearchModal : React.FC = ({onClose, onImageInsert, unsplashConf}) => { - const unsplashRepo = useMemo(() => new UnsplashProvider(unsplashConf.defaultHeaders), [unsplashConf.defaultHeaders]); - const photoUseCase = useMemo(() => new PhotoUseCases(unsplashRepo), [unsplashRepo]); - const masonryService = useMemo(() => new MasonryService(3), []); - const UnsplashLib = useMemo(() => new UnsplashService(photoUseCase, masonryService), [photoUseCase, masonryService]); - const galleryRef = useRef(null); - const [scrollPos, setScrollPos] = useState(0); - const [lastScrollPos, setLastScrollPos] = useState(0); - const [isLoading, setIsLoading] = useState(UnsplashLib.searchIsRunning() || true); - const initLoadRef = useRef(false); - const [searchTerm, setSearchTerm] = useState(''); - const [zoomedImg, setZoomedImg] = useState(null); - const [dataset, setDataset] = useState([]); - - React.useEffect(() => { - if (galleryRef.current && zoomedImg === null && lastScrollPos !== 0) { - galleryRef.current.scrollTop = lastScrollPos; - setLastScrollPos(0); - } - }, [zoomedImg, scrollPos, lastScrollPos]); - - React.useEffect(() => { - const handleKeyDown = (e:KeyboardEvent) => { - if (e.key === 'Escape') { - onClose(); - } - }; - window.addEventListener('keydown', handleKeyDown); - return () => { - window.removeEventListener('keydown', handleKeyDown); - }; - }, [onClose]); - - React.useEffect(() => { - const ref = galleryRef.current; - if (!zoomedImg) { - if (ref) { - ref.addEventListener('scroll', () => { - setScrollPos(ref.scrollTop); - }); - } - // unmount - return () => { - if (ref) { - ref.removeEventListener('scroll', () => { - setScrollPos(ref.scrollTop); - }); - } - }; - } - }, [galleryRef, zoomedImg]); - - const loadInitPhotos = React.useCallback(async () => { - if (initLoadRef.current === false || searchTerm.length === 0) { - setDataset([]); - UnsplashLib.clearPhotos(); - await UnsplashLib.loadNew(); - const columns = UnsplashLib.getColumns(); - setDataset(columns || []); - if (galleryRef.current && galleryRef.current.scrollTop !== 0) { - galleryRef.current.scrollTop = 0; - } - setIsLoading(false); - } - }, [UnsplashLib, searchTerm]); - - const handleSearch = async (e: React.ChangeEvent) => { - const query = e.target.value; - if (query.length > 2) { - setZoomedImg(null); - setSearchTerm(query); - } - if (query.length === 0) { - setSearchTerm(''); - initLoadRef.current = false; - await loadInitPhotos(); - } - }; - - const search = React.useCallback(async () => { - if (searchTerm) { - setIsLoading(true); - setDataset([]); - UnsplashLib.clearPhotos(); - await UnsplashLib.updateSearch(searchTerm); - const columns = UnsplashLib.getColumns(); - if (columns) { - setDataset(columns); - } - if (galleryRef.current && galleryRef.current.scrollTop !== 0) { - galleryRef.current.scrollTop = 0; - } - setIsLoading(false); - } - }, [searchTerm, UnsplashLib]); - - React.useEffect(() => { - const timeoutId = setTimeout(async () => { - if (searchTerm.length > 2) { - await search(); - } else { - await loadInitPhotos(); - } - }, 300); - return () => { - initLoadRef.current = true; - clearTimeout(timeoutId); - }; - }, [searchTerm, search, loadInitPhotos]); - - const loadMorePhotos = React.useCallback(async () => { - setIsLoading(true); - await UnsplashLib.loadNextPage(); - const columns = UnsplashLib.getColumns(); - setDataset(columns || []); - setIsLoading(false); - }, [UnsplashLib]); - - React.useEffect(() => { - const ref = galleryRef.current; - if (ref) { - const handleScroll = async () => { - if (zoomedImg === null && ref.scrollTop + ref.clientHeight >= ref.scrollHeight - 1000) { - await loadMorePhotos(); - } - }; - ref.addEventListener('scroll', handleScroll); - return () => { - ref.removeEventListener('scroll', handleScroll); - }; - } - }, [galleryRef, loadMorePhotos, zoomedImg]); - - const selectImg = (payload:Photo) => { - if (payload) { - setZoomedImg(payload); - setLastScrollPos(scrollPos); - } - - if (payload === null) { - setZoomedImg(null); - if (galleryRef.current) { - galleryRef.current.scrollTop = lastScrollPos; - } - } - }; - - async function insertImage(image:Photo) { - if (image.src) { - UnsplashLib.triggerDownload(image); - onImageInsert(image); - } - } - return ( - - - - - - ); -}; - -export default UnsplashSearchModal; diff --git a/apps/admin-x-settings/src/unsplash/UnsplashService.ts b/apps/admin-x-settings/src/unsplash/UnsplashService.ts deleted file mode 100644 index 2bc0941563..0000000000 --- a/apps/admin-x-settings/src/unsplash/UnsplashService.ts +++ /dev/null @@ -1,68 +0,0 @@ -import MasonryService from './masonry/MasonryService'; -import {Photo} from './UnsplashTypes'; -import {PhotoUseCases} from './photo/PhotoUseCase'; - -export interface IUnsplashService { - loadNew(): Promise; - layoutPhotos(): void; - getColumns(): Photo[][] | [] | null; - updateSearch(term: string): Promise; - loadNextPage(): Promise; - clearPhotos(): void; - triggerDownload(photo: Photo): void; - photos: Photo[]; - searchIsRunning(): boolean; -} - -export class UnsplashService implements IUnsplashService { - private photoUseCases: PhotoUseCases; - private masonryService: MasonryService; - public photos: Photo[] = []; - - constructor(photoUseCases: PhotoUseCases, masonryService: MasonryService) { - this.photoUseCases = photoUseCases; - this.masonryService = masonryService; - } - - async loadNew() { - let images = await this.photoUseCases.fetchPhotos(); - this.photos = images; - await this.layoutPhotos(); - } - - async layoutPhotos() { - this.masonryService.reset(); - this.photos.forEach((photo) => { - photo.ratio = photo.height / photo.width; - this.masonryService.addPhotoToColumns(photo); - }); - } - - getColumns() { - return this.masonryService.getColumns(); - } - - async updateSearch(term: string) { - let results = await this.photoUseCases.searchPhotos(term); - this.photos = results; - this.layoutPhotos(); - } - - async loadNextPage() { - const newPhotos = await this.photoUseCases.fetchNextPage() || []; - this.photos = [...this.photos, ...newPhotos]; - this.layoutPhotos(); - } - - clearPhotos() { - this.photos = []; - } - - triggerDownload(photo: Photo) { - this.photoUseCases.triggerDownload(photo); - } - - searchIsRunning() { - return this.photoUseCases.searchIsRunning(); - } -} diff --git a/apps/admin-x-settings/src/unsplash/UnsplashTypes.ts b/apps/admin-x-settings/src/unsplash/UnsplashTypes.ts deleted file mode 100644 index 3c78aa223f..0000000000 --- a/apps/admin-x-settings/src/unsplash/UnsplashTypes.ts +++ /dev/null @@ -1,80 +0,0 @@ -export type URLS = { - raw: string; - full: string; - regular: string; - small: string; - thumb: string; - }; - -export type Links = { - self: string; - html: string; - download: string; - download_location: string; - }; - -export type ProfileImage = { - small: string; - medium: string; - large: string; - }; - -export type User = { - id: string; - updated_at: string; - username: string; - name: string; - first_name: string; - last_name: string; - twitter_username: string; - portfolio_url: string; - bio: string; - location: string; - links: Links; - profile_image: ProfileImage; - instagram_username: string; - total_collections: number; - total_likes: number; - total_photos: number; - accepted_tos: boolean; - for_hire: boolean; - social: { - instagram_username: string; - portfolio_url: string; - twitter_username: string; - paypal_email: null | string; - }; - }; - -export type Photo = { - id: string; - slug: string; - created_at: string; - updated_at: string; - promoted_at: string | null; // Nullable - width: number; - height: number; - color: string; - blur_hash: string; - description: null | string; // Nullable - alt_description: string; - breadcrumbs: []; // You could make this more specific - urls: URLS; - links: Links; - likes: number; - liked_by_user: boolean; - current_user_collections: []; // You could make this more specific - sponsorship: null | []; // Nullable - topic_submissions: []; // You could make this more specific - user: User; - ratio: number; - src? : string; - }; - -export type DefaultHeaderTypes = { - Authorization: string; - 'Accept-Version': string; - 'Content-Type': string; - 'App-Pragma': string; - 'X-Unsplash-Cache': boolean; -}; diff --git a/apps/admin-x-settings/src/unsplash/api/InMemoryUnsplashProvider.ts b/apps/admin-x-settings/src/unsplash/api/InMemoryUnsplashProvider.ts deleted file mode 100644 index c58b530539..0000000000 --- a/apps/admin-x-settings/src/unsplash/api/InMemoryUnsplashProvider.ts +++ /dev/null @@ -1,54 +0,0 @@ -// for testing purposes -import {Photo} from '../UnsplashTypes'; -import {fixturePhotos} from './unsplashFixtures'; - -export class InMemoryUnsplashProvider { - photos: Photo[] = fixturePhotos; - PAGINATION: { [key: string]: string } = {}; - REQUEST_IS_RUNNING: boolean = false; - SEARCH_IS_RUNNING: boolean = false; - LAST_REQUEST_URL: string = ''; - ERROR: string | null = null; - IS_LOADING: boolean = false; - currentPage: number = 1; - - public async fetchPhotos(): Promise { - this.IS_LOADING = true; - - const start = (this.currentPage - 1) * 30; - const end = this.currentPage * 30; - this.currentPage += 1; - - this.IS_LOADING = false; - - return this.photos.slice(start, end); - } - - public async fetchNextPage(): Promise { - if (this.REQUEST_IS_RUNNING || this.SEARCH_IS_RUNNING) { - return null; - } - - const photos = await this.fetchPhotos(); - return photos.length > 0 ? photos : null; - } - - public async searchPhotos(term: string): Promise { - this.SEARCH_IS_RUNNING = true; - const filteredPhotos = this.photos.filter(photo => photo.description?.includes(term) || photo.alt_description?.includes(term) - ); - this.SEARCH_IS_RUNNING = false; - - return filteredPhotos; - } - - searchIsRunning(): boolean { - return this.SEARCH_IS_RUNNING; - } - - triggerDownload(photo: Photo): void { - () => { - photo; - }; - } -} diff --git a/apps/admin-x-settings/src/unsplash/api/UnsplashProvider.ts b/apps/admin-x-settings/src/unsplash/api/UnsplashProvider.ts deleted file mode 100644 index 7050e62943..0000000000 --- a/apps/admin-x-settings/src/unsplash/api/UnsplashProvider.ts +++ /dev/null @@ -1,161 +0,0 @@ -import {DefaultHeaderTypes, Photo} from '../UnsplashTypes'; - -export class UnsplashProvider { - API_URL: string = 'https://api.unsplash.com'; - HEADERS: DefaultHeaderTypes; - ERROR: string | null = null; - PAGINATION: { [key: string]: string } = {}; - REQUEST_IS_RUNNING: boolean = false; - SEARCH_IS_RUNNING: boolean = false; - LAST_REQUEST_URL: string = ''; - IS_LOADING: boolean = false; - - constructor(HEADERS: DefaultHeaderTypes) { - this.HEADERS = HEADERS; - } - - private async makeRequest(url: string): Promise { - if (this.REQUEST_IS_RUNNING) { - return null; - } - - this.LAST_REQUEST_URL = url; - const options = { - method: 'GET', - headers: this.HEADERS as unknown as HeadersInit - }; - - try { - this.REQUEST_IS_RUNNING = true; - this.IS_LOADING = true; - - const response = await fetch(url, options); - const checkedResponse = await this.checkStatus(response); - this.extractPagination(checkedResponse); - - const jsonResponse = await checkedResponse.json(); - - if ('results' in jsonResponse) { - return jsonResponse.results; - } else { - return jsonResponse; - } - } catch (error) { - this.ERROR = error as string; - return null; - } finally { - this.REQUEST_IS_RUNNING = false; - this.IS_LOADING = false; - } - } - - private extractPagination(response: Response): Response { - let linkRegex = new RegExp('<(.*)>; rel="(.*)"'); - - let links = []; - - let pagination : { [key: string]: string } = {}; - - for (let entry of response.headers.entries()) { - if (entry[0] === 'link') { - links.push(entry[1]); - } - } - - if (links) { - links.toString().split(',').forEach((link) => { - if (link){ - let linkParts = linkRegex.exec(link); - if (linkParts) { - pagination[linkParts[2]] = linkParts[1]; - } - } - }); - } - - this.PAGINATION = pagination; - - return response; - } - - public async fetchPhotos(): Promise { - const url = `${this.API_URL}/photos?per_page=30`; - const request = await this.makeRequest(url); - return request as Photo[]; - } - - public async fetchNextPage(): Promise { - if (this.REQUEST_IS_RUNNING) { - return null; - } - - if (this.SEARCH_IS_RUNNING) { - return null; - } - - if (this.PAGINATION.next) { - const url = `${this.PAGINATION.next}`; - const response = await this.makeRequest(url); - if (response) { - return response as Photo[]; - } - } - - return null; - } - - public async searchPhotos(term: string): Promise { - const url = `${this.API_URL}/search/photos?query=${term}&per_page=30`; - - const request = await this.makeRequest(url); - if (request) { - return request as Photo[]; - } - - return []; - } - - public async triggerDownload(photo: Photo): Promise { - if (photo.links.download_location) { - await this.makeRequest(photo.links.download_location); - } - } - - private async checkStatus(response: Response): Promise { - if (response.status >= 200 && response.status < 300) { - return response; - } - - let errorText = ''; - let responseTextPromise: Promise; // or Promise if you know the type - - const contentType = response.headers.get('content-type'); - if (contentType === 'application/json') { - responseTextPromise = response.json().then(json => (json).errors[0]); // or cast to a specific type if you know it - } else if (contentType === 'text/xml') { - responseTextPromise = response.text(); - } else { - throw new Error('Unsupported content type'); - } - - return responseTextPromise.then((responseText: string) => { // you can type responseText based on what you expect - if (response.status === 403 && response.headers.get('x-ratelimit-remaining') === '0') { - // we've hit the rate limit on the API - errorText = 'Unsplash API rate limit reached, please try again later.'; - } - - errorText = errorText || responseText || `Error ${response.status}: Uh-oh! Trouble reaching the Unsplash API`; - - // set error text for display in UI - this.ERROR = errorText; - - // throw error to prevent further processing - let error = new Error(errorText) as Error; // or create a custom Error class - throw error; - }); - } - - searchIsRunning(): boolean { - return this.SEARCH_IS_RUNNING; - } -} diff --git a/apps/admin-x-settings/src/unsplash/api/unsplashFixtures.ts b/apps/admin-x-settings/src/unsplash/api/unsplashFixtures.ts deleted file mode 100644 index 3a56b8ed6b..0000000000 --- a/apps/admin-x-settings/src/unsplash/api/unsplashFixtures.ts +++ /dev/null @@ -1,142 +0,0 @@ -import {Photo} from '../UnsplashTypes'; - -export const fixturePhotos: Photo[] = [ - { - id: '1', - slug: 'photo1', - created_at: '2021-01-01', - updated_at: '2021-01-02', - promoted_at: null, - width: 1080, - height: 720, - color: '#ffffff', - blur_hash: 'abc123', - description: 'A nice photo', - alt_description: 'alt1', - breadcrumbs: [], - urls: { - raw: 'http://example.com/raw1', - full: 'http://example.com/full1', - regular: 'http://example.com/regular1', - small: 'http://example.com/small1', - thumb: 'http://example.com/thumb1' - }, - links: { - self: 'http://example.com/self1', - html: 'http://example.com/html1', - download: 'http://example.com/download1', - download_location: 'http://example.com/download_location1' - }, - likes: 100, - liked_by_user: true, - current_user_collections: [], - sponsorship: null, - topic_submissions: [], - user: { - id: 'user1', - updated_at: '2021-01-01', - username: 'user1', - name: 'User One', - first_name: 'User', - last_name: 'One', - twitter_username: 'user1_twitter', - portfolio_url: 'http://portfolio1.com', - bio: 'Bio1', - location: 'Location1', - links: { - self: 'http://example.com/self1', - html: 'http://example.com/html1', - download: 'http://example.com/download1', - download_location: 'http://example.com/download_location1' - }, - profile_image: { - small: 'http://small1.com', - medium: 'http://medium1.com', - large: 'http://large1.com' - }, - instagram_username: 'insta1', - total_collections: 10, - total_likes: 100, - total_photos: 1000, - accepted_tos: true, - for_hire: false, - social: { - instagram_username: 'insta1', - portfolio_url: 'http://portfolio1.com', - twitter_username: 'user1_twitter', - paypal_email: null - } - }, - ratio: 1.5, - src: 'http://src1.com' - }, - { - id: '2', - slug: 'photo1', - created_at: '2021-01-01', - updated_at: '2021-01-02', - promoted_at: null, - width: 1080, - height: 720, - color: '#ffffff', - blur_hash: 'abc123', - description: 'hello world', - alt_description: 'alt1', - breadcrumbs: [], - urls: { - raw: 'http://example.com/raw1', - full: 'http://example.com/full1', - regular: 'http://example.com/regular1', - small: 'http://example.com/small1', - thumb: 'http://example.com/thumb1' - }, - links: { - self: 'http://example.com/self1', - html: 'http://example.com/html1', - download: 'http://example.com/download1', - download_location: 'http://example.com/download_location1' - }, - likes: 100, - liked_by_user: true, - current_user_collections: [], - sponsorship: null, - topic_submissions: [], - user: { - id: 'user1', - updated_at: '2021-01-01', - username: 'user1', - name: 'User One', - first_name: 'User', - last_name: 'One', - twitter_username: 'user1_twitter', - portfolio_url: 'http://portfolio1.com', - bio: 'Bio1', - location: 'Location1', - links: { - self: 'http://example.com/self1', - html: 'http://example.com/html1', - download: 'http://example.com/download1', - download_location: 'http://example.com/download_location1' - }, - profile_image: { - small: 'http://small1.com', - medium: 'http://medium1.com', - large: 'http://large1.com' - }, - instagram_username: 'insta1', - total_collections: 10, - total_likes: 100, - total_photos: 1000, - accepted_tos: true, - for_hire: false, - social: { - instagram_username: 'insta1', - portfolio_url: 'http://portfolio1.com', - twitter_username: 'user1_twitter', - paypal_email: null - } - }, - ratio: 1.5, - src: 'http://src1.com' - } -]; diff --git a/apps/admin-x-settings/src/unsplash/assets/kg-card-type-unsplash.svg b/apps/admin-x-settings/src/unsplash/assets/kg-card-type-unsplash.svg deleted file mode 100644 index 805b50f3e5..0000000000 --- a/apps/admin-x-settings/src/unsplash/assets/kg-card-type-unsplash.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/apps/admin-x-settings/src/unsplash/assets/kg-close.svg b/apps/admin-x-settings/src/unsplash/assets/kg-close.svg deleted file mode 100644 index 30bce27c3b..0000000000 --- a/apps/admin-x-settings/src/unsplash/assets/kg-close.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/apps/admin-x-settings/src/unsplash/assets/kg-download.svg b/apps/admin-x-settings/src/unsplash/assets/kg-download.svg deleted file mode 100644 index 2d1c72bfa4..0000000000 --- a/apps/admin-x-settings/src/unsplash/assets/kg-download.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/apps/admin-x-settings/src/unsplash/assets/kg-search.svg b/apps/admin-x-settings/src/unsplash/assets/kg-search.svg deleted file mode 100644 index dd56d96d42..0000000000 --- a/apps/admin-x-settings/src/unsplash/assets/kg-search.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/apps/admin-x-settings/src/unsplash/assets/kg-unsplash-heart.svg b/apps/admin-x-settings/src/unsplash/assets/kg-unsplash-heart.svg deleted file mode 100644 index 1b31419ccb..0000000000 --- a/apps/admin-x-settings/src/unsplash/assets/kg-unsplash-heart.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/apps/admin-x-settings/src/unsplash/masonry/MasonryService.ts b/apps/admin-x-settings/src/unsplash/masonry/MasonryService.ts deleted file mode 100644 index 6a624cf983..0000000000 --- a/apps/admin-x-settings/src/unsplash/masonry/MasonryService.ts +++ /dev/null @@ -1,55 +0,0 @@ -import {Photo} from '../UnsplashTypes'; - -export default class MasonryService { - public columnCount: number; - public columns: Photo[][] | [] = []; - public columnHeights: number[] | null; - - constructor(columnCount: number = 3) { - this.columnCount = columnCount; - this.columns = [[]]; - this.columnHeights = null; - } - - reset(): void { - let columns: Photo[][] = []; - let columnHeights: number[] = []; - - for (let i = 0; i < this.columnCount; i += 1) { - columns[i] = []; - columnHeights[i] = 0; - } - - this.columns = columns; - this.columnHeights = columnHeights; - } - - addColumns(): void { - for (let i = 0; i < this.columnCount; i++) { - (this.columns as Photo[][]).push([]); - this.columnHeights!.push(0); - } - } - - addPhotoToColumns(photo: Photo): void { - if (!this.columns) { - this.reset(); - } - let min = Math.min(...this.columnHeights!); - let columnIndex = this.columnHeights!.indexOf(min); - - this.columnHeights![columnIndex] += 300 * photo.ratio; - this.columns![columnIndex].push(photo); - } - - getColumns(): Photo[][] | null { - return this.columns; - } - - changeColumnCount(newColumnCount: number): void { - if (newColumnCount !== this.columnCount) { - this.columnCount = newColumnCount; - this.reset(); - } - } -} diff --git a/apps/admin-x-settings/src/unsplash/photo/PhotoUseCase.ts b/apps/admin-x-settings/src/unsplash/photo/PhotoUseCase.ts deleted file mode 100644 index d46cd8e131..0000000000 --- a/apps/admin-x-settings/src/unsplash/photo/PhotoUseCase.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {InMemoryUnsplashProvider} from '../api/InMemoryUnsplashProvider'; -import {Photo} from '../UnsplashTypes'; -import {UnsplashProvider} from '../api/UnsplashProvider'; - -export class PhotoUseCases { - private _provider: UnsplashProvider | InMemoryUnsplashProvider; // InMemoryUnsplashProvider is for testing purposes - - constructor(provider: UnsplashProvider | InMemoryUnsplashProvider) { - this._provider = provider; - } - - async fetchPhotos(): Promise { - return await this._provider.fetchPhotos(); - } - - async searchPhotos(term: string): Promise { - return await this._provider.searchPhotos(term); - } - - async triggerDownload(photo: Photo): Promise { - this._provider.triggerDownload(photo); - } - - async fetchNextPage(): Promise { - let request = await this._provider.fetchNextPage(); - - if (request) { - return request; - } - - return null; - } - - searchIsRunning(): boolean { - return this._provider.searchIsRunning(); - } -} diff --git a/apps/admin-x-settings/src/unsplash/ui/UnsplashButton.tsx b/apps/admin-x-settings/src/unsplash/ui/UnsplashButton.tsx deleted file mode 100644 index 3fb835663d..0000000000 --- a/apps/admin-x-settings/src/unsplash/ui/UnsplashButton.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React, {HTMLProps} from 'react'; -import {ReactComponent as DownloadIcon} from '../assets/kg-download.svg'; -import {ReactComponent as UnsplashHeartIcon} from '../assets/kg-unsplash-heart.svg'; - -// Define the available icon types -type ButtonIconType = 'heart' | 'download'; - -// Define the props type -interface UnsplashButtonProps extends HTMLProps { - icon?: ButtonIconType; - label?: string; -} - -const BUTTON_ICONS: Record>>> = { - heart: UnsplashHeartIcon, - download: DownloadIcon -}; - -const UnsplashButton: React.FC = ({icon, label, ...props}) => { - let Icon = null; - if (icon) { - Icon = BUTTON_ICONS[icon]; - } - - return ( - e.stopPropagation()} - {...props} - > - {icon && Icon && } - {label && {label}} - - ); -}; - -export default UnsplashButton; diff --git a/apps/admin-x-settings/src/unsplash/ui/UnsplashGallery.tsx b/apps/admin-x-settings/src/unsplash/ui/UnsplashGallery.tsx deleted file mode 100644 index e162358094..0000000000 --- a/apps/admin-x-settings/src/unsplash/ui/UnsplashGallery.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import React, {ReactNode, RefObject} from 'react'; -import UnsplashImage from './UnsplashImage'; -import UnsplashZoomed from './UnsplashZoomed'; -import {Photo} from '../UnsplashTypes'; - -interface MasonryColumnProps { - children: ReactNode; -} - -interface UnsplashGalleryColumnsProps { - columns?: Photo[][] | []; - insertImage?: any; - selectImg?: any; - zoomed?: Photo | null; -} - -interface GalleryLayoutProps { - children?: ReactNode; - galleryRef: RefObject; - isLoading?: boolean; - zoomed?: Photo | null; -} - -interface UnsplashGalleryProps extends GalleryLayoutProps { - error?: string | null; - dataset?: Photo[][] | []; - selectImg?: any; - insertImage?: any; -} - -const UnsplashGalleryLoading: React.FC = () => { - return ( -
-
-
- ); -}; - -export const MasonryColumn: React.FC = (props) => { - return ( -
- {props.children} -
- ); -}; - -const UnsplashGalleryColumns: React.FC = (props) => { - if (!props?.columns) { - return null; - } - - return ( - props?.columns.map((array, index) => ( - // eslint-disable-next-line react/no-array-index-key - - { - array.map((payload: Photo) => ( - - )) - } - - )) - ); -}; - -const GalleryLayout: React.FC = (props) => { - return ( -
-
- {props.children} - {props?.isLoading && } -
-
- ); -}; - -const UnsplashGallery: React.FC = ({zoomed, - error, - galleryRef, - isLoading, - dataset, - selectImg, - insertImage}) => { - if (zoomed) { - return ( - - - - ); - } - - if (error) { - return ( - -
-

Error

-

{error}

-
-
- ); - } - - return ( - - - - ); -}; - -export default UnsplashGallery; diff --git a/apps/admin-x-settings/src/unsplash/ui/UnsplashImage.tsx b/apps/admin-x-settings/src/unsplash/ui/UnsplashImage.tsx deleted file mode 100644 index 824ba883b0..0000000000 --- a/apps/admin-x-settings/src/unsplash/ui/UnsplashImage.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import UnsplashButton from './UnsplashButton'; -import {FC, MouseEvent} from 'react'; -import {Links, Photo, User} from '../UnsplashTypes'; - -export interface UnsplashImageProps { - payload: Photo; - srcUrl: string; - links: Links; - likes: number; - user: User; - alt: string; - urls: { regular: string }; - height: number; - width: number; - zoomed: Photo | null; - insertImage: (options: { - src: string, - caption: string, - height: number, - width: number, - alt: string, - links: Links - }) => void; - selectImg: (payload: Photo | null) => void; -} - -const UnsplashImage: FC = ({payload, srcUrl, links, likes, user, alt, urls, height, width, zoomed, insertImage, selectImg}) => { - const handleClick = (e: MouseEvent) => { - e.stopPropagation(); - selectImg(zoomed ? null : payload); - }; - - return ( -
- {alt} -
-
- - -
-
-
- author -
{user.name}
-
- { - e.stopPropagation(); - insertImage({ - src: urls.regular.replace(/&w=1080/, '&w=2000'), - caption: `Photo by ${user.name} / Unsplash`, - height: height, - width: width, - alt: alt, - links: links - }); - }} /> -
-
-
- ); -}; - -export default UnsplashImage; diff --git a/apps/admin-x-settings/src/unsplash/ui/UnsplashSelector.tsx b/apps/admin-x-settings/src/unsplash/ui/UnsplashSelector.tsx deleted file mode 100644 index 6873de96dc..0000000000 --- a/apps/admin-x-settings/src/unsplash/ui/UnsplashSelector.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import {ChangeEvent, FunctionComponent, ReactNode} from 'react'; -import {ReactComponent as CloseIcon} from '../assets/kg-close.svg'; -import {ReactComponent as SearchIcon} from '../assets/kg-search.svg'; -import {ReactComponent as UnsplashIcon} from '../assets/kg-card-type-unsplash.svg'; - -interface UnsplashSelectorProps { - closeModal: () => void; - handleSearch: (e: ChangeEvent) => void; - children: ReactNode; -} - -const UnsplashSelector: FunctionComponent = ({closeModal, handleSearch, children}) => { - return ( - <> -
-
- -
-
-

- - Unsplash -

-
- - -
-
- {children} -
-
- - ); -}; - -export default UnsplashSelector; diff --git a/apps/admin-x-settings/src/unsplash/ui/UnsplashZoomed.tsx b/apps/admin-x-settings/src/unsplash/ui/UnsplashZoomed.tsx deleted file mode 100644 index 88860d3b18..0000000000 --- a/apps/admin-x-settings/src/unsplash/ui/UnsplashZoomed.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import UnsplashImage, {UnsplashImageProps} from './UnsplashImage'; -import {FC} from 'react'; -import {Photo} from '../UnsplashTypes'; - -interface UnsplashZoomedProps extends Omit { - zoomed: Photo | null; - selectImg: (photo: Photo | null) => void; -} - -const UnsplashZoomed: FC = ({payload, insertImage, selectImg, zoomed}) => { - return ( -
selectImg(null)}> - -
- ); -}; - -export default UnsplashZoomed; diff --git a/apps/admin-x-settings/src/unsplash/portal.tsx b/apps/admin-x-settings/src/utils/portal.tsx similarity index 100% rename from apps/admin-x-settings/src/unsplash/portal.tsx rename to apps/admin-x-settings/src/utils/portal.tsx diff --git a/apps/admin-x-settings/test/unit/unsplash/Masonry.test.ts b/apps/admin-x-settings/test/unit/unsplash/Masonry.test.ts deleted file mode 100644 index e42ecafe94..0000000000 --- a/apps/admin-x-settings/test/unit/unsplash/Masonry.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import MasonryService from '../../../src/unsplash/masonry/MasonryService'; -import {Photo} from '../../../src/unsplash/UnsplashTypes'; -import {fixturePhotos} from '../../../src/unsplash/api/unsplashFixtures'; - -describe('MasonryService', () => { - let service: MasonryService; - let mockPhotos: Photo[]; - - beforeEach(() => { - service = new MasonryService(3); - mockPhotos = fixturePhotos; - }); - - it('should initialize with default column count', () => { - expect(service.columnCount).toEqual(3); - }); - - describe('reset', () => { - it('should reset columns and columnHeights', () => { - service.reset(); - expect(service.columns.length).toEqual(3); - expect(service.columnHeights!.length).toEqual(3); - }); - }); - - describe('addPhotoToColumns', () => { - it('should add photo to columns with the minimum height)', () => { - service.reset(); - service.addPhotoToColumns(mockPhotos[0]); - expect(service.columns![0]).toContain(mockPhotos[0]); - }); - }); - - describe('getColumns', () => { - it('should return the columns', () => { - service.reset(); - const columns = service.getColumns(); - expect(columns).toEqual(service.columns); - }); - }); - - describe('changeColumnCount', () => { - it('should change the column count and reset', () => { - service.changeColumnCount(4); - expect(service.columnCount).toEqual(4); - expect(service.columns.length).toEqual(4); - expect(service.columnHeights!.length).toEqual(4); - }); - - it('should not reset if the column count is not changed', () => { - const prevColumns = service.getColumns(); - service.changeColumnCount(3); - expect(service.getColumns()).toEqual(prevColumns); - }); - }); -}); diff --git a/apps/admin-x-settings/test/unit/unsplash/UnsplashService.test.ts b/apps/admin-x-settings/test/unit/unsplash/UnsplashService.test.ts deleted file mode 100644 index 35ae259827..0000000000 --- a/apps/admin-x-settings/test/unit/unsplash/UnsplashService.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import MasonryService from '../../../src/unsplash/masonry/MasonryService'; -import {IUnsplashService, UnsplashService} from '../../../src/unsplash/UnsplashService'; -import {InMemoryUnsplashProvider} from '../../../src/unsplash/api/InMemoryUnsplashProvider'; -import {PhotoUseCases} from '../../../src/unsplash/photo/PhotoUseCase'; -import {fixturePhotos} from '../../../src/unsplash/api/unsplashFixtures'; - -describe('UnsplashService', () => { - let unsplashService: IUnsplashService; - let UnsplashProvider: InMemoryUnsplashProvider; - let masonryService: MasonryService; - let photoUseCases: PhotoUseCases; - - beforeEach(() => { - UnsplashProvider = new InMemoryUnsplashProvider(); - masonryService = new MasonryService(3); - photoUseCases = new PhotoUseCases(UnsplashProvider); - unsplashService = new UnsplashService(photoUseCases, masonryService); - }); - - it('can load new photos', async function () { - await unsplashService.loadNew(); - const photos = unsplashService.photos; - expect(photos).toEqual(fixturePhotos); - }); - - it('set up new columns of 3', async function () { - await unsplashService.loadNew(); - const columns = unsplashService.getColumns(); - if (columns) { - expect(columns.length).toBe(3); - } - }); - - it('can search for photos', async function () { - await unsplashService.updateSearch('cat'); - const photos = unsplashService.photos; - expect(photos.length).toBe(0); - await unsplashService.updateSearch('photo'); - const photos2 = unsplashService.photos; - expect(photos2.length).toBe(1); - }); - - it('can check if search is running', async function () { - const isRunning = unsplashService.searchIsRunning(); - expect(isRunning).toBe(false); - }); - - it('can load next page', async function () { - await unsplashService.loadNextPage(); - const photos = unsplashService.photos; - expect(photos.length).toBe(2); - }); -}); diff --git a/apps/admin-x-settings/vite.config.mjs b/apps/admin-x-settings/vite.config.mjs index 27795efe4e..79df33d790 100644 --- a/apps/admin-x-settings/vite.config.mjs +++ b/apps/admin-x-settings/vite.config.mjs @@ -20,6 +20,9 @@ export default (function viteConfig() { // @TODO: Remove this when @tryghost/nql is updated mingo: resolve(__dirname, '../../node_modules/mingo/dist/mingo.js') } + }, + optimizeDeps: { + include: ['@tryghost/kg-unsplash-selector'] } } }); diff --git a/yarn.lock b/yarn.lock index 9f324ce122..42cd2a1dab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2242,6 +2242,14 @@ "@elastic/transport" "^8.3.4" tslib "^2.4.0" +"@elastic/elasticsearch@8.12.2": + version "8.12.2" + resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-8.12.2.tgz#7a241f739a509cc59faee85f79a4c9e9e5ba9128" + integrity sha512-04NvH3LIgcv1Uwguorfw2WwzC9Lhfsqs9f0L6uq6MrCw0lqe/HOQ6E8vJ6EkHAA15iEfbhtxOtenbZVVcE+mAQ== + dependencies: + "@elastic/transport" "^8.4.1" + tslib "^2.4.0" + "@elastic/transport@^8.3.4": version "8.4.0" resolved "https://registry.npmjs.org/@elastic/transport/-/transport-8.4.0.tgz#e1ec05f7a2857162c161e2c97008f9b21301a673" @@ -2254,6 +2262,18 @@ tslib "^2.4.0" undici "^5.22.1" +"@elastic/transport@^8.4.1": + version "8.4.1" + resolved "https://registry.yarnpkg.com/@elastic/transport/-/transport-8.4.1.tgz#f98c5a5e2156bcb3f01170b4aca7e7de4d8b61b8" + integrity sha512-/SXVuVnuU5b4dq8OFY4izG+dmGla185PcoqgK6+AJMpmOeY1QYVNbWtCwvSvoAANN5D/wV+EBU8+x7Vf9EphbA== + dependencies: + debug "^4.3.4" + hpagent "^1.0.0" + ms "^2.1.3" + secure-json-parse "^2.4.0" + tslib "^2.4.0" + undici "^5.22.1" + "@ember-data/adapter@3.24.0": version "3.24.0" resolved "https://registry.yarnpkg.com/@ember-data/adapter/-/adapter-3.24.0.tgz#995c19bc6fb95c94cbb83b8c3c7bc08253346cba" @@ -6947,6 +6967,14 @@ "@tryghost/root-utils" "^0.3.25" debug "^4.3.1" +"@tryghost/debug@^0.1.28": + version "0.1.28" + resolved "https://registry.yarnpkg.com/@tryghost/debug/-/debug-0.1.28.tgz#498ef3450aa654ebb15a47553c2478a33164c0e6" + integrity sha512-iZKKlDDcZZa77GCgZ+o/Vp5oz520SOOpKCnoapgKGkFLRFT/0/D54jw/KY2pHGTFBXrcrE8kqTulgeuMNP+ABA== + dependencies: + "@tryghost/root-utils" "^0.3.26" + debug "^4.3.1" + "@tryghost/elasticsearch@^3.0.15": version "3.0.15" resolved "https://registry.yarnpkg.com/@tryghost/elasticsearch/-/elasticsearch-3.0.15.tgz#d4be60b79155d95de063e17ea90ff0151a0a35d9" @@ -6956,6 +6984,15 @@ "@tryghost/debug" "^0.1.26" split2 "4.2.0" +"@tryghost/elasticsearch@^3.0.16": + version "3.0.17" + resolved "https://registry.yarnpkg.com/@tryghost/elasticsearch/-/elasticsearch-3.0.17.tgz#408e8ba7ce35c9357f6814bf0fd9b88cb56c2ebb" + integrity sha512-4uYnFJQ0QDNleko1J26E0byWnHrEBZzd3S1WVTbCztlC14KQweZxmfou3fc5JmcT/GNiyXd5Pgx+bLMtVi017g== + dependencies: + "@elastic/elasticsearch" "8.12.2" + "@tryghost/debug" "^0.1.28" + split2 "4.2.0" + "@tryghost/email-mock-receiver@0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@tryghost/email-mock-receiver/-/email-mock-receiver-0.3.2.tgz#abd8086935a95a996b6c5c803478a9f81dcae19a" @@ -6977,7 +7014,24 @@ focus-trap "^6.7.2" postcss-preset-env "^7.3.1" -"@tryghost/errors@1.2.26", "@tryghost/errors@1.3.0", "@tryghost/errors@1.3.1", "@tryghost/errors@^1.2.26", "@tryghost/errors@^1.2.27", "@tryghost/errors@^1.2.3": +"@tryghost/errors@1.2.26": + version "1.2.26" + resolved "https://registry.yarnpkg.com/@tryghost/errors/-/errors-1.2.26.tgz#0d0503a51e681998421548fbddbdd7376384c457" + integrity sha512-s/eynvVUiAhHP0HB7CPQs7qH7Pm1quJ2iUMTCuH7HV8LqiGoQFNc21/5R4lRv+2Jt3yf69UPCs/6G+kAgZipNw== + dependencies: + "@stdlib/utils-copy" "^0.0.7" + lodash "^4.17.21" + uuid "^9.0.0" + +"@tryghost/errors@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@tryghost/errors/-/errors-1.3.0.tgz#273beb4c91bd7eb8a44b2e4154e57cc5321ad9dc" + integrity sha512-XI3Gw+6Mbua7FiCdNMY+0bVQ0pP6YDY5UpIAd9YIb1PWtISmiqEA2St1bvBk08Blfks3+lFPigh/YUxjuTKjRw== + dependencies: + "@stdlib/utils-copy" "^0.0.7" + uuid "^9.0.0" + +"@tryghost/errors@1.3.1", "@tryghost/errors@^1.2.26", "@tryghost/errors@^1.2.27", "@tryghost/errors@^1.2.3", "@tryghost/errors@^1.3.1": version "1.3.1" resolved "https://registry.npmjs.org/@tryghost/errors/-/errors-1.3.1.tgz#32a00c5e5293c46e54d03a66da871ac34b2ab35c" integrity sha512-iZqT0vZ3NVZNq9o1HYxW00k1mcUAC+t5OLiI8O29/uQwAfy7NemY+Cabl9mWoIwgvBmw7l0Z8pHTcXMo1c+xMw== @@ -7023,6 +7077,14 @@ "@tryghost/errors" "^1.2.26" "@tryghost/request" "^1.0.0" +"@tryghost/http-stream@^0.1.27": + version "0.1.28" + resolved "https://registry.yarnpkg.com/@tryghost/http-stream/-/http-stream-0.1.28.tgz#ae38ef2e113e23b582d194fc76c5196a54171a11" + integrity sha512-E1TyZE5e121TPjMgJvlSPTcxJl3Xdq78kugmx2deLxADa5W/vuchidZ6yYQQzxCXPciQFUciVYT8B5tggZb+Xw== + dependencies: + "@tryghost/errors" "^1.3.1" + "@tryghost/request" "^1.0.3" + "@tryghost/image-transform@1.2.11": version "1.2.11" resolved "https://registry.yarnpkg.com/@tryghost/image-transform/-/image-transform-1.2.11.tgz#82463d97f8747db6db70165a04e824eed6791fee" @@ -7169,6 +7231,11 @@ dependencies: "@tryghost/kg-clean-basic-html" "^4.0.1" +"@tryghost/kg-unsplash-selector@^0.1.11": + version "0.1.11" + resolved "https://registry.yarnpkg.com/@tryghost/kg-unsplash-selector/-/kg-unsplash-selector-0.1.11.tgz#d5fe973c01217fa5854a528a7b74e6d008b25309" + integrity sha512-5guN8Z8vpF2zCvfziKqFvQN/LQGuV2/ZKz+JiDk6dVBOwHZ/flitMDmnlodCNYT141yQE9x9DCrZAC216koHqw== + "@tryghost/kg-utils@^1.0.24": version "1.0.24" resolved "https://registry.yarnpkg.com/@tryghost/kg-utils/-/kg-utils-1.0.24.tgz#4ef358ef803272cbe257993b9f79ea0a6b432077" @@ -7190,7 +7257,24 @@ lodash "^4.17.21" luxon "^1.26.0" -"@tryghost/logging@2.4.10", "@tryghost/logging@2.4.8", "@tryghost/logging@^2.4.7": +"@tryghost/logging@2.4.10": + version "2.4.10" + resolved "https://registry.yarnpkg.com/@tryghost/logging/-/logging-2.4.10.tgz#2e5b56c53364be330c1e6f2ffa33e3c30b7bac8e" + integrity sha512-l356vLSQmszY14y7ef5YxY4CZ3418NXn5+LvFdlweeTRk0ilWx1mVUoXi8IlVh90rIVbemv+pXi1dusJB6peQA== + dependencies: + "@tryghost/bunyan-rotating-filestream" "^0.0.7" + "@tryghost/elasticsearch" "^3.0.16" + "@tryghost/http-stream" "^0.1.27" + "@tryghost/pretty-stream" "^0.1.21" + "@tryghost/root-utils" "^0.3.25" + bunyan "^1.8.15" + bunyan-loggly "^1.4.2" + fs-extra "^11.0.0" + gelf-stream "^1.1.1" + json-stringify-safe "^5.0.1" + lodash "^4.17.21" + +"@tryghost/logging@2.4.8", "@tryghost/logging@^2.4.7": version "2.4.8" resolved "https://registry.yarnpkg.com/@tryghost/logging/-/logging-2.4.8.tgz#a9e9abdbec823f0c6a009aa2f6847ce454b35475" integrity sha512-/pIeTcw9jpqWJ5/VyUn5sa3rsUxUHortykB4oYd5vKr16KgnrVOuGPCg4JqmdGfz2zrkgKaGd9cAsTNE++0Deg== @@ -7305,6 +7389,15 @@ moment "^2.29.1" prettyjson "^1.2.5" +"@tryghost/pretty-stream@^0.1.21": + version "0.1.22" + resolved "https://registry.yarnpkg.com/@tryghost/pretty-stream/-/pretty-stream-0.1.22.tgz#856a6b8eb3bd17b2fdc5ca668ac27bdd2f55b04b" + integrity sha512-97/JRI7rmdQIG6zwPzux58Kfc/UJfdvKiJgYgH7+CuNQqdl0Zy2+X0wlnRnYjck7tj781/rhGTEGGZg6PHZbaw== + dependencies: + lodash "^4.17.21" + moment "^2.29.1" + prettyjson "^1.2.5" + "@tryghost/promise@0.3.4": version "0.3.4" resolved "https://registry.yarnpkg.com/@tryghost/promise/-/promise-0.3.4.tgz#b5437eb14a3d06e7d32f277e10975ff77061e16e" @@ -7327,6 +7420,18 @@ got "13.0.0" lodash "^4.17.21" +"@tryghost/request@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tryghost/request/-/request-1.0.3.tgz#73f7417bd4eb382133ea1eaa092ff14d070c45aa" + integrity sha512-ruHs3omvxTYGIm87gGJSRx0r64y4mBWV52d0wiwgOeCyyYL2WDYQ1dTgHWZbSjl8YHc2p0lc8gkPPxBZ+9ZnUA== + dependencies: + "@tryghost/errors" "^1.3.1" + "@tryghost/validator" "^0.2.9" + "@tryghost/version" "^0.1.26" + cacheable-lookup "7.0.0" + got "13.0.0" + lodash "^4.17.21" + "@tryghost/root-utils@0.3.24": version "0.3.24" resolved "https://registry.yarnpkg.com/@tryghost/root-utils/-/root-utils-0.3.24.tgz#91653fbadc882fb8510844f163a2231c87f30fab" @@ -7343,6 +7448,14 @@ caller "^1.0.1" find-root "^1.1.0" +"@tryghost/root-utils@^0.3.26": + version "0.3.26" + resolved "https://registry.yarnpkg.com/@tryghost/root-utils/-/root-utils-0.3.26.tgz#26e626706b67dddd13f59812826a806c45722122" + integrity sha512-akbI+mmnU6mlwbVAEy8Ay1MYbMEocKEVP+QHbYCKk3d++2TM2lmSZm6CFIP5+10NeFiP2Em6jSaGXBpaalR9VQ== + dependencies: + caller "^1.0.1" + find-root "^1.1.0" + "@tryghost/server@^0.1.37": version "0.1.37" resolved "https://registry.yarnpkg.com/@tryghost/server/-/server-0.1.37.tgz#04ee5671b19a4a5be05e361e293d47eb9c6c2482" @@ -7382,6 +7495,13 @@ dependencies: lodash.template "^4.5.0" +"@tryghost/tpl@^0.1.28": + version "0.1.28" + resolved "https://registry.yarnpkg.com/@tryghost/tpl/-/tpl-0.1.28.tgz#c1453eedf33da7010b1c556f2e4d92f656351fd9" + integrity sha512-z8DBIDntaJQMHEmp/ZhCpPjc5TXIsu7ZdnOVbAVK2YnzhLcjDl/JPpmt2FXLV3VBo7VM1XBT9nptiYd2kFnZFg== + dependencies: + lodash.template "^4.5.0" + "@tryghost/url-utils@4.4.6", "@tryghost/url-utils@^4.0.0": version "4.4.6" resolved "https://registry.yarnpkg.com/@tryghost/url-utils/-/url-utils-4.4.6.tgz#4938e55fcc11620fd17c64346249d420f6f97129" @@ -7406,6 +7526,17 @@ moment-timezone "^0.5.23" validator "7.2.0" +"@tryghost/validator@^0.2.9": + version "0.2.9" + resolved "https://registry.yarnpkg.com/@tryghost/validator/-/validator-0.2.9.tgz#6b33d884d96e0bca20a750d9dd1ed534a0efbab6" + integrity sha512-7EBFiXUGhU6ReuryAnqh5BM0Fa918NSEN3UR2dqrgk861W/pwofmx8r179jVKBNA0cSYot/DVj5bWlUV25cWvQ== + dependencies: + "@tryghost/errors" "^1.3.1" + "@tryghost/tpl" "^0.1.28" + lodash "^4.17.21" + moment-timezone "^0.5.23" + validator "7.2.0" + "@tryghost/version@0.1.24", "@tryghost/version@^0.1.24": version "0.1.24" resolved "https://registry.yarnpkg.com/@tryghost/version/-/version-0.1.24.tgz#eb8bc345929ba8f67c3f36757bd91c12f701a5f5" @@ -7414,6 +7545,14 @@ "@tryghost/root-utils" "^0.3.24" semver "^7.3.5" +"@tryghost/version@^0.1.26": + version "0.1.26" + resolved "https://registry.yarnpkg.com/@tryghost/version/-/version-0.1.26.tgz#00829961bfef66b0aae01f6b866068df63859236" + integrity sha512-um1GihMBOs+1+p6tPGgIOHGlPeYyj0w+JxRHSIstPyywMmuM+kH3LUrHt3N3hb7zzgWfehM0L81k28MCGTqV5Q== + dependencies: + "@tryghost/root-utils" "^0.3.26" + semver "^7.3.5" + "@tryghost/webhook-mock-receiver@0.2.8": version "0.2.8" resolved "https://registry.yarnpkg.com/@tryghost/webhook-mock-receiver/-/webhook-mock-receiver-0.2.8.tgz#1cb3bb5de667f597f2eaa25aff3e9e572212cafa" @@ -22815,18 +22954,57 @@ module-details-from-path@^1.0.3: resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== -moment-timezone@0.5.23, moment-timezone@0.5.34, moment-timezone@^0.5.23, moment-timezone@^0.5.31, moment-timezone@^0.5.33: +moment-timezone@0.5.23, moment-timezone@^0.5.23: version "0.5.23" resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.23.tgz#7cbb00db2c14c71b19303cb47b0fb0a6d8651463" integrity sha512-WHFH85DkCfiNMDX5D3X7hpNH3/PUhjTGcD0U1SgfBGZxJ3qUmJh5FdvaFjcClxOvB3rzdfj4oRffbI38jEnC1w== dependencies: moment ">= 2.9.0" -moment@2.24.0, moment@2.27.0, moment@2.29.1, moment@2.29.3, moment@2.29.4, "moment@>= 2.9.0", moment@^2.10.2, moment@^2.18.1, moment@^2.19.3, moment@^2.27.0, moment@^2.29.1: +moment-timezone@0.5.34: + version "0.5.34" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c" + integrity sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg== + dependencies: + moment ">= 2.9.0" + +moment-timezone@^0.5.31, moment-timezone@^0.5.33: + version "0.5.45" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.45.tgz#cb685acd56bac10e69d93c536366eb65aa6bcf5c" + integrity sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ== + dependencies: + moment "^2.29.4" + +moment@2.24.0, "moment@>= 2.9.0", moment@^2.10.2, moment@^2.18.1, moment@^2.19.3: version "2.24.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== +moment@2.27.0: + version "2.27.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d" + integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ== + +moment@2.29.1: + version "2.29.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" + integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== + +moment@2.29.3: + version "2.29.3" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.3.tgz#edd47411c322413999f7a5940d526de183c031f3" + integrity sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw== + +moment@2.29.4: + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + +moment@^2.27.0, moment@^2.29.1, moment@^2.29.4: + version "2.30.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== + moo@^0.5.0, moo@^0.5.1: version "0.5.2" resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" @@ -28331,7 +28509,7 @@ string-template@~0.2.1: resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw== -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -28349,15 +28527,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" @@ -28451,7 +28620,7 @@ stringify-entities@^2.0.0: is-decimal "^1.0.2" is-hexadecimal "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -28479,13 +28648,6 @@ strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" @@ -30975,7 +31137,7 @@ workerpool@^6.0.2, workerpool@^6.0.3, workerpool@^6.1.5, workerpool@^6.4.0: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -30993,15 +31155,6 @@ wrap-ansi@^6.0.1: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"