mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2025-12-19 07:14:22 +01:00
feat: nostr activity and tag support
This commit is contained in:
@@ -56,6 +56,7 @@ export function ContactViewer(props: {
|
||||
showToast(result.error);
|
||||
return;
|
||||
} else {
|
||||
result.value.privateTag = props.contact.name;
|
||||
if (
|
||||
result.value?.address ||
|
||||
result.value?.invoice ||
|
||||
|
||||
@@ -6,9 +6,11 @@ import {
|
||||
Show,
|
||||
Switch
|
||||
} from "solid-js";
|
||||
import { Dynamic } from "solid-js/web";
|
||||
|
||||
import rightArrow from "~/assets/icons/right-arrow.svg";
|
||||
import { AmountSats, VStack } from "~/components";
|
||||
import { AmountSats, TinyText, VStack } from "~/components";
|
||||
import { useI18n } from "~/i18n/context";
|
||||
import { useMegaStore } from "~/state/megaStore";
|
||||
import { fetchZaps, getHexpubFromNpub } from "~/utils";
|
||||
import { timeAgo } from "~/utils/prettyPrintTime";
|
||||
@@ -31,6 +33,7 @@ function formatProfileLink(hexpub: string): string {
|
||||
}
|
||||
|
||||
export function NostrActivity() {
|
||||
const i18n = useI18n();
|
||||
const [state, _actions] = useMegaStore();
|
||||
|
||||
const [data, { refetch }] = createResource(state.npub, fetchZaps);
|
||||
@@ -93,10 +96,10 @@ export function NostrActivity() {
|
||||
</a>
|
||||
</Match>
|
||||
<Match when={zap.kind === "private"}>
|
||||
Private
|
||||
{i18n.t("activity.private")}
|
||||
</Match>
|
||||
<Match when={zap.kind === "anonymous"}>
|
||||
Anonymous
|
||||
{i18n.t("activity.anonymous")}
|
||||
</Match>
|
||||
</Switch>
|
||||
</span>
|
||||
@@ -151,10 +154,36 @@ export function NostrActivity() {
|
||||
</div>
|
||||
<Show when={zap.content}>
|
||||
<hr class="my-2 border-m-grey-750" />
|
||||
<p
|
||||
class="truncate text-center text-sm font-light text-neutral-200"
|
||||
textContent={zap.content}
|
||||
/>
|
||||
<TinyText>
|
||||
<Dynamic
|
||||
component={
|
||||
zap.content?.includes("From:") ||
|
||||
zap.content?.includes("://")
|
||||
? "a"
|
||||
: "p"
|
||||
}
|
||||
href={
|
||||
zap.content?.split("nostr:")[1]
|
||||
? formatProfileLink(
|
||||
getHexpubFromNpub(
|
||||
zap.content?.split(
|
||||
"nostr:"
|
||||
)[1]
|
||||
) ?? ""
|
||||
)
|
||||
: zap.content
|
||||
}
|
||||
class="block truncate text-center text-sm font-light text-neutral-200"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{zap.content?.includes("From:")
|
||||
? `${i18n.t(
|
||||
"activity.from"
|
||||
)} ${zap.content?.split("nostr:")[1]}`
|
||||
: zap.content}
|
||||
</Dynamic>
|
||||
</TinyText>
|
||||
</Show>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -16,6 +16,7 @@ export function TagEditor(props: {
|
||||
selectedValues: Partial<MutinyTagItem>[];
|
||||
setSelectedValues: (value: Partial<MutinyTagItem>[]) => void;
|
||||
placeholder: string;
|
||||
autoFillTag?: string | undefined;
|
||||
}) {
|
||||
const [_state, actions] = useMegaStore();
|
||||
const [availableTags, setAvailableTags] = createSignal<MutinyTagItem[]>([]);
|
||||
@@ -28,12 +29,24 @@ export function TagEditor(props: {
|
||||
.filter((tag) => tag.kind === "Contact")
|
||||
.sort(sortByLastUsed)
|
||||
);
|
||||
if (props.autoFillTag && availableTags()) {
|
||||
const tagToAutoSelect = availableTags().find(
|
||||
(tag) => tag.name === props.autoFillTag
|
||||
);
|
||||
if (tagToAutoSelect) {
|
||||
props.setSelectedValues([
|
||||
...props.selectedValues,
|
||||
tagToAutoSelect
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const selectProps = createMemo(() => {
|
||||
return createOptions(availableTags() || [], {
|
||||
key: "name",
|
||||
disable: (value) => props.selectedValues.includes(value),
|
||||
filterable: true, // Default
|
||||
createable: createLabelValue
|
||||
});
|
||||
@@ -42,8 +55,6 @@ export function TagEditor(props: {
|
||||
const onChange = (selected: MutinyTagItem[]) => {
|
||||
props.setSelectedValues(selected);
|
||||
|
||||
console.log(selected);
|
||||
|
||||
const lastValue = selected[selected.length - 1];
|
||||
if (
|
||||
lastValue &&
|
||||
@@ -54,7 +65,6 @@ export function TagEditor(props: {
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: eslint is mad about reactivity
|
||||
const onTagTap = (tag: MutinyTagItem) => {
|
||||
props.setSelectedValues([...props.selectedValues!, tag]);
|
||||
};
|
||||
@@ -70,10 +80,16 @@ export function TagEditor(props: {
|
||||
/>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<Show when={availableTags() && availableTags()!.length > 0}>
|
||||
<For each={availableTags()!.slice(0, 3)}>
|
||||
<For
|
||||
each={availableTags()!.slice(0, 3).sort(sortByLastUsed)}
|
||||
>
|
||||
{(tag) => (
|
||||
// eslint-disable-next-line solid/reactivity
|
||||
<TinyButton tag={tag} onClick={() => onTagTap(tag)}>
|
||||
<TinyButton
|
||||
hidden={props.selectedValues.includes(tag)}
|
||||
tag={tag}
|
||||
// eslint-disable-next-line solid/reactivity
|
||||
onClick={() => onTagTap(tag)}
|
||||
>
|
||||
{tag.name}
|
||||
</TinyButton>
|
||||
)}
|
||||
|
||||
@@ -258,6 +258,7 @@ export const TinyText: ParentComponent = (props) => {
|
||||
export const TinyButton: ParentComponent<{
|
||||
onClick: () => void;
|
||||
tag?: MutinyTagItem;
|
||||
hidden?: boolean;
|
||||
}> = (props) => {
|
||||
// TODO: don't need to run this if it's not a contact
|
||||
const [gradient] = createResource(async () => {
|
||||
@@ -272,6 +273,7 @@ export const TinyButton: ParentComponent<{
|
||||
return (
|
||||
<button
|
||||
class="rounded-lg bg-white/10 px-2 py-1"
|
||||
classList={{ hidden: props.hidden }}
|
||||
onClick={() => props.onClick()}
|
||||
style={{ background: bg() }}
|
||||
>
|
||||
|
||||
@@ -151,7 +151,10 @@ export default {
|
||||
unknown: "Unknown",
|
||||
import_contacts:
|
||||
"Import your contacts from nostr to see who they're zapping.",
|
||||
coming_soon: "Coming soon"
|
||||
coming_soon: "Coming soon",
|
||||
private: "Private",
|
||||
anonymous: "Anonymous",
|
||||
from: "From:"
|
||||
},
|
||||
redshift: {
|
||||
title: "Redshift",
|
||||
|
||||
@@ -11,6 +11,7 @@ export type ParsedParams = {
|
||||
amount_sats?: bigint;
|
||||
network?: string;
|
||||
memo?: string;
|
||||
privateTag?: string;
|
||||
node_pubkey?: string;
|
||||
lnurl?: string;
|
||||
};
|
||||
|
||||
@@ -685,6 +685,9 @@ export default function Send() {
|
||||
{i18n.t("common.private_tags")}
|
||||
</SmallHeader>
|
||||
<TagEditor
|
||||
autoFillTag={
|
||||
destination()?.privateTag
|
||||
}
|
||||
selectedValues={selectedContacts()}
|
||||
setSelectedValues={
|
||||
setSelectedContacts
|
||||
|
||||
Reference in New Issue
Block a user