Skip to content

added credits and play duplicate song on approve logic #135

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions next-app/app/api/remcreds/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { authOptions } from "@/lib/auth-options";
import prisma from "@/lib/db";
import { getServerSession } from "next-auth";
import { NextRequest, NextResponse } from "next/server";


export const POST = async (req:NextRequest)=>{

try{
const session = await getServerSession(authOptions);
if (!session?.user.id) {
return NextResponse.json(
{
message: "Unauthenticated",
},
{
status: 403,
},
);
}
const data = await req.json();
if(!data.spaceId || (typeof data.spaceId)!=="string"){
return NextResponse.json(
{
message: "Space Id is required",
},
{
status: 403,
},
);
}
// check if space exists
const space = await prisma.space.findUnique({
where: { id: data.spaceId },
});
if (!space) {
return NextResponse.json(
{
message: "Space not found",
},
{
status: 403,
},
);
}
// check if users creds are available
const remainingCreds = await prisma.remainingCreds.findFirst({
where: {
userId: session.user.id,
spaceId: data.spaceId,
},
select: { remainingCreds: true },
});

if(!remainingCreds){
return NextResponse.json(
{
message: "No Credits available",
ok:false,
}
)
}

return NextResponse.json(
{
message: "Credits available",
ok:true,
}
)
}
catch(error:any){
if (error.message === "Unauthenticated Request") {
return NextResponse.json(
{ success: false, message: "You must be logged in to create a space" },
{ status: 401 }
);
}


return NextResponse.json(
{ success: false, message: `An unexpected error occurred: ${error.message}` },
{ status: 500 }
);
}

}
128 changes: 93 additions & 35 deletions next-app/components/StreamView/AddSongForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { YT_REGEX } from "@/lib/utils";
import { useSocket } from "@/context/socket-context";
import React from "react";
import React, { useCallback, useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card, CardContent } from "@/components/ui/card";
Expand All @@ -20,7 +20,8 @@ type Props = {
loading: boolean;
enqueueToast: (type: "error" | "success", message: string) => void;
spaceId:string,
isSpectator:boolean
isSpectator:boolean,
checkIsDuplicate:(url:string)=>boolean
};

export default function AddSongForm({
Expand All @@ -32,11 +33,13 @@ export default function AddSongForm({
userId,
spaceId,
isSpectator,
checkIsDuplicate
}: Props) {
const { sendMessage } = useSocket();
const wallet = useWallet();
const {connection} = useConnection();
const user = useSession().data?.user;
const [duplicateDiv, setDuplicateDiv] = useState(false);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
Expand All @@ -55,7 +58,57 @@ export default function AddSongForm({
setInputLink("");
};

const handlePayAndPlay = async (e: React.FormEvent) => {
const finallyPlaySong = async (songLink:string)=>{
if(!wallet.publicKey || !connection){
enqueueToast("error", "Please connect your wallet");
return;
}
// check if user creds for this space is pending or not
const response = await fetch(`/api/remcreds`,{
method:"POST",
body:JSON.stringify({
spaceId:spaceId,
})
});
const data = await response.json();

if(!data.ok){
const transaction = new Transaction();
transaction.add(
SystemProgram.transfer({
fromPubkey: wallet.publicKey,
toPubkey: new PublicKey(process.env.NEXT_PUBLIC_PUBLICKEY as string),
lamports: Number(process.env.NEXT_PUBLIC_SOL_PER_PAYMENT) * LAMPORTS_PER_SOL,
})
)

// sign Transaction steps
const blockHash = await connection.getLatestBlockhash();
transaction.feePayer = wallet.publicKey;
transaction.recentBlockhash = blockHash.blockhash;
//@ts-ignore
const signed = await wallet.signTransaction(transaction);


const signature = await connection.sendRawTransaction(signed.serialize());

enqueueToast("success", `Transaction signature: ${signature}`);
await connection.confirmTransaction({
blockhash: blockHash.blockhash,
lastValidBlockHeight: blockHash.lastValidBlockHeight,
signature
});
enqueueToast("success", `Payment successful`);
}

sendMessage("pay-and-play-next", {
spaceId,
userId: user?.id,
url:songLink
});
}

const handlePayAndPlay = useCallback(async (e: React.FormEvent) => {
e.preventDefault();

if(!wallet.publicKey || !connection){
Expand All @@ -67,44 +120,31 @@ export default function AddSongForm({
}
try{
setLoading(true);
const transaction = new Transaction();
transaction.add(
SystemProgram.transfer({
fromPubkey: wallet.publicKey,
toPubkey: new PublicKey(process.env.NEXT_PUBLIC_PUBLICKEY as string),
lamports: Number(process.env.NEXT_PUBLIC_SOL_PER_PAYMENT) * LAMPORTS_PER_SOL,
})
)

// sign Transaction steps
const blockHash = await connection.getLatestBlockhash();
transaction.feePayer = wallet.publicKey;
transaction.recentBlockhash = blockHash.blockhash;
//@ts-ignore
const signed = await wallet.signTransaction(transaction);


const signature = await connection.sendRawTransaction(signed.serialize());

enqueueToast("success", `Transaction signature: ${signature}`);
await connection.confirmTransaction({
blockhash: blockHash.blockhash,
lastValidBlockHeight: blockHash.lastValidBlockHeight,
signature
});
enqueueToast("success", `Payment successful`);
sendMessage("pay-and-play-next", {
spaceId,
userId: user?.id,
url:inputLink
});
//check if it is duplicate or not if it is accepted by user then play
if(checkIsDuplicate(inputLink)){

if(!duplicateDiv){
setDuplicateDiv(true);
return;
}
if(duplicateDiv){

setDuplicateDiv(false);
await finallyPlaySong(inputLink);
setLoading(false);
}
return;
}
await finallyPlaySong(inputLink);

}
catch(error){
enqueueToast("error", `Payment unsuccessful`);
}
setLoading(false);

};
},[ duplicateDiv,wallet , connection, inputLink]);

const videoId = inputLink ? inputLink.match(YT_REGEX)?.[1] : undefined;

Expand All @@ -114,13 +154,31 @@ export default function AddSongForm({
<h1 className="text-xl font-bold">Add a song</h1>
</div>

<form onSubmit={handleSubmit} className="space-y-2">
<form onSubmit={handleSubmit} className="space-y-2 relative">
<Input
type="text"
placeholder="Please paste your link"
value={inputLink}
onChange={(e) => setInputLink(e.target.value)}
/>
{ duplicateDiv &&
<div className="w-full z-10 absolute top-10 right-0 transition-all ease-in duration-500 bg-black rounded-md flex flex-col gap-3 px-4 py-2 justify-center items-center ">
<h1 className="font-semibold text-lg">Duplicate Song</h1>
<Button
className="w-full"
type="submit"
onClick={handlePayAndPlay}
>
Accept
</Button>
<Button
className="w-full bg-red-500 hover:bg-red-400"
onClick={()=>{setDuplicateDiv(false);setLoading(false)}}
>
Cancel
</Button>
</div>
}
<Button
disabled={loading}
onClick={handleSubmit}
Expand Down
4 changes: 4 additions & 0 deletions next-app/components/StreamView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ export default function StreamView({
setInputLink("");
setLoading(false);
}
const checkIsDuplicate = (url:string)=>{
return (queue.some((video)=>video.url===url) || currentVideo?.url===url)
}

async function refreshStreams() {
try {
Expand Down Expand Up @@ -149,6 +152,7 @@ export default function StreamView({
setLoading={setLoading}
spaceId={spaceId}
isSpectator={!playVideo}
checkIsDuplicate={checkIsDuplicate}
/>
<NowPlaying
currentVideo={currentVideo}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-- AlterEnum
ALTER TYPE "Provider" ADD VALUE 'Credentials';

-- AlterTable
ALTER TABLE "User" ADD COLUMN "name" TEXT,
ADD COLUMN "password" TEXT;

-- CreateTable
CREATE TABLE "RemainingCreds" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"spaceId" TEXT NOT NULL,
"remainingCreds" INTEGER NOT NULL,

CONSTRAINT "RemainingCreds_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "RemainingCreds_userId_spaceId_key" ON "RemainingCreds"("userId", "spaceId");

-- AddForeignKey
ALTER TABLE "RemainingCreds" ADD CONSTRAINT "RemainingCreds_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "RemainingCreds" ADD CONSTRAINT "RemainingCreds_spaceId_fkey" FOREIGN KEY ("spaceId") REFERENCES "Space"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
45 changes: 29 additions & 16 deletions next-app/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ datasource db {
}

model User {
id String @id @default(uuid())
email String @unique
name String?
password String?
provider Provider
streams Stream[] @relation("user")
upvotes Upvote[]
addedStreams Stream[] @relation("addedBy")
hostedSpaces Space[] @relation("hostedBy")
id String @id @default(uuid())
email String @unique
name String?
password String?
provider Provider
streams Stream[] @relation("user")
upvotes Upvote[]
addedStreams Stream[] @relation("addedBy")
hostedSpaces Space[] @relation("hostedBy")
remainingCreds RemainingCreds[]
}

model Stream {
Expand Down Expand Up @@ -67,13 +68,25 @@ model Upvote {
}

model Space {
id String @id @default(uuid())
name String
streams Stream[] @relation("spaceStreams")
hostId String
host User @relation("hostedBy", fields: [hostId], references: [id])
isActive Boolean @default(true)
currentStream CurrentStream?
id String @id @default(uuid())
name String
streams Stream[] @relation("spaceStreams")
hostId String
host User @relation("hostedBy", fields: [hostId], references: [id])
isActive Boolean @default(true)
currentStream CurrentStream?
remainingCreds RemainingCreds[]
}

model RemainingCreds {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id])
spaceId String
space Space @relation(fields: [spaceId], references: [id])
remainingCreds Int

@@unique([userId, spaceId])
}

enum StreamType {
Expand Down
Loading
Loading