Skip to content

Commit

Permalink
feat: Meow webapp
Browse files Browse the repository at this point in the history
  • Loading branch information
lukepark327 committed Jul 18, 2024
1 parent 6650c28 commit 3f30db8
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 16 deletions.
26 changes: 26 additions & 0 deletions src/components/ChatBalloon.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script>
import { thinkingMsg } from "../assets/chat.js";
import { ContractInfo } from "../assets/chainInfo";
export default {
name: "ChatBalloon",
Expand All @@ -8,6 +9,11 @@ export default {
thinkingMsg: thinkingMsg,
};
},
computed: {
explorerUrl() {
return `https://www.mintscan.io/archway-testnet/address/${ContractInfo.contractAddr}`;
},
},
props: ["type", "msg"],
};
</script>
Expand Down Expand Up @@ -36,6 +42,15 @@ export default {
<span>{{ msg }}</span>
</div>
</div>
<a
class="explorer"
v-if="type === 'ai'"
:href="explorerUrl"
target="_blank"
rel="noopener noreferrer"
>
↗ Explorer
</a>
</div>
</template>

Expand All @@ -58,6 +73,17 @@ export default {
left: 65px;
}
.explorer {
padding: 0px 20px 10px 20px;
max-width: min(50%, 400px);
position: relative;
display: inline-block;
font-size: 8pt;
color: #ffffff;
background-color: #ffffff00;
left: 45px;
}
.user {
color: #ffffffee;
background-color: #6944b1;
Expand Down
164 changes: 148 additions & 16 deletions src/components/ChatBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,38 @@
import axios from "axios";
import ChatBalloon from "./ChatBalloon.vue";
import { getChatArray, disclaimerText } from "../assets/chat.js";
import { ConstantineInfo } from "../assets/chainInfo";
import { ConstantineInfo, ContractInfo } from "../assets/chainInfo";
import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
import { GasPrice } from "@cosmjs/stargate";
import { calculateFee, GasPrice } from "@cosmjs/stargate";
export default {
name: "ChatBox",
data() {
return {
chatArray: getChatArray(),
prompt: "",
inputText: "",
typing: false,
keplrAddress: null, // Store Keplr wallet address
keplrClient: null, // Store Keplr client
gasPrice: GasPrice.fromString(
"0" + ConstantineInfo.currencies[0].coinMinimalDenom
),
};
},
mounted() {
this.loadOnMounted();
},
computed: {
placeholderText() {
return this.keplrAddress
? "Input your message to mint CW7007 ..."
: "Connect wallet first ...";
},
connectButtonClass() {
return this.keplrAddress ? "connected" : "";
},
},
components: {
ChatBalloon: ChatBalloon,
},
Expand All @@ -39,14 +53,11 @@ export default {
ConstantineInfo.chainId
);
const accounts = await offlineSigner.getAccounts();
const gasPrice = GasPrice.fromString(
"0" + ConstantineInfo.currencies[0].coinMinimalDenom
);
const client = await SigningCosmWasmClient.connectWithSigner(
ConstantineInfo.rpc,
offlineSigner,
{
gasPrice,
gasPrice: this.gasPrice,
}
);
Expand All @@ -57,17 +68,71 @@ export default {
return `${address.slice(0, 11)}...${address.slice(-4)}`;
},
async getPrompt(client) {
const queryResult = await this.keplrClient.queryContractSmart(
ContractInfo.contractAddr,
{
prompt: {},
}
);
return queryResult ? queryResult : null;
},
async getNftInfo(token_id) {
const queryResult = await this.keplrClient.queryContractSmart(
ContractInfo.contractAddr,
{
nft_info: { token_id },
}
);
return queryResult ? queryResult : null;
},
async mintNft(input) {
const executeFee = calculateFee(300_000, this.gasPrice);
const msg = {
mint: {
token_id: "0", // has no meaning
owner: this.keplrAddress,
extension: {
description: input,
},
},
};
const executeResult = await this.keplrClient.execute(
this.keplrAddress,
ContractInfo.contractAddr,
msg,
executeFee
);
return executeResult ? executeResult : null;
},
// async callChatGPT(apiKey, content) {
// const url = "https://api.openai.com/v1/chat/completions";
// const headers = {
// "Content-Type": "application/json",
// Authorization: `Bearer ${this.apiKey}`,
// };
// const body = JSON.stringify({
// model: "gpt-4o",
// messages: [{ role: "user", content: content }],
// max_tokens: 150,
// });
// const response = await axios.post(url, body, { headers });
// return response.data.choices[0].message.content;
// },
async loadOnMounted() {
this.scrollToBottom(true);
},
async send(msg) {
if (this.keplrAddress === null) return;
if (msg === "") return;
this.inputText = "";
await this.pushMsg(msg);
const tokenId = await this.pushMsg(msg);
this.scrollToBottom(true);
await this.typingMsg();
this.scrollToBottom(true);
await this.postMsg(msg);
await this.postMsg(tokenId, msg);
this.scrollToBottom(true);
},
async pushMsg(msg) {
Expand All @@ -79,12 +144,61 @@ export default {
example: false,
},
});
// TODO: error catch
const execRes = await this.mintNft(msg);
const tokenId = execRes.logs[0].events
.find((e) => e.type === "wasm")
.attributes.find((attr) => attr.key === "token_id").value;
console.log(`mint tx for ${tokenId}: ${execRes.transactionHash}`);
return tokenId;
},
async typingMsg() {
this.typing = true;
},
async postMsg(msg) {
let words = `${msg} MEOW.`;
async postMsg(tokenId, msg) {
let nftInfo;
let words;
const fetchNftInfo = (tokenId) => {
const maxRetryTime = 30000; // Maximum retry time (30 seconds)
const retryDelay = 2000; // Delay between retries (e.g., 2 seconds)
const startTime = Date.now();
return new Promise(async (resolve, reject) => {
const attemptFetch = async () => {
try {
nftInfo = await this.getNftInfo(tokenId);
if (nftInfo.extension.image !== null) {
resolve(nftInfo);
} else {
const elapsedTime = Date.now() - startTime;
if (elapsedTime < maxRetryTime) {
setTimeout(attemptFetch, retryDelay);
} else {
reject(
new Error(
"Timeout: NFT info image is still null after 30 seconds."
)
);
}
}
} catch (error) {
console.error(error);
reject(error);
}
};
attemptFetch();
});
};
try {
nftInfo = await fetchNftInfo(tokenId);
words = nftInfo.extension.image;
} catch (error) {
console.error(error);
words = "Meow! Timeout. Try again, please.";
}
this.typing = false;
const index = this.chatArray.push({
type: "ai",
Expand All @@ -103,7 +217,7 @@ export default {
setTimeout(() => {
this.chatArray[index - 1].data.content += words[i];
resolve();
}, 50);
}, 30);
});
this.scrollToBottom(false);
}
Expand Down Expand Up @@ -134,7 +248,11 @@ export default {
<div>
<div id="navbar">
<span class="text">CW7007 CAT</span>
<button class="connect-button" @click="connectKeplr">
<button
class="connect-button"
:class="connectButtonClass"
@click="connectKeplr"
>
{{ keplrAddress ? formatAddress(keplrAddress) : "Connect Wallet" }}
</button>
</div>
Expand All @@ -153,7 +271,7 @@ export default {
<input
v-model="inputText"
type="text"
placeholder="Input your message to mint CW7007 ..."
:placeholder="placeholderText"
aria-label="Input"
@keyup.enter="send(inputText)"
/>
Expand Down Expand Up @@ -281,7 +399,7 @@ input:focus {
cursor: pointer;
}
button.connect-button {
.connect-button {
border: 1px solid #ffffff99;
color: white; /* White text */
padding: 10px 20px;
Expand All @@ -296,10 +414,24 @@ button.connect-button {
position: absolute;
right: 30px; /* Position the button to the right */
}
button.connect-button:hover {
.connect-button:hover {
transform: scale(1.05); /* Slightly larger on hover */
cursor: pointer; /* Ensures pointer cursor on hover */
}
.connect-button {
.connect-button.connected {
border: 1px solid #ffffff99;
color: rgb(32, 6, 71); /* Blank text */
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 12px;
cursor: pointer;
border-radius: 15px; /* Rounded corners */
background-color: #ffffff;
transition: transform 0.2s; /* Animation */
position: absolute;
right: 30px; /* Position the but */
}
</style>

0 comments on commit 3f30db8

Please sign in to comment.