Skip to content

Commit c72f60f

Browse files
authored
Merge pull request #1143 from streamethorg/payments
✨ Payments
2 parents 784118a + ceb2c3b commit c72f60f

34 files changed

+1713
-162
lines changed

docker-compose-dev.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ x-common-environment: &common-environment
5050
REDIS_HOST: redis
5151
REDIS_PORT: 6379
5252
OPENAI_API_KEY_FILE: ${OPENAI_API_KEY_FILE}
53+
STRIPE_API_KEY_FILE: ${STRIPE_API_KEY_FILE}
54+
STRIPE_PUBLISHABLE_KEY_FILE: ${STRIPE_PUBLISHABLE_KEY_FILE}
5355

5456
x-common-worker: &common-worker
5557
volumes:
@@ -85,7 +87,7 @@ services:
8587
- /app/packages/reel-creator/node_modules
8688
environment:
8789
NODE_ENV: ${NODE_ENV}
88-
SERVER_WEBHOOK_URL: https://kodiak-feasible-distinctly.ngrok-free.app/webhook/remotion
90+
SERVER_WEBHOOK_URL: https://6c5a-83-38-191-227.ngrok-free.app/webhook/remotion
8991
SERVER_WEBHOOK_SECRET_FILE: ${LIVEPEER_WEBHOOK_SECRET_FILE}
9092
AWS_ACCESS_KEY_ID_FILE: ${AWS_ACCESS_KEY_ID_FILE}
9193
AWS_SECRET_ACCESS_KEY_FILE: ${AWS_SECRET_ACCESS_KEY_FILE}

docker-compose.prod.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ x-common-environment: &common-environment
5454
OPENAI_API_KEY_FILE: /run/secrets/openai-api-key
5555
AWS_ACCESS_KEY_ID: /run/secrets/aws-access-key
5656
AWS_SECRET_ACCESS_KEY: /run/secrets/aws-secret-key
57+
STRIPE_SECRET_KEY_FILE: /run/secrets/prod-stripe-secret-key
58+
STRIPE_PUBLISHABLE_KEY_FILE: /run/secrets/prod-stripe-publishable-key
5759

5860
x-common-deploy: &common-deploy
5961
restart_policy:

docker-compose.staging.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ x-common-environment: &common-environment
5454
OPENAI_API_KEY_FILE: /run/secrets/openai-api-key
5555
AWS_ACCESS_KEY_ID_FILE: /run/secrets/aws-access-key
5656
AWS_SECRET_ACCESS_KEY_FILE: /run/secrets/aws-secret-key
57+
STRIPE_SECRET_KEY_FILE: /run/secrets/stripe-secret-key
58+
STRIPE_PUBLISHABLE_KEY_FILE: /run/secrets/stripe-publishable-key
5759

5860
x-common-deploy: &common-deploy
5961
restart_policy:

packages/app/app/studio/[organization]/(root)/library/components/TableCells.tsx

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import { TableCell } from '@/components/ui/table';
2-
import { IExtendedSession, eLayout } from '@/lib/types';
2+
import { IExtendedSession } from '@/lib/types';
33
import { Users, FilePenLine, Loader2 } from 'lucide-react';
44
import Link from 'next/link';
55
import { formatDate, formatDuration } from '@/lib/utils/time';
6-
import { generateThumbnailAction } from '@/lib/actions/sessions';
76
import Thumbnail from '@/components/misc/VideoCard/thumbnail';
87
import { ProcessingStatus } from 'streameth-new-server/src/interfaces/session.interface';
9-
import { Button } from '@/components/ui/button';
108
import { getTypeLabel } from '@/lib/utils/utils';
119
import {
1210
LuClapperboard,
@@ -17,8 +15,9 @@ import {
1715
LuVideo,
1816
} from 'react-icons/lu';
1917
import DropdownActions from './DropdownActions';
18+
import FeatureButton from '@/components/ui/feature-button';
2019

21-
const TableCells = async ({
20+
const TableCells = ({
2221
item,
2322
organization,
2423
}: {
@@ -104,25 +103,34 @@ const TableCells = async ({
104103
{isPending && <Loader2 className="animate-spin w-4 h-4" />}
105104
</div>
106105
</TableCell>
107-
<TableCell className={rowBackgroundClass}>
108-
<div className="flex justify-end items-center space-x-2 max-w-[100px]">
106+
<TableCell className={`${rowBackgroundClass} w-[220px]`}>
107+
<div className="flex justify-end items-center gap-2">
109108
{!isDisabled && (
110-
// item.createdAt &&
111-
// new Date(item.createdAt).getTime() >
112-
// Date.now() - 7 * 24 * 60 * 60 * 1000 && (
113109
<Link
114110
href={`/studio/${organization}/clips/${item.stageId}?sessionId=${item._id}&videoType=recording`}
115111
>
116-
<Button variant="primary" size="icon" className="mr-2">
117-
<LuScissorsLineDashed className="w-5 h-5 cursor-pointer" />
118-
</Button>
112+
<FeatureButton
113+
organizationId={organization}
114+
variant="ghost"
115+
size="sm"
116+
className="flex items-center gap-2 h-8"
117+
>
118+
<LuScissorsLineDashed className="w-4 h-4" />
119+
<span>Clip</span>
120+
</FeatureButton>
119121
</Link>
120122
)}
121123
{!isDisabled && (
122124
<Link href={`/studio/${organization}/library/${item._id}`}>
123-
<Button variant="outline" size="icon" className="mr-2">
124-
<FilePenLine className="w-5 h-5 cursor-pointer" />
125-
</Button>
125+
<FeatureButton
126+
organizationId={organization}
127+
variant="ghost"
128+
size="sm"
129+
className="flex items-center gap-2 h-8"
130+
>
131+
<FilePenLine className="w-4 h-4" />
132+
<span>Edit</span>
133+
</FeatureButton>
126134
</Link>
127135
)}
128136
{!isDisabled && (

packages/app/app/studio/[organization]/(root)/library/components/UploadVideoDialog.tsx

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ import Dropzone from './upload/Dropzone';
1515
import UploadVideoForm from './upload/UploadVideoForm';
1616
import * as z from 'zod';
1717
import { sessionSchema } from '@/lib/schema';
18+
import FeatureButton from '@/components/ui/feature-button';
19+
import { useSubscription } from '@/lib/hooks/useSubscription';
20+
import { useRouter } from 'next/navigation';
21+
import { Button } from '@/components/ui/button';
1822

1923
type UploadStatus = {
2024
progress: number;
@@ -35,12 +39,26 @@ const UploadVideoDialog = ({ organizationId }: { organizationId: string }) => {
3539
updatedSession: z.infer<typeof sessionSchema>;
3640
} | null>(null);
3741

42+
const { canUseFeatures, organizationSlug } = useSubscription(organizationId);
43+
const router = useRouter();
44+
45+
const handleClick = (e: React.MouseEvent) => {
46+
if (!canUseFeatures) {
47+
e.preventDefault();
48+
e.stopPropagation();
49+
router.push(`/studio/${organizationSlug}/payments`);
50+
return;
51+
}
52+
setOpen(true);
53+
};
54+
3855
const handleSessionUpdate = useCallback(
3956
(uploadId: string, updatedSession: z.infer<typeof sessionSchema>) => {
4057
setPendingUpdate({ uploadId, updatedSession });
4158
},
4259
[]
4360
);
61+
4462
useEffect(() => {
4563
if (pendingUpdate) {
4664
const { uploadId, updatedSession } = pendingUpdate;
@@ -103,11 +121,15 @@ const UploadVideoDialog = ({ organizationId }: { organizationId: string }) => {
103121

104122
return (
105123
<Dialog open={open} onOpenChange={setOpen}>
106-
<DialogTrigger className="flex flex-row items-center pr-2 bg-white rounded-xl border w-fit hover:bg-secondary">
107-
<div className="p-2 text-white rounded-xl">
108-
<LuFileUp size={20} className="text-primary" />
109-
</div>
110-
<span className="text-sm">Upload Video</span>
124+
<DialogTrigger asChild>
125+
<Button
126+
variant="ghost"
127+
className="flex items-center gap-2 h-10"
128+
onClick={handleClick}
129+
>
130+
<LuFileUp className="w-5 h-5" />
131+
<span>Upload Video</span>
132+
</Button>
111133
</DialogTrigger>
112134
{dialogContent}
113135
</Dialog>

packages/app/app/studio/[organization]/(root)/livestreams/components/CreateLivestreamModal.tsx

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,37 @@ import ImageUpload from '@/components/misc/form/imageUpload';
3737
import { Checkbox } from '@/components/ui/checkbox';
3838
import { Label } from '@/components/ui/label';
3939
import { LuRadio } from 'react-icons/lu';
40+
import { cn } from '@/lib/utils/utils';
41+
import FeatureButton from '@/components/ui/feature-button';
42+
import { useSubscription } from '@/lib/hooks/useSubscription';
4043

4144
const CreateLivestreamModal = ({
4245
organization,
4346
show,
47+
variant = 'outline',
48+
className,
4449
}: {
4550
show?: boolean;
4651
organization: IExtendedOrganization;
52+
variant?: 'outline' | 'ghost' | 'primary' | 'default';
53+
className?: string;
4754
}) => {
4855
const [open, setOpen] = useState(show ?? false);
4956
const [isMultiDate, setIsMultiDate] = useState(false);
5057
const [isLoading, setIsLoading] = useState(false);
51-
const [streamType, setStreamType] = useState<
52-
'instant' | 'schedule' | undefined
53-
>();
58+
const [streamType, setStreamType] = useState<'instant' | 'schedule' | undefined>();
5459
const router = useRouter();
60+
const { canUseFeatures, organizationSlug } = useSubscription(organization._id.toString());
61+
62+
const handleClick = (e: React.MouseEvent) => {
63+
if (!canUseFeatures) {
64+
e.preventDefault();
65+
e.stopPropagation();
66+
router.push(`/studio/${organizationSlug}/payments`);
67+
return;
68+
}
69+
setOpen(true);
70+
};
5571

5672
const form = useForm<z.infer<typeof StageSchema>>({
5773
resolver: zodResolver(StageSchema),
@@ -113,8 +129,8 @@ const CreateLivestreamModal = ({
113129

114130
router.push(`/studio/${organization?.slug}/livestreams/${streamId}`);
115131
})
116-
.catch(() => {
117-
toast.error('Error creating stream');
132+
.catch((error) => {
133+
toast.error(error.message);
118134
setIsLoading(false);
119135
})
120136
.finally(() => {
@@ -132,14 +148,13 @@ const CreateLivestreamModal = ({
132148
}}
133149
>
134150
<DialogTrigger asChild>
135-
<Button
136-
variant={'outline'}
137-
className="flex flex-row justify-start items-center p-2 bg-white rounded-xl border w-fit"
151+
<Button
152+
variant="primary"
153+
className="flex items-center gap-2"
154+
onClick={handleClick}
138155
>
139-
<div className="p-2 text-white rounded-xl">
140-
<LuRadio size={20} className="text-primary" />
141-
</div>
142-
<span className="text-sm">Create Livestream</span>
156+
<LuRadio className="w-5 h-5" />
157+
Create Livestream
143158
</Button>
144159
</DialogTrigger>
145160
{!streamType ? (

packages/app/app/studio/[organization]/(root)/livestreams/components/LivestreamActions.tsx

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,47 @@ import EditLivestream from './EditLivestream';
77
import Link from 'next/link';
88
import { Button } from '@/components/ui/button';
99
import { ScissorsIcon } from 'lucide-react';
10+
import FeatureButton from '@/components/ui/feature-button';
1011

1112
const LivestreamActions = ({
1213
stream,
1314
organizationSlug,
15+
paidStages,
16+
currentStages,
1417
}: {
1518
stream: IExtendedStage;
1619
organizationSlug: string;
20+
paidStages: number;
21+
currentStages: number;
1722
}) => {
23+
const isOverLimit = currentStages > paidStages;
24+
1825
return (
1926
<div className="space-y-2 flex flex-col items-start w-full">
20-
<EditLivestream organizationSlug={organizationSlug} stage={stream} />
21-
<ShareLivestream
22-
organization={organizationSlug}
23-
variant="ghost"
24-
streamId={stream._id!}
25-
/>
26-
{stream.streamSettings?.isActive && (
27-
<Link
28-
href={`/studio/${organizationSlug}/clips/${stream._id}?videoType=livestream`}
29-
className="w-full"
30-
>
31-
<Button
27+
{!isOverLimit && (
28+
<>
29+
<EditLivestream organizationSlug={organizationSlug} stage={stream} />
30+
<ShareLivestream
31+
organization={organizationSlug}
3232
variant="ghost"
33-
className="flex flex-row items-center justify-start gap-3 w-full"
34-
>
35-
<ScissorsIcon className="w-5 h-5" />
36-
<p>Clip</p>
37-
</Button>
38-
</Link>
33+
streamId={stream._id!}
34+
/>
35+
{stream.streamSettings?.isActive && (
36+
<Link
37+
href={`/studio/${organizationSlug}/clips/${stream._id}?videoType=livestream`}
38+
className="w-full"
39+
>
40+
<FeatureButton
41+
organizationId={organizationSlug}
42+
variant="ghost"
43+
className="flex flex-row items-center justify-start gap-3 w-full"
44+
>
45+
<ScissorsIcon className="w-5 h-5" />
46+
<p>Clip</p>
47+
</FeatureButton>
48+
</Link>
49+
)}
50+
</>
3951
)}
4052
<DeleteLivestream stream={stream} />
4153
</div>

0 commit comments

Comments
 (0)