mirror of
https://github.com/believethehype/nostrdvm.git
synced 2025-04-08 20:08:08 +02:00
noogle: add reactions, ui fixes
This commit is contained in:
parent
1e35c652e8
commit
57d4452ea8
14
.idea/dataSources.xml
generated
14
.idea/dataSources.xml
generated
@ -87,5 +87,19 @@
|
||||
<jdbc-url>jdbc:sqlite:identifier.sqlite</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
<data-source source="LOCAL" name="identifier.sqlite [2]" uuid="6d783b7b-9c23-46fa-9057-ac42e5e18a1e">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:identifier.sqlite</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
<data-source source="LOCAL" name="identifier.sqlite [3]" uuid="4d771519-8188-464e-ba40-636a043fda3e">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:identifier.sqlite</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
@ -15,7 +15,7 @@
|
||||
<div tabIndex={0} role="button" class="button" >
|
||||
<img class="avatar" :src="this.avatar" alt="" />
|
||||
</div>
|
||||
<div tabIndex={0} className="dropdown-content -start-44 z-[1] horizontal card card-compact w-64 p-2 shadow bg-primary text-primary-content">
|
||||
<div tabIndex={0} className="dropdown-content -start-56 z-[1] horizontal card card-compact w-64 p-2 shadow bg-nostr text-primary-content">
|
||||
<div className="card-body">
|
||||
<h3 className="card-title">Sign out of your account</h3>
|
||||
<!--<p>Sign out</p> -->
|
||||
@ -23,13 +23,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>{{ this.current_user }}</p>
|
||||
<!--<p>{{ this.current_user }}</p> -->
|
||||
</div>
|
||||
|
||||
<template v-if="!current_user">
|
||||
<div className="dropdown">
|
||||
<div tabIndex={0} role="button" class="v-Button" >Sign in</div>
|
||||
<div tabIndex={0} className="dropdown-content -start-44 z-[1] horizontal card card-compact w-64 p-2 shadow bg-primary text-primary-content">
|
||||
<div tabIndex={0} className="dropdown-content -start-56 z-[1] horizontal card card-compact w-64 p-2 shadow bg-nostr text-primary-content">
|
||||
<div className="card-body">
|
||||
<h3 className="card-title">Login</h3>
|
||||
<p>Use a Browser Nip07 Extension like getalby, nos2x or nsec.app, a nsec or ncryptsec or use Amber on Android to sign-in</p>
|
||||
|
@ -4,7 +4,7 @@
|
||||
:items="data"
|
||||
:sort-by="sortBy"
|
||||
:sort-type="sortType">
|
||||
<template #item-content="{content, author, authorurl, avatar, indicator, links, lud16, id, authorid, zapped, zapAmount}">
|
||||
<template #item-content="{content, author, authorurl, avatar, indicator, links, lud16, id, authorid, zapped, zapAmount, reacted, reactions}">
|
||||
|
||||
<div class="playeauthor-wrapper">
|
||||
|
||||
@ -29,10 +29,31 @@
|
||||
<!--<a class="menusmall" :href="links.highlighter" target="_blank">Highlighter</a> -->
|
||||
<a class="menusmall":href="links.nostrudel" target="_blank">Nostrudel</a>
|
||||
|
||||
<div class="flex" >
|
||||
|
||||
<div class="flex" style="margin-left: auto; margin-right: 5px;" v-if="!reacted" @click="react(id, authorid)">
|
||||
<div style="margin-left: auto; margin-right: 5px; float: left;">
|
||||
<svg style="margin-top:4px" width="14" height="12" xmlns="http://www.w3.org/2000/svg" class="bi bi-heart" fill-rule="evenodd" fill="currentColor" viewBox="0 0 20 25" clip-rule="evenodd"><path d="M12 21.593c-5.63-5.539-11-10.297-11-14.402 0-3.791 3.068-5.191 5.281-5.191 1.312 0 4.151.501 5.719 4.457 1.59-3.968 4.464-4.447 5.726-4.447 2.54 0 5.274 1.621 5.274 5.181 0 4.069-5.136 8.625-11 14.402m5.726-20.583c-2.203 0-4.446 1.042-5.726 3.238-1.285-2.206-3.522-3.248-5.719-3.248-3.183 0-6.281 2.187-6.281 6.191 0 4.661 5.571 9.429 12 15.809 6.43-6.38 12-11.148 12-15.809 0-4.011-3.095-6.181-6.274-6.181"/></svg> </div>
|
||||
<div>
|
||||
<p style="float: left;">{{reactions}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex" v-if="reacted" style="margin-left: auto; margin-right: 5px;" @click="react(id, authorid)">
|
||||
<div style="margin-left: auto; margin-right: 5px; float: left;">
|
||||
<svg style="margin-top:4px" xmlns="http://www.w3.org/2000/svg" width="14" height="12" class="bi bi-heart fill-nostr" viewBox="0 0 20 25"><path d="M12 4.419c-2.826-5.695-11.999-4.064-11.999 3.27 0 7.27 9.903 10.938 11.999 15.311 2.096-4.373 12-8.041 12-15.311 0-7.327-9.17-8.972-12-3.27z"/></svg> </div>
|
||||
|
||||
<div>
|
||||
<p className="text-nostr" style="float: left;">{{reactions}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex" v-if="!zapped" @click="zap(lud16, id, authorid)">
|
||||
<div style="margin-left: auto; margin-right: 5px; float: left;">
|
||||
<svg style="margin-top:4px" xmlns="http://www.w3.org/2000/svg" width="14" height="16" fill="currentColor" class="bi bi-lightning" viewBox="0 0 16 20">
|
||||
<path d="M5.52.359A.5.5 0 0 1 6 0h4a.5.5 0 0 1 .474.658L8.694 6H12.5a.5.5 0 0 1 .395.807l-7 9a.5.5 0 0 1-.873-.454L6.823 9.5H3.5a.5.5 0 0 1-.48-.641zM6.374 1 4.168 8.5H7.5a.5.5 0 0 1 .478.647L6.78 13.04 11.478 7H8a.5.5 0 0 1-.474-.658L9.306 1z"/> </svg> </div>
|
||||
<path d="M5.52.359A.5.5 0 0 1 6 0h4a.5.5 0 0 1 .474.658L8.694 6H12.5a.5.5 0 0 1 .395.807l-7 9a.5.5 0 0 1-.873-.454L6.823 9.5H3.5a.5.5 0 0 1-.48-.641zM6.374 1 4.168 8.5H7.5a.5.5 0 0 1 .478.647L6.78 13.04 11.478 7H8a.5.5 0 0 1-.474-.658L9.306 1z"/>
|
||||
|
||||
</svg> </div>
|
||||
<div>
|
||||
<p style="float: left;">{{zapAmount/1000}}</p>
|
||||
</div>
|
||||
@ -40,13 +61,15 @@
|
||||
<div class="flex" v-if="zapped" @click="zap(lud16, id, authorid)" >
|
||||
<div style="margin-left: auto; margin-right: 5px;">
|
||||
<svg style="margin-top:4px" xmlns="http://www.w3.org/2000/svg" width="14" height="16" class="bi bi-lightning fill-amber-400" viewBox="0 0 16 20">
|
||||
<path d="M5.52.359A.5.5 0 0 1 6 0h4a.5.5 0 0 1 .474.658L8.694 6H12.5a.5.5 0 0 1 .395.807l-7 9a.5.5 0 0 1-.873-.454L6.823 9.5H3.5a.5.5 0 0 1-.48-.641zM6.374 1 4.168 8.5H7.5a.5.5 0 0 1 .478.647L6.78 13.04 11.478 7H8a.5.5 0 0 1-.474-.658L9.306 1z"/></svg></div>
|
||||
<path d="M5.52.359A.5.5 0 0 1 6 0h4a.5.5 0 0 1 .474.658L8.694 6H12.5a.5.5 0 0 1 .395.807l-7 9a.5.5 0 0 1-.873-.454L6.823 9.5H3.5a.5.5 0 0 1-.48-.641z"/>
|
||||
</svg></div>
|
||||
<div>
|
||||
<p style="float: left;" className="text-amber-400">{{zapAmount/1000}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@ -66,6 +89,8 @@ import Null = types.Null;
|
||||
import StringUtil from "@/components/helper/string";
|
||||
import {copyinvoice, createBolt11Lud16, parseandreplacenpubs, zaprequest} from "@/components/helper/Helper.vue";
|
||||
import {requestProvider} from "webln";
|
||||
import {Event, EventBuilder, EventId, PublicKey} from "@rust-nostr/nostr-sdk";
|
||||
import amberSignerService from "@/components/android-signer/AndroidSigner";
|
||||
|
||||
|
||||
const props = defineProps<{
|
||||
@ -83,6 +108,43 @@ const headers: Header[] = [
|
||||
];
|
||||
|
||||
|
||||
async function react(eventid, authorid){
|
||||
|
||||
|
||||
let event_id = EventId.parse(eventid)
|
||||
let public_key = PublicKey.parse(authorid);
|
||||
let signer = store.state.signer
|
||||
let client = store.state.client
|
||||
let objects = (props.data.find(x=> x.id === eventid))
|
||||
if (objects !== undefined){
|
||||
if(!objects.reacted ){
|
||||
|
||||
if (localStorage.getItem('nostr-key-method') === 'android-signer') {
|
||||
let draft = {
|
||||
content: "🧡",
|
||||
kind: 7,
|
||||
pubkey: store.state.pubkey.toHex(),
|
||||
tags: [],
|
||||
createdAt: Date.now()
|
||||
};
|
||||
|
||||
let res = await amberSignerService.signEvent(draft)
|
||||
await client.sendEvent(Event.fromJson(JSON.stringify(res)))
|
||||
let requestid = res.id;
|
||||
}
|
||||
else {
|
||||
let event = EventBuilder.reaction(event_id, public_key, "🧡")
|
||||
let res = await client.sendEventBuilder(event);
|
||||
}
|
||||
|
||||
objects.reacted = true
|
||||
objects.reactions += 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function zap(lud16, eventid, authorid){
|
||||
|
||||
if(lud16 != Null && lud16 != ""){
|
||||
|
@ -22,7 +22,7 @@ import {data} from "autoprefixer";
|
||||
import {requestProvider} from "webln";
|
||||
import Newnote from "@/components/Newnote.vue";
|
||||
import SummarizationGeneration from "@/components/SummarizationGeneration.vue"
|
||||
import {post_note, schedule, copyurl, copyinvoice, sleep, getEvents, get_user_infos, get_zaps, nextInput, createBolt11Lud16, getEventsOriginalOrder, parseandreplacenpubsName} from "../components/helper/Helper.vue"
|
||||
import {post_note, schedule, copyurl, copyinvoice, sleep, getEvents, get_user_infos, get_zaps, get_reactions, nextInput, createBolt11Lud16, getEventsOriginalOrder, parseandreplacenpubsName} from "../components/helper/Helper.vue"
|
||||
import amberSignerService from "./android-signer/AndroidSigner";
|
||||
import StringUtil from "@/components/helper/string.ts";
|
||||
|
||||
@ -144,8 +144,25 @@ async function listen() {
|
||||
//miniToastr.showMessage("DVM: " + dvmname, event.content, VueNotifications.types.info)
|
||||
for (const tag in event.tags) {
|
||||
if (event.tags[tag].asVec()[0] === "status") {
|
||||
dvms.find(i => i.id === event.author.toHex()).status = event.tags[tag].asVec()[1]
|
||||
}
|
||||
|
||||
if (event.content !== "" && event.tags[tag].asVec()[1] === "processing") {
|
||||
if(event.tags[tag].asVec().length > 2) {
|
||||
dvms.find(i => i.id === event.author.toHex()).status = event.tags[tag].asVec()[2]
|
||||
|
||||
}
|
||||
else{
|
||||
dvms.find(i => i.id === event.author.toHex()).status = event.content
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
dvms.find(i => i.id === event.author.toHex()).status = event.tags[tag].asVec()[1]
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (event.tags[tag].asVec()[0] === "amount") {
|
||||
dvms.find(i => i.id === event.author.toHex()).amount = event.tags[tag].asVec()[1]
|
||||
@ -246,6 +263,8 @@ async function listen() {
|
||||
|
||||
|
||||
if (items.find(e => e.id === evt.id.toHex()) === undefined) {
|
||||
|
||||
let react = zaps.find(x => x.id === evt.id.toHex())
|
||||
items.push({
|
||||
id: evt.id.toHex(),
|
||||
content: await parseandreplacenpubsName(evt.content),
|
||||
@ -262,8 +281,10 @@ async function listen() {
|
||||
index: index,
|
||||
indicator: {"time": evt.createdAt.toHumanDatetime(), "index": index},
|
||||
lud16: lud16,
|
||||
zapped: zaps.find(x => x.id === evt.id.toHex()).zappedbyUser,
|
||||
zapAmount: zaps.find(x => x.id === evt.id.toHex()).amount
|
||||
zapped: react.zappedbyUser,
|
||||
zapAmount: react.amount,
|
||||
reacted: react.reactedbyUser,
|
||||
reactions: react.reactions,
|
||||
|
||||
})
|
||||
index = index + 1
|
||||
@ -342,7 +363,7 @@ async function addAllContentDVMs() {
|
||||
}
|
||||
|
||||
// console.log(last_active)
|
||||
// If DVM hasnt been active for 3 weeks, don't consider it.
|
||||
// If DVM hasn't been active for 3 weeks, don't consider it.
|
||||
console.log(active_dvms)
|
||||
let final_dvms = []
|
||||
for (let element of active_dvms) {
|
||||
@ -455,7 +476,7 @@ async function addDVM(event){
|
||||
if (event.content !== "" && status !== "payment-required" && status !== "error" && status !== "finished" && status !== "paid"){
|
||||
status = event.content
|
||||
}
|
||||
jsonentry.status = status
|
||||
|
||||
console.log(dvms)
|
||||
if (dvms.filter(i => i.id === jsonentry.id).length === 0) {
|
||||
dvms.push(jsonentry)
|
||||
|
@ -139,17 +139,91 @@ export async function get_user_infos(pubkeys){
|
||||
|
||||
}
|
||||
export async function get_zaps(ids){
|
||||
let zaps = []
|
||||
let jsonentry = {}
|
||||
let zapsandreactions = []
|
||||
|
||||
for (let id of ids){
|
||||
zaps.push({
|
||||
zapsandreactions.push({
|
||||
id: id.toHex(),
|
||||
amount: 0,
|
||||
zappedbyUser: false,})
|
||||
reactions: 0,
|
||||
zappedbyUser: false,
|
||||
reactedbyUser: false,})
|
||||
}
|
||||
|
||||
let client = store.state.client
|
||||
const zap_filter = new Filter().kind(9735).events(ids)
|
||||
const zap_filter = new Filter().kinds([9735, 7]).events(ids)
|
||||
let evts = await client.getEventsOf([zap_filter], Duration.fromSecs(10))
|
||||
|
||||
for (const entry of evts){
|
||||
try{
|
||||
//let contentjson = JSON.parse(entry.content)
|
||||
if (entry.kind === 9735){
|
||||
for (let tag of entry.tags) {
|
||||
if (tag.asVec()[0] === "description") {
|
||||
let request = JSON.parse(tag.asVec()[1])
|
||||
let etag = ""
|
||||
let amount = 0
|
||||
for (let tg of request.tags) {
|
||||
if (tg[0] === "amount") {
|
||||
amount = parseInt(tg[1])
|
||||
}
|
||||
if (tg[0] === "e") {
|
||||
etag = tg[1]
|
||||
console.log(request.pubkey)
|
||||
if (request.pubkey === localStorage.getItem("nostr-key")) {
|
||||
zapsandreactions.find(x => x.id === etag).zappedbyUser = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zapsandreactions.find(x => x.id === etag).amount += amount
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (entry.kind === 7) {
|
||||
for (let tag of entry.tags) {
|
||||
|
||||
if (tag.asVec()[0] === "e") {
|
||||
console.log(entry.pubkey)
|
||||
if (entry.author.toHex() === localStorage.getItem("nostr-key")) {
|
||||
zapsandreactions.find(x => x.id === tag.asVec()[1]).reactedbyUser = true
|
||||
}
|
||||
zapsandreactions.find(x => x.id === tag.asVec()[1]).reactions += 1
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
//console.log(contentjson)
|
||||
//zaps.push({profile: contentjson, author: entry.author.toHex(), createdAt: entry.createdAt});
|
||||
}
|
||||
catch(error){
|
||||
console.log("error")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
console.log(zapsandreactions)
|
||||
|
||||
return zapsandreactions
|
||||
|
||||
}
|
||||
|
||||
export async function get_reactions(ids){
|
||||
let reactions = []
|
||||
let jsonentry = {}
|
||||
for (let id of ids){
|
||||
reactions.push({
|
||||
id: id.toHex(),
|
||||
amount: 0,
|
||||
ReactedbyUser: false,})
|
||||
}
|
||||
|
||||
let client = store.state.client
|
||||
const zap_filter = new Filter().kind(7).events(ids)
|
||||
let evts = await client.getEventsOf([zap_filter], Duration.fromSecs(10))
|
||||
|
||||
for (const entry of evts){
|
||||
@ -157,26 +231,13 @@ export async function get_zaps(ids){
|
||||
//let contentjson = JSON.parse(entry.content)
|
||||
|
||||
for (let tag of entry.tags){
|
||||
if (tag.asVec()[0] === "description"){
|
||||
let request = JSON.parse(tag.asVec()[1])
|
||||
let etag = ""
|
||||
let amount = 0
|
||||
for (let tg of request.tags){
|
||||
if (tg[0] === "amount") {
|
||||
amount = parseInt(tg[1])
|
||||
}
|
||||
if (tg[0] === "e") {
|
||||
etag = tg[1]
|
||||
console.log(request.pubkey )
|
||||
if(request.pubkey === localStorage.getItem("nostr-key")){
|
||||
zaps.find(x=> x.id === etag).zappedbyUser = true
|
||||
}
|
||||
}
|
||||
|
||||
if (tag.asVec()[0] === "e") {
|
||||
console.log(entry.pubkey )
|
||||
if(entry.pubkey === localStorage.getItem("nostr-key")){
|
||||
reactions.find(x=> x.id === tag.asVec()[1]).ReactedbyUser = true
|
||||
}
|
||||
|
||||
zaps.find(x=> x.id === etag).amount += amount
|
||||
|
||||
|
||||
reactions.find(x=> x.id === tag.asVec()[1]).amount += 1
|
||||
|
||||
}
|
||||
}
|
||||
@ -189,9 +250,9 @@ export async function get_zaps(ids){
|
||||
|
||||
}
|
||||
|
||||
console.log(zaps)
|
||||
console.log(reactions)
|
||||
|
||||
return zaps
|
||||
return reactions
|
||||
|
||||
}
|
||||
|
||||
@ -203,8 +264,9 @@ export const sleep = (ms) => {
|
||||
|
||||
|
||||
export async function copyinvoice(invoice){
|
||||
await navigator.clipboard.writeText(invoice)
|
||||
|
||||
window.open("lightning:" + invoice,"_blank")
|
||||
await navigator.clipboard.writeText(invoice)
|
||||
miniToastr.showMessage("", "Copied Invoice to clipboard", VueNotifications.types.info)
|
||||
}
|
||||
|
||||
@ -333,26 +395,24 @@ export async function zaprequest(lud16, amount, content, zapped_event_id, zapped
|
||||
|
||||
const urlBytes = new TextEncoder().encode(url);
|
||||
const encoded_lnurl = bech32.encode('lnurl', bech32.toWords(urlBytes), 1023);
|
||||
console.log(encoded_lnurl)
|
||||
console.log( relay_list.toString())
|
||||
console.log(zapped_event_id)
|
||||
console.log(zapped_user_id)
|
||||
const amount_tag = Tag.parse(['amount', (amount * 1000).toString()]);
|
||||
|
||||
|
||||
const amount_tag = ['amount', (amount * 1000).toString()];
|
||||
let relays = ['relays']
|
||||
relays.push.apply(relays, relay_list)
|
||||
let relays_tag = Tag.parse(relays);
|
||||
//let relays_tag = Tag.parse(relays);
|
||||
|
||||
const lnurl_tag = Tag.parse(['lnurl', encoded_lnurl]);
|
||||
const lnurl_tag = ['lnurl', encoded_lnurl];
|
||||
|
||||
let tags
|
||||
let p_tag = Tag.parse(['p', zapped_user_id])
|
||||
let tags = []
|
||||
let p_tag = ['p', zapped_user_id]
|
||||
if (zapped_event_id !== null){
|
||||
let e_tag = Tag.parse(['e', zapped_event_id])
|
||||
tags = [amount_tag, relays_tag, p_tag, e_tag, lnurl_tag]
|
||||
let e_tag = ['e', zapped_event_id]
|
||||
tags = [amount_tag, relays, p_tag, e_tag, lnurl_tag]
|
||||
}
|
||||
|
||||
else{
|
||||
tags = [amount_tag, relays_tag, p_tag, lnurl_tag]
|
||||
tags = [amount_tag, relays, p_tag, lnurl_tag]
|
||||
}
|
||||
/*if (zaptype === "private") {
|
||||
const key_str = keys.secret_key().to_hex() + zapped_event.id().to_hex() + zapped_event.created_at().as_secs().toString();
|
||||
@ -368,16 +428,31 @@ export async function zaprequest(lud16, amount, content, zapped_event_id, zapped
|
||||
|
||||
|
||||
let signer = store.state.signer
|
||||
let zap_request = ""
|
||||
|
||||
let noteevent = new EventBuilder(9734, content, tags).toUnsignedEvent(store.state.pubkey)
|
||||
let signedEvent = await signer.signEvent(noteevent)
|
||||
let zap_request = signedEvent.asJson()
|
||||
if (localStorage.getItem('nostr-key-method') === 'android-signer') {
|
||||
let draft = {
|
||||
content: content,
|
||||
kind: 9734,
|
||||
pubkey: store.state.pubkey.toHex(),
|
||||
tags: tags,
|
||||
createdAt: Date.now()
|
||||
};
|
||||
|
||||
//console.log(encoded_zap_request)
|
||||
let res = await amberSignerService.signEvent(draft)
|
||||
zap_request = JSON.stringify(res)
|
||||
//await sleep(3000)
|
||||
|
||||
|
||||
//`amount=${(amount * 1000).toString()}&nostr=${encodeURIComponent(zap_request)}&lnurl=${encoded_lnurl}`;
|
||||
// const queryString = `amount=${(amount * 1000).toString()}&nostr=${encodeURIComponent(zap_request)}&lnurl=${encoded_lnurl}`;
|
||||
}
|
||||
else {
|
||||
let tags_t = []
|
||||
for (let tag of tags){
|
||||
tags_t.push(Tag.parse(tag))
|
||||
}
|
||||
let noteevent = new EventBuilder(9734, content, tags_t).toUnsignedEvent(store.state.pubkey)
|
||||
let signedEvent = await signer.signEvent(noteevent)
|
||||
zap_request = signedEvent.asJson()
|
||||
}
|
||||
|
||||
try{
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user