diff --git a/src/lib/components/messaging/ChatPreview.svelte b/src/lib/components/messaging/ChatPreview.svelte index 638969165..b4d0ade60 100644 --- a/src/lib/components/messaging/ChatPreview.svelte +++ b/src/lib/components/messaging/ChatPreview.svelte @@ -34,7 +34,6 @@ $: user = chat.typing_indicator.users().map(u => { return $lookupUsers[u] }) - $: self = get(Store.state.user) let timeago = getTimeAgo(chat.last_message_at) const dispatch = createEventDispatcher() let ownId = get(Store.state.user) @@ -57,9 +56,11 @@ ? $_("message_previews.coin_requested", { values: { username: sendingUserDetails.name, amount: amountPreview } }) : $_("message_previews.request_sent", { values: { amount: amountPreview } }) } catch (error) { + console.log(chat.last_message_preview) return "Invalid message format" } - } else if (chat.last_message_preview.startsWith(PaymentRequestsEnum.Reject)) { + } + if (chat.last_message_preview.startsWith(PaymentRequestsEnum.Reject)) { try { const sendingUserId = ConversationStore.getMessage(chat.id, chat.last_message_id)?.details.origin const sendingUserDetails = get(Store.getUser(sendingUserId!)) @@ -69,6 +70,35 @@ return $_("message_previews.coin_canceled") } } catch (error) { + console.log(chat.last_message_preview) + return "Invalid message format" + } + } + if (chat.last_message_preview.startsWith(PaymentRequestsEnum.Send)) { + try { + const sendingUserId = ConversationStore.getMessage(chat.id, chat.last_message_id)?.details.origin + const sendingUserDetails = get(Store.getUser(sendingUserId!)) + const jsonStartIndex = chat.last_message_preview.indexOf("{") + if (jsonStartIndex === -1) { + console.error("No JSON found in last_message_preview:", chat.last_message_preview) + return "Invalid message format" + } + const jsonPart = chat.last_message_preview.slice(jsonStartIndex) + let parsedMessage + try { + parsedMessage = JSON.parse(jsonPart) + } catch (error) { + console.error("Error parsing JSON:", error, chat.last_message_preview) + return "Invalid message format" + } + const amountWei = parsedMessage.details.amount + if (sendingUserDetails.key !== ownId.key) { + return `${sendingUserDetails.name} sent you ${amountWei}` + } else { + return `You sent ${amountWei} to ${sendingUserDetails.name}` + } + } catch (error) { + console.error("Error in PaymentRequestsEnum.Send condition:", error) return "Invalid message format" } } diff --git a/src/lib/components/wallet/CreateTransaction.svelte b/src/lib/components/wallet/CreateTransaction.svelte index d962117ac..0a4a86050 100644 --- a/src/lib/components/wallet/CreateTransaction.svelte +++ b/src/lib/components/wallet/CreateTransaction.svelte @@ -2,52 +2,107 @@ import Button from "$lib/elements/Button.svelte" import { ConversationStore } from "$lib/state/conversation" import { Store } from "$lib/state/Store" - import { AssetType, shortenAddr, Transfer, wallet, type Asset } from "$lib/utils/Wallet" + import { AssetType, getValidPaymentRequest, shortenAddr, Transfer, wallet, type Asset } from "$lib/utils/Wallet" import { RaygunStoreInstance } from "$lib/wasm/RaygunStore" import { get } from "svelte/store" import { _ } from "svelte-i18n" - import { Select } from "$lib/elements" + import { Icon, Input, Label, Select, Text } from "$lib/elements" + import { Appearance, PaymentRequestsEnum, Shape, Size } from "$lib/enums" export let onClose - + enum ViewMode { + None = "", + Send = "send", + Receive = "receive", + QRScanner = "send", + SendFunds = "SendFunds", + } let transfer = new Transfer() - + let sendCoin = ViewMode.None + let initialSelect = "Select an asset" async function sendMessage(text: string) { - let chat = get(Store.state.activeChat) - let txt = text.split("\n") - let result = await RaygunStoreInstance.send(chat.id, txt, []) - result.onSuccess(res => { - ConversationStore.addPendingMessages(chat.id, res.message, txt) - }) + if (sendCoin === ViewMode.Send) { + let chat = get(Store.state.activeChat) + let txt = text.split("\n") + let whatReturned = await getValidPaymentRequest(txt[0])?.execute() + if (whatReturned !== undefined) { + let result = await RaygunStoreInstance.send(chat.id, txt, []) + result.onSuccess(res => { + ConversationStore.addPendingMessages(chat.id, res.message, txt) + }) + } + } + if (sendCoin === ViewMode.Receive) { + let chat = get(Store.state.activeChat) + let txt = text.split("\n") + let result = await RaygunStoreInstance.send(chat.id, txt, []) + result.onSuccess(res => { + console.log(txt) + ConversationStore.addPendingMessages(chat.id, res.message, txt) + }) + } } let inputAmount = "" - function onInputAmount() { - //remove any input that is not a number or a dot + isAmountTouched = true + console.log(transfer.asset.kind) inputAmount = inputAmount.replace(/[^0-9.]/g, "") - //if there is more than 1 dot, only keep the first one if (inputAmount.split(".").length > 2) { - let i = inputAmount.indexOf(".") - inputAmount = inputAmount.substring(0, i + 1) + inputAmount.substring(i + 1, inputAmount.length).replace(".", "") + const i = inputAmount.indexOf(".") + inputAmount = inputAmount.substring(0, i + 1) + inputAmount.substring(i + 1).replace(".", "") } - //convert string amount to bigint - wallet.toBigIntAmount(transfer.asset, inputAmount).then(amount => { - transfer.amount = amount - wallet.toAmountPreviewString(transfer.asset, transfer.amount).then(amountPreview => { - transfer.amountPreview = amountPreview + const decimalPlaces = 18 // Max decimals for the asset type + const parts = inputAmount.split(".") + if (parts[1] && parts[1].length > decimalPlaces) { + inputAmount = parts[0] + "." + parts[1].substring(0, decimalPlaces) + } + + isValidAmount = /^[0-9]+(\.[0-9]+)?$/.test(inputAmount) + + if (isValidAmount) { + wallet.toBigIntAmount(transfer.asset, inputAmount).then(amount => { + transfer.amount = amount + wallet.toAmountPreviewString(transfer.asset, transfer.amount).then(amountPreview => { + transfer.amountPreview = amountPreview + }) }) - }) + } } + + let addressInput = "" + let isValidAddress = false + let isAddressTouched = false + let isValidAmount = false + let isAmountTouched = false + function onAddressInput() { + isAddressTouched = true + + const bitcoinRegex = /^(1|3|bc1|tb1)[a-zA-HJ-NP-Z0-9]{25,62}$/ + const ethereumRegex = /^0x[a-fA-F0-9]{40}$/ + + isValidAddress = bitcoinRegex.test(addressInput) || ethereumRegex.test(addressInput) + + if (isValidAddress) { + transfer.toAddress = addressInput + } + } + onInputAmount() function onChangeAssetKind() { transfer.asset.id = "" transfer.amount = BigInt(0) onInputAmount() - wallet.myAddress(transfer.asset).then(address => { - transfer.toAddress = address - }) + if (sendCoin !== ViewMode.Send) { + wallet.myAddress(transfer.asset).then(address => { + transfer.toAddress = address + }) + } else { + wallet.myAddress(transfer.asset).then(address => { + transfer.toAddress = addressInput + }) + } } onChangeAssetKind() @@ -56,21 +111,146 @@ } -
-
{$_("payments.assetType") + ":"}
- {/if} -
{transfer.amountPreview}
-
{$_("payments.amount") + ":"}
- {#if transfer.toAddress !== ""} -
{$_("payments.receiving_to")}: {shortenAddr(transfer.toAddress, 6)}
- {/if} +
+
+ Transfer Funds +
+
+ + +
+ {#if sendCoin === ViewMode.Send} +
+ + + {#if isAddressTouched && !isValidAddress} + Invalid wallet address. + {/if} +
+
+ {/if} - + {#if isValidAddress && addressInput !== ""} +
+
+ {/if} + + {#if needsAssetId()} +
+ {$_("payments.assetId") + ":"} + +
+ {/if} + + {#if isValidAddress && isValidAmount} +
+ +
+ {/if} + {:else if sendCoin === ViewMode.Receive} +
+ +
+
+
+
+ {/if} + + {#if needsAssetId()} +
{$_("payments.assetId") + ":"}
+ {/if} +
+ +
+ {/if} + + diff --git a/src/lib/enums/icons.ts b/src/lib/enums/icons.ts index f5cf38f9f..9980f7d5c 100644 --- a/src/lib/enums/icons.ts +++ b/src/lib/enums/icons.ts @@ -15,6 +15,18 @@ export const enum Shape { Boot = ``, ID = ``, Details = ``, + QRCodeScan = ``, + QRCode = ` + + + + + + + + + + `, Tag = ``, Shield = ``, Popout = ``, diff --git a/src/lib/enums/index.ts b/src/lib/enums/index.ts index 6255ea875..2f3d69874 100644 --- a/src/lib/enums/index.ts +++ b/src/lib/enums/index.ts @@ -23,6 +23,7 @@ export enum Color { export enum PaymentRequestsEnum { Reject = "/reject", Request = "/request", + Send = "/send", } export enum Format { diff --git a/src/lib/lang/en.json b/src/lib/lang/en.json index fd5e378bb..0372753aa 100644 --- a/src/lib/lang/en.json +++ b/src/lib/lang/en.json @@ -209,6 +209,7 @@ "payments": { "send": "Send", "receive": "Receive", + "type": "Type", "receiving_to": "Receiving to", "sendCoin": "Send Coin", "amount": "Amount", @@ -217,15 +218,25 @@ "note": "Note", "date": "Date", "decline": "Decline", + "enter_address": "Enter Address", + "address": "Address", + "create_transaction": "Create Transaction", "create": "Create Payment Request", + "decline_payment": "Decline Payment", + "sent_request": "Sent Request", + "canceled_request": "Canceled Request", "declinePayment": "Decline Payment", "sentRequest": "Request has been sent", "cancel_request": "Cancel Request", "canceledRequest": "Canceled Request", "network": "Network", - "paymentDeclined": "Payment Canceled", - "youCanceledRequest": "You Canceled Payment Request", - "declinedPayment": "{user} Canceled Payment", + "payment_success": "Payment Success", + "payment_declined": "Payment Declined", + "you_canceled_request": "You Canceled Payment Request", + "payment_canceled": "Payment Canceled", + "declined_payment": "{user} Canceled Payment", + "recievedPayment": "{user} Sent You {amount}", + "sentPayment": "You Sent {amount}", "assetType": "Asset type", "assetName": "Asset name", "assetId": "Asset Id", diff --git a/src/lib/utils/Wallet.ts b/src/lib/utils/Wallet.ts index e18f9ae1d..a577ea140 100644 --- a/src/lib/utils/Wallet.ts +++ b/src/lib/utils/Wallet.ts @@ -1,6 +1,8 @@ import Wallet, { AddressPurpose, AddressType, RpcErrorCode } from "sats-connect" import { ethers } from "ethers" import { log } from "./Logger" +import { PaymentRequestsEnum } from "$lib/enums" +import type { MessageType } from "warp-wasm" export type Account = { address: string @@ -361,7 +363,7 @@ async function ethToBigIntAmount(amount: string): Promise { } async function ethTransfer(ethWallet: EthWallet, amount: bigint, toAddress: string) { - let tx = await ethWallet.signer.sendTransaction({ + return await ethWallet.signer.sendTransaction({ to: toAddress, value: amount, }) @@ -488,33 +490,45 @@ export class Transfer { toCmdString(): string { let transfer = JSON.stringify(this, (k, v) => (k === "amount" && typeof v === "bigint" ? v.toString() : v)) + console.log(transfer) return `/request ${transfer}` } toRejectString(id: string) { - let transfer = JSON.stringify(this, (k, v) => (k === "amount" && typeof v === "bigint" ? v.toString() : v)) - return `/reject ${id}` + const transfer = JSON.stringify(this, (key, value) => (key === "amount" && typeof value === "bigint" ? value.toString() : value)) + return `/reject ${id} {"details":${transfer}}` } - toDisplayString(): string { - let id = this.asset.id === "n/a" ? "" : this.asset.id - return `Send ${this.amountPreview} to ${shortenAddr(this.toAddress, 6)}` + toDisplayString(kind: string, amount: string, toAddress: string, messageId?: string): string { + const transfer = JSON.stringify(this, (key, value) => (key === "amount" && typeof value === "bigint" ? value.toString() : value)) + const message = { + kind, + details: { + amount, + toAddress, + messageID: messageId, + transfer: JSON.parse(transfer), + }, + } + return `/send ${JSON.stringify(message)}` } async execute() { if (this.isValid()) { - await wallet.transfer(this.asset, this.amount, this.toAddress) + let verifyTansfer = await wallet.transfer(this.asset, this.amount, this.toAddress) + if (verifyTansfer === undefined) { + return false + } else { + return true + } } } } export function getValidPaymentRequest(msg: string, msgId?: string): Transfer | undefined { - let requestCmd = "/request" - let rejectCmd = "/reject" - - if (msg.startsWith(requestCmd)) { - let json = msg.substring(requestCmd.length, msg.length).trim() - let transfer = new Transfer() + let transfer = new Transfer() + if (msg.startsWith(PaymentRequestsEnum.Request)) { + let json = msg.substring(PaymentRequestsEnum.Request.length, msg.length).trim() try { let parsed = JSON.parse(json, (k, v) => (k === "amount" && typeof v === "string" ? BigInt(v) : v)) transfer.asset = parsed.asset @@ -527,9 +541,8 @@ export function getValidPaymentRequest(msg: string, msgId?: string): Transfer | if (transfer.asset.kind !== AssetType.None && transfer.isValid()) { return transfer } - } else if (msg.startsWith(rejectCmd)) { - let json = msg.substring(rejectCmd.length, msg.length).trim() - let transfer = new Transfer() + } else if (msg.startsWith(PaymentRequestsEnum.Reject)) { + let json = msg.substring(PaymentRequestsEnum.Reject.length, msg.length).trim() if (json.startsWith("{")) { try { @@ -539,16 +552,53 @@ export function getValidPaymentRequest(msg: string, msgId?: string): Transfer | transfer.toAddress = parsed.toAddress transfer.amountPreview = parsed.amountPreview } catch (err) { - console.log("Parse Failed", err) + console.error("Parse Failed", err) } } else { - console.log("Reject message is not JSON, possibly an ID or UUID:", json) + console.error("Reject message is not JSON, possibly an ID or UUID:", json) return undefined } if (transfer.asset.kind !== AssetType.None && transfer.isValid()) { return transfer } + } else if (msg.startsWith(PaymentRequestsEnum.Send)) { + let json = msg.substring(PaymentRequestsEnum.Send.length).trim() + let jsonStartIndex = json.indexOf("{") + if (jsonStartIndex !== -1) { + json = json.substring(jsonStartIndex).trim() + try { + let parsed = JSON.parse(json, (key, value) => { + if (key === "amount" && typeof value === "string") { + if (/^\d+(\.\d+)?$/.test(value)) { + return BigInt(Math.round(Number(value) * 1e18)) + } + } + return value + }) + const details = parsed.details || {} + transfer.asset = details.asset && typeof details.asset === "object" ? details.asset : { kind: details.asset || "Unknown" } + + transfer.amount = details.amount ? BigInt(Math.round(Number(details.amount) * 1e18)) : BigInt(0) + + transfer.amountPreview = details.amount ? `${(Number(details.amount) / 1e18).toFixed(18)} ETH` : "" + + transfer.toAddress = details.toAddress || "" + } catch (err) { + console.error("Parse Failed", err, "JSON Input:", json) + return undefined + } + } else { + console.error("Send message is not JSON:", json) + return undefined + } + + if (transfer.asset && transfer.asset.kind !== undefined && transfer.asset.kind !== AssetType.None && transfer.isValid()) { + return transfer + } else { + console.error("Invalid transfer object:", transfer) + return undefined + } } return undefined diff --git a/src/routes/chat/+page.svelte b/src/routes/chat/+page.svelte index ab6f31ecc..cf503a0fb 100644 --- a/src/routes/chat/+page.svelte +++ b/src/routes/chat/+page.svelte @@ -38,6 +38,7 @@ import BrowseFiles from "../files/BrowseFiles.svelte" import AttachmentRenderer from "$lib/components/messaging/AttachmentRenderer.svelte" import ShareFile from "$lib/components/files/ShareFile.svelte" + import { StateEffect } from "@codemirror/state" import { ToastMessage } from "$lib/state/ui/toast" import AddMembers from "$lib/components/group/AddMembers.svelte" @@ -129,25 +130,50 @@ } function sanitizePaymentRequest(message: string, sender: string): string { - // Match and extract "kind", "amountPreview", and "toAddress" from the input string const kindMatch = message.match(/"kind":"(.*?)"/) const amountPreviewMatch = message.match(/"amountPreview":"(.*?)"/) - // const toAddressMatch = message.match(/"toAddress":"(.*?)"/) - - // Extract the values from the match results, defaulting to an empty string if not found const kind = kindMatch ? kindMatch[1] : "" let amountPreview = amountPreviewMatch ? amountPreviewMatch[1] : "" - // const toAddress = toAddressMatch ? toAddressMatch[1] : "" - - // Remove any extra occurrence of the currency symbol in `amountPreview` if (amountPreview.includes(kind)) { amountPreview = amountPreview.replace(kind, "").trim() } amountPreview = amountPreview.replace(/(\.\d*?[1-9])0+$|\.0*$/, "$1") - // Return the formatted string - return `Send ${amountPreview} ${kind}` + const result = `Send ${amountPreview} ${kind}` + return result } + $: sanitizePaymentSent = (() => { + const sendingUserId = ConversationStore.getMessage($activeChat.id, $activeChat.last_message_id)?.details.origin + const sendingUserDetails = get(Store.getUser(sendingUserId!)) + const jsonStartIndex = $activeChat.last_message_preview.indexOf("{") + if (jsonStartIndex === -1) { + console.error("No JSON found in last_message_preview:", $activeChat.last_message_preview) + return "Invalid message format" + } + const jsonPart = $activeChat.last_message_preview.slice(jsonStartIndex) + let parsedMessage + try { + parsedMessage = JSON.parse(jsonPart) + } catch (error) { + console.error("Error parsing JSON:", error, $activeChat.last_message_preview) + return "Invalid message format" + } + console.log(parsedMessage) + if (parsedMessage.details) { + if (sendingUserDetails.key !== $own_user.key) { + return `${sendingUserDetails.name} sent you ${parsedMessage.details.amount}` + } else { + return `You sent ${parsedMessage.details.amount} to ${sendingUserDetails.name}` + } + } else { + if (sendingUserDetails.key !== $own_user.key) { + return `${sendingUserDetails.name} sent you ${parsedMessage.amount}` + } else { + return `You sent ${parsedMessage.amount} to ${sendingUserDetails.name}` + } + } + })() + function addFilesToUpload(selected: File[]) { let files: [File?, string?][] = [] for (let file of selected) { @@ -302,24 +328,20 @@ $: rejectedPayments = Store.state.paymentTracker - async function sendPaymentMessage(message: MessageType, paymentType: string) { + async function sendPaymentMessage(message: MessageType, line: string, paymentType: string) { let transfer = new Transfer() let chat = get(Store.state.activeChat) - let rejectTransfer = transfer.toRejectString(message.id) - let txt = rejectTransfer.split("\n") - if (paymentType === "result") { + if (paymentType === PaymentRequestsEnum.Request) { + let rejectTranfser = transfer.toCmdString() + let txt = rejectTranfser.split("\n") let result = await RaygunStoreInstance.send(chat.id, txt, []) result.onSuccess(res => { - if (getValidPaymentRequest(message.text[0])) { - getValidPaymentRequest(message.text[0])?.execute() - } Store.state.paymentTracker.update(payments => { const alreadyRejected = payments.some(payment => payment.messageId === message.id) - if (!alreadyRejected) { return [...payments, { messageId: message.id, senderId: message.details.origin, rejectedPayment: false }] } else { - console.log(`MessageId ${message.id} is already in the rejected payments list`) + console.error(`MessageId ${message.id} is already in the rejected payments list`) return payments } }) @@ -328,6 +350,8 @@ }) } if (paymentType === PaymentRequestsEnum.Reject) { + let rejectTranfser = transfer.toRejectString(message.id) + let txt = rejectTranfser.split("\n") let result = await RaygunStoreInstance.send(chat.id, txt, []) result.onSuccess(res => { Store.state.paymentTracker.update(payments => { @@ -336,7 +360,7 @@ if (!alreadyRejected) { return [...payments, { messageId: message.id, senderId: message.details.origin, rejectedPayment: true }] } else { - console.log(`MessageId ${message.id} is already in the rejected payments list`) + console.error(`MessageId ${message.id} is already in the rejected payments list`) return payments } }) @@ -344,6 +368,80 @@ ConversationStore.addPendingMessages(chat.id, res.message, txt) }) } + if (paymentType === PaymentRequestsEnum.Send) { + try { + const jsonStartIndex = message.text[0].indexOf("{") + if (jsonStartIndex === -1) { + throw new Error("No JSON part found in message") + } + const jsonPart = message.text[0].slice(jsonStartIndex) + const paymentDetails = JSON.parse(jsonPart) + const kind = paymentDetails.asset?.kind || "unknown" + const amount = paymentDetails.amountPreview || "0" + const toAddress = paymentDetails.toAddress || "0" + const formattedMessage = transfer.toDisplayString(kind, amount, toAddress, message.id) + + let chat = get(Store.state.activeChat) + let txt = formattedMessage.split("\n") + + let walletSuccess = await getValidPaymentRequest(line, message.id)?.execute() + if (walletSuccess) { + let result = await RaygunStoreInstance.send(chat.id, txt, []) + result.onSuccess(res => { + Store.state.paymentTracker.update(payments => { + const alreadyRejected = payments.some(payment => payment.messageId === message.id) + + if (!alreadyRejected) { + return [...payments, { messageId: message.id, senderId: message.details.origin, rejectedPayment: false }] + } else { + console.error(`MessageId ${message.id} is already in the rejected payments list`) + return payments + } + }) + ConversationStore.addPendingMessages(chat.id, res.message, txt) + }) + } else { + let rejectTranfser = transfer.toRejectString(message.id) + let txt = rejectTranfser.split("\n") + let result = await RaygunStoreInstance.send(chat.id, txt, []) + result.onSuccess(res => { + Store.state.paymentTracker.update(payments => { + const alreadyRejected = payments.some(payment => payment.messageId === message.id) + + if (!alreadyRejected) { + return [...payments, { messageId: message.id, senderId: message.details.origin, rejectedPayment: true }] + } else { + console.error(`MessageId ${message.id} is already in the rejected payments list`) + return payments + } + }) + transfer.toRejectString(message.id) + ConversationStore.addPendingMessages(chat.id, res.message, txt) + }) + Store.addToastNotification(new ToastMessage("", "Error extracting payment details or sending message:", 4, undefined, Appearance.Warning)) + } + } catch (error) { + let rejectTranfser = transfer.toRejectString(message.id) + let txt = rejectTranfser.split("\n") + let result = await RaygunStoreInstance.send(chat.id, txt, []) + result.onSuccess(res => { + Store.state.paymentTracker.update(payments => { + const alreadyRejected = payments.some(payment => payment.messageId === message.id) + + if (!alreadyRejected) { + return [...payments, { messageId: message.id, senderId: message.details.origin, rejectedPayment: true }] + } else { + console.error(`MessageId ${message.id} is already in the rejected payments list`) + return payments + } + }) + transfer.toRejectString(message.id) + ConversationStore.addPendingMessages(chat.id, res.message, txt) + }) + Store.addToastNotification(new ToastMessage("", "Error extracting payment details or sending message:", 4, undefined, Appearance.Warning)) + console.error("Error extracting payment details or sending message:", error) + } + } } let activeCallInProgress = false @@ -372,13 +470,30 @@ } }, 500) }) - + $: sanitizePaymentSent function checkForActiveRequest(message: MessageType, messageLine: string) { - const idMatch = messageLine.match(/^\/reject\s([a-f0-9-]{36})$/) - if (idMatch) { - const messageId = idMatch[1] + const rejectidMatch = messageLine.match(/^\/reject\s([a-f0-9-]{36})(?:\s|$)/) + const sendidMatch = messageLine.match(/^\/send\s.*"messageID":"([a-f0-9-]{36})"/) + if (rejectidMatch) { + const messageId = rejectidMatch[1] + let wasAdded = false + Store.state.paymentTracker.update(payments => { + const alreadyRejected = payments.some(payment => payment.messageId === messageId) + + if (!alreadyRejected) { + wasAdded = true + return [...payments, { messageId, senderId: message.details.origin, rejectedPayment: true }] + } + return payments + }) + return wasAdded + } + + if (sendidMatch) { + const messageId = sendidMatch[1] let wasAdded = false + Store.state.paymentTracker.update(payments => { const alreadyRejected = payments.some(payment => payment.messageId === messageId) @@ -391,6 +506,7 @@ return wasAdded } + return false } @@ -425,7 +541,6 @@ if (group.details.at > $activeChat.last_view_date) { unreads.push(group) } else { - // Individual messages in a group can still be new since messages are grouped each min let [readMessages, unreadsMessages] = splitMessages(group) if (unreadsMessages.length > 0) { unreads.push({ @@ -819,13 +934,21 @@ {#each message.text as line} {#if line.startsWith(PaymentRequestsEnum.Reject)} {#if !checkForActiveRequest(message, line)} - {#if $own_user.key !== message.details.origin} + {#if $own_user.key === message.details.origin} + + {:else} - {:else} - + {/if} + {/if} + {:else if line.startsWith(PaymentRequestsEnum.Send)} + {#if !checkForActiveRequest(message, line)} + {#if $own_user.key !== message.details.origin} + {sanitizePaymentSent} + {:else if $own_user.key === message.details.origin} + {sanitizePaymentSent} {/if} {/if} {:else if getValidPaymentRequest(line) !== undefined} @@ -836,39 +959,41 @@ hook="text-chat-message" class="send_coin" text={sanitizePaymentRequest(line, resolved.name)} - on:click={async () => getValidPaymentRequest(line, message.id)?.execute()}> + on:click={async () => { + sendPaymentMessage(message, line, PaymentRequestsEnum.Send) + }}> - {:else if !checkForActiveRequest(message, line)} + {:else if !checkForActiveRequest(message, line) && !$rejectedPayments.some(payment => payment.messageId === message.id)} {:else} {/if} - {:else if $own_user.key === message.details.origin && !checkForActiveRequest(message, line)} -