diff --git a/dev-dist/sw.js b/dev-dist/sw.js
index 40d995c..f0d0468 100644
--- a/dev-dist/sw.js
+++ b/dev-dist/sw.js
@@ -79,7 +79,7 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict';
*/
workbox.precacheAndRoute([{
"url": "index.html",
- "revision": "0.36o4mscev7"
+ "revision": "0.qrl00u05iuo"
}], {});
workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
diff --git a/src/components/ConnectionStatus.vue b/src/components/ConnectionStatus.vue
new file mode 100644
index 0000000..6d76609
--- /dev/null
+++ b/src/components/ConnectionStatus.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/SupportChat.vue b/src/components/SupportChat.vue
index 2e873d1..ebd956c 100644
--- a/src/components/SupportChat.vue
+++ b/src/components/SupportChat.vue
@@ -10,6 +10,7 @@ import { ScrollArea } from '@/components/ui/scroll-area'
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
import { Send } from 'lucide-vue-next'
import MessageBubble from '@/components/ui/message-bubble/MessageBubble.vue'
+import ConnectionStatus from '@/components/ConnectionStatus.vue'
const nostrStore = useNostrStore()
const input = ref('')
@@ -69,7 +70,7 @@ watch(() => nostrStore.currentMessages.length, () => {
onMounted(async () => {
try {
if (!SUPPORT_NPUB) return
-
+
const supportPubkeyHex = npubToHex(SUPPORT_NPUB)
nostrStore.activeChat = supportPubkeyHex
@@ -177,8 +178,7 @@ const getMessageGroupClasses = (sent: boolean) => {
@@ -218,16 +218,10 @@ const getMessageGroupClasses = (sent: boolean) => {
@@ -236,8 +230,7 @@ const getMessageGroupClasses = (sent: boolean) => {
-
+
@@ -398,4 +392,4 @@ a:active {
.scrollarea-viewport {
height: 100%;
}
-
\ No newline at end of file
+
diff --git a/src/components/layout/Navbar.vue b/src/components/layout/Navbar.vue
index d929b67..bb3a831 100644
--- a/src/components/layout/Navbar.vue
+++ b/src/components/layout/Navbar.vue
@@ -9,6 +9,7 @@ import { useRouter } from 'vue-router'
import LogoutDialog from '@/components/ui/logout-dialog/LogoutDialog.vue'
import Login from '@/components/Login.vue'
import { Dialog, DialogContent } from '@/components/ui/dialog'
+import ConnectionStatus from '@/components/ConnectionStatus.vue'
const { t, locale } = useI18n()
const { theme, setTheme } = useTheme()
@@ -84,6 +85,8 @@ const openLogin = () => {
{{ locale === 'en' ? 'πͺπΈ ES' : 'πΊπΈ EN' }}
+
+
diff --git a/src/composables/useChat.ts b/src/composables/useChat.ts
new file mode 100644
index 0000000..2284122
--- /dev/null
+++ b/src/composables/useChat.ts
@@ -0,0 +1,23 @@
+export function useChat(pubkey: string) {
+ const messageStore = useMessageStore()
+ const nostrStore = useNostrStore()
+
+ const messages = computed(() =>
+ messageStore.messages.get(pubkey) || []
+ )
+
+ const sendMessage = async (content: string) => {
+ if (!content.trim()) return
+ await nostrStore.sendMessage(pubkey, content)
+ }
+
+ const loadHistory = async () => {
+ await nostrStore.subscribeToMessages()
+ }
+
+ return {
+ messages,
+ sendMessage,
+ loadHistory
+ }
+}
\ No newline at end of file
diff --git a/src/lib/encryption.ts b/src/lib/encryption.ts
new file mode 100644
index 0000000..275bcc3
--- /dev/null
+++ b/src/lib/encryption.ts
@@ -0,0 +1,9 @@
+export class NostrEncryption {
+ static async encrypt(privkey: string, pubkey: string, content: string) {
+ return await window.NostrTools.nip04.encrypt(privkey, pubkey, content)
+ }
+
+ static async decrypt(privkey: string, pubkey: string, content: string) {
+ return await window.NostrTools.nip04.decrypt(privkey, pubkey, content)
+ }
+}
\ No newline at end of file
diff --git a/src/lib/error.ts b/src/lib/error.ts
new file mode 100644
index 0000000..802947c
--- /dev/null
+++ b/src/lib/error.ts
@@ -0,0 +1,14 @@
+export class ErrorHandler {
+ static handle(error: unknown, context: string) {
+ console.error(`Error in ${context}:`, error)
+
+ if (error instanceof Error) {
+ // Handle specific error types
+ if (error.name === 'TimeoutError') {
+ return 'Connection timed out. Please try again.'
+ }
+ }
+
+ return 'An unexpected error occurred'
+ }
+}
\ No newline at end of file
diff --git a/src/lib/storage.ts b/src/lib/storage.ts
new file mode 100644
index 0000000..5c9bf40
--- /dev/null
+++ b/src/lib/storage.ts
@@ -0,0 +1,22 @@
+export class MessageStorage {
+ static saveMessages(pubkey: string, messages: DirectMessage[]) {
+ try {
+ localStorage.setItem(
+ `messages_${pubkey}`,
+ JSON.stringify(messages)
+ )
+ } catch (err) {
+ console.error('Failed to save messages:', err)
+ }
+ }
+
+ static loadMessages(pubkey: string): DirectMessage[] {
+ try {
+ const stored = localStorage.getItem(`messages_${pubkey}`)
+ return stored ? JSON.parse(stored) : []
+ } catch (err) {
+ console.error('Failed to load messages:', err)
+ return []
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/lib/websocket.ts b/src/lib/websocket.ts
new file mode 100644
index 0000000..111f827
--- /dev/null
+++ b/src/lib/websocket.ts
@@ -0,0 +1,35 @@
+// Create a new WebSocket manager class
+export class NostrWebSocketManager {
+ private connections: Map = new Map()
+ private subscriptions: Map = new Map()
+
+ async connect(url: string) {
+ if (this.connections.has(url)) return this.connections.get(url)
+
+ const relay = window.NostrTools.relayInit(url)
+ try {
+ await relay.connect()
+ this.connections.set(url, relay)
+ this.subscriptions.set(url, [])
+ return relay
+ } catch (err) {
+ console.error(`Failed to connect to ${url}:`, err)
+ return null
+ }
+ }
+
+ addSubscription(url: string, sub: any) {
+ const subs = this.subscriptions.get(url) || []
+ subs.push(sub)
+ this.subscriptions.set(url, subs)
+ }
+
+ cleanup() {
+ for (const [url, subs] of this.subscriptions.entries()) {
+ subs.forEach(sub => sub.unsub?.())
+ this.connections.get(url)?.close()
+ }
+ this.connections.clear()
+ this.subscriptions.clear()
+ }
+}
\ No newline at end of file
diff --git a/src/stores/messages.ts b/src/stores/messages.ts
new file mode 100644
index 0000000..83e4d6d
--- /dev/null
+++ b/src/stores/messages.ts
@@ -0,0 +1,21 @@
+// Separate message handling into its own store
+export const useMessageStore = defineStore('messages', () => {
+ const messages = ref