mirror of
https://github.com/PrimalHQ/primal-web-app.git
synced 2024-10-03 02:10:55 +00:00
Fix Exception when grouping images in gallery
This commit is contained in:
parent
a5a7359423
commit
9cf6bf5ffe
@ -44,7 +44,6 @@ const App: Component = () => {
|
|||||||
<HomeProvider>
|
<HomeProvider>
|
||||||
<ExploreProvider>
|
<ExploreProvider>
|
||||||
<ThreadProvider>
|
<ThreadProvider>
|
||||||
<input id="defocus" class={styles.invisible}/>
|
|
||||||
<Router />
|
<Router />
|
||||||
</ThreadProvider>
|
</ThreadProvider>
|
||||||
</ExploreProvider>
|
</ExploreProvider>
|
||||||
|
@ -1,29 +1,18 @@
|
|||||||
import { useIntl } from '@cookbook/solid-intl';
|
import { useIntl } from '@cookbook/solid-intl';
|
||||||
import { Component, createEffect, createSignal, For, Match, Show, Switch } from 'solid-js';
|
import { Component, createEffect, createSignal } from 'solid-js';
|
||||||
import { useAccountContext } from '../../contexts/AccountContext';
|
|
||||||
import { useSettingsContext } from '../../contexts/SettingsContext';
|
|
||||||
import { zapNote } from '../../lib/zap';
|
|
||||||
import { userName } from '../../stores/profile';
|
|
||||||
import { toastZapFail, zapCustomOption } from '../../translations';
|
|
||||||
import { PrimalNote } from '../../types/primal';
|
|
||||||
import { debounce } from '../../utils';
|
|
||||||
import Modal from '../Modal/Modal';
|
import Modal from '../Modal/Modal';
|
||||||
import { useToastContext } from '../Toaster/Toaster';
|
import { useToastContext } from '../Toaster/Toaster';
|
||||||
import { base64 } from '@scure/base';
|
|
||||||
|
|
||||||
import { nip19, utils } from 'nostr-tools';
|
import { nip19 } from 'nostr-tools';
|
||||||
|
|
||||||
|
|
||||||
import { login as tLogin, pin as tPin, actions as tActions } from '../../translations';
|
import { pin as tPin, actions as tActions } from '../../translations';
|
||||||
|
|
||||||
import styles from './EnterPinModal.module.scss';
|
import styles from './EnterPinModal.module.scss';
|
||||||
import { hookForDev } from '../../lib/devTools';
|
import { hookForDev } from '../../lib/devTools';
|
||||||
import ButtonPrimary from '../Buttons/ButtonPrimary';
|
import ButtonPrimary from '../Buttons/ButtonPrimary';
|
||||||
import ButtonLink from '../Buttons/ButtonLink';
|
|
||||||
import { useNavigate } from '@solidjs/router';
|
|
||||||
import TextInput from '../TextInput/TextInput';
|
import TextInput from '../TextInput/TextInput';
|
||||||
import ButtonSecondary from '../Buttons/ButtonSecondary';
|
import { decryptWithPin, setCurrentPin } from '../../lib/PrimalNostr';
|
||||||
import { decryptWithPin, encryptWithPin, setCurrentPin } from '../../lib/PrimalNostr';
|
|
||||||
|
|
||||||
const EnterPinModal: Component<{
|
const EnterPinModal: Component<{
|
||||||
id?: string,
|
id?: string,
|
||||||
@ -43,9 +32,6 @@ const EnterPinModal: Component<{
|
|||||||
const decWithPin = async () => {
|
const decWithPin = async () => {
|
||||||
const val = props.valueToDecrypt || '';
|
const val = props.valueToDecrypt || '';
|
||||||
const dec = await decryptWithPin(pin(), val);
|
const dec = await decryptWithPin(pin(), val);
|
||||||
// console.log('ENCODED: ', dec);
|
|
||||||
// console.log('PIN: ', pin());
|
|
||||||
// console.log('DECODE: ', decryptWithPin);
|
|
||||||
return dec;
|
return dec;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,10 +27,10 @@ const FeedSelect: Component<{ isPhone?: boolean, id?: string}> = (props) => {
|
|||||||
const selectFeed = (option: FeedOption) => {
|
const selectFeed = (option: FeedOption) => {
|
||||||
|
|
||||||
const [hex, includeReplies] = option.value?.split('_') || [];
|
const [hex, includeReplies] = option.value?.split('_') || [];
|
||||||
const selector = document.getElementById('defocus');
|
// const selector = document.getElementById('defocus');
|
||||||
|
|
||||||
selector?.focus();
|
// selector?.focus();
|
||||||
selector?.blur();
|
// selector?.blur();
|
||||||
|
|
||||||
if (hex && !isSelected(option)) {
|
if (hex && !isSelected(option)) {
|
||||||
const feed = findFeed(decodeURI(hex), includeReplies);
|
const feed = findFeed(decodeURI(hex), includeReplies);
|
||||||
|
@ -23,7 +23,7 @@ import {
|
|||||||
import { truncateNpub, userName } from '../../stores/profile';
|
import { truncateNpub, userName } from '../../stores/profile';
|
||||||
import EmbeddedNote from '../EmbeddedNote/EmbeddedNote';
|
import EmbeddedNote from '../EmbeddedNote/EmbeddedNote';
|
||||||
import {
|
import {
|
||||||
Component, createEffect, createSignal, For, JSXElement, onMount, Show,
|
Component, createSignal, For, JSXElement, onMount, Show,
|
||||||
} from 'solid-js';
|
} from 'solid-js';
|
||||||
import {
|
import {
|
||||||
PrimalNote,
|
PrimalNote,
|
||||||
@ -47,16 +47,6 @@ import PhotoSwipeLightbox from 'photoswipe/lightbox';
|
|||||||
|
|
||||||
const groupGridLimit = 7;
|
const groupGridLimit = 7;
|
||||||
|
|
||||||
const convertHTMLEntity = (text: string) => {
|
|
||||||
const span = document.createElement('span');
|
|
||||||
|
|
||||||
return text
|
|
||||||
.replace(/&[#A-Za-z0-9]+;/gi, (entity)=> {
|
|
||||||
span.innerHTML = entity;
|
|
||||||
return span.innerText;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const groupGalleryImages = (noteHolder: HTMLDivElement | undefined) => {
|
export const groupGalleryImages = (noteHolder: HTMLDivElement | undefined) => {
|
||||||
|
|
||||||
@ -147,6 +137,8 @@ const ParsedNote: Component<{
|
|||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const media = useMediaContext();
|
const media = useMediaContext();
|
||||||
|
|
||||||
|
const dev = localStorage.getItem('devMode') === 'true';
|
||||||
|
|
||||||
const id = () => {
|
const id = () => {
|
||||||
// if (props.id) return props.id;
|
// if (props.id) return props.id;
|
||||||
|
|
||||||
@ -155,10 +147,6 @@ const ParsedNote: Component<{
|
|||||||
|
|
||||||
let thisNote: HTMLDivElement | undefined;
|
let thisNote: HTMLDivElement | undefined;
|
||||||
|
|
||||||
let imageGroup: string = generatePrivateKey()
|
|
||||||
let consecutiveImages: number = 0;
|
|
||||||
let imgCount = 0;
|
|
||||||
|
|
||||||
const lightbox = new PhotoSwipeLightbox({
|
const lightbox = new PhotoSwipeLightbox({
|
||||||
gallery: `#${id()}`,
|
gallery: `#${id()}`,
|
||||||
children: `a.image_${props.note.post.noteId}`,
|
children: `a.image_${props.note.post.noteId}`,
|
||||||
@ -173,29 +161,12 @@ const ParsedNote: Component<{
|
|||||||
lightbox.init();
|
lightbox.init();
|
||||||
});
|
});
|
||||||
|
|
||||||
let allImagesLoaded = false;
|
|
||||||
|
|
||||||
createEffect(() => {
|
|
||||||
if (imagesLoaded() > 0 && imagesLoaded() === imgCount && !allImagesLoaded) {
|
|
||||||
allImagesLoaded = true;
|
|
||||||
groupGalleryImages(thisNote);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const [tokens, setTokens] = createStore<string[]>([]);
|
const [tokens, setTokens] = createStore<string[]>([]);
|
||||||
const [imagesLoaded, setImagesLoaded] = createSignal(0);
|
|
||||||
|
|
||||||
let wordsDisplayed = 0;
|
const [wordsDisplayed, setWordsDisplayed] = createSignal(0);
|
||||||
|
|
||||||
const shouldShowToken = () => {
|
const isNoteTooLong = () => {
|
||||||
if (!props.shorten) return true;
|
return props.shorten && wordsDisplayed() > shortNoteWords;
|
||||||
|
|
||||||
|
|
||||||
if (wordsDisplayed < shortNoteWords) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseContent = () => {
|
const parseContent = () => {
|
||||||
@ -208,264 +179,458 @@ const ParsedNote: Component<{
|
|||||||
setTokens(() => [...tokens]);
|
setTokens(() => [...tokens]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseToken: (token: string) => JSXElement = (token: string) => {
|
type NoteContent = {
|
||||||
|
type: string,
|
||||||
|
tokens: string[],
|
||||||
|
meta?: Record<string, any>,
|
||||||
|
};
|
||||||
|
|
||||||
if (token === '__LB__') {
|
const [content, setContent] = createStore<NoteContent[]>([]);
|
||||||
return <br />;
|
|
||||||
|
const updateContent = (contentArray: NoteContent[], type: string, token: string, meta?: Record<string, any>) => {
|
||||||
|
if (contentArray.length > 0 && contentArray[contentArray.length -1].type === type) {
|
||||||
|
setContent(content.length -1, 'tokens' , (els) => [...els, token]);
|
||||||
|
meta && setContent(content.length -1, 'meta' , () => ({ ...meta }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setContent(content.length, () => ({ type, tokens: [token], meta }));
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastSignificantContent = 'text';
|
||||||
|
|
||||||
|
const parseToken = (token: string) => {
|
||||||
|
if (token === '__LB__') {
|
||||||
|
lastSignificantContent !== 'image' && updateContent(content, 'linebreak', token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token === '__SP__') {
|
||||||
|
lastSignificantContent !== 'image' && updateContent(content, 'text', ' ');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInterpunction(token)) {
|
||||||
|
lastSignificantContent = 'text';
|
||||||
|
updateContent(content, 'text', token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUrl(token)) {
|
||||||
|
const index = token.indexOf('http');
|
||||||
|
|
||||||
|
if (index > 0) {
|
||||||
|
const prefix = token.slice(0, index);
|
||||||
|
|
||||||
|
const matched = (token.match(urlExtractRegex) || [])[0];
|
||||||
|
|
||||||
|
if (matched) {
|
||||||
|
const suffix = token.substring(matched.length + index, token.length);
|
||||||
|
|
||||||
|
parseToken(prefix);
|
||||||
|
parseToken(matched);
|
||||||
|
parseToken(suffix);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
parseToken(prefix);
|
||||||
|
parseToken(token.slice(index));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token === '__SP__') {
|
if (!props.ignoreMedia) {
|
||||||
return <> </>;
|
if (isImage(token)) {
|
||||||
|
lastSignificantContent = 'image';
|
||||||
|
updateContent(content, 'image', token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMp4Video(token)) {
|
||||||
|
lastSignificantContent = 'video';
|
||||||
|
updateContent(content, 'video', token, { videoType: 'video/mp4'});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOggVideo(token)) {
|
||||||
|
lastSignificantContent = 'video';
|
||||||
|
updateContent(content, 'video', token, { videoType: 'video/ogg'});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isWebmVideo(token)) {
|
||||||
|
lastSignificantContent = 'video';
|
||||||
|
updateContent(content, 'video', token, { videoType: 'video/webm'});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isYouTube(token)) {
|
||||||
|
lastSignificantContent = 'youtube';
|
||||||
|
updateContent(content, 'youtube', token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSpotify(token)) {
|
||||||
|
lastSignificantContent = 'spotify';
|
||||||
|
updateContent(content, 'spotify', token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTwitch(token)) {
|
||||||
|
lastSignificantContent = 'twitch';
|
||||||
|
updateContent(content, 'twitch', token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMixCloud(token)) {
|
||||||
|
lastSignificantContent = 'mixcloud';
|
||||||
|
updateContent(content, 'mixcloud', token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSoundCloud(token)) {
|
||||||
|
lastSignificantContent = 'soundcloud';
|
||||||
|
updateContent(content, 'soundcloud', token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAppleMusic(token)) {
|
||||||
|
lastSignificantContent = 'applemusic';
|
||||||
|
updateContent(content, 'applemusic', token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isWavelake(token)) {
|
||||||
|
lastSignificantContent = 'wavelake';
|
||||||
|
updateContent(content, 'wavelake', token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wordsDisplayed++;
|
if (props.noLinks === 'text') {
|
||||||
|
lastSignificantContent = 'text';
|
||||||
if (isInterpunction(token)) {
|
updateContent(content, 'text', token);
|
||||||
return <span>{token}</span>;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUrl(token)) {
|
lastSignificantContent = 'link';
|
||||||
const index = token.indexOf('http');
|
updateContent(content, 'link', token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (index > 0) {
|
if (isNoteMention(token)) {
|
||||||
const prefix = token.slice(0, index);
|
lastSignificantContent = 'notemention';
|
||||||
|
updateContent(content, 'notemention', token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const matched = (token.match(urlExtractRegex) || [])[0];
|
if (isUserMention(token)) {
|
||||||
|
lastSignificantContent = 'usermention';
|
||||||
|
updateContent(content, 'usermention', token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (matched) {
|
if (isTagMention(token)) {
|
||||||
const suffix = token.substring(matched.length + index, token.length);
|
lastSignificantContent = 'tagmention';
|
||||||
return <>{parseToken(prefix)}{parseToken(matched)}{parseToken(suffix)}</>;
|
updateContent(content, 'tagmention', token);
|
||||||
} else {
|
return;
|
||||||
return <>{parseToken(prefix)}{parseToken(token.slice(index))}</>;
|
}
|
||||||
}
|
|
||||||
|
if (isHashtag(token)) {
|
||||||
|
lastSignificantContent = 'hashtag';
|
||||||
|
updateContent(content, 'hashtag', token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastSignificantContent = 'text';
|
||||||
|
updateContent(content, 'text', token);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateContent = () => {
|
||||||
|
|
||||||
|
parseContent();
|
||||||
|
|
||||||
|
for (let i=0; i<tokens.length; i++) {
|
||||||
|
const token = tokens[i];
|
||||||
|
|
||||||
|
parseToken(token);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderLinebreak = (item: NoteContent) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
|
// Allow only one consecutive linebreak
|
||||||
|
return <br />
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderText = (item: NoteContent) => {
|
||||||
|
return <For each={item.tokens}>
|
||||||
|
{token => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
if (token.trim().length > 0) {
|
||||||
|
setWordsDisplayed(w => w + 1);
|
||||||
|
}
|
||||||
|
return token
|
||||||
|
}}
|
||||||
|
</For>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderImage = (item: NoteContent) => {
|
||||||
|
|
||||||
|
const groupCount = item.tokens.length;
|
||||||
|
const imageGroup = generatePrivateKey();
|
||||||
|
|
||||||
|
if (groupCount === 1) {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
|
const token = item.tokens[0];
|
||||||
|
let image = media?.actions.getMedia(token, 'o');
|
||||||
|
const url = image?.media_url || getMediaUrlDefault(token);
|
||||||
|
|
||||||
|
// Images tell a 100 words :)
|
||||||
|
setWordsDisplayed(w => w + 100);
|
||||||
|
|
||||||
|
return <NoteImage
|
||||||
|
class={`noteimage image_${props.note.post.noteId}`}
|
||||||
|
src={url}
|
||||||
|
isDev={dev}
|
||||||
|
media={image}
|
||||||
|
width={514}
|
||||||
|
imageGroup={imageGroup}
|
||||||
|
shortHeight={props.shorten}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
const gridClass = groupCount < groupGridLimit ? `grid-${groupCount}` : 'grid-large';
|
||||||
|
|
||||||
|
return <div class={`imageGrid ${gridClass}`}>
|
||||||
|
<For each={item.tokens}>
|
||||||
|
{(token, index) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
|
let image = media?.actions.getMedia(token, 'o');
|
||||||
|
const url = image?.media_url || getMediaUrlDefault(token);
|
||||||
|
|
||||||
|
// There are consecutive images, so reduce the impact of each image in order to show them grouped
|
||||||
|
setWordsDisplayed(w => w + 10 * groupCount);
|
||||||
|
|
||||||
|
return <NoteImage
|
||||||
|
class={`noteimage_gallery image_${props.note.post.noteId} cell_${index()}`}
|
||||||
|
src={url}
|
||||||
|
isDev={dev}
|
||||||
|
media={image}
|
||||||
|
width={514}
|
||||||
|
imageGroup={imageGroup}
|
||||||
|
shortHeight={props.shorten}
|
||||||
|
plainBorder={true}
|
||||||
|
/>
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderVideo = (item: NoteContent) => {
|
||||||
|
return <For each={item.tokens}>{
|
||||||
|
(token) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
|
let mVideo = media?.actions.getMedia(token, 'o');
|
||||||
|
|
||||||
|
let h: number | undefined = undefined;
|
||||||
|
let w: number | undefined = undefined;
|
||||||
|
|
||||||
|
if (mVideo) {
|
||||||
|
const ratio = mVideo.w / mVideo.h;
|
||||||
|
h = (524 / ratio);
|
||||||
|
w = h > 680 ? 680 * ratio : 524;
|
||||||
|
h = h > 680 ? 680 : h;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!props.ignoreMedia) {
|
let klass = mVideo ? 'w-cen' : 'w-max';
|
||||||
if (isImage(token)) {
|
|
||||||
imgCount++;
|
|
||||||
consecutiveImages++;
|
|
||||||
const dev = localStorage.getItem('devMode') === 'true';
|
|
||||||
let image = media?.actions.getMedia(token, 'o');
|
|
||||||
const url = image?.media_url || getMediaUrlDefault(token);
|
|
||||||
|
|
||||||
if (consecutiveImages > 1) {
|
if (dev && !mVideo) {
|
||||||
// There are consecutive images, so reduce the impact of each image in order to show them grouped
|
klass += ' redBorder';
|
||||||
wordsDisplayed += 10;
|
|
||||||
} else {
|
|
||||||
wordsDisplayed += shortMentionInWords
|
|
||||||
}
|
|
||||||
|
|
||||||
return <NoteImage
|
|
||||||
class={`noteimage image_${props.note.post.noteId}`}
|
|
||||||
src={url}
|
|
||||||
isDev={dev}
|
|
||||||
media={image}
|
|
||||||
width={514}
|
|
||||||
imageGroup={imageGroup}
|
|
||||||
onImageLoaded={() => setImagesLoaded(i => i+1)}
|
|
||||||
shortHeight={props.shorten}
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
consecutiveImages = 0;
|
|
||||||
imageGroup = generatePrivateKey();
|
|
||||||
|
|
||||||
if (isMp4Video(token)) {
|
|
||||||
wordsDisplayed += shortMentionInWords;
|
|
||||||
let mVideo = media?.actions.getMedia(token, 'o');
|
|
||||||
|
|
||||||
let h: number | undefined = undefined;
|
|
||||||
let w: number | undefined = undefined;
|
|
||||||
|
|
||||||
if (mVideo) {
|
|
||||||
const ratio = mVideo.w / mVideo.h;
|
|
||||||
h = (524 / ratio);
|
|
||||||
w = h > 680 ? 680 * ratio : 524;
|
|
||||||
h = h > 680 ? 680 : h;
|
|
||||||
}
|
|
||||||
|
|
||||||
// const h = mVideo ? mVideo?.h > 524 ? 524 * mVideo?.h / mVideo?.w : mVideo?.h : undefined;
|
|
||||||
// const w = mVideo ? mVideo?.w > 524 ? 524 : mVideo?.w : undefined;
|
|
||||||
const klass = mVideo ? 'w-cen' : 'w-max';
|
|
||||||
|
|
||||||
const video = <video class={klass} width={w} height={h} controls muted={true} ><source src={token} type="video/mp4" /></video>;
|
|
||||||
media?.actions.addVideo(video as HTMLVideoElement);
|
|
||||||
|
|
||||||
|
|
||||||
return video;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isOggVideo(token)) {
|
|
||||||
wordsDisplayed += shortMentionInWords;
|
|
||||||
let mVideo = media?.actions.getMedia(token, 'o');
|
|
||||||
|
|
||||||
let h: number | undefined = undefined;
|
|
||||||
let w: number | undefined = undefined;
|
|
||||||
|
|
||||||
if (mVideo) {
|
|
||||||
const ratio = mVideo.w / mVideo.h;
|
|
||||||
h = (524 / ratio);
|
|
||||||
w = h > 680 ? 680 * ratio : 524;
|
|
||||||
h = h > 680 ? 680 : h;
|
|
||||||
}
|
|
||||||
|
|
||||||
// const h = mVideo ? mVideo?.h > 524 ? 524 * mVideo?.h / mVideo?.w : mVideo?.h : undefined;
|
|
||||||
// const w = mVideo ? mVideo?.w > 524 ? 524 : mVideo?.w : undefined;
|
|
||||||
const klass = mVideo ? 'w-cen' : 'w-max';
|
|
||||||
|
|
||||||
const video =
|
|
||||||
<video
|
|
||||||
class={klass}
|
|
||||||
width={w}
|
|
||||||
height={h}
|
|
||||||
controls
|
|
||||||
muted={true}
|
|
||||||
>
|
|
||||||
<source src={token} type="video/ogg" />
|
|
||||||
</video>;
|
|
||||||
media?.actions.addVideo(video as HTMLVideoElement);
|
|
||||||
return video;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isWebmVideo(token)) {
|
|
||||||
wordsDisplayed += shortMentionInWords;
|
|
||||||
let mVideo = media?.actions.getMedia(token, 'o');
|
|
||||||
|
|
||||||
let h: number | undefined = undefined;
|
|
||||||
let w: number | undefined = undefined;
|
|
||||||
|
|
||||||
if (mVideo) {
|
|
||||||
const ratio = mVideo.w / mVideo.h;
|
|
||||||
h = (524 / ratio);
|
|
||||||
w = h > 680 ? 680 * ratio : 524;
|
|
||||||
h = h > 680 ? 680 : h;
|
|
||||||
}
|
|
||||||
|
|
||||||
// const h = mVideo ? mVideo?.h > 524 ? 524 * mVideo?.h / mVideo?.w : mVideo?.h : undefined;
|
|
||||||
// const w = mVideo ? mVideo?.w > 524 ? 524 : mVideo?.w : undefined;
|
|
||||||
const klass = mVideo ? 'w-cen' : 'w-max';
|
|
||||||
|
|
||||||
const video = <video class={klass} width={w} height={h} controls muted={true} ><source src={token} type="video/webm" /></video>;
|
|
||||||
media?.actions.addVideo(video as HTMLVideoElement);
|
|
||||||
return video;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isYouTube(token)) {
|
|
||||||
wordsDisplayed += shortMentionInWords;
|
|
||||||
|
|
||||||
const youtubeId = isYouTube(token) && RegExp.$1;
|
|
||||||
|
|
||||||
return <iframe
|
|
||||||
class="w-max"
|
|
||||||
src={`https://www.youtube.com/embed/${youtubeId}`}
|
|
||||||
title="YouTube video player"
|
|
||||||
// @ts-ignore no property
|
|
||||||
key={youtubeId}
|
|
||||||
frameBorder="0"
|
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
|
||||||
allowFullScreen
|
|
||||||
></iframe>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSpotify(token)) {
|
|
||||||
wordsDisplayed += shortMentionInWords;
|
|
||||||
|
|
||||||
const convertedUrl = token.replace(/\/(track|album|playlist|episode)\/([a-zA-Z0-9]+)/, "/embed/$1/$2");
|
|
||||||
|
|
||||||
return <iframe
|
|
||||||
style="borderRadius: 12"
|
|
||||||
src={convertedUrl}
|
|
||||||
width="100%"
|
|
||||||
height="352"
|
|
||||||
// @ts-ignore no property
|
|
||||||
frameBorder="0"
|
|
||||||
allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture"
|
|
||||||
loading="lazy"
|
|
||||||
></iframe>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isTwitch(token)) {
|
|
||||||
wordsDisplayed += shortMentionInWords;
|
|
||||||
|
|
||||||
const channel = token.split("/").slice(-1);
|
|
||||||
|
|
||||||
const args = `?channel=${channel}&parent=${window.location.hostname}&muted=true`;
|
|
||||||
|
|
||||||
return <iframe
|
|
||||||
src={`https://player.twitch.tv/${args}`}
|
|
||||||
// @ts-ignore no property
|
|
||||||
className="w-max"
|
|
||||||
allowFullScreen
|
|
||||||
></iframe>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMixCloud(token)) {
|
|
||||||
wordsDisplayed += shortMentionInWords;
|
|
||||||
|
|
||||||
const feedPath = (isMixCloud(token) && RegExp.$1) + "%2F" + (isMixCloud(token) && RegExp.$2);
|
|
||||||
|
|
||||||
return <div>
|
|
||||||
<iframe
|
|
||||||
title="SoundCloud player"
|
|
||||||
width="100%"
|
|
||||||
height="120"
|
|
||||||
// @ts-ignore no property
|
|
||||||
frameBorder="0"
|
|
||||||
src={`https://www.mixcloud.com/widget/iframe/?hide_cover=1&feed=%2F${feedPath}%2F`}
|
|
||||||
></iframe>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSoundCloud(token)) {
|
|
||||||
wordsDisplayed += shortMentionInWords;
|
|
||||||
|
|
||||||
return <iframe
|
|
||||||
width="100%"
|
|
||||||
height="166"
|
|
||||||
// @ts-ignore no property
|
|
||||||
scrolling="no"
|
|
||||||
allow="autoplay"
|
|
||||||
src={`https://w.soundcloud.com/player/?url=${token}`}
|
|
||||||
></iframe>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAppleMusic(token)) {
|
|
||||||
wordsDisplayed += shortMentionInWords;
|
|
||||||
|
|
||||||
const convertedUrl = token.replace("music.apple.com", "embed.music.apple.com");
|
|
||||||
const isSongLink = /\?i=\d+$/.test(convertedUrl);
|
|
||||||
|
|
||||||
return <iframe
|
|
||||||
allow="autoplay *; encrypted-media *; fullscreen *; clipboard-write"
|
|
||||||
// @ts-ignore no property
|
|
||||||
frameBorder="0"
|
|
||||||
height={`${isSongLink ? 175 : 450}`}
|
|
||||||
style="width: 100%; maxWidth: 660; overflow: hidden; background: transparent;"
|
|
||||||
sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-storage-access-by-user-activation allow-top-navigation-by-user-activation"
|
|
||||||
src={convertedUrl}
|
|
||||||
></iframe>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isWavelake(token)) {
|
|
||||||
wordsDisplayed += shortMentionInWords;
|
|
||||||
|
|
||||||
const convertedUrl = token.replace(/(?:player\.|www\.)?wavlake\.com/, "embed.wavlake.com");
|
|
||||||
|
|
||||||
return <iframe
|
|
||||||
style="borderRadius: 12"
|
|
||||||
src={convertedUrl}
|
|
||||||
width="100%"
|
|
||||||
height="380"
|
|
||||||
// @ts-ignore no property
|
|
||||||
frameBorder="0"
|
|
||||||
loading="lazy"
|
|
||||||
></iframe>;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.noLinks === 'text') {
|
setWordsDisplayed(w => w + shortMentionInWords);
|
||||||
return <span class="whole">{token}</span>;
|
|
||||||
}
|
const video = <video
|
||||||
|
class={klass}
|
||||||
|
width={w}
|
||||||
|
height={h}
|
||||||
|
controls
|
||||||
|
muted={true}
|
||||||
|
>
|
||||||
|
<source src={token} type={item.meta?.videoType} />
|
||||||
|
</video>;
|
||||||
|
|
||||||
|
media?.actions.addVideo(video as HTMLVideoElement);
|
||||||
|
|
||||||
|
return video;
|
||||||
|
}
|
||||||
|
}</For>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderYouTube = (item: NoteContent) => {
|
||||||
|
|
||||||
|
return <For each={item.tokens}>
|
||||||
|
{(token) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
|
setWordsDisplayed(w => w + shortMentionInWords);
|
||||||
|
|
||||||
|
const youtubeId = isYouTube(token) && RegExp.$1;
|
||||||
|
|
||||||
|
return <iframe
|
||||||
|
class="w-max"
|
||||||
|
src={`https://www.youtube.com/embed/${youtubeId}`}
|
||||||
|
title="YouTube video player"
|
||||||
|
// @ts-ignore no property
|
||||||
|
key={youtubeId}
|
||||||
|
frameBorder="0"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||||
|
allowFullScreen
|
||||||
|
></iframe>;
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderSpotify = (item: NoteContent) => {
|
||||||
|
return <For each={item.tokens}>
|
||||||
|
{(token) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
|
setWordsDisplayed(w => w + shortMentionInWords);
|
||||||
|
|
||||||
|
const convertedUrl = token.replace(/\/(track|album|playlist|episode)\/([a-zA-Z0-9]+)/, "/embed/$1/$2");
|
||||||
|
|
||||||
|
return <iframe
|
||||||
|
style="borderRadius: 12"
|
||||||
|
src={convertedUrl}
|
||||||
|
width="100%"
|
||||||
|
height="352"
|
||||||
|
// @ts-ignore no property
|
||||||
|
frameBorder="0"
|
||||||
|
allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture"
|
||||||
|
loading="lazy"
|
||||||
|
></iframe>;
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderTwitch = (item: NoteContent) => {
|
||||||
|
return <For each={item.tokens}>
|
||||||
|
{(token) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
|
setWordsDisplayed(w => w + shortMentionInWords);
|
||||||
|
|
||||||
|
const channel = token.split("/").slice(-1);
|
||||||
|
|
||||||
|
const args = `?channel=${channel}&parent=${window.location.hostname}&muted=true`;
|
||||||
|
|
||||||
|
return <iframe
|
||||||
|
src={`https://player.twitch.tv/${args}`}
|
||||||
|
// @ts-ignore no property
|
||||||
|
className="w-max"
|
||||||
|
allowFullScreen
|
||||||
|
></iframe>;
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderMixCloud = (item: NoteContent) => {
|
||||||
|
return <For each={item.tokens}>
|
||||||
|
{(token) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
|
setWordsDisplayed(w => w + shortMentionInWords);
|
||||||
|
|
||||||
|
const feedPath = (isMixCloud(token) && RegExp.$1) + "%2F" + (isMixCloud(token) && RegExp.$2);
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<iframe
|
||||||
|
title="SoundCloud player"
|
||||||
|
width="100%"
|
||||||
|
height="120"
|
||||||
|
// @ts-ignore no property
|
||||||
|
frameBorder="0"
|
||||||
|
src={`https://www.mixcloud.com/widget/iframe/?hide_cover=1&feed=%2F${feedPath}%2F`}
|
||||||
|
></iframe>
|
||||||
|
</div>;
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderSoundCloud = (item: NoteContent) => {
|
||||||
|
return <For each={item.tokens}>
|
||||||
|
{(token) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
|
setWordsDisplayed(w => w + shortMentionInWords);
|
||||||
|
|
||||||
|
return <iframe
|
||||||
|
width="100%"
|
||||||
|
height="166"
|
||||||
|
// @ts-ignore no property
|
||||||
|
scrolling="no"
|
||||||
|
allow="autoplay"
|
||||||
|
src={`https://w.soundcloud.com/player/?url=${token}`}
|
||||||
|
></iframe>;
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderAppleMusic = (item: NoteContent) => {
|
||||||
|
return <For each={item.tokens}>
|
||||||
|
{(token) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
|
setWordsDisplayed(w => w + shortMentionInWords);
|
||||||
|
|
||||||
|
const convertedUrl = token.replace("music.apple.com", "embed.music.apple.com");
|
||||||
|
const isSongLink = /\?i=\d+$/.test(convertedUrl);
|
||||||
|
|
||||||
|
return <iframe
|
||||||
|
allow="autoplay *; encrypted-media *; fullscreen *; clipboard-write"
|
||||||
|
// @ts-ignore no property
|
||||||
|
frameBorder="0"
|
||||||
|
height={`${isSongLink ? 175 : 450}`}
|
||||||
|
style="width: 100%; maxWidth: 660; overflow: hidden; background: transparent;"
|
||||||
|
sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-storage-access-by-user-activation allow-top-navigation-by-user-activation"
|
||||||
|
src={convertedUrl}
|
||||||
|
></iframe>;
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderWavelake = (item: NoteContent) => {
|
||||||
|
return <For each={item.tokens}>
|
||||||
|
{(token) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
|
setWordsDisplayed(w => w + shortMentionInWords);
|
||||||
|
|
||||||
|
const convertedUrl = token.replace(/(?:player\.|www\.)?wavlake\.com/, "embed.wavlake.com");
|
||||||
|
|
||||||
|
return <iframe
|
||||||
|
style="borderRadius: 12"
|
||||||
|
src={convertedUrl}
|
||||||
|
width="100%"
|
||||||
|
height="380"
|
||||||
|
// @ts-ignore no property
|
||||||
|
frameBorder="0"
|
||||||
|
loading="lazy"
|
||||||
|
></iframe>;
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderLinks = (item: NoteContent) => {
|
||||||
|
return <For each={item.tokens}>
|
||||||
|
{(token) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
const preview = getLinkPreview(token);
|
const preview = getLinkPreview(token);
|
||||||
|
|
||||||
@ -478,21 +643,25 @@ const ParsedNote: Component<{
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (hasMinimalPreviewData) {
|
if (hasMinimalPreviewData) {
|
||||||
wordsDisplayed += shortMentionInWords;
|
setWordsDisplayed(w => w + shortMentionInWords);
|
||||||
return <LinkPreview preview={preview} bordered={props.isEmbeded} />;
|
return <LinkPreview preview={preview} bordered={props.isEmbeded} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setWordsDisplayed(w => w + 1);
|
||||||
return <span data-url={token}><a link href={token.toLowerCase()} target="_blank" >{token}</a></span>;
|
return <span data-url={token}><a link href={token.toLowerCase()} target="_blank" >{token}</a></span>;
|
||||||
}
|
}}
|
||||||
|
</For>
|
||||||
|
};
|
||||||
|
|
||||||
consecutiveImages = 0;
|
const renderNoteMention = (item: NoteContent) => {
|
||||||
imageGroup = generatePrivateKey();
|
return <For each={item.tokens}>
|
||||||
|
{(token) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
if (isNoteMention(token)) {
|
|
||||||
let [_, id] = token.split(':');
|
let [_, id] = token.split(':');
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return token;
|
return <>{token}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let end = '';
|
let end = '';
|
||||||
@ -524,7 +693,7 @@ const ParsedNote: Component<{
|
|||||||
link = <A href={path}>{token}</A>;
|
link = <A href={path}>{token}</A>;
|
||||||
|
|
||||||
if (ment) {
|
if (ment) {
|
||||||
wordsDisplayed += shortMentionInWords;
|
setWordsDisplayed(w => w + shortMentionInWords);
|
||||||
|
|
||||||
link = <div>
|
link = <div>
|
||||||
<EmbeddedNote
|
<EmbeddedNote
|
||||||
@ -536,17 +705,25 @@ const ParsedNote: Component<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
setWordsDisplayed(w => w + 1);
|
||||||
link = <span class={styles.error}>{token}</span>;
|
link = <span class={styles.error}>{token}</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <span class="whole"> {link}{end}</span>;
|
return link;}}
|
||||||
}
|
</For>
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderUserMention = (item: NoteContent) => {
|
||||||
|
return <For each={item.tokens}>
|
||||||
|
{(token) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
|
setWordsDisplayed(w => w + 1);
|
||||||
|
|
||||||
if (isUserMention(token)) {
|
|
||||||
let [_, id] = token.split(':');
|
let [_, id] = token.split(':');
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return token;
|
return <>{token}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let end = '';
|
let end = '';
|
||||||
@ -582,17 +759,23 @@ const ParsedNote: Component<{
|
|||||||
<><A href={path}>@{label}</A>{end}</> :
|
<><A href={path}>@{label}</A>{end}</> :
|
||||||
<>{MentionedUserLink({ user })}{end}</>;
|
<>{MentionedUserLink({ user })}{end}</>;
|
||||||
}
|
}
|
||||||
|
return link;
|
||||||
return <span class="whole"> {link}</span>;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return <span class={styles.error}> {token}</span>;
|
return <span class={styles.error}> {token}</span>;
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
|
</For>
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderTagMention = (item: NoteContent) => {
|
||||||
|
return <For each={item.tokens}>
|
||||||
|
{(token) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
|
setWordsDisplayed(w => w + 1);
|
||||||
|
|
||||||
if (isTagMention(token)) {
|
|
||||||
let t = `${token}`;
|
let t = `${token}`;
|
||||||
|
|
||||||
|
|
||||||
let end = t[t.length - 1];
|
let end = t[t.length - 1];
|
||||||
|
|
||||||
if ([',', '?', ';', '!'].some(x => end === x)) {
|
if ([',', '?', ';', '!'].some(x => end === x)) {
|
||||||
@ -628,7 +811,7 @@ const ParsedNote: Component<{
|
|||||||
embeded = <><A href={path}>{noteId}</A>{end}</>;
|
embeded = <><A href={path}>{noteId}</A>{end}</>;
|
||||||
|
|
||||||
if (ment) {
|
if (ment) {
|
||||||
wordsDisplayed += shortMentionInWords;
|
setWordsDisplayed(w => w + shortMentionInWords - 1);
|
||||||
|
|
||||||
embeded = <div>
|
embeded = <div>
|
||||||
<EmbeddedNote
|
<EmbeddedNote
|
||||||
@ -661,12 +844,19 @@ const ParsedNote: Component<{
|
|||||||
<><A href={path}>@{label}</A>{end}</> :
|
<><A href={path}>@{label}</A>{end}</> :
|
||||||
<>{MentionedUserLink({ user })}{end}</>;
|
<>{MentionedUserLink({ user })}{end}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <span> {link}</span>;
|
return <span> {link}</span>;
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
|
</For>
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderHashtag = (item: NoteContent) => {
|
||||||
|
return <For each={item.tokens}>
|
||||||
|
{(token) => {
|
||||||
|
if (isNoteTooLong()) return;
|
||||||
|
|
||||||
|
setWordsDisplayed(w => w + 1);
|
||||||
|
|
||||||
if (isHashtag(token)) {
|
|
||||||
let [_, term] = token.split('#');
|
let [_, term] = token.split('#');
|
||||||
let end = '';
|
let end = '';
|
||||||
|
|
||||||
@ -683,25 +873,46 @@ const ParsedNote: Component<{
|
|||||||
<A href={`/search/%23${term}`}>#{term}</A>;
|
<A href={`/search/%23${term}`}>#{term}</A>;
|
||||||
|
|
||||||
return <span class="whole"> {embeded}{end}</span>;
|
return <span class="whole"> {embeded}{end}</span>;
|
||||||
}
|
}}
|
||||||
|
</For>
|
||||||
|
};
|
||||||
|
|
||||||
return <span class="whole">{convertHTMLEntity(token)}</span>;
|
const renderContent = (item: NoteContent) => {
|
||||||
|
|
||||||
|
const renderers: Record<string, (item: NoteContent) => JSXElement> = {
|
||||||
|
linebreak: renderLinebreak,
|
||||||
|
text: renderText,
|
||||||
|
image: renderImage,
|
||||||
|
video: renderVideo,
|
||||||
|
youtube: renderYouTube,
|
||||||
|
spotify: renderSpotify,
|
||||||
|
twitch: renderTwitch,
|
||||||
|
mixcloud: renderMixCloud,
|
||||||
|
soundcloud: renderSoundCloud,
|
||||||
|
applemusic: renderAppleMusic,
|
||||||
|
wavelake: renderWavelake,
|
||||||
|
link: renderLinks,
|
||||||
|
notemention: renderNoteMention,
|
||||||
|
usermention: renderUserMention,
|
||||||
|
tagmention: renderTagMention,
|
||||||
|
hashtag: renderHashtag,
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderers[item.type] ?
|
||||||
|
renderers[item.type](item) :
|
||||||
|
<></>;
|
||||||
};
|
};
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
parseContent();
|
generateContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={thisNote} id={id()} class={styles.parsedNote} >
|
<div ref={thisNote} id={id()} class={styles.parsedNote} >
|
||||||
<For each={tokens}>
|
<For each={content}>
|
||||||
{(token) =>
|
{(item) => renderContent(item)}
|
||||||
<Show when={shouldShowToken()}>
|
|
||||||
<>{parseToken(token)}</>
|
|
||||||
</Show>
|
|
||||||
}
|
|
||||||
</For>
|
</For>
|
||||||
<Show when={props.shorten && tokens.length > shortNoteWords}>
|
<Show when={isNoteTooLong()}>
|
||||||
<span class={styles.more}>
|
<span class={styles.more}>
|
||||||
... <span class="linkish">{intl.formatMessage(actions.seeMore)}</span>
|
... <span class="linkish">{intl.formatMessage(actions.seeMore)}</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
import { Relay } from "nostr-tools";
|
import { Relay } from "nostr-tools";
|
||||||
import { createStore } from "solid-js/store";
|
import { createStore } from "solid-js/store";
|
||||||
import LinkPreview from "../components/LinkPreview/LinkPreview";
|
import LinkPreview from "../components/LinkPreview/LinkPreview";
|
||||||
import NoteImage from "../components/NoteImage/NoteImage";
|
|
||||||
import { appleMusicRegex, hashtagRegex, interpunctionRegex, Kind, linebreakRegex, mixCloudRegex, nostrNestsRegex, noteRegex, profileRegex, soundCloudRegex, spotifyRegex, tagMentionRegex, twitchRegex, urlRegex, urlRegexG, wavlakeRegex, youtubeRegex } from "../constants";
|
import { appleMusicRegex, hashtagRegex, interpunctionRegex, Kind, linebreakRegex, mixCloudRegex, nostrNestsRegex, noteRegex, profileRegex, soundCloudRegex, spotifyRegex, tagMentionRegex, twitchRegex, urlRegex, urlRegexG, wavlakeRegex, youtubeRegex } from "../constants";
|
||||||
import { sendMessage, subscribeTo } from "../sockets";
|
import { sendMessage, subscribeTo } from "../sockets";
|
||||||
import { MediaSize, NostrRelays, NostrRelaySignedEvent, PrimalNote, SendNoteResult } from "../types/primal";
|
import { MediaSize, NostrRelays, NostrRelaySignedEvent, PrimalNote, SendNoteResult } from "../types/primal";
|
||||||
|
@ -125,7 +125,7 @@ const Thread: Component = () => {
|
|||||||
scrollWindowTo(rect.top - header - banner);
|
scrollWindowTo(rect.top - header - banner);
|
||||||
|
|
||||||
// repliesHolder.setAttribute('style', `height: ${document.documentElement.scrollHeight}px;`)
|
// repliesHolder.setAttribute('style', `height: ${document.documentElement.scrollHeight}px;`)
|
||||||
}, 0)
|
}, 1000)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user