feat: providers

This commit is contained in:
Nevo David 2024-05-24 12:07:46 +07:00
parent fe10030a68
commit 818d172d88
10 changed files with 330 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,107 @@
import { FC } from 'react';
import { withProvider } from '@gitroom/frontend/components/launches/providers/high.order.provider';
import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration';
import { useFormatting } from '@gitroom/frontend/components/launches/helpers/use.formatting';
import { useMediaDirectory } from '@gitroom/react/helpers/use.media.directory';
import {afterLinkedinCompanyPreventRemove, linkedinCompanyPreventRemove} from "@gitroom/helpers/utils/linkedin.company.prevent.remove";
const LinkedinPreview: FC = (props) => {
const { value: topValue, integration } = useIntegration();
const mediaDir = useMediaDirectory();
const newValues = useFormatting(topValue, {
removeMarkdown: true,
saveBreaklines: true,
beforeSpecialFunc: (text: string) => {
return linkedinCompanyPreventRemove(text);
},
specialFunc: (text: string) => {
return afterLinkedinCompanyPreventRemove(text.slice(0, 280));
},
});
const [firstPost, ...morePosts] = newValues;
if (!firstPost) {
return null;
}
return (
<div className="rounded-[8px] flex flex-col gap-[8px] border border-black/90 w-[555px] pt-[12px] pl-[16px] pb-[12px] pr-[40px] bg-white text-black font-['helvetica']">
<div className="flex gap-[8px]">
<div className="w-[48px] h-[48px]">
<img
src={integration?.picture}
alt="x"
className="rounded-full w-full h-full relative z-[2]"
/>
</div>
<div className="flex flex-col leading-[16px]">
<div className="text-[14px] font-[600]">{integration?.name}</div>
<div className="text-[12px] font-[400] text-black/60">
CEO @ Gitroom
</div>
<div className="text-[12px] font-[400] text-black/60">1m</div>
</div>
</div>
<div>
<pre className="font-['helvetica'] text-[14px] font-[400] text-wrap" dangerouslySetInnerHTML={{__html: firstPost?.text}} />
{!!firstPost?.images?.length && (
<div className="-ml-[16px] -mr-[40px] flex-1 h-[555px] flex overflow-hidden mt-[12px] gap-[2px]">
{firstPost.images.map((image, index) => (
<a
key={`image_${index}`}
href={mediaDir.set(image.path)}
className="flex-1"
target="_blank"
>
<img
className="w-full h-full object-cover"
src={mediaDir.set(image.path)}
/>
</a>
))}
</div>
)}
</div>
{morePosts.map((p, index) => (
<div className="flex gap-[8px]" key={index}>
<div className="w-[40px] h-[40px]">
<img
src={integration?.picture}
alt="x"
className="rounded-full w-full h-full relative z-[2]"
/>
</div>
<div className="flex-1 flex flex-col leading-[16px] bg-[#F2F2F2] w-full pt-[8px] pr-[64px] pl-[12px] pb-[8px] rounded-[8px]">
<div className="text-[14px] font-[600]">{integration?.name}</div>
<div className="text-[12px] font-[400] text-black/60">
CEO @ Gitroom
</div>
<div className="text-[14px] mt-[8px] font-[400] text-black/90">
{p.text}
</div>
{!!p?.images?.length && (
<div className="w-full h-[120px] flex overflow-hidden mt-[12px] gap-[2px]">
{p.images.map((image, index) => (
<a
key={`image_${index}`}
href={mediaDir.set(image.path)}
target="_blank"
>
<img
className="w-[120px] h-full object-cover"
src={mediaDir.set(image.path)}
/>
</a>
))}
</div>
)}
</div>
</div>
))}
</div>
);
};
export default withProvider(null, LinkedinPreview);

View File

@ -0,0 +1,107 @@
import { FC } from 'react';
import { withProvider } from '@gitroom/frontend/components/launches/providers/high.order.provider';
import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration';
import { useFormatting } from '@gitroom/frontend/components/launches/helpers/use.formatting';
import { useMediaDirectory } from '@gitroom/react/helpers/use.media.directory';
import {afterLinkedinCompanyPreventRemove, linkedinCompanyPreventRemove} from "@gitroom/helpers/utils/linkedin.company.prevent.remove";
const FacebookPreview: FC = (props) => {
const { value: topValue, integration } = useIntegration();
const mediaDir = useMediaDirectory();
const newValues = useFormatting(topValue, {
removeMarkdown: true,
saveBreaklines: true,
beforeSpecialFunc: (text: string) => {
return linkedinCompanyPreventRemove(text);
},
specialFunc: (text: string) => {
return afterLinkedinCompanyPreventRemove(text.slice(0, 280));
},
});
const [firstPost, ...morePosts] = newValues;
if (!firstPost) {
return null;
}
return (
<div className="rounded-[8px] flex flex-col gap-[8px] border border-black/90 w-[555px] pt-[12px] pl-[16px] pb-[12px] pr-[40px] bg-white text-black font-['helvetica']">
<div className="flex gap-[8px]">
<div className="w-[48px] h-[48px]">
<img
src={integration?.picture}
alt="x"
className="rounded-full w-full h-full relative z-[2]"
/>
</div>
<div className="flex flex-col leading-[16px]">
<div className="text-[14px] font-[600]">{integration?.name}</div>
<div className="text-[12px] font-[400] text-black/60">
CEO @ Gitroom
</div>
<div className="text-[12px] font-[400] text-black/60">1m</div>
</div>
</div>
<div>
<pre className="font-['helvetica'] text-[14px] font-[400] text-wrap" dangerouslySetInnerHTML={{__html: firstPost?.text}} />
{!!firstPost?.images?.length && (
<div className="-ml-[16px] -mr-[40px] flex-1 h-[555px] flex overflow-hidden mt-[12px] gap-[2px]">
{firstPost.images.map((image, index) => (
<a
key={`image_${index}`}
href={mediaDir.set(image.path)}
className="flex-1"
target="_blank"
>
<img
className="w-full h-full object-cover"
src={mediaDir.set(image.path)}
/>
</a>
))}
</div>
)}
</div>
{morePosts.map((p, index) => (
<div className="flex gap-[8px]" key={index}>
<div className="w-[40px] h-[40px]">
<img
src={integration?.picture}
alt="x"
className="rounded-full w-full h-full relative z-[2]"
/>
</div>
<div className="flex-1 flex flex-col leading-[16px] bg-[#F2F2F2] w-full pt-[8px] pr-[64px] pl-[12px] pb-[8px] rounded-[8px]">
<div className="text-[14px] font-[600]">{integration?.name}</div>
<div className="text-[12px] font-[400] text-black/60">
CEO @ Gitroom
</div>
<div className="text-[14px] mt-[8px] font-[400] text-black/90">
{p.text}
</div>
{!!p?.images?.length && (
<div className="w-full h-[120px] flex overflow-hidden mt-[12px] gap-[2px]">
{p.images.map((image, index) => (
<a
key={`image_${index}`}
href={mediaDir.set(image.path)}
target="_blank"
>
<img
className="w-[120px] h-full object-cover"
src={mediaDir.set(image.path)}
/>
</a>
))}
</div>
)}
</div>
</div>
))}
</div>
);
};
export default withProvider(null, FacebookPreview);

View File

@ -0,0 +1,116 @@
import {
AuthTokenDetails,
PostDetails,
PostResponse,
SocialProvider,
} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface';
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
export class FacebookProvider implements SocialProvider {
identifier = 'facebook';
name = 'Facebook';
async refreshToken(refresh_token: string): Promise<AuthTokenDetails> {
const { access_token, expires_in, ...all } = await (
await fetch(
'https://graph.facebook.com/v19.0/oauth/access_token' +
'?grant_type=fb_exchange_token' +
`&client_id=${process.env.FACEBOOK_APP_ID}` +
`&client_secret=${process.env.FACEBOOK_APP_SECRET}` +
`&fb_exchange_token=${refresh_token}`
)
).json();
const {
id,
name,
picture: {
data: { url },
},
} = await (
await fetch(
`https://graph.facebook.com/v19.0/me?fields=id,name,picture&access_token=${access_token}`
)
).json();
return {
id,
name,
accessToken: access_token,
refreshToken: access_token,
expiresIn: expires_in,
picture: url,
username: '',
};
}
async generateAuthUrl() {
const state = makeId(6);
return {
url:
'https://www.facebook.com/v19.0/dialog/oauth' +
`?client_id=${process.env.FACEBOOK_APP_ID}` +
`&redirect_uri=${encodeURIComponent(
`${process.env.FRONTEND_URL}/integrations/social/facebook`
)}` +
`&state=${state}` +
'&scope=email,public_profile',
// '&scope=email,public_profile,pages_manage_posts,pages_read_engagement,publish_to_groups,groups_access_member_info',
codeVerifier: makeId(10),
state,
};
}
async authenticate(params: { code: string; codeVerifier: string }) {
const getAccessToken = await (
await fetch(
'https://graph.facebook.com/v19.0/oauth/access_token' +
`?client_id=${process.env.FACEBOOK_APP_ID}` +
`&redirect_uri=${encodeURIComponent(
`${process.env.FRONTEND_URL}/integrations/social/facebook`
)}` +
`&client_secret=${process.env.FACEBOOK_APP_SECRET}` +
`&code=${params.code}`
)
).json();
const { access_token, expires_in, ...all } = await (
await fetch(
'https://graph.facebook.com/v19.0/oauth/access_token' +
'?grant_type=fb_exchange_token' +
`&client_id=${process.env.FACEBOOK_APP_ID}` +
`&client_secret=${process.env.FACEBOOK_APP_SECRET}` +
`&fb_exchange_token=${getAccessToken.access_token}`
)
).json();
const {
id,
name,
picture: {
data: { url },
},
} = await (
await fetch(
`https://graph.facebook.com/v19.0/me?fields=id,name,picture&access_token=${access_token}`
)
).json();
return {
id,
name,
accessToken: access_token,
refreshToken: access_token,
expiresIn: expires_in,
picture: url,
username: '',
};
}
async post(
id: string,
accessToken: string,
postDetails: PostDetails[]
): Promise<PostResponse[]> {
return [];
}
}