@threenine/nuxstr-comments 1.4.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -64,12 +64,19 @@ export default defineNuxtConfig({
64
64
  nuxstrComments: {
65
65
  relays: ['wss://relay.damus.io', 'wss://relay.nostr.band'],
66
66
  tagStrategy: 'path',
67
- tagPrefix: 'content:',
67
+ tagPrefix: 'comment:',
68
68
  },
69
69
  })
70
70
  ```
71
+ Select your preferred relays from the list of [relays](https://nostrwat.ch/) and configure the tag strategy and tag prefix.
72
+
73
+ When a user attempts to post, they will be prompted to log in with their Nostr browser extension [NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md).
74
+
75
+ Comments are published as kind: 1111 as defined in [NIP-22](https://github.com/nostr-protocol/nips/blob/master/22.md)
76
+ notes tagged with a `t` tag containing the content tag (e.g., `comment:/blog/my-post`).
77
+
78
+ Replys to comments are enable, and are also published as kind: 1111, as defined in [NIP-22](https://github.com/nostr-protocol/nips/blob/master/22.md)
71
79
 
72
- When a user attempts to post, they will be prompted to log in with their Nostr browser extension (NIP-07). Comments are published as kind:1 notes tagged with a `t` tag containing the content tag (e.g., `content:/blog/my-post`). Rendering of comment bodies uses @nuxt/content's ContentRendererMarkdown component so users can write markdown.
73
80
 
74
81
 
75
82
  ## Contribution
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@threenine/nuxstr-comments",
3
3
  "configKey": "nuxstrComments",
4
- "version": "1.4.0",
4
+ "version": "1.5.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -55,8 +55,8 @@ const module = defineNuxtModule({
55
55
  filePath: resolver.resolve("./runtime/components/ScaffoldComment.vue")
56
56
  });
57
57
  addComponent({
58
- name: "CommentCommandBar",
59
- filePath: resolver.resolve("./runtime/components/CommentCommandBar.vue")
58
+ name: "ReplyButton",
59
+ filePath: resolver.resolve("./runtime/components/ReplyButton.vue")
60
60
  });
61
61
  addComponent({
62
62
  name: "CommentView",
@@ -7,13 +7,13 @@ const props = defineProps({
7
7
  </script>
8
8
 
9
9
  <template>
10
- <div class="prose prose-sm prose-invert mt-2 mb-2">
10
+ <div class="mt-2 mb-2">
11
11
  <!-- eslint-disable-next-line vue/no-v-html -->
12
12
  <div
13
- class="mb-4"
13
+ class="mb-1"
14
14
  v-html="marked.parse(props.content)"
15
15
  />
16
16
  <!-- eslint-disable-next-line vue/no-v-html -->
17
- <comment-command-bar :content-id="props.id" />
17
+ <ReplyButton :content-id="props.id" />
18
18
  </div>
19
19
  </template>
@@ -1,7 +1,7 @@
1
1
  <script setup>
2
2
  import { onMounted } from "vue";
3
- import { useNuxstr } from "../composables/useNuxstr";
4
- import { useComments } from "../composables/useComments";
3
+ import useNuxstr from "../composables/useNuxstr";
4
+ import useComments from "../composables/useComments";
5
5
  const props = defineProps({
6
6
  contentId: { type: String, required: false }
7
7
  });
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import { ref } from "vue";
3
- import { useComments } from "../composables/useComments";
3
+ import useComments from "../composables/useComments";
4
4
  const props = defineProps({
5
5
  contentId: { type: String, required: false }
6
6
  });
@@ -1,5 +1,6 @@
1
1
  <script setup>
2
- import { useReplies } from "../composables/useReplies";
2
+ import useReplies from "../composables/useReplies";
3
+ import { ref } from "vue";
3
4
  const props = defineProps({
4
5
  rootId: { type: String, required: true }
5
6
  });
@@ -22,7 +23,7 @@ async function postReply(comment) {
22
23
  </script>
23
24
 
24
25
  <template>
25
- <div class="text-sm text-muted-foreground mt-16 p-6">
26
+ <div class="text-sm text-muted-foreground mt-4 p-6">
26
27
  <div class="flex gap-2">
27
28
  <div class="flex-1">
28
29
  <UTextarea
@@ -32,13 +33,13 @@ async function postReply(comment) {
32
33
  :rows="4"
33
34
  />
34
35
  </div>
35
- <div class="flex flex-col justify-center items-center p-2">
36
+ <div class="flex flex-col justify-center items-centerp-2">
36
37
  <UButton
37
38
  icon="mingcute:send-line"
38
39
  size="xl"
39
40
  color="primary"
40
41
  variant="solid"
41
- class="mb-4 mr-2"
42
+
42
43
  @click="postReply(content)"
43
44
  />
44
45
  </div>
@@ -1,7 +1,7 @@
1
1
  <script setup>
2
- import { useNuxstr } from "../composables/useNuxstr";
3
- import { useReplies } from "../composables/useReplies";
4
- import { onMounted, ref } from "vue";
2
+ import useNuxstr from "../composables/useNuxstr";
3
+ import useReplies from "../composables/useReplies";
4
+ import { computed, onMounted, ref } from "vue";
5
5
  const props = defineProps({
6
6
  contentId: { type: String, required: true }
7
7
  });
@@ -14,42 +14,44 @@ function toggleReply() {
14
14
  onMounted(() => {
15
15
  subscribeReplies();
16
16
  });
17
+ const showChip = computed(() => replies.value.length > 0);
17
18
  </script>
18
19
 
19
20
  <template>
20
21
  <div>
21
- <div class="flex items-center gap-4 mx-auto mt-4 mb-4">
22
+ <div class="flex items-center gap-4 mx-auto mt-2 mb-1">
22
23
  <u-chip
24
+ :show="showChip"
23
25
  :text="replies.length"
24
26
  size="3xl"
25
27
  inset
26
28
  >
27
29
  <u-button
28
- variant="ghost"
30
+ variant="subtle"
29
31
  icon="mdi:message-reply-text-outline"
30
32
  title="Reply"
31
- square
33
+ color="primary"
32
34
  class="rounded-full hover:bg-gray-900"
33
35
  @click="toggleReply"
34
36
  />
35
37
  </u-chip>
36
38
  </div>
37
- <UCollapsible
38
- class="flex flex-col gap-2 w-48 p-16"
39
+ <u-collapsible
40
+ class="flex flex-col gap-2 w-48 p-4"
39
41
  :open
40
42
  >
41
43
  <template #content>
42
- <div>
44
+ <div class="space-y-4">
43
45
  <reply-view :replies="replies" />
44
46
  </div>
45
47
 
46
48
  <div
47
49
  v-if="isLoggedIn"
48
- class="mt-4"
50
+ class="mt-2"
49
51
  >
50
52
  <post-reply :root-id="props.contentId" />
51
53
  </div>
52
54
  </template>
53
- </UCollapsible>
55
+ </u-collapsible>
54
56
  </div>
55
57
  </template>
@@ -9,7 +9,7 @@ const props = defineProps({
9
9
  <div
10
10
  v-for="reply in props.replies"
11
11
  :key="reply.id"
12
- class="rounded border border-gray-900 p-3 mt-2 mb-2"
12
+ class="rounded-md border p-3 mt-2 mb-2"
13
13
  >
14
14
  <div>
15
15
  <comment-author
@@ -1,23 +1,8 @@
1
- import type { Comment } from '~/src/runtime/types';
2
- export declare function useComments(customContentId?: string): {
3
- loading: import("vue").Ref<boolean, boolean>;
4
- error: import("vue").Ref<string | null, string | null>;
5
- comments: import("vue").Ref<{
6
- id: string;
7
- pubkey: string;
8
- created_at: number;
9
- content: string;
10
- profile?: {
11
- pubkey: string;
12
- display_name?: string | undefined;
13
- about?: string | undefined;
14
- image?: string | undefined;
15
- nip05?: string | undefined;
16
- lud06?: string | undefined;
17
- lud16?: string | undefined;
18
- website?: string | undefined;
19
- } | undefined;
20
- }[], Comment[] | {
1
+ import { type Ref, type UnwrapRef } from 'vue';
2
+ declare function useComments(customContentId?: string): {
3
+ loading: Ref<boolean, boolean>;
4
+ error: Ref<UnwrapRef<string | null>, UnwrapRef<string | null>>;
5
+ comments: import("vue").ComputedRef<{
21
6
  id: string;
22
7
  pubkey: string;
23
8
  created_at: number;
@@ -37,3 +22,4 @@ export declare function useComments(customContentId?: string): {
37
22
  subscribeComments: () => Promise<void>;
38
23
  postComment: (comment: string) => Promise<boolean>;
39
24
  };
25
+ export default useComments;
@@ -1,15 +1,18 @@
1
1
  import { computed, ref } from "vue";
2
- import { useRoute, useRuntimeConfig, useRequestURL } from "#imports";
3
- import { useNuxstr } from "./useNuxstr.js";
2
+ import { useRequestURL, useRoute, useRuntimeConfig } from "#imports";
3
+ import useNuxstr from "./useNuxstr.js";
4
4
  import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
5
- export function useComments(customContentId) {
5
+ function useComments(customContentId) {
6
6
  const { ndk, connect, isLoggedIn, mapComment, pubkey, fetchProfile } = useNuxstr();
7
7
  const route = useRoute();
8
8
  const config = useRuntimeConfig();
9
9
  const opts = config.public?.nuxstrComments || {};
10
10
  const loading = ref(false);
11
11
  const error = ref(null);
12
- const comments = ref([]);
12
+ const commentsData = ref([]);
13
+ const comments = computed(() => {
14
+ return commentsData.value.slice().sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
15
+ });
13
16
  const contentId = computed(() => {
14
17
  if (customContentId) return customContentId;
15
18
  return route.path;
@@ -27,12 +30,18 @@ export function useComments(customContentId) {
27
30
  }
28
31
  async function subscribeComments() {
29
32
  await connect();
30
- const filter = { kinds: [NDKKind.GenericReply], ["#t"]: [tagValue()], limit: 100, ["#k"]: ["web"], ["#A"]: [fullUrl(contentId.value)] };
31
- const sub = await ndk.subscribe(filter);
33
+ const filter = {
34
+ kinds: [NDKKind.GenericReply],
35
+ ["#t"]: [tagValue()],
36
+ limit: 100,
37
+ ["#k"]: ["web"],
38
+ ["#A"]: [fullUrl(contentId.value)]
39
+ };
40
+ const sub = ndk.subscribe(filter);
32
41
  sub.on("event", async (event) => {
33
42
  const comment = mapComment(event);
34
43
  comment.profile = await fetchProfile(event.pubkey);
35
- comments.value.push(comment);
44
+ commentsData.value.push(comment);
36
45
  });
37
46
  }
38
47
  async function postComment(comment) {
@@ -58,9 +67,10 @@ export function useComments(customContentId) {
58
67
  // Defined NIP 73
59
68
  ["K", "web"],
60
69
  // Defined NIP 73,
61
- ["p", pubkey.value]
70
+ ["p", pubkey ?? ""]
62
71
  ];
63
72
  return event;
64
73
  }
65
74
  return { loading, error, comments, isLoggedIn, subscribeComments, postComment };
66
75
  }
76
+ export default useComments;
@@ -1,13 +1,14 @@
1
- import NDK, { type NDKEvent } from '@nostr-dev-kit/ndk';
1
+ import NDK, { type NDKEvent, type NDKUserProfile } from '@nostr-dev-kit/ndk';
2
2
  import type { Comment, Profile } from '../types/index.js';
3
- export declare function useNuxstr(): {
3
+ declare function useNuxstr(): {
4
4
  readonly ndk: NDK;
5
5
  connect: () => Promise<NDK>;
6
6
  login: () => Promise<void>;
7
7
  logout: () => void;
8
8
  isLoggedIn: import("vue").ComputedRef<boolean>;
9
- pubkey: import("vue").Ref<string | null | undefined, string | null | undefined>;
10
- mapProfile: (profile: NDKUserProfile) => Profile;
9
+ pubkey: string | undefined;
10
+ mapProfile: (profile: NDKUserProfile | null) => Profile;
11
11
  mapComment: (event: NDKEvent) => Comment;
12
12
  fetchProfile: (pubkey: string) => Promise<Profile | undefined>;
13
13
  };
14
+ export default useNuxstr;
@@ -2,19 +2,21 @@ import { computed, ref } from "vue";
2
2
  import { useRuntimeConfig } from "#imports";
3
3
  import NDK, { NDKNip07Signer } from "@nostr-dev-kit/ndk";
4
4
  import { useToast } from "#ui/composables/useToast";
5
- export function useNuxstr() {
5
+ function useNuxstr() {
6
+ const DEFAULT_PUBKEY = "";
6
7
  const w = globalThis;
7
8
  if (!w.__nuxstr) {
8
9
  w.__nuxstr = {
9
10
  ndk: null,
10
11
  signer: null,
11
- pubkey: ref(null),
12
+ pubkey: ref(DEFAULT_PUBKEY),
12
13
  isConnecting: ref(false),
13
14
  isConnected: ref(false),
14
- userProfile: ref(null)
15
+ userProfile: ref(void 0)
15
16
  };
16
17
  }
17
18
  const state = w.__nuxstr;
19
+ const DEFAULT_TIMESTAMP = 0;
18
20
  const config = useRuntimeConfig();
19
21
  const opts = config.public?.nuxstrComments || {};
20
22
  function initializeNDK() {
@@ -52,13 +54,14 @@ export function useNuxstr() {
52
54
  async function login() {
53
55
  if (await checkExtension()) {
54
56
  const ndk = initializeNDK();
57
+ await connect();
55
58
  const signer = new NDKNip07Signer();
56
59
  ndk.signer = signer;
57
- const user = await signer.user();
60
+ const account = await signer.user();
58
61
  state.signer = signer;
59
- state.pubkey.value = user.pubkey;
60
- await connect();
61
- const profile = await ndk.getUser({ pubkey: user.pubkey });
62
+ state.pubkey.value = account.pubkey;
63
+ const user = ndk.getUser({ pubkey: account.pubkey });
64
+ const profile = await user.fetchProfile();
62
65
  state.userProfile.value = mapProfile(profile);
63
66
  }
64
67
  }
@@ -66,14 +69,15 @@ export function useNuxstr() {
66
69
  return {
67
70
  id: event.id,
68
71
  pubkey: event.pubkey,
69
- created_at: event.created_at || 0,
72
+ created_at: event.created_at || DEFAULT_TIMESTAMP,
70
73
  content: event.content,
71
- profile: null
74
+ profile: void 0
72
75
  };
73
76
  }
74
77
  async function fetchProfile(pubkey) {
75
78
  try {
76
- const user = state.ndk.getUser({ pubkey });
79
+ const ndk = initializeNDK();
80
+ const user = ndk.getUser({ pubkey });
77
81
  const profile = await user.fetchProfile();
78
82
  return mapProfile(profile);
79
83
  } catch (error) {
@@ -82,6 +86,7 @@ export function useNuxstr() {
82
86
  }
83
87
  }
84
88
  function mapProfile(profile) {
89
+ if (profile === null) return {};
85
90
  return {
86
91
  display_name: profile.displayName,
87
92
  about: profile.about,
@@ -94,7 +99,7 @@ export function useNuxstr() {
94
99
  }
95
100
  function logout() {
96
101
  state.signer = null;
97
- state.pubkey.value = null;
102
+ state.pubkey.value = DEFAULT_PUBKEY;
98
103
  }
99
104
  return {
100
105
  get ndk() {
@@ -104,9 +109,10 @@ export function useNuxstr() {
104
109
  login,
105
110
  logout,
106
111
  isLoggedIn,
107
- pubkey: state.pubkey,
112
+ pubkey: state.pubkey.value,
108
113
  mapProfile,
109
114
  mapComment,
110
115
  fetchProfile
111
116
  };
112
117
  }
118
+ export default useNuxstr;
@@ -1,4 +1,4 @@
1
- export declare function useReplies(rootCommentId?: string): {
1
+ declare function useReplies(rootCommentId: string): {
2
2
  subscribeReplies: () => Promise<void>;
3
3
  replies: import("vue").ComputedRef<{
4
4
  id: string;
@@ -17,5 +17,5 @@ export declare function useReplies(rootCommentId?: string): {
17
17
  } | undefined;
18
18
  }[]>;
19
19
  reply: (comment: string) => Promise<boolean>;
20
- count: import("vue").ComputedRef<Promise<string>>;
21
20
  };
21
+ export default useReplies;
@@ -1,7 +1,7 @@
1
1
  import { computed, ref } from "vue";
2
- import { useNuxstr } from "./useNuxstr.js";
2
+ import useNuxstr from "./useNuxstr.js";
3
3
  import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
4
- export function useReplies(rootCommentId) {
4
+ function useReplies(rootCommentId) {
5
5
  const { ndk, connect, mapComment, pubkey, fetchProfile } = useNuxstr();
6
6
  const repliesData = ref([]);
7
7
  const error = ref(null);
@@ -11,7 +11,7 @@ export function useReplies(rootCommentId) {
11
11
  async function subscribeReplies() {
12
12
  await connect();
13
13
  const filter = { kinds: [NDKKind.GenericReply], limit: 100, ["#e"]: [rootCommentId] };
14
- const sub = await ndk.subscribe(filter);
14
+ const sub = ndk.subscribe(filter);
15
15
  sub.on("event", async (event) => {
16
16
  const reply2 = mapComment(event);
17
17
  reply2.profile = await fetchProfile(event.pubkey);
@@ -33,16 +33,10 @@ export function useReplies(rootCommentId) {
33
33
  ["e", `${rootCommentId}`],
34
34
  ["k", `${NDKKind.GenericReply}`],
35
35
  // The parent kind
36
- ["p", pubkey.value]
36
+ ["p", pubkey ?? ""]
37
37
  ];
38
38
  return event;
39
39
  }
40
- const count = computed(async () => {
41
- await connect();
42
- const filter = { kinds: [NDKKind.GenericReply], limit: 100, ["#e"]: [rootCommentId] };
43
- const events = await ndk.fetchEvents(filter);
44
- console.log(events);
45
- return `${Array.from(events).length}`;
46
- });
47
- return { subscribeReplies, replies, reply, count };
40
+ return { subscribeReplies, replies, reply };
48
41
  }
42
+ export default useReplies;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threenine/nuxstr-comments",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
4
4
  "description": "Nuxt module to enable Nostr Comments on Nuxt 4 based websites",
5
5
  "repository": "threenine/nuxstr-comments",
6
6
  "license": "MIT",