feat: tiktok fixes

This commit is contained in:
Nevo David 2024-10-08 16:40:16 +07:00
parent 5a7be6e184
commit 16c9c45d1e
4 changed files with 123 additions and 16 deletions

View File

@ -69,7 +69,7 @@ export const EditorWrapper: FC<{ children: ReactNode }> = ({ children }) => {
};
export const withProvider = (
SettingsComponent: FC | null,
SettingsComponent: FC<{values?: any}> | null,
CustomPreviewComponent?: FC<{maximumCharacters?: number}>,
dto?: any,
checkValidity?: (
@ -403,7 +403,7 @@ export const withProvider = (
)}
{(showTab === 0 || showTab === 2) && (
<div className={clsx('mt-[20px]', showTab !== 2 && 'hidden')}>
<Component />
<Component values={editInPlace ? InPlaceValue : props.value} />
</div>
)}
{showTab === 0 && (

View File

@ -1,8 +1,16 @@
import { FC } from 'react';
import {
FC,
ReactEventHandler,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { withProvider } from '@gitroom/frontend/components/launches/providers/high.order.provider';
import { TikTokDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/tiktok.dto';
import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values';
import { Select } from '@gitroom/react/form/select';
import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function';
const privacyLevel = [
{
@ -34,10 +42,72 @@ const yesNo = [
},
];
const TikTokSettings: FC = () => {
const CheckTikTokValidity: FC<{ picture: string }> = (props) => {
const { register } = useSettings();
const func = useCustomProviderFunction();
const [maxVideoLength, setMaxVideoLength] = useState(0);
const [isValidVideo, setIsValidVideo] = useState<undefined | boolean>(
undefined
);
const registerVideo = register('isValidVideo');
const video = useMemo(() => {
return props.picture;
}, [props.picture]);
useEffect(() => {
loadStats();
}, []);
const loadStats = useCallback(async () => {
const { maxDurationSeconds } = await func.get('maxVideoLength');
// setMaxVideoLength(5);
setMaxVideoLength(maxDurationSeconds);
}, []);
const loadVideo: ReactEventHandler<HTMLVideoElement> = useCallback(
(e) => {
// @ts-ignore
setIsValidVideo(e.target.duration <= maxVideoLength);
registerVideo.onChange({
target: {
name: 'isValidVideo',
// @ts-ignore
value: String(e.target.duration <= maxVideoLength),
},
});
},
[maxVideoLength, registerVideo]
);
if (!maxVideoLength || !video || video.indexOf('mp4') === -1) {
return null;
}
return (
<>
{isValidVideo === false && (
<div className="text-red-600 my-[20px]">
Video length is invalid, must be up to {maxVideoLength} seconds
</div>
)}
<video
controls
onLoadedMetadata={loadVideo}
className="w-0 h-0 overflow-hidden pointer-events-none"
>
<source src={video} type="video/mp4" />
</video>
</>
);
};
const TikTokSettings: FC<{ values?: any }> = (props) => {
const { register, control } = useSettings();
return (
<div className="flex flex-col">
<CheckTikTokValidity picture={props?.values?.[0]?.image?.[0]?.path} />
<Select
label="Privacy Level"
{...register('privacy_level', {

View File

@ -1,9 +1,18 @@
import { IsBoolean, IsIn, IsString } from 'class-validator';
import { IsBoolean, IsDefined, IsIn, IsString } from 'class-validator';
export class TikTokDto {
@IsIn(['PUBLIC_TO_EVERYONE', 'MUTUAL_FOLLOW_FRIENDS', 'FOLLOWER_OF_CREATOR', 'SELF_ONLY'])
@IsIn([
'PUBLIC_TO_EVERYONE',
'MUTUAL_FOLLOW_FRIENDS',
'FOLLOWER_OF_CREATOR',
'SELF_ONLY',
])
@IsString()
privacy_level: 'PUBLIC_TO_EVERYONE' | 'MUTUAL_FOLLOW_FRIENDS' | 'FOLLOWER_OF_CREATOR' | 'SELF_ONLY';
privacy_level:
| 'PUBLIC_TO_EVERYONE'
| 'MUTUAL_FOLLOW_FRIENDS'
| 'FOLLOWER_OF_CREATOR'
| 'SELF_ONLY';
@IsBoolean()
disable_duet: boolean;
@ -19,4 +28,8 @@ export class TikTokDto {
@IsBoolean()
brand_organic_toggle: boolean;
@IsIn(['true'])
@IsDefined()
isValidVideo: boolean;
}

View File

@ -15,7 +15,12 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider {
identifier = 'tiktok';
name = 'Tiktok';
isBetweenSteps = false;
scopes = ['user.info.basic', 'video.publish', 'video.upload'];
scopes = [
'user.info.basic',
'video.publish',
'video.upload',
'user.info.profile',
];
async refreshToken(refreshToken: string): Promise<AuthTokenDetails> {
const value = {
@ -37,11 +42,11 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider {
const {
data: {
user: { avatar_url, display_name, open_id },
user: { avatar_url, display_name, open_id, username },
},
} = await (
await fetch(
'https://open.tiktokapis.com/v2/user/info/?fields=open_id,avatar_url,display_name',
'https://open.tiktokapis.com/v2/user/info/?fields=open_id,avatar_url,display_name,username',
{
method: 'GET',
headers: {
@ -58,7 +63,7 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider {
id: open_id.replace(/-/g, ''),
name: display_name,
picture: avatar_url,
username: display_name.toLowerCase(),
username: username,
};
}
@ -119,7 +124,7 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider {
},
} = await (
await fetch(
'https://open.tiktokapis.com/v2/user/info/?fields=open_id,avatar_url,display_name,union_id',
'https://open.tiktokapis.com/v2/user/info/?fields=open_id,avatar_url,display_name,union_id,username',
{
method: 'GET',
headers: {
@ -129,8 +134,6 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider {
)
).json();
console.log(username);
return {
id: open_id.replace(/-/g, ''),
name: display_name,
@ -138,7 +141,28 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider {
refreshToken: refresh_token,
expiresIn: dayjs().add(23, 'hours').unix() - dayjs().unix(),
picture: avatar_url,
username: display_name,
username: username,
};
}
async maxVideoLength(accessToken: string) {
const {
data: { max_video_post_duration_sec },
} = await (
await this.fetch(
'https://open.tiktokapis.com/v2/post/publish/creator_info/query/',
{
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
Authorization: `Bearer ${accessToken}`,
},
}
)
).json();
return {
maxDurationSeconds: max_video_post_duration_sec,
};
}
@ -190,7 +214,7 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider {
} catch (err) {
throw new BadBody('titok-error', JSON.stringify(err), {
// @ts-ignore
postDetails
postDetails,
});
}
}