Skip to content
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

Issue 222 #225

Merged
merged 3 commits into from
Sep 20, 2023
Merged
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
67 changes: 48 additions & 19 deletions components/IssueCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import { TxOutputRef } from 'scrypt-ts';
import axios from 'axios';
import { parse } from 'path';

import artifact from "../artifacts/issue.json";
import Link from 'next/link';

Issue.loadArtifact(artifact)


export interface Signer {
// Define the properties of the Signer interface as per your requirement
Expand All @@ -32,6 +37,7 @@ interface Comment {

interface IssueCardProps {
issue: Issue;
contractLocation: string;
onAddBounty: (issue: Issue) => Promise<void>;
onLeaveComment: (issue: Issue) => Promise<void>;
onMarkAsComplete: (issue: Issue) => Promise<void>;
Expand All @@ -42,6 +48,7 @@ interface IssueCardProps {

const IssueCard: React.FC<IssueCardProps> = (props: {
issue: Issue,
contractLocation: string,
onAddBounty: (issue: Issue) => Promise<void>,
onLeaveComment: (issue: Issue) => Promise<void>,
onMarkAsComplete: (issue: Issue) => Promise<void>,
Expand All @@ -53,10 +60,10 @@ const IssueCard: React.FC<IssueCardProps> = (props: {

const [addingBounty, setAddingBounty] = useState(false);
const [satoshis, setSatoshis] = useState<number | null>(null);
const [newBounty, setNewBounty] = useState<bigint>(BigInt(props.issue.balance -1));
const [newBounty, setNewBounty] = useState<bigint>(0n);
const [issue, setIssue] = useState<Issue>(props.issue);
const [location, setLocation] = useState<string | null>((props.issue.from as TxOutputRef)?.tx?.hash);
const [origin, setOrigin] = useState<string | null>(null);
const [location, setLocation] = useState<string | null>(props.contractLocation);
const [origin, setOrigin] = useState<string | null>(props.origin);
const [completionStatus, setCompletionStatus] = useState<'incomplete' | 'posting' | 'complete'>('incomplete');
const [commentBoxVisible, setCommentBoxVisible] = useState(false);
const [newComment, setNewComment] = useState('');
Expand All @@ -66,6 +73,21 @@ const IssueCard: React.FC<IssueCardProps> = (props: {
const [assignPopupVisible, setAssignPopupVisible] = useState(false);
const [publicKeyInput, setPublicKeyInput] = useState('');

useEffect(() => {

if(location){

let [txid, vout] = location.split('_')
axios.get(`https://api.whatsonchain.com/v1/bsv/main/tx/hash/${txid}`).then((resp) => {
console.log("location tx", resp.data.vout[vout])
let amount = Math.round(resp.data.vout[vout].value * 1e8)
setNewBounty(BigInt(amount) - 1n)
})

}

},[location])

const handleAssignButtonClick = () => {
setAssignPopupVisible(true);
};
Expand Down Expand Up @@ -105,7 +127,10 @@ const IssueCard: React.FC<IssueCardProps> = (props: {
});
console.log({ result })
setIssue(newIssue);
setNewBounty(BigInt(newIssue.balance - 1));
let [txid,vout] = result.data.contract.location.split('_')
let newLocTx = await axios.get(`https://api.whatsonchain.com/v1/bsv/main/tx/hash/${txid}`)
let amount = Math.round(newLocTx.data.vout[vout].value * 1e8)
setNewBounty(BigInt(amount)- 1n)
props.refresh();

setAssignSuccess(true);
Expand Down Expand Up @@ -187,7 +212,10 @@ useEffect(() => {
const result = await axios.get(`https://pow.co/api/v1/issues/${(newIssue.from as TxOutputRef)?.tx?.hash}`);
console.log({ result })
setIssue(newIssue);
setNewBounty(BigInt(newIssue.balance - 1));
let [txid,vout] = result.data.contract.location.split('_')
let newLocTx = await axios.get(`https://api.whatsonchain.com/v1/bsv/main/tx/hash/${txid}`)
let amount = Math.round(newLocTx.data.vout[vout].value * 1e8)
setNewBounty(BigInt(amount)- 1n)
props.refresh();

};
Expand Down Expand Up @@ -230,7 +258,10 @@ useEffect(() => {
const result = await axios.get(`https://pow.co/api/v1/issues/${tx.hash}`);
console.log({ result })
setIssue(newIssue);
setNewBounty(BigInt(newIssue.balance - 1));
let [txid,vout] = result.data.contract.location.split('_')
let newLocTx = await axios.get(`https://api.whatsonchain.com/v1/bsv/main/tx/hash/${txid}`)
let amount = Math.round(newLocTx.data.vout[vout].value * 1e8)
setNewBounty(BigInt(amount)- 1n)
props.refresh();

// Assuming onLeaveComment is a function that posts the comment and returns the JSON response
Expand All @@ -245,21 +276,18 @@ useEffect(() => {
}
};

const title = Buffer.from(issue.title, 'hex').toString('utf8');
const description = Buffer.from(issue.description, 'hex').toString('utf8');
const organization = Buffer.from(issue.organization, 'hex').toString('utf8');
const repo = Buffer.from(issue.repo, 'hex').toString('utf8');

console.log('ORIGIN', props.origin)

return (
<div className="border p-4 rounded-md space-y-2">
<h2 className="text-xl font-bold">{title}</h2>
<p className="text-gray-600">{organization}/{repo}</p>
<p>{description}</p>
<p className="text-sm text-gray-400">Bounty: {newBounty.toString()}</p>
<p className="text-sm text-gray-400">Location: {props.origin.location}</p>
<p className="text-sm text-gray-400">Origin: {props.origin.origin}</p>
<div className="border rounded-md space-y-2">
<Link href={`/issues/${origin?.split('_')}`}>
<h2 className="cursor-pointer bg-primary-500 p-4 text-xl font-bold">{issue.title}</h2>
</Link>
<div className='p-4'>
<p className="text-gray-600">{issue.organization}/{issue.repo}</p>
<p>{issue.description}</p>
<p className="text-sm font-semibold">Bounty: <span className='text-xl font-bold text-primary-500'>{newBounty.toString()} sats</span></p>
<p className="text-sm opacity-60 whitespace-pre-line break-words">Location: <a className='hover:underline' href={`https://whatsonchain.com/tx/${location?.split('_')[0]}`} target='_blank' rel='noreferrer'>{location}</a></p>
<p className="text-sm opacity-60 whitespace-pre-line break-words">Origin: <a className='hover:underline' href={`https://whatsonchain.com/tx/${origin?.split('_')[0]}`} target='_blank' rel='noreferrer'>{origin}</a></p>

<div className="space-x-2">

Expand Down Expand Up @@ -351,6 +379,7 @@ useEffect(() => {

))}
</ul>
</div>
</div>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions pages/issues/[txid].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ const IssuePage = () => {
onMarkAsComplete={onMarkAsComplete}
refresh={refresh}
origin={origin}
contractLocation={origin}
methodCalls={methodCalls}
/>

Expand Down
13 changes: 8 additions & 5 deletions pages/issues/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,21 @@ export interface ScryptRanking {

const RankedIssueCard = ({origin, totaldifficulty}: ScryptRanking) => {
const [issue, setIssue] = useState<Issue | null>(null)
const [location, setLocation] = useState<string | null>(null)
const wallet = useWallet()


const getIssueData = () => {
getIssue({txid: origin}).then((data) => {
console.log(data)
setIssue(data)
getIssue({txid: origin.split('_')[0]}).then((data) => {
console.log("issue data", data)
setIssue(data.props)
setLocation(data.location)
})
}

useEffect(() => {
getIssueData()
console.log(origin)
getIssueData()
},[origin])

const handleRefresh = () => {
Expand All @@ -54,7 +57,7 @@ const RankedIssueCard = ({origin, totaldifficulty}: ScryptRanking) => {
}
return (
<div className=''>
<IssueCard origin={origin} refresh={handleRefresh} methodCalls={[]} issue={issue!} onAddBounty={handleAddBounty} onLeaveComment={handleComment} onMarkAsComplete={handleComplete}/>
{issue ? <IssueCard origin={origin} contractLocation={location!} refresh={handleRefresh} methodCalls={[]} issue={issue} onAddBounty={handleAddBounty} onLeaveComment={handleComment} onMarkAsComplete={handleComplete}/>: "loading"}
</div>
)
}
Expand Down
3 changes: 1 addition & 2 deletions services/calendar_event_operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import { Meeting } from '../src/contracts/meeting';

import ContractOperator from "./contract_operator";
import { fetchTransaction } from './whatsonchain';
import { Meeting as MeetingContract } from "../src/contracts/meeting"
import artifact from "../artifacts/meeting.json";

MeetingContract.loadArtifact(artifact);
Meeting.loadArtifact(artifact);

export class CalendarEventOperator extends ContractOperator<Meeting> {

Expand Down
131 changes: 131 additions & 0 deletions services/issue_operator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { ContractTransaction, HashedSet, MethodCallOptions, PubKey, Signer, bsv, findSig, hash160 } from 'scrypt-ts';
import { Issue } from "../src/contracts/issue";

import ContractOperator from "./contract_operator";
import { fetchTransaction } from "./whatsonchain";

import artifact from '../artifacts/issue.json'

Issue.loadArtifact(artifact)

export class IssueOperator extends ContractOperator<Issue> {

static async load({ origin, signer }: { origin: string, signer: Signer }): Promise<IssueOperator> {

// load record from server to get current location and pre-image of hashed props

const { location, props } = await ContractOperator.loadRecord({ origin });

// load current location transaction from blockchain

//const tx = await fetchTransaction({ txid: location.split('_')[0] });
//temporary hack
const tx = await fetchTransaction({ txid: origin});

// initialize all HashSet / HashedMap props
const invitees = new HashedSet<PubKey>()
const attendees = new HashedSet<PubKey>()

// fill values based on props (pre-images) from server
if (props.invitees){
props.invitees.forEach((str: string) => {
invitees.add(PubKey(str))
});
}
if (props.attendees){
props.attendees.forEach((str: string) => {
attendees.add(PubKey(str))
});
}

let contract
try {
contract = Issue.fromTx(tx, 0, {
invitees,
attendees
});

console.log("contract",contract)

} catch (error) {
throw error
}

return new IssueOperator({ origin, contract, signer });
}

async close(): Promise<IssueOperator | null> {

return null

}

async complete(): Promise<IssueOperator | null> {

return null

}

async assign(): Promise<IssueOperator | null> {

return null

}

async addBounty(satoshis: bigint): Promise<IssueOperator> {

this.contract.bindTxBuilder('addBounty', async (
current: Issue,
options: MethodCallOptions<Issue>
): Promise<ContractTransaction> => {

const nextInstance = current.next()

const newBalance = current.balance + Number(satoshis)

const tx = new bsv.Transaction()
tx.addInput(current.buildContractInput(options.fromUTXO)).addOutput(
new bsv.Transaction.Output({
script: nextInstance.lockingScript,
satoshis: newBalance
})
)
const changeAddress = await this.signer.getDefaultAddress()
tx.change(changeAddress)

return Promise.resolve({
tx,
atInputIndex: 0,
nexts: [
{
instance: nextInstance,
balance: newBalance,
atOutputIndex: 0
}
]
})
})

const { tx } = await this.contract.methods.addBounty(satoshis)

this.contract = Issue.fromTx(tx,0)

return this



}

async addComment(): Promise<IssueOperator | null> {

return null

}

async reopen(): Promise<IssueOperator | null> {

return null

}

}
6 changes: 3 additions & 3 deletions services/issues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,13 +243,13 @@ export interface NewIssue {
return issue;
}

export async function getIssue({ txid }: {txid: string}): Promise<Issue | null> {
export async function getIssue({ txid }: {txid: string}): Promise<any | null> {

try {

const { data } = await axios.get(`https://pow.co/api/v1/issues/${txid}`)

return data.issue as Issue
console.log(data)
return data.origin

} catch(error) {

Expand Down