mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-17 05:54:19 +01:00
feat: add audio player
This commit is contained in:
42
src/components/event/textNote/AudioDisplay.tsx
Normal file
42
src/components/event/textNote/AudioDisplay.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { Component, createSignal, Show } from 'solid-js';
|
||||||
|
|
||||||
|
import LazyLoad from '@/components/utils/LazyLoad';
|
||||||
|
import SafeLink from '@/components/utils/SafeLink';
|
||||||
|
import { useTranslation } from '@/i18n/useTranslation';
|
||||||
|
|
||||||
|
type AudioDisplayProps = {
|
||||||
|
url: string;
|
||||||
|
initialHidden: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AudioDisplay: Component<AudioDisplayProps> = (props) => {
|
||||||
|
const i18n = useTranslation();
|
||||||
|
const [hidden, setHidden] = createSignal(props.initialHidden);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Show
|
||||||
|
when={!hidden()}
|
||||||
|
fallback={
|
||||||
|
<button
|
||||||
|
class="rounded bg-bg-tertiary p-3 text-xs text-fg-secondary hover:shadow"
|
||||||
|
onClick={() => setHidden(false)}
|
||||||
|
>
|
||||||
|
{i18n.t('post.showAudio')}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<LazyLoad fallback={<div class="max-w-full" />}>
|
||||||
|
{() => (
|
||||||
|
<SafeLink class="my-2 block" href={props.url}>
|
||||||
|
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
|
||||||
|
<audio src={props.url} controls>
|
||||||
|
<a href={props.url}>{i18n.t('post.download')}</a>
|
||||||
|
</audio>
|
||||||
|
</SafeLink>
|
||||||
|
)}
|
||||||
|
</LazyLoad>
|
||||||
|
</Show>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AudioDisplay;
|
||||||
@@ -5,6 +5,7 @@ import * as Kind from 'nostr-tools/kinds';
|
|||||||
// eslint-disable-next-line import/no-cycle
|
// eslint-disable-next-line import/no-cycle
|
||||||
import EventDisplayById from '@/components/event/EventDisplayById';
|
import EventDisplayById from '@/components/event/EventDisplayById';
|
||||||
// import ParameterizedReplaceableEventDisplayById from '@/components/event/ParameterizedReplaceableEventDisplayById';
|
// import ParameterizedReplaceableEventDisplayById from '@/components/event/ParameterizedReplaceableEventDisplayById';
|
||||||
|
import AudioDisplay from '@/components/event/textNote/AudioDisplay';
|
||||||
import ImageDisplay from '@/components/event/textNote/ImageDisplay';
|
import ImageDisplay from '@/components/event/textNote/ImageDisplay';
|
||||||
import MentionedEventDisplay from '@/components/event/textNote/MentionedEventDisplay';
|
import MentionedEventDisplay from '@/components/event/textNote/MentionedEventDisplay';
|
||||||
import MentionedUserDisplay from '@/components/event/textNote/MentionedUserDisplay';
|
import MentionedUserDisplay from '@/components/event/textNote/MentionedUserDisplay';
|
||||||
@@ -15,7 +16,7 @@ import { createRelaysColumn, createSearchColumn } from '@/core/column';
|
|||||||
import useConfig from '@/core/useConfig';
|
import useConfig from '@/core/useConfig';
|
||||||
import { useRequestCommand } from '@/hooks/useCommandBus';
|
import { useRequestCommand } from '@/hooks/useCommandBus';
|
||||||
import { ParsedTextNoteResolvedNode, type ParsedTextNoteResolved } from '@/nostr/parseTextNote';
|
import { ParsedTextNoteResolvedNode, type ParsedTextNoteResolved } from '@/nostr/parseTextNote';
|
||||||
import { isImageUrl, isVideoUrl, isWebSocketUrl } from '@/utils/url';
|
import { isImageUrl, isVideoUrl, isAudioUrl, isWebSocketUrl } from '@/utils/url';
|
||||||
|
|
||||||
export type TextNoteContentDisplayProps = {
|
export type TextNoteContentDisplayProps = {
|
||||||
parsed: ParsedTextNoteResolved;
|
parsed: ParsedTextNoteResolved;
|
||||||
@@ -54,6 +55,9 @@ const TextNoteContentDisplay = (props: TextNoteContentDisplayProps) => {
|
|||||||
if (isVideoUrl(item.content)) {
|
if (isVideoUrl(item.content)) {
|
||||||
return <VideoDisplay url={item.content} initialHidden={initialHidden()} />;
|
return <VideoDisplay url={item.content} initialHidden={initialHidden()} />;
|
||||||
}
|
}
|
||||||
|
if (isAudioUrl(item.content)) {
|
||||||
|
return <AudioDisplay url={item.content} initialHidden={initialHidden()} />;
|
||||||
|
}
|
||||||
if (isWebSocketUrl(item.content)) {
|
if (isWebSocketUrl(item.content)) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ export default {
|
|||||||
failedToDelete: 'Failed to delete',
|
failedToDelete: 'Failed to delete',
|
||||||
showImage: 'Show image',
|
showImage: 'Show image',
|
||||||
showVideo: 'Show video',
|
showVideo: 'Show video',
|
||||||
|
showAudio: 'Show audio player',
|
||||||
showPreview: 'Show preview',
|
showPreview: 'Show preview',
|
||||||
showOverflow: 'Read more',
|
showOverflow: 'Read more',
|
||||||
hideOverflow: 'Hide',
|
hideOverflow: 'Hide',
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ export default {
|
|||||||
failedToDelete: 'すべてのリレーで削除に失敗しました',
|
failedToDelete: 'すべてのリレーで削除に失敗しました',
|
||||||
showImage: '画像を表示する',
|
showImage: '画像を表示する',
|
||||||
showVideo: '動画を表示する',
|
showVideo: '動画を表示する',
|
||||||
|
showAudio: '音声プレイヤーを表示する',
|
||||||
showPreview: 'プレビューを表示する',
|
showPreview: 'プレビューを表示する',
|
||||||
showOverflow: '続きを読む',
|
showOverflow: '続きを読む',
|
||||||
hideOverflow: '隠す',
|
hideOverflow: '隠す',
|
||||||
|
|||||||
@@ -10,7 +10,16 @@ export const isImageUrl = (urlString: string): boolean => {
|
|||||||
export const isVideoUrl = (urlString: string): boolean => {
|
export const isVideoUrl = (urlString: string): boolean => {
|
||||||
try {
|
try {
|
||||||
const url = new URL(urlString);
|
const url = new URL(urlString);
|
||||||
return /\.(mpg|mpeg|mp4|avi|mov|webm|ogv)$/i.test(url.pathname);
|
return /\.(mpg|mpeg|mp4|avi|mov|mkv|webm|ogv)$/i.test(url.pathname);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isAudioUrl = (urlString: string): boolean => {
|
||||||
|
try {
|
||||||
|
const url = new URL(urlString);
|
||||||
|
return /\.(wav|mp3|flac|wma|m4a|aac|ogg|oga|opus)$/i.test(url.pathname);
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user