mirror of
https://github.com/PrimalHQ/primal-web-app.git
synced 2024-10-01 17:31:13 +00:00
Add lnbc to DMs
This commit is contained in:
parent
8d2124a56b
commit
f4b3cf831e
@ -150,6 +150,170 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.noBack {
|
||||
background-color: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.lnbcAlter {
|
||||
width: 100%;
|
||||
min-height: 158px;
|
||||
background-color: none;
|
||||
border-radius: var(--border-radius-small);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 12px 0;
|
||||
position: relative;
|
||||
|
||||
.paymentOverlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--background-site);
|
||||
opacity: 0.6;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
color: white;
|
||||
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.headerActions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
|
||||
button {
|
||||
.qrIcon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: inline-block;
|
||||
margin: 0px;
|
||||
background-color: white;
|
||||
-webkit-mask: url(../../assets/icons/qr_code.svg) no-repeat 0px / 18px;
|
||||
mask: url(../../assets/icons/qr_code.svg) no-repeat 0px / 18px;
|
||||
}
|
||||
.copyIcon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: inline-block;
|
||||
margin: 0px;
|
||||
background-color: white;
|
||||
-webkit-mask: url(../../assets/icons/copy_border.svg) no-repeat 0px / 18px;
|
||||
mask: url(../../assets/icons/copy_border.svg) no-repeat 0px / 18px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.qrIcon, .copyIcon {
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.copyDone {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.checkIcon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: inline-block;
|
||||
margin: 0px;
|
||||
background-color: var(--success-bright);
|
||||
-webkit-mask: url(../../assets/icons/check.svg) no-repeat 0px / 18px;
|
||||
mask: url(../../assets/icons/check.svg) no-repeat 0px / 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
gap: 8px;
|
||||
|
||||
.description {
|
||||
color: white;
|
||||
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.amount {
|
||||
color: white;
|
||||
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
height: 36px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
|
||||
.expiryDate {
|
||||
color: white;
|
||||
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
letter-spacing: 0.15px;
|
||||
}
|
||||
.expiredDate {
|
||||
color: white;
|
||||
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
letter-spacing: 0.15px;
|
||||
}
|
||||
|
||||
.payAction {
|
||||
height: 36px;
|
||||
min-width: 120px;
|
||||
button {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: var(--accent);
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.noBack {
|
||||
background-color: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.lnIcon {
|
||||
|
@ -22,7 +22,7 @@ import { useIntl } from '@cookbook/solid-intl';
|
||||
import { lnInvoice } from '../../translations';
|
||||
|
||||
|
||||
const Lnbc: Component< { id?: string, lnbc: string } > = (props) => {
|
||||
const Lnbc: Component< { id?: string, lnbc: string, alternative?: boolean, noBack?: boolean } > = (props) => {
|
||||
|
||||
const app = useAppContext();
|
||||
const toast = useToastContext();
|
||||
@ -151,8 +151,17 @@ const Lnbc: Component< { id?: string, lnbc: string } > = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const klass = () => {
|
||||
let k = props.alternative ? styles.lnbcAlter : styles.lnbc;
|
||||
if (props.noBack) {
|
||||
k += ` ${styles.noBack}`
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
return (
|
||||
<div id={props.id} class={styles.lnbc}>
|
||||
<div id={props.id} class={klass()}>
|
||||
<Show when={paymentInProgress()}>
|
||||
<div class={styles.paymentOverlay}>
|
||||
<Loader />
|
||||
|
@ -26,7 +26,7 @@
|
||||
align-items: flex-start;
|
||||
max-width: calc(100% - 48px);
|
||||
|
||||
.message {
|
||||
.message, .messageLn {
|
||||
@include messageContent();
|
||||
|
||||
@if $align-end {
|
||||
@ -46,12 +46,18 @@
|
||||
background-color: var(--subtile-devider);
|
||||
}
|
||||
}
|
||||
.messageLn {
|
||||
display: flex;
|
||||
width: calc(500px + 12px - 40px) !important;
|
||||
}
|
||||
|
||||
.threadTime {
|
||||
color: var(--text-tertiary-2);
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.messagesContent {
|
||||
@ -258,7 +264,7 @@
|
||||
.myThread {
|
||||
@include thread(true);
|
||||
.threadMessages {
|
||||
.message {
|
||||
.message, .messageLn {
|
||||
color: var(--text-primary-button);
|
||||
background-color: var(--accent);
|
||||
border-radius: 12px 0px 0px 12px;
|
||||
@ -279,7 +285,7 @@
|
||||
.theirThread {
|
||||
@include thread(false);
|
||||
.threadMessages {
|
||||
.message {
|
||||
.message, .messageLn {
|
||||
background-color: var(--background-input);
|
||||
border-radius: 0px 12px 12px 0px;
|
||||
&:last-child {
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { useIntl } from '@cookbook/solid-intl';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { Component, createEffect, createSignal, For, onCleanup, onMount, Show } from 'solid-js';
|
||||
import { Component, createEffect, createSignal, For, JSXElement, Match, onCleanup, onMount, Show, Switch } from 'solid-js';
|
||||
import Avatar from '../components/Avatar/Avatar';
|
||||
import { useAccountContext } from '../contexts/AccountContext';
|
||||
import { useMessagesContext } from '../contexts/MessagesContext';
|
||||
import { nip05Verification, truncateNpub, userName } from '../stores/profile';
|
||||
import { PrimalNote, PrimalUser } from '../types/primal';
|
||||
import { DirectMessage, DirectMessageThread, PrimalNote, PrimalUser } from '../types/primal';
|
||||
import { date } from '../lib/dates';
|
||||
|
||||
import styles from './Messages.module.scss';
|
||||
@ -20,7 +20,7 @@ import SearchOption from '../components/Search/SearchOption';
|
||||
import { debounce, isVisibleInContainer, uuidv4 } from '../utils';
|
||||
import { useSearchContext } from '../contexts/SearchContext';
|
||||
import { createStore } from 'solid-js/store';
|
||||
import { editMentionRegex, emojiSearchLimit } from '../constants';
|
||||
import { editMentionRegex, emojiSearchLimit, linebreakRegex } from '../constants';
|
||||
import Search from '../components/Search/Search';
|
||||
import { useProfileContext } from '../contexts/ProfileContext';
|
||||
import Paginator from '../components/Paginator/Paginator';
|
||||
@ -35,6 +35,7 @@ import {
|
||||
import PageCaption from '../components/PageCaption/PageCaption';
|
||||
import { useMediaContext } from '../contexts/MediaContext';
|
||||
import PageTitle from '../components/PageTitle/PageTitle';
|
||||
import Lnbc from '../components/Lnbc/Lnbc';
|
||||
|
||||
type AutoSizedTextArea = HTMLTextAreaElement & { _baseScrollHeight: number };
|
||||
|
||||
@ -294,6 +295,7 @@ const Messages: Component = () => {
|
||||
if (!messages) {
|
||||
return message;
|
||||
}
|
||||
|
||||
return parseNoteLinks(
|
||||
parseNpubLinks(
|
||||
highlightHashtags(
|
||||
@ -831,6 +833,13 @@ const Messages: Component = () => {
|
||||
newMessageInput.dispatchEvent(e);
|
||||
};
|
||||
|
||||
const msgHasInvoice = (msg: DirectMessage) => {
|
||||
const r =/(\s+|\r\n|\r|\n|^)lnbc[a-zA-Z0-9]+/;
|
||||
const test = r.test(msg.content);
|
||||
|
||||
return test
|
||||
};
|
||||
|
||||
createEffect(() => {
|
||||
if (account?.hasPublicKey()) {
|
||||
profile?.actions.setProfileKey(account.publicKey)
|
||||
@ -884,6 +893,75 @@ const Messages: Component = () => {
|
||||
newMessageInput && setMessage(newMessageInput.value)
|
||||
}
|
||||
|
||||
const renderMessage = (msg: DirectMessage, thread: DirectMessageThread) => {
|
||||
if (!msgHasInvoice(msg)) {
|
||||
return (
|
||||
<div
|
||||
class={styles.message}
|
||||
data-event-id={msg.id}
|
||||
title={date(msg.created_at || 0).date.toLocaleString()}
|
||||
innerHTML={parseMessage(msg.content)}
|
||||
></div>
|
||||
);
|
||||
};
|
||||
|
||||
let sections: string[] = [];
|
||||
|
||||
let content = msg.content.replace(linebreakRegex, ' __LB__ ').replace(/\s+/g, ' __SP__ ');
|
||||
|
||||
let tokens: string[] = content.split(/[\s]+/);
|
||||
|
||||
let sectionIndex = 0;
|
||||
tokens.forEach((t) => {
|
||||
if (t.startsWith('lnbc')) {
|
||||
if (sections[sectionIndex]) sectionIndex++;
|
||||
|
||||
sections[sectionIndex] = t;
|
||||
|
||||
sectionIndex++;
|
||||
}
|
||||
else {
|
||||
let c = t;
|
||||
const prev = sections[sectionIndex] || '';
|
||||
|
||||
if (t === '__SP__') {
|
||||
c = prev.length === 0 ? '' : ' ';
|
||||
}
|
||||
|
||||
if (t === '__LB__') {
|
||||
c = prev.length === 0 ? '' : '\r';
|
||||
}
|
||||
|
||||
sections[sectionIndex] = prev + c;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<For each={sections.reverse()}>
|
||||
{section => (
|
||||
<Switch fallback={
|
||||
<div
|
||||
class={styles.message}
|
||||
data-event-id={msg.id}
|
||||
title={date(msg.created_at || 0).date.toLocaleString()}
|
||||
innerHTML={parseMessage(section)}
|
||||
></div>
|
||||
}>
|
||||
<Match when={section.startsWith('lnbc')}>
|
||||
<div
|
||||
class={styles.messageLn}
|
||||
data-event-id={msg.id}
|
||||
title={date(msg.created_at || 0).date.toLocaleString()}
|
||||
>
|
||||
<Lnbc lnbc={section} noBack={true} alternative={!isSelectedSender(thread.author)} />
|
||||
</div>
|
||||
</Match>
|
||||
</Switch>
|
||||
)}
|
||||
</For>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageTitle title={intl.formatMessage(tMessages.title)} />
|
||||
@ -1064,14 +1142,7 @@ const Messages: Component = () => {
|
||||
</A>
|
||||
<div class={styles.threadMessages}>
|
||||
<For each={thread.messages}>
|
||||
{(msg) => (
|
||||
<div
|
||||
class={styles.message}
|
||||
data-event-id={msg.id}
|
||||
title={date(msg.created_at || 0).date.toLocaleString()}
|
||||
innerHTML={parseMessage(msg.content)}
|
||||
></div>
|
||||
)}
|
||||
{msg => renderMessage(msg, thread)}
|
||||
</For>
|
||||
</div>
|
||||
<Show when={thread.messages[0]}>
|
||||
@ -1091,14 +1162,7 @@ const Messages: Component = () => {
|
||||
</A>
|
||||
<div class={styles.threadMessages}>
|
||||
<For each={thread.messages}>
|
||||
{(msg) => (
|
||||
<div
|
||||
class={styles.message}
|
||||
data-event-id={msg.id}
|
||||
title={date(msg.created_at || 0).date.toLocaleString()}
|
||||
innerHTML={parseMessage(msg.content)}
|
||||
></div>
|
||||
)}
|
||||
{msg => renderMessage(msg, thread)}
|
||||
</For>
|
||||
</div>
|
||||
<Show when={thread.messages[0]}>
|
||||
|
Loading…
Reference in New Issue
Block a user