diff --git a/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts
index 92777a35..c314c473 100644
--- a/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts
+++ b/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts
@@ -9,6 +9,7 @@ import {MediumSettingsDto} from "@gitroom/nestjs-libraries/dtos/posts/providers-
import {HashnodeSettingsDto} from "@gitroom/nestjs-libraries/dtos/posts/providers-settings/hashnode.settings.dto";
import {RedditSettingsDto} from "@gitroom/nestjs-libraries/dtos/posts/providers-settings/reddit.dto";
import { YoutubeSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/youtube.settings.dto';
+import { PinterestSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/pinterest.dto';
export class EmptySettings {}
export class Integration {
@@ -62,6 +63,7 @@ export class Post {
{ value: HashnodeSettingsDto, name: 'hashnode' },
{ value: RedditSettingsDto, name: 'reddit' },
{ value: YoutubeSettingsDto, name: 'youtube' },
+ { value: PinterestSettingsDto, name: 'pinterest' },
],
},
})
diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts
index 8801f4d9..042e3ad7 100644
--- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts
+++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts
@@ -2,9 +2,13 @@ import { DevToSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers
import { MediumSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/medium.settings.dto';
import { HashnodeSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/hashnode.settings.dto';
import { RedditSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/reddit.dto';
+import { PinterestSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/pinterest.dto';
+import { YoutubeSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/youtube.settings.dto';
export type AllProvidersSettings =
| DevToSettingsDto
| MediumSettingsDto
| HashnodeSettingsDto
- | RedditSettingsDto;
+ | RedditSettingsDto
+ | YoutubeSettingsDto
+ | PinterestSettingsDto;
diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/dev.to.settings.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/dev.to.settings.dto.ts
index 9cf51fa1..deb048d9 100644
--- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/dev.to.settings.dto.ts
+++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/dev.to.settings.dto.ts
@@ -11,7 +11,7 @@ import {
} from 'class-validator';
import { MediaDto } from '@gitroom/nestjs-libraries/dtos/media/media.dto';
import { Type } from 'class-transformer';
-import { DevToTagsSettings } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/dev.to.tags.settings';
+import { DevToTagsSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/dev.to.tags.settings.dto';
export class DevToSettingsDto {
@IsString()
@@ -42,5 +42,5 @@ export class DevToSettingsDto {
@IsArray()
@ArrayMaxSize(4)
@IsOptional()
- tags: DevToTagsSettings[];
+ tags: DevToTagsSettingsDto[];
}
diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/dev.to.tags.settings.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/dev.to.tags.settings.dto.ts
similarity index 77%
rename from libraries/nestjs-libraries/src/dtos/posts/providers-settings/dev.to.tags.settings.ts
rename to libraries/nestjs-libraries/src/dtos/posts/providers-settings/dev.to.tags.settings.dto.ts
index f71b241e..688fb284 100644
--- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/dev.to.tags.settings.ts
+++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/dev.to.tags.settings.dto.ts
@@ -1,6 +1,6 @@
import {IsNumber, IsString} from "class-validator";
-export class DevToTagsSettings {
+export class DevToTagsSettingsDto {
@IsNumber()
value: number;
diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/pinterest.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/pinterest.dto.ts
new file mode 100644
index 00000000..7f3ff938
--- /dev/null
+++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/pinterest.dto.ts
@@ -0,0 +1,32 @@
+import { IsDefined, IsOptional, IsString, IsUrl, MinLength, ValidateNested } from 'class-validator';
+import { Type } from 'class-transformer';
+
+export class PinterestSettingsDto {
+ @IsString()
+ @IsOptional()
+ title: string;
+
+ @IsString()
+ @IsOptional()
+ description: string;
+
+ @IsString()
+ @IsOptional()
+ @IsUrl()
+ link: string;
+
+ @IsString()
+ @IsOptional()
+ dominant_color: string;
+
+ @IsDefined({
+ message: 'Board is required'
+ })
+ @IsString({
+ message: 'Board is required'
+ })
+ @MinLength(1, {
+ message: 'Board is required'
+ })
+ board: string;
+}
diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/youtube.settings.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/youtube.settings.dto.ts
index 27dbfd42..d4fd7c67 100644
--- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/youtube.settings.dto.ts
+++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/youtube.settings.dto.ts
@@ -1,5 +1,4 @@
import {
- ArrayMaxSize,
IsArray,
IsDefined,
IsOptional,
diff --git a/libraries/nestjs-libraries/src/integrations/integration.manager.ts b/libraries/nestjs-libraries/src/integrations/integration.manager.ts
index 50b5c138..efa47538 100644
--- a/libraries/nestjs-libraries/src/integrations/integration.manager.ts
+++ b/libraries/nestjs-libraries/src/integrations/integration.manager.ts
@@ -11,6 +11,7 @@ import { FacebookProvider } from '@gitroom/nestjs-libraries/integrations/social/
import { InstagramProvider } from '@gitroom/nestjs-libraries/integrations/social/instagram.provider';
import { YoutubeProvider } from '@gitroom/nestjs-libraries/integrations/social/youtube.provider';
import { TiktokProvider } from '@gitroom/nestjs-libraries/integrations/social/tiktok.provider';
+import { PinterestProvider } from '@gitroom/nestjs-libraries/integrations/social/pinterest.provider';
const socialIntegrationList = [
new XProvider(),
@@ -20,6 +21,7 @@ const socialIntegrationList = [
new InstagramProvider(),
new YoutubeProvider(),
new TiktokProvider(),
+ new PinterestProvider()
];
const articleIntegrationList = [
diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts
index 9515792f..8f11cca8 100644
--- a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts
+++ b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts
@@ -219,7 +219,7 @@ export class InstagramProvider implements SocialProvider {
let containerIdGlobal = '';
let linkGlobal = '';
if (medias.length === 1) {
- const { id: mediaId, ...all } = await (
+ const { id: mediaId } = await (
await fetch(
`https://graph.facebook.com/v20.0/${id}/media_publish?creation_id=${medias[0]}&access_token=${accessToken}&field=id`,
{
@@ -228,8 +228,6 @@ export class InstagramProvider implements SocialProvider {
)
).json();
- console.log(all);
-
containerIdGlobal = mediaId;
const { permalink } = await (
diff --git a/libraries/nestjs-libraries/src/integrations/social/pinterest.provider.ts b/libraries/nestjs-libraries/src/integrations/social/pinterest.provider.ts
new file mode 100644
index 00000000..3708702f
--- /dev/null
+++ b/libraries/nestjs-libraries/src/integrations/social/pinterest.provider.ts
@@ -0,0 +1,222 @@
+import {
+ AuthTokenDetails,
+ PostDetails,
+ PostResponse,
+ SocialProvider,
+} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface';
+import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
+import { timer } from '@gitroom/helpers/utils/timer';
+import dayjs from 'dayjs';
+import { PinterestSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/pinterest.dto';
+import axios from 'axios';
+import FormData from 'form-data';
+const form = new FormData();
+
+export class PinterestProvider implements SocialProvider {
+ identifier = 'pinterest';
+ name = 'Pinterest';
+ isBetweenSteps = false;
+
+ async refreshToken(refresh_token: string): Promise
{
+ return {
+ refreshToken: '',
+ expiresIn: 0,
+ accessToken: '',
+ id: '',
+ name: '',
+ picture: '',
+ username: '',
+ };
+ }
+
+ async generateAuthUrl(refresh?: string) {
+ const state = makeId(6);
+ return {
+ url: `https://www.pinterest.com/oauth/?client_id=${
+ process.env.PINTEREST_CLIENT_ID
+ }&redirect_uri=${encodeURIComponent(
+ `${process.env.FRONTEND_URL}/integrations/social/pinterest${
+ refresh ? `?refresh=${refresh}` : ''
+ }`
+ )}&response_type=code&scope=${encodeURIComponent(
+ 'boards:read,boards:write,pins:read,pins:write,user_accounts:read'
+ )}&state=${state}`,
+ codeVerifier: makeId(10),
+ state,
+ };
+ }
+
+ async authenticate(params: {
+ code: string;
+ codeVerifier: string;
+ refresh: string;
+ }) {
+ const { access_token, refresh_token, expires_in } = await (
+ await fetch('https://api-sandbox.pinterest.com/v5/oauth/token', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ Authorization: `Basic ${Buffer.from(
+ `${process.env.PINTEREST_CLIENT_ID}:${process.env.PINTEREST_CLIENT_SECRET}`
+ ).toString('base64')}`,
+ },
+ body: new URLSearchParams({
+ grant_type: 'authorization_code',
+ code: params.code,
+ redirect_uri: `${process.env.FRONTEND_URL}/integrations/social/pinterest`,
+ }),
+ })
+ ).json();
+
+ const { id, profile_image, username } = await (
+ await fetch('https://api-sandbox.pinterest.com/v5/user_account', {
+ method: 'GET',
+ headers: {
+ Authorization: `Bearer ${access_token}`,
+ },
+ })
+ ).json();
+
+ return {
+ id: id,
+ name: username,
+ accessToken: access_token,
+ refreshToken: refresh_token,
+ expiresIn: expires_in,
+ picture: profile_image,
+ username,
+ };
+ }
+
+ async boards(accessToken: string) {
+ const { items } = await (
+ await fetch('https://api-sandbox.pinterest.com/v5/boards', {
+ method: 'GET',
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ },
+ })
+ ).json();
+
+ return (
+ items?.map((item: any) => ({
+ name: item.name,
+ id: item.id,
+ })) || []
+ );
+ }
+
+ async post(
+ id: string,
+ accessToken: string,
+ postDetails: PostDetails[]
+ ): Promise {
+ let mediaId = '';
+ if ((postDetails?.[0]?.media?.[0]?.path?.indexOf('mp4') || -1) > -1) {
+ const { upload_url, media_id, upload_parameters } = await (
+ await fetch('https://api-sandbox.pinterest.com/v5/media', {
+ method: 'POST',
+ body: JSON.stringify({
+ media_type: 'video',
+ }),
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken}`,
+ },
+ })
+ ).json();
+
+ console.log(media_id, upload_url);
+
+ try {
+ const { data } = await axios({
+ url: postDetails?.[0]?.media?.[0]?.url,
+ method: 'GET',
+ responseType: 'stream',
+ });
+
+ const p = await (
+ await fetch(upload_url, {
+ method: 'PUT',
+ body: data.buffer,
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ ...upload_parameters,
+ },
+ })
+ ).json();
+
+ console.log(p);
+ } catch (err) {
+ console.log(err);
+ }
+
+ mediaId = media_id;
+ }
+
+ const mapImages = postDetails?.[0]?.media?.map((m) => ({
+ url: m.url,
+ }));
+
+ console.log('1');
+
+ try {
+ const {
+ id: pId,
+ link,
+ ...all
+ } = await (
+ await fetch('https://api-sandbox.pinterest.com/v5/pins', {
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ ...(postDetails?.[0]?.settings.link
+ ? { link: postDetails?.[0]?.settings.link }
+ : {}),
+ ...(postDetails?.[0]?.settings.title
+ ? { title: postDetails?.[0]?.settings.title }
+ : {}),
+ ...(postDetails?.[0]?.settings.description
+ ? { title: postDetails?.[0]?.settings.description }
+ : {}),
+ ...(postDetails?.[0]?.settings.dominant_color
+ ? { title: postDetails?.[0]?.settings.dominant_color }
+ : {}),
+ board_id: postDetails?.[0]?.settings.board,
+ media_source: mediaId
+ ? {
+ source_type: 'video',
+ media_id: mediaId,
+ }
+ : mapImages?.length === 1
+ ? {
+ source_type: 'image_url',
+ url: mapImages?.[0]?.url,
+ }
+ : {
+ source_type: 'multiple_image_urls',
+ items: mapImages,
+ },
+ }),
+ })
+ ).json();
+
+ console.log(all);
+
+ return [
+ {
+ id,
+ postId: pId,
+ releaseURL: link,
+ status: 'success',
+ },
+ ];
+ } catch (err) {
+ console.log(err);
+ return [];
+ }
+ }
+}
diff --git a/libraries/react-shared-libraries/src/form/color.picker.tsx b/libraries/react-shared-libraries/src/form/color.picker.tsx
new file mode 100644
index 00000000..da05c09f
--- /dev/null
+++ b/libraries/react-shared-libraries/src/form/color.picker.tsx
@@ -0,0 +1,68 @@
+import { FC, useCallback, useState } from 'react';
+import { Button } from './button';
+import { HexColorPicker } from 'react-colorful';
+import { useFormContext } from 'react-hook-form';
+import interClass from '../helpers/inter.font';
+
+export const ColorPicker: FC<{
+ name: string;
+ label: string;
+ enabled: boolean;
+ canBeCancelled: boolean;
+}> = (props) => {
+ const { name, label, enabled, canBeCancelled } = props;
+ const form = useFormContext();
+ const [enabledState, setEnabledState] = useState(enabled);
+ const color = form.register(name);
+ const watch = form.watch(name);
+
+ const enable = useCallback(async () => {
+ await color.onChange({ target: { name, value: '#FFFFFF' } });
+ setEnabledState(true);
+ }, []);
+
+ const cancel = useCallback(async () => {
+ await color.onChange({ target: { name, value: '' } });
+ setEnabledState(false);
+ }, []);
+
+ if (!enabledState) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+ {canBeCancelled && (
+
+
+
+ )}
+
+
+ color.onChange({ target: { name, value } })}
+ />
+
+
+
+
+ );
+};
diff --git a/package-lock.json b/package-lock.json
index 5d4cad91..7020d979 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -70,6 +70,7 @@
"openai": "^4.47.1",
"prisma-paginate": "^5.2.1",
"react": "18.2.0",
+ "react-colorful": "^5.6.1",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "18.2.0",
@@ -33283,6 +33284,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-colorful": {
+ "version": "5.6.1",
+ "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
+ "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==",
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
"node_modules/react-dnd": {
"version": "16.0.1",
"resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz",
diff --git a/package.json b/package.json
index fd5e051a..b59a51dc 100644
--- a/package.json
+++ b/package.json
@@ -74,6 +74,7 @@
"openai": "^4.47.1",
"prisma-paginate": "^5.2.1",
"react": "18.2.0",
+ "react-colorful": "^5.6.1",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "18.2.0",