feat: social media

This commit is contained in:
Nevo David 2024-05-30 20:04:07 +07:00
parent 7d3302e3b8
commit 4e9c181719
9 changed files with 106 additions and 95 deletions

View File

@ -250,44 +250,16 @@ export const AddEditModal: FC<{
group: existingData?.group,
trigger: values[v].trigger,
settings: values[v].settings(),
firstCommentRequirements: values[v].firstCommentRequirements,
maximumMediaRequirements: values[v].maximumMediaRequirements,
minimumMediaRequirements: values[v].minimumMediaRequirements,
checkValidity: values[v].checkValidity
}));
for (const key of allKeys) {
// @ts-ignore
const images = key?.value[0].image;
if (
(images?.length || 0) > (key.maximumMediaRequirements || 100000) ||
(images?.length || 0) < (key.minimumMediaRequirements || 0)
) {
toaster.show(
`The amount of ${capitalize(key?.integration?.identifier)} media attached supposed to be ${
(key.maximumMediaRequirements === key.minimumMediaRequirements) || !key.maximumMediaRequirements
? key.minimumMediaRequirements
: `between ${key.minimumMediaRequirements} to ${key.maximumMediaRequirements}`
}`,
'warning'
);
return;
}
if (
key.firstCommentRequirements &&
!images?.every((p: any) =>
key.firstCommentRequirements === 'video'
? p.name.includes('mp4')
: !p.name.includes('mp4')
)
) {
toaster.show(
`${capitalize(key?.integration?.identifier?.toUpperCase())} media should be a ${
key.firstCommentRequirements === 'video' ? 'video' : 'image'
}`,
'warning'
);
return;
if (key.checkValidity) {
const check = await key.checkValidity(key?.value.map((p: any) => p.image || {path: ''}));
if (typeof check === 'string') {
toaster.show(check, 'warning');
return;
}
}
if (key.value.some((p) => !p.content || p.content.length < 6)) {
@ -322,7 +294,7 @@ export const AddEditModal: FC<{
);
modal.closeAll();
},
[postFor, dateState, value, integrations, existingData]
[postFor, dateState, value, integrations, existingData, selectedIntegrations]
);
const getPostsMarketplace = useCallback(async () => {

View File

@ -8,9 +8,7 @@ const finalInformation = {} as {
settings: () => object;
trigger: () => Promise<boolean>;
isValid: boolean;
firstCommentRequirements?: 'video' | 'image';
minimumMediaRequirements?: number;
maximumMediaRequirements?: number;
checkValidity?: (value: Array<Array<{path: string}>>) => Promise<string|true>;
};
};
export const useValues = (
@ -19,9 +17,7 @@ export const useValues = (
identifier: string,
value: Array<{ id?: string; content: string; media?: Array<string> }>,
dto: any,
firstCommentRequirements?: 'video' | 'image',
minimumMediaRequirements?: number,
maximumMediaRequirements?: number
checkValidity?: (value: Array<Array<{path: string}>>) => Promise<string|true>,
) => {
const resolver = useMemo(() => {
return classValidatorResolver(dto);
@ -44,17 +40,9 @@ export const useValues = (
finalInformation[integration].settings = getValues;
finalInformation[integration].trigger = form.trigger;
if (firstCommentRequirements) {
finalInformation[integration].firstCommentRequirements =
firstCommentRequirements;
}
if (minimumMediaRequirements) {
finalInformation[integration].minimumMediaRequirements =
minimumMediaRequirements;
}
if (maximumMediaRequirements) {
finalInformation[integration].maximumMediaRequirements =
maximumMediaRequirements;
if (checkValidity) {
finalInformation[integration].checkValidity =
checkValidity;
}
useEffect(() => {

View File

@ -67,9 +67,7 @@ export const withProvider = (
SettingsComponent: FC | null,
PreviewComponent: FC,
dto?: any,
firstCommentRequirements?: 'video' | 'image',
minimumMediaRequirements?: number,
maximumMediaRequirements?: number,
checkValidity?: (value: Array<Array<{path: string}>>) => Promise<string|true>
) => {
return (props: {
identifier: string;
@ -121,9 +119,7 @@ export const withProvider = (
props.identifier,
editInPlace ? InPlaceValue : props.value,
dto,
firstCommentRequirements,
minimumMediaRequirements,
maximumMediaRequirements
checkValidity
);
// change editor value

View File

@ -109,4 +109,10 @@ const InstagramPreview: FC = (props) => {
);
};
export default withProvider(null, InstagramPreview, undefined, undefined, 1, 10);
export default withProvider(null, InstagramPreview, undefined, async ([firstPost, ...otherPosts]) => {
if (!firstPost.length) {
return 'Instagram should have at least one media';
}
return true;
});

View File

@ -130,6 +130,47 @@ export default withProvider(
PinterestSettings,
PinterestPreview,
PinterestSettingsDto,
undefined,
1
async ([firstItem, ...otherItems]) => {
const isMp4 = firstItem.find((item) => item.path.indexOf('mp4') > -1);
const isPicture = firstItem.find((item) => item.path.indexOf('mp4') === -1);
if (firstItem.length === 0) {
return 'Pinterest requires at least one media';
}
if (isMp4 && firstItem.length !== 2 && !isPicture) {
return 'If posting a video to Pinterest you have to also include a cover image as second media';
}
if (isMp4 && firstItem.length > 2) {
return 'If posting a video to Pinterest you can only have two media items';
}
if (otherItems.length) {
return 'Pinterest can only have one post';
}
if (firstItem.length > 1 && firstItem.every(p => p.path.indexOf('mp4') == -1)) {
const loadAll: Array<{width: number, height: number}> = await Promise.all(firstItem.map(p => {
return new Promise((resolve, reject) => {
const url = new Image();
url.onload = function() {
// @ts-ignore
resolve({width: this.width, height: this.height});
}
url.src = p.path;
});
})) as any;
const checkAllTheSameWidthHeight = loadAll.every((p, i, arr) => {
return p.width === arr[0].width && p.height === arr[0].height;
});
if (!checkAllTheSameWidthHeight) {
return 'Pinterest requires all images to have the same width and height';
}
}
return true;
}
);

View File

@ -12,6 +12,7 @@ import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.titl
import clsx from 'clsx';
import interClass from '@gitroom/react/helpers/inter.font';
import { VideoFrame } from '@gitroom/react/helpers/video.frame';
import { useToaster } from '@gitroom/react/toaster/toaster';
const showModalEmitter = new EventEmitter();
export const ShowMediaBoxModal: FC = () => {
@ -58,6 +59,7 @@ export const MediaBox: FC<{
const [mediaList, setListMedia] = useState<Media[]>([]);
const fetch = useFetch();
const mediaDirectory = useMediaDirectory();
const toaster = useToaster();
const loadMedia = useCallback(async () => {
return (await fetch('/media')).json();
@ -69,8 +71,10 @@ export const MediaBox: FC<{
if (
!file?.target?.files?.length ||
file?.target?.files?.[0]?.size > maxFileSize
)
) {
toaster.show('Maximum file size 10mb', 'warning');
return;
}
const formData = new FormData();
formData.append('file', file?.target?.files?.[0]);
const data = await (
@ -226,7 +230,7 @@ export const MediaBox: FC<{
.map((media) => (
<div
key={media.id}
className="w-[200px] h-[200px] border-tableBorder border-2 cursor-pointer"
className="w-[200px] h-[200px] flex border-tableBorder border-2 cursor-pointer"
onClick={setNewMedia(media)}
>
{media.path.indexOf('mp4') > -1 ? (
@ -319,7 +323,7 @@ export const MultiMediaComponent: FC<{
{!!currentMedia &&
currentMedia.map((media, index) => (
<>
<div className="cursor-pointer w-[40px] h-[40px] border-2 border-tableBorder relative">
<div className="cursor-pointer w-[40px] h-[40px] border-2 border-tableBorder relative flex">
<div
onClick={() => window.open(mediaDirectory.set(media.path))}
>

View File

@ -1,5 +1,4 @@
import { IsDefined, IsOptional, IsString, IsUrl, MinLength, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { IsDefined, IsOptional, IsString, IsUrl, MinLength } from 'class-validator';
export class PinterestSettingsDto {
@IsString()

View File

@ -34,7 +34,7 @@ export class FacebookProvider implements SocialProvider {
`${process.env.FRONTEND_URL}/integrations/social/facebook${refresh ? `?refresh=${refresh}` : ''}`
)}` +
`&state=${state}` +
'&scope=pages_show_list,business_management,pages_manage_posts,publish_video,pages_manage_engagement,pages_read_engagement',
'&scope=pages_show_list,business_management,pages_manage_posts,pages_manage_engagement,pages_read_engagement',
codeVerifier: makeId(10),
state,
};

View File

@ -5,12 +5,10 @@ import {
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();
import { timer } from '@gitroom/helpers/utils/timer';
export class PinterestProvider implements SocialProvider {
identifier = 'pinterest';
@ -126,29 +124,40 @@ export class PinterestProvider implements SocialProvider {
})
).json();
console.log(media_id, upload_url);
try {
const { data } = await axios({
url: postDetails?.[0]?.media?.[0]?.url,
method: 'GET',
const { data, status } = await axios.get(
postDetails?.[0]?.media?.[0]?.url!,
{
responseType: 'stream',
});
}
);
const p = await (
await fetch(upload_url, {
method: 'PUT',
body: data.buffer,
headers: {
Authorization: `Bearer ${accessToken}`,
...upload_parameters,
},
})
const formData = Object.keys(upload_parameters)
.filter((f) => f)
.reduce((acc, key) => {
acc.append(key, upload_parameters[key]);
return acc;
}, new FormData());
formData.append('file', data);
await axios.post(upload_url, formData);
let statusCode = '';
while (statusCode !== 'succeeded') {
console.log('trying');
const mediafile = await (
await fetch(
'https://api-sandbox.pinterest.com/v5/media/' + media_id,
{
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`,
},
}
)
).json();
console.log(p);
} catch (err) {
console.log(err);
await timer(3000);
statusCode = mediafile.status;
}
mediaId = media_id;
@ -158,8 +167,6 @@ export class PinterestProvider implements SocialProvider {
url: m.url,
}));
console.log('1');
try {
const {
id: pId,
@ -188,7 +195,7 @@ export class PinterestProvider implements SocialProvider {
board_id: postDetails?.[0]?.settings.board,
media_source: mediaId
? {
source_type: 'video',
source_type: 'video_id',
media_id: mediaId,
}
: mapImages?.length === 1
@ -204,8 +211,6 @@ export class PinterestProvider implements SocialProvider {
})
).json();
console.log(all);
return [
{
id,