diff --git a/.changeset/cuddly-walls-fix.md b/.changeset/cuddly-walls-fix.md
new file mode 100644
index 000000000..141e07b0f
--- /dev/null
+++ b/.changeset/cuddly-walls-fix.md
@@ -0,0 +1,5 @@
+---
+"nostrudel": minor
+---
+
+Add nostrapp.link option in profile and note menus
diff --git a/.changeset/quick-kiwis-peel.md b/.changeset/quick-kiwis-peel.md
new file mode 100644
index 000000000..39bc23aca
--- /dev/null
+++ b/.changeset/quick-kiwis-peel.md
@@ -0,0 +1,5 @@
+---
+"nostrudel": minor
+---
+
+add embeds for wavlake tracks
diff --git a/src/components/embed-types/app-music.tsx b/src/components/embed-types/app-music.tsx
deleted file mode 100644
index 4ef9e5716..000000000
--- a/src/components/embed-types/app-music.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { EmbedableContent, embedJSX } from "../../helpers/embeds";
-
-// note1tvqk2mu829yr6asf7w5dgpp8t0mlp2ax5t26ctfdx8m0ptkssamqsleeux
-// note1ygx9tec3af92704d92jwrj3zs7cws2jl29yvrlxzqlcdlykhwssqpupa7t
-export function embedAppleMusic(content: EmbedableContent) {
- return embedJSX(content, {
- regexp: /https?:\/\/music\.apple\.com(?:\/[\+~%\/\.\w\-_]*)?(\??(?:[\?#\-\+=&;%@\.\w_]*)#?(?:[\-\.\!\/\\\w]*))?/,
- render: (match) => (
-
- ),
- name: "Apple Music",
- });
-}
diff --git a/src/components/embed-types/index.ts b/src/components/embed-types/index.ts
index 2999304af..953877606 100644
--- a/src/components/embed-types/index.ts
+++ b/src/components/embed-types/index.ts
@@ -1,9 +1,6 @@
export * from "./twitter";
export * from "./lightning";
-export * from "./app-music";
+export * from "./music";
export * from "./common";
-export * from "./spotify";
-export * from "./tidal";
export * from "./youtube";
-export * from "./spotify";
export * from "./nostr";
diff --git a/src/components/embed-types/music.tsx b/src/components/embed-types/music.tsx
new file mode 100644
index 000000000..8f4266705
--- /dev/null
+++ b/src/components/embed-types/music.tsx
@@ -0,0 +1,75 @@
+import { EmbedableContent, embedJSX } from "../../helpers/embeds";
+
+export function embedWavlakeTrack(content: EmbedableContent) {
+ return embedJSX(content, {
+ name: "Wavlake Track",
+ regexp: /https?:\/\/wavlake\.com\/track\/[\w-]+/i,
+ render: (match) => (
+
+ ),
+ });
+}
+
+// note1tvqk2mu829yr6asf7w5dgpp8t0mlp2ax5t26ctfdx8m0ptkssamqsleeux
+// note1ygx9tec3af92704d92jwrj3zs7cws2jl29yvrlxzqlcdlykhwssqpupa7t
+export function embedAppleMusic(content: EmbedableContent) {
+ return embedJSX(content, {
+ regexp: /https?:\/\/music\.apple\.com(?:\/[\+~%\/\.\w\-_]*)?(\??(?:[\?#\-\+=&;%@\.\w_]*)#?(?:[\-\.\!\/\\\w]*))?/,
+ render: (match) => (
+
+ ),
+ name: "Apple Music",
+ });
+}
+
+// nostr:nevent1qqs9r94qeqhqayvuz6q6u88spvuz0d25nhpyv0c39wympmfu646x4pgpz3mhxue69uhhyetvv9ujuerpd46hxtnfduq3samnwvaz7tmjv4kxz7fwwdhx7un59eek7cmfv9kqmhxhvq
+export function embedSpotifyMusic(content: EmbedableContent) {
+ return embedJSX(content, {
+ regexp:
+ /https?:\/\/open\.spotify\.com\/(track|episode|album|playlist)\/(\w+)(\??(?:[\?#\-\+=&;%@\.\w_]*)#?(?:[\-\.\!\/\\\w]*))?/im,
+ render: (match) => {
+ const isList = match[1] === "album" || match[1] === "playlist";
+ return (
+
+ );
+ },
+ name: "Spotify",
+ });
+}
+
+// note132m5xc3zhj7fap67vzwx5x3s8xqgz49k669htcn8kppr4m654tuq960tuu
+export function embedTidalMusic(content: EmbedableContent) {
+ return embedJSX(content, {
+ regexp: /https?:\/\/tidal\.com(\/browse)?\/(track|album)\/(\d+)/im,
+ render: (match) => (
+
+ ),
+ name: "Tidal",
+ });
+}
diff --git a/src/components/embed-types/spotify.tsx b/src/components/embed-types/spotify.tsx
deleted file mode 100644
index 1e8c3c06e..000000000
--- a/src/components/embed-types/spotify.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { EmbedableContent, embedJSX } from "../../helpers/embeds";
-
-// nostr:nevent1qqs9r94qeqhqayvuz6q6u88spvuz0d25nhpyv0c39wympmfu646x4pgpz3mhxue69uhhyetvv9ujuerpd46hxtnfduq3samnwvaz7tmjv4kxz7fwwdhx7un59eek7cmfv9kqmhxhvq
-export function embedSpotifyMusic(content: EmbedableContent) {
- return embedJSX(content, {
- regexp:
- /https?:\/\/open\.spotify\.com\/(track|episode|album|playlist)\/(\w+)(\??(?:[\?#\-\+=&;%@\.\w_]*)#?(?:[\-\.\!\/\\\w]*))?/im,
- render: (match) => {
- const isList = match[1] === "album" || match[1] === "playlist";
- return (
-
- );
- },
- name: "Spotify",
- });
-}
diff --git a/src/components/embed-types/tidal.tsx b/src/components/embed-types/tidal.tsx
deleted file mode 100644
index 68a7185ba..000000000
--- a/src/components/embed-types/tidal.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { EmbedableContent, embedJSX } from "../../helpers/embeds";
-
-// note132m5xc3zhj7fap67vzwx5x3s8xqgz49k669htcn8kppr4m654tuq960tuu
-export function embedTidalMusic(content: EmbedableContent) {
- return embedJSX(content, {
- regexp: /https?:\/\/tidal\.com(\/browse)?\/(track|album)\/(\d+)/im,
- render: (match) => (
-
- ),
- name: "Tidal",
- });
-}
diff --git a/src/components/note/note-contents.tsx b/src/components/note/note-contents.tsx
index fd8f8e117..397218926 100644
--- a/src/components/note/note-contents.tsx
+++ b/src/components/note/note-contents.tsx
@@ -19,6 +19,7 @@ import {
embedNostrMentions,
embedAppleMusic,
embedNostrHashtags,
+ embedWavlakeTrack,
} from "../embed-types";
import { ImageGalleryProvider } from "../image-gallery";
import { useTrusted } from "./trust";
@@ -31,6 +32,7 @@ function buildContents(event: NostrEvent | DraftNostrEvent, trusted = false) {
content = embedYoutubeVideo(content);
content = embedYoutubePlaylist(content);
content = embedYoutubeMusic(content);
+ content = embedWavlakeTrack(content);
content = embedTidalMusic(content);
content = embedAppleMusic(content);
content = embedSpotifyMusic(content);
diff --git a/src/components/note/note-menu.tsx b/src/components/note/note-menu.tsx
index 53d168e74..4c1f4c46b 100644
--- a/src/components/note/note-menu.tsx
+++ b/src/components/note/note-menu.tsx
@@ -18,7 +18,7 @@ import { Bech32Prefix, getSharableNoteId, normalizeToBech32 } from "../../helper
import { NostrEvent } from "../../types/nostr-event";
import { MenuIconButton, MenuIconButtonProps } from "../menu-icon-button";
-import { ClipboardIcon, CodeIcon, LikeIcon, RepostIcon, TrashIcon } from "../icons";
+import { ClipboardIcon, CodeIcon, ExternalLinkIcon, LikeIcon, RepostIcon, TrashIcon } from "../icons";
import NoteReactionsModal from "./note-zaps-modal";
import NoteDebugModal from "../debug-modals/note-debug-modal";
import { useCurrentAccount } from "../../hooks/use-current-account";
@@ -68,6 +68,12 @@ export const NoteMenu = ({ event, ...props }: { event: NostrEvent } & Omit}>
Zaps/Reactions
+
diff --git a/src/views/user/components/user-profile-menu.tsx b/src/views/user/components/user-profile-menu.tsx
index ddc4c6b13..aec7dcef1 100644
--- a/src/views/user/components/user-profile-menu.tsx
+++ b/src/views/user/components/user-profile-menu.tsx
@@ -1,7 +1,7 @@
import { MenuItem, useDisclosure } from "@chakra-ui/react";
import { MenuIconButton, MenuIconButtonProps } from "../../../components/menu-icon-button";
-import { ClipboardIcon, CodeIcon, RelayIcon, SpyIcon } from "../../../components/icons";
+import { ClipboardIcon, CodeIcon, ExternalLinkIcon, RelayIcon, SpyIcon } from "../../../components/icons";
import accountService from "../../../services/account";
import { useUserMetadata } from "../../../hooks/use-user-metadata";
import { getUserDisplayName } from "../../../helpers/user-metadata";
@@ -41,6 +41,12 @@ export const UserProfileMenu = ({
} onClick={() => loginAsUser()}>
Login as {getUserDisplayName(metadata, pubkey)}
+