From a29dc1100caa5be6ab50a833f479dd795ce605e4 Mon Sep 17 00:00:00 2001 From: SondreB Date: Sun, 28 May 2023 20:49:37 +0200 Subject: [PATCH 1/3] Add display of a limited set of public chats --- src/app/app.html | 7 +- src/app/app.module.ts | 4 +- src/app/chat/chat.html | 47 ++++++++++- src/app/chat/chat.scss | 17 ++++ src/app/chat/chat.ts | 14 +++- src/app/services/chat.service.ts | 36 +++++++++ src/app/services/data.ts | 5 +- src/app/services/interfaces.ts | 6 ++ src/app/services/relay.ts | 8 ++ src/app/services/ui.ts | 79 ++++++++++++++++++- .../shared/chat-item/chat-item.component.ts | 2 +- .../shared/chat-list/chat-list.component.ts | 3 +- .../message-list/message-list.component.html | 35 ++++++++ .../message-list/message-list.component.scss | 15 ++++ .../message-list.component.spec.ts | 25 ++++++ .../message-list/message-list.component.ts | 35 ++++++++ 16 files changed, 323 insertions(+), 15 deletions(-) create mode 100644 src/app/shared/message-list/message-list.component.html create mode 100644 src/app/shared/message-list/message-list.component.scss create mode 100644 src/app/shared/message-list/message-list.component.spec.ts create mode 100644 src/app/shared/message-list/message-list.component.ts diff --git a/src/app/app.html b/src/app/app.html index 84074423..1b7281e6 100644 --- a/src/app/app.html +++ b/src/app/app.html @@ -87,14 +87,15 @@ notifications {{ 'App.Notifications' | translate }} - + people {{ 'App.People' | translate }} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 7b254da5..940ec483 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -154,6 +154,7 @@ import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field'; import { DragScrollModule } from 'ngx-drag-scroll'; import { ZappersListDialogComponent } from './shared/zappers-list-dialog/zappers-list-dialog.component'; import { ExampleComponent } from './example/example'; +import { MessageListComponent } from './shared/message-list/message-list.component'; @NgModule({ declarations: [ AppComponent, @@ -245,7 +246,8 @@ import { ExampleComponent } from './example/example'; TagsComponent, BadgeComponent, ZappersListDialogComponent, - ExampleComponent + ExampleComponent, + MessageListComponent ], imports: [ HttpClientModule, diff --git a/src/app/chat/chat.html b/src/app/chat/chat.html index 1811959a..0364b007 100644 --- a/src/app/chat/chat.html +++ b/src/app/chat/chat.html @@ -1,4 +1,47 @@ - + + + + + + +

{{ chat.name }}

+

{{ chat.about }}

+ +
+ + +
+ + +
+ + + + + + + + + + + + diff --git a/src/app/chat/chat.scss b/src/app/chat/chat.scss index 16fab21f..3ebeb3a9 100644 --- a/src/app/chat/chat.scss +++ b/src/app/chat/chat.scss @@ -29,3 +29,20 @@ .spacer { flex: 1; } + + +.form { + padding: 16px 16px 0 16px; +} + +.input-full-width { + position: relative; + margin: auto; +} + +.search { + position: sticky; + top: 0; + padding: 10px; + z-index: 999; +} diff --git a/src/app/chat/chat.ts b/src/app/chat/chat.ts index 966c1a9b..f85269f8 100644 --- a/src/app/chat/chat.ts +++ b/src/app/chat/chat.ts @@ -1,6 +1,10 @@ import { Component, ChangeDetectorRef, ViewChild, ViewEncapsulation } from '@angular/core'; import { MatSidenav } from '@angular/material/sidenav'; import { ApplicationState } from '../services/applicationstate'; +import { ChatService } from '../services/chat.service'; +import { RelayService } from '../services/relay'; +import { Kind } from 'nostr-tools'; +import { UIService } from '../services/ui'; @Component({ selector: 'app-chat', templateUrl: './chat.html', @@ -10,7 +14,10 @@ import { ApplicationState } from '../services/applicationstate'; export class ChatComponent { @ViewChild('chatSidebar', { static: false }) chatSidebar!: MatSidenav; @ViewChild('userSidebar', { static: false }) userSidebar!: MatSidenav; - constructor(private appState: ApplicationState) {} + + subscription: any; + + constructor(private appState: ApplicationState, private chatService: ChatService, private relayService: RelayService, public ui: UIService) {} sidebarTitles = { user: '', chat: '', @@ -29,8 +36,13 @@ export class ChatComponent { }; async ngOnInit() { + this.ui.clearChats(); + this.appState.updateTitle('Chat'); this.appState.goBack = true; this.appState.actions = []; + + this.subscription = this.relayService.subscribe([{ kinds: [Kind.ChannelCreation, Kind.ChannelMetadata], limit: 10 }]).id; + // this.chatService.downloadChatRooms(); } } diff --git a/src/app/services/chat.service.ts b/src/app/services/chat.service.ts index 3a3d6895..21673675 100644 --- a/src/app/services/chat.service.ts +++ b/src/app/services/chat.service.ts @@ -51,6 +51,42 @@ export class ChatService { subscriptions: Subscription[] = []; + downloadChatRooms() { + debugger; + // this.chats2 = []; + this.#chats = []; + + this.dataService + .downloadEventsByQuery([{ kinds: [40, 41] }], 3000) + .pipe( + finalize(async () => { + debugger; + for (let index = 0; index < this.#chats.length; index++) { + const event = this.#chats[index]; + const content = await this.nostr.decrypt(event.pubkey, event.content); + event.content = content; + console.log('DECRYPTED EVENT:', event); + } + }) + ) + .subscribe(async (event) => { + if (this.#chats.findIndex((e) => e.id === event.id) > -1) { + return; + } + + // const gt = globalThis as any; + // const content = await gt.nostr.nip04.decrypt(event.pubkey, event.content); + // event.content = content; + + this.#chats.unshift(event); + + // this.chats2.push(event); + // this.#chatsChanged2.next(this.chats2); + }); + + // this.subscriptions.push(this.dataService.downloadEventsByQuery([{}])); + } + download() { // this.chats2 = []; this.#chats = []; diff --git a/src/app/services/data.ts b/src/app/services/data.ts index 9acdb189..10b00c92 100644 --- a/src/app/services/data.ts +++ b/src/app/services/data.ts @@ -582,7 +582,6 @@ export class DataService { downloadFromRelay(filters: Filter[], relay: NostrRelay, requestTimeout = 10000): Observable { return new Observable((observer: Observer) => { const sub = relay.sub([...filters], {}) as NostrSubscription; - // relay.subscriptions.push(sub); sub.on('event', (originalEvent: any) => { const event = this.eventService.processEvent(originalEvent); @@ -599,8 +598,6 @@ export class DataService { }); return () => { - // console.log('downloadFromRelay:finished:unsub'); - // When the observable is finished, this return function is called. sub.unsub(); }; }).pipe( @@ -608,7 +605,7 @@ export class DataService { catchError((error) => { console.warn('The observable was timed out.'); return of(); - }) // Simply return undefined when the timeout is reached. + }) ); } diff --git a/src/app/services/interfaces.ts b/src/app/services/interfaces.ts index 70459fa4..42f65603 100644 --- a/src/app/services/interfaces.ts +++ b/src/app/services/interfaces.ts @@ -331,6 +331,12 @@ export interface CustomObjectModel { formatted?: string; } +export interface NostrEventChat extends NostrEvent { + about: string; + name: string; + picture: string; +} + export class ChatModel { 'id': number; 'targetUserId': number; diff --git a/src/app/services/relay.ts b/src/app/services/relay.ts index 6473cb76..afd1a021 100644 --- a/src/app/services/relay.ts +++ b/src/app/services/relay.ts @@ -427,6 +427,14 @@ export class RelayService { this.zapUi.addZap(event); } + if (event.kind == Kind.ChannelCreation) { + this.ui.putChat(event); + } + + if (event.kind == Kind.ChannelMetadata) { + this.ui.putChatMetadata(event); + } + if (response.subscription) { const sub = this.subs.get(response.subscription); if (sub) { diff --git a/src/app/services/ui.ts b/src/app/services/ui.ts index 554d6d87..8c6e6ede 100644 --- a/src/app/services/ui.ts +++ b/src/app/services/ui.ts @@ -3,7 +3,7 @@ import { ActivatedRoute } from '@angular/router'; import { Kind } from 'nostr-tools'; import { BehaviorSubject, map, Observable, filter, flatMap, mergeMap, concatMap, tap, take, single, takeWhile, from, of } from 'rxjs'; import { EventService } from './event'; -import { EmojiEnum, LoadMoreOptions, NostrEvent, NostrEventDocument, NostrProfileDocument, NotificationModel, ThreadEntry } from './interfaces'; +import { EmojiEnum, LoadMoreOptions, NostrEvent, NostrEventChat, NostrEventDocument, NostrProfileDocument, NotificationModel, ThreadEntry } from './interfaces'; import { OptionsService } from './options'; import { ProfileService } from './profile'; import { ZapService } from './zap.service'; @@ -26,6 +26,7 @@ export class UIService { rootEventsView: [] as NostrEventDocument[], replyEventsView: [] as NostrEventDocument[], reactions: new Map(), + chats: [] as NostrEventChat[], }; viewCounts = { @@ -156,6 +157,14 @@ export class UIService { // return this.#eventsChanged.asObservable().pipe(map((data) => data.sort((a, b) => (a.created_at > b.created_at ? -1 : 1)))); } + chats: NostrEventChat[] = []; + + #chatsChanged: BehaviorSubject = new BehaviorSubject(this.chats); + + get chats$(): Observable { + return this.#chatsChanged.asObservable(); + } + #loadMore: BehaviorSubject = new BehaviorSubject(undefined); get loadMore$(): Observable { @@ -285,6 +294,69 @@ export class UIService { this.triggerUnreadNotifications(); } + putChat(event: NostrEvent) { + const index = this.chats.findIndex((n) => n.id == event.id); + + if (index == -1) { + const chat = event as NostrEventChat; + const parsed = JSON.parse(chat.content); + + chat.picture = parsed.picture; + chat.name = parsed.name; + chat.about = parsed.about; + + this.chats.push(chat); + this.#chatsChanged.next(this.chats); + } + + // if (index == -1) { + // this.#notifications.unshift(notification); + + // this.#notifications = this.#notifications.sort((a, b) => { + // return a.created < b.created ? 1 : -1; + // }); + // } else { + // this.#notifications[index] = notification; + // } + + // this.#activityFeed = this.#notifications.slice(0, 5); + // this.triggerUnreadNotifications(); + } + + putChatMetadata(event: NostrEvent) { + const channelId = this.eventService.lastETag(event); + + if (!channelId) { + console.debug('This channel metadata does not have eTag:', event); + return; + } + + // Find the existing chat creation, but verify both channel ID and the public key. + const index = this.chats.findIndex((n) => n.id == channelId && n.pubkey == event.pubkey); + + // TODO: We are subscribing to both 40 and 41 at the same time and we are receiving 41 (metadata updates) + // before some of the 40 (create) events, meaning we'll never show the latest metadata for certain chats. + if (index == -1) { + return; + } + + this.chats[index].content = event.content; + this.#chatsChanged.next(this.chats); + + // if (index == -1) { + // this.#notifications.unshift(notification); + + // this.#notifications = this.#notifications.sort((a, b) => { + // return a.created < b.created ? 1 : -1; + // }); + // } else { + // this.#notifications[index] = notification; + // } + + // this.#activityFeed = this.#notifications.slice(0, 5); + // this.triggerUnreadNotifications(); + } + viewEventsStart = 0; viewEventsCount = 5; @@ -709,6 +781,7 @@ export class UIService { this.#lists.followingEventsView = []; this.#lists.reactions = new Map(); + this.#lists.chats = []; this.#notifications = []; this.#activityFeed = []; @@ -766,6 +839,10 @@ export class UIService { this.previousFeedSinceValue = 0; } + clearChats() { + this.#lists.chats = []; + } + // #parentEventId: string | undefined = undefined; // get parentEventId() { diff --git a/src/app/shared/chat-item/chat-item.component.ts b/src/app/shared/chat-item/chat-item.component.ts index e2a529df..6a2cd920 100644 --- a/src/app/shared/chat-item/chat-item.component.ts +++ b/src/app/shared/chat-item/chat-item.component.ts @@ -9,7 +9,7 @@ import { ChatModel, NostrEventDocument } from 'src/app/services/interfaces'; }) export class ChatItemComponent { @Output() openChatSidebar: EventEmitter = new EventEmitter(); - @Input() chat!: ChatModel; + @Input() chat!: ChatModel | any; @Input() event!: NostrEventDocument; constructor(private service: ChatService) {} diff --git a/src/app/shared/chat-list/chat-list.component.ts b/src/app/shared/chat-list/chat-list.component.ts index 3d3124f9..9c4dc833 100644 --- a/src/app/shared/chat-list/chat-list.component.ts +++ b/src/app/shared/chat-list/chat-list.component.ts @@ -18,8 +18,7 @@ export class ChatListComponent implements OnInit { constructor(public chatService: ChatService) {} ngOnInit() { - this.chatService.download(); - + // this.chatService.download(); // this.chatService.uniqueChats$.subscribe((data) => { // console.log('YEEH!', data); // }); diff --git a/src/app/shared/message-list/message-list.component.html b/src/app/shared/message-list/message-list.component.html new file mode 100644 index 00000000..dc9188b7 --- /dev/null +++ b/src/app/shared/message-list/message-list.component.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + +
{{ chat.pubkey }} : {{ chat.content }}
+ + + + + + + + + + diff --git a/src/app/shared/message-list/message-list.component.scss b/src/app/shared/message-list/message-list.component.scss new file mode 100644 index 00000000..42faf705 --- /dev/null +++ b/src/app/shared/message-list/message-list.component.scss @@ -0,0 +1,15 @@ +.form { + padding: 16px 16px 0 16px; +} + +.input-full-width { + position: relative; + margin: auto; +} + +.search { + position: sticky; + top: 0; + padding: 10px; + z-index: 999; +} diff --git a/src/app/shared/message-list/message-list.component.spec.ts b/src/app/shared/message-list/message-list.component.spec.ts new file mode 100644 index 00000000..5f7894e2 --- /dev/null +++ b/src/app/shared/message-list/message-list.component.spec.ts @@ -0,0 +1,25 @@ +// import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +// import {ChatListComponent} from './chat-list.component'; + +// describe('ChatListComponent', () => { +// let component: ChatListComponent; +// let fixture: ComponentFixture; + +// beforeEach(async(() => { +// TestBed.configureTestingModule({ +// declarations: [ChatListComponent] +// }) +// .compileComponents(); +// })); + +// beforeEach(() => { +// fixture = TestBed.createComponent(ChatListComponent); +// component = fixture.componentInstance; +// fixture.detectChanges(); +// }); + +// it('should create', () => { +// expect(component).toBeTruthy(); +// }); +// }); diff --git a/src/app/shared/message-list/message-list.component.ts b/src/app/shared/message-list/message-list.component.ts new file mode 100644 index 00000000..ebfad266 --- /dev/null +++ b/src/app/shared/message-list/message-list.component.ts @@ -0,0 +1,35 @@ +import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; +import { from, Observable, of } from 'rxjs'; +import { ChatService } from 'src/app/services/chat.service'; + +interface ChatModel { + id: string; + name: string; +} + +@Component({ + selector: 'app-message-list', + templateUrl: './message-list.component.html', + styleUrls: ['./message-list.component.scss'], +}) +export class MessageListComponent implements OnInit { + @Output() openChatSidebar: EventEmitter = new EventEmitter(); + + constructor(public chatService: ChatService) {} + + ngOnInit() { + this.chatService.download(); + + // this.chatService.uniqueChats$.subscribe((data) => { + // console.log('YEEH!', data); + // }); + } + + add() { + // this.#chats.unshift({ id: '123', name: 'Yes!' }); + } + + reset() { + // this.#chats = []; + } +} From f3e7422c1a8491040dae2dc5103f7592c8474237 Mon Sep 17 00:00:00 2001 From: SondreB Date: Sun, 28 May 2023 20:56:59 +0200 Subject: [PATCH 2/3] Improve the listing of public chats --- src/app/chat/chat.html | 5 +++-- src/app/chat/chat.scss | 15 +++++++++++++++ src/app/services/ui.ts | 17 +++++++++++------ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/app/chat/chat.html b/src/app/chat/chat.html index 0364b007..055d15de 100644 --- a/src/app/chat/chat.html +++ b/src/app/chat/chat.html @@ -9,8 +9,9 @@ -

{{ chat.name }}

-

{{ chat.about }}

+ {{ chat.name }} + {{ chat.about }} +
diff --git a/src/app/chat/chat.scss b/src/app/chat/chat.scss index 3ebeb3a9..c0039185 100644 --- a/src/app/chat/chat.scss +++ b/src/app/chat/chat.scss @@ -46,3 +46,18 @@ padding: 10px; z-index: 999; } + + +.last-message { + text-overflow: ellipsis; + overflow: hidden; + width: 90%; + white-space: nowrap; +} +.user-avatar{ + height: 50px; + width: 50px; +} +mat-list-item{ + height: 90px !important; +} diff --git a/src/app/services/ui.ts b/src/app/services/ui.ts index 8c6e6ede..91d546ee 100644 --- a/src/app/services/ui.ts +++ b/src/app/services/ui.ts @@ -299,14 +299,19 @@ export class UIService { if (index == -1) { const chat = event as NostrEventChat; - const parsed = JSON.parse(chat.content); - chat.picture = parsed.picture; - chat.name = parsed.name; - chat.about = parsed.about; + try { + const parsed = JSON.parse(chat.content); - this.chats.push(chat); - this.#chatsChanged.next(this.chats); + chat.picture = parsed.picture; + chat.name = parsed.name; + chat.about = parsed.about; + + this.chats.push(chat); + this.#chatsChanged.next(this.chats); + } catch (err) { + console.debug('Failed to parse: ', chat.content); + } } // if (index == -1) { From c88de0d3e3a24949b7fc9f3e130efcb454ffd0a5 Mon Sep 17 00:00:00 2001 From: SondreB Date: Sun, 28 May 2023 21:44:32 +0200 Subject: [PATCH 3/3] Add very basic chat display --- src/app/chat/chat.html | 52 ++----------------- src/app/chat/chat.scss | 32 ------------ src/app/chat/chat.ts | 16 +++--- src/app/message/message.ts | 2 +- src/app/services/interfaces.ts | 12 ++--- src/app/services/relay.ts | 4 ++ src/app/services/ui.ts | 46 ++++++++++++++++ .../chat-detail/chat-detail.component.html | 34 +++++------- .../chat-detail/chat-detail.component.ts | 41 +++++++++++++-- .../shared/chat-item/chat-item.component.html | 11 ++-- .../shared/chat-item/chat-item.component.ts | 2 +- .../shared/chat-list/chat-list.component.html | 8 +-- .../shared/chat-list/chat-list.component.ts | 11 +++- .../message-bubble.component.html | 7 +-- .../message-bubble.component.ts | 17 ++++-- 15 files changed, 159 insertions(+), 136 deletions(-) diff --git a/src/app/chat/chat.html b/src/app/chat/chat.html index 055d15de..b395b595 100644 --- a/src/app/chat/chat.html +++ b/src/app/chat/chat.html @@ -1,53 +1,9 @@ - - - - - - - {{ chat.name }} - {{ chat.about }} - - - - - - - - - - - - - - - - - - - - - +
diff --git a/src/app/chat/chat.scss b/src/app/chat/chat.scss index c0039185..16fab21f 100644 --- a/src/app/chat/chat.scss +++ b/src/app/chat/chat.scss @@ -29,35 +29,3 @@ .spacer { flex: 1; } - - -.form { - padding: 16px 16px 0 16px; -} - -.input-full-width { - position: relative; - margin: auto; -} - -.search { - position: sticky; - top: 0; - padding: 10px; - z-index: 999; -} - - -.last-message { - text-overflow: ellipsis; - overflow: hidden; - width: 90%; - white-space: nowrap; -} -.user-avatar{ - height: 50px; - width: 50px; -} -mat-list-item{ - height: 90px !important; -} diff --git a/src/app/chat/chat.ts b/src/app/chat/chat.ts index f85269f8..bafd90d9 100644 --- a/src/app/chat/chat.ts +++ b/src/app/chat/chat.ts @@ -1,10 +1,9 @@ import { Component, ChangeDetectorRef, ViewChild, ViewEncapsulation } from '@angular/core'; import { MatSidenav } from '@angular/material/sidenav'; import { ApplicationState } from '../services/applicationstate'; -import { ChatService } from '../services/chat.service'; +import { UIService } from '../services/ui'; import { RelayService } from '../services/relay'; import { Kind } from 'nostr-tools'; -import { UIService } from '../services/ui'; @Component({ selector: 'app-chat', templateUrl: './chat.html', @@ -14,10 +13,9 @@ import { UIService } from '../services/ui'; export class ChatComponent { @ViewChild('chatSidebar', { static: false }) chatSidebar!: MatSidenav; @ViewChild('userSidebar', { static: false }) userSidebar!: MatSidenav; + subscription?: string; - subscription: any; - - constructor(private appState: ApplicationState, private chatService: ChatService, private relayService: RelayService, public ui: UIService) {} + constructor(private appState: ApplicationState, private relayService: RelayService, public ui: UIService) {} sidebarTitles = { user: '', chat: '', @@ -29,6 +27,7 @@ export class ChatComponent { this.me.sidebarTitles.user = title; }, chatSideBar: function (title: string = '') { + debugger; this.me.chatSidebar.open(); this.me.userSidebar.close(); this.me.sidebarTitles.chat = title; @@ -37,12 +36,15 @@ export class ChatComponent { async ngOnInit() { this.ui.clearChats(); + this.subscription = this.relayService.subscribe([{ kinds: [Kind.ChannelCreation, Kind.ChannelMetadata], limit: 10 }]).id; this.appState.updateTitle('Chat'); this.appState.goBack = true; this.appState.actions = []; + } - this.subscription = this.relayService.subscribe([{ kinds: [Kind.ChannelCreation, Kind.ChannelMetadata], limit: 10 }]).id; - // this.chatService.downloadChatRooms(); + ngOnDestroy() { + // this.relayService.unsubscribe(this.subscription!); + // this.ui.clearChats(); } } diff --git a/src/app/message/message.ts b/src/app/message/message.ts index e74787e4..7e4755ae 100644 --- a/src/app/message/message.ts +++ b/src/app/message/message.ts @@ -32,7 +32,7 @@ export class MessageComponent { }; async ngOnInit() { - this.appState.updateTitle('@Milad'); + this.appState.updateTitle(''); this.appState.goBack = true; this.appState.showBackButton = true; this.appState.actions = []; diff --git a/src/app/services/interfaces.ts b/src/app/services/interfaces.ts index 42f65603..5db1f70f 100644 --- a/src/app/services/interfaces.ts +++ b/src/app/services/interfaces.ts @@ -319,11 +319,11 @@ export interface UserModel { bio: string; } -export interface MessageModel { - id: number; - cover: string; - message: string; -} +// export interface MessageModel { +// id: number; +// cover: string; +// message: string; +// } export interface CustomObjectModel { tmpl: string; @@ -344,7 +344,7 @@ export class ChatModel { 'cover': string; 'lastMessage': string; 'lastMessageLength': string | number; - 'chat': Array; + // 'chat': Array; } export interface LabelModel { diff --git a/src/app/services/relay.ts b/src/app/services/relay.ts index afd1a021..00f71083 100644 --- a/src/app/services/relay.ts +++ b/src/app/services/relay.ts @@ -435,6 +435,10 @@ export class RelayService { this.ui.putChatMetadata(event); } + if (event.kind == Kind.ChannelMessage) { + this.ui.putChatMessage(event); + } + if (response.subscription) { const sub = this.subs.get(response.subscription); if (sub) { diff --git a/src/app/services/ui.ts b/src/app/services/ui.ts index 91d546ee..ce0fff43 100644 --- a/src/app/services/ui.ts +++ b/src/app/services/ui.ts @@ -27,6 +27,7 @@ export class UIService { replyEventsView: [] as NostrEventDocument[], reactions: new Map(), chats: [] as NostrEventChat[], + chatMessages: [] as NostrEventChat[], }; viewCounts = { @@ -165,6 +166,14 @@ export class UIService { return this.#chatsChanged.asObservable(); } + chatMessage: NostrEvent[] = []; + + #chatMessageChanged: BehaviorSubject = new BehaviorSubject(this.chatMessage); + + get chatMessage$(): Observable { + return this.#chatMessageChanged.asObservable(); + } + #loadMore: BehaviorSubject = new BehaviorSubject(undefined); get loadMore$(): Observable { @@ -362,6 +371,34 @@ export class UIService { // this.triggerUnreadNotifications(); } + putChatMessage(event: NostrEvent) { + const index = this.chatMessage.findIndex((n) => n.id == event.id); + + if (index == -1) { + const chat = event as NostrEvent; + + try { + this.chatMessage.push(chat); + this.#chatMessageChanged.next(this.chatMessage); + } catch (err) { + console.debug('Failed to parse: ', chat.content); + } + } + + // if (index == -1) { + // this.#notifications.unshift(notification); + + // this.#notifications = this.#notifications.sort((a, b) => { + // return a.created < b.created ? 1 : -1; + // }); + // } else { + // this.#notifications[index] = notification; + // } + + // this.#activityFeed = this.#notifications.slice(0, 5); + // this.triggerUnreadNotifications(); + } + viewEventsStart = 0; viewEventsCount = 5; @@ -787,6 +824,7 @@ export class UIService { this.#lists.reactions = new Map(); this.#lists.chats = []; + this.#lists.chatMessages = []; this.#notifications = []; this.#activityFeed = []; @@ -846,6 +884,14 @@ export class UIService { clearChats() { this.#lists.chats = []; + this.chats = []; + this.#chatsChanged.next(this.chats); + } + + clearChatMessages() { + this.#lists.chatMessages = []; + this.chatMessage = []; + this.#chatMessageChanged.next(this.chatMessage); } // #parentEventId: string | undefined = undefined; diff --git a/src/app/shared/chat-detail/chat-detail.component.html b/src/app/shared/chat-detail/chat-detail.component.html index 8747665f..a70bfd7c 100644 --- a/src/app/shared/chat-detail/chat-detail.component.html +++ b/src/app/shared/chat-detail/chat-detail.component.html @@ -1,5 +1,3 @@ - - @@ -8,8 +6,9 @@
- - + + + @@ -17,15 +16,14 @@
- + - + - + attach_file_add - - Length : {{message?.length}} - sentiment_satisfied + + Length : {{ message?.length }} + sentiment_satisfied mic send @@ -53,10 +49,4 @@
-
- - - - - diff --git a/src/app/shared/chat-detail/chat-detail.component.ts b/src/app/shared/chat-detail/chat-detail.component.ts index 37a1ae12..9f43e838 100644 --- a/src/app/shared/chat-detail/chat-detail.component.ts +++ b/src/app/shared/chat-detail/chat-detail.component.ts @@ -1,10 +1,15 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { MatSidenav } from '@angular/material/sidenav'; +import { ActivatedRoute } from '@angular/router'; +import { Kind } from 'nostr-tools'; import { Subscription } from 'rxjs'; import { ApplicationState } from 'src/app/services/applicationstate'; import { ChatService } from 'src/app/services/chat.service'; import { ChatModel } from 'src/app/services/interfaces'; import { MessageControlService } from 'src/app/services/message-control.service'; +import { RelayService } from 'src/app/services/relay'; +import { UIService } from 'src/app/services/ui'; +import { Utilities } from 'src/app/services/utilities'; @Component({ selector: 'app-chat-detail', @@ -16,16 +21,45 @@ export class ChatDetailComponent implements OnInit, OnDestroy { @ViewChild('picker') picker: unknown; isEmojiPickerVisible: boolean | undefined; - subscription!: Subscription; chat!: ChatModel; sending: boolean = false; message: any; displayList = true; - constructor(private service: ChatService, private control: MessageControlService, private appState: ApplicationState) { } + constructor(private relayService: RelayService, public ui: UIService, private service: ChatService, private activatedRoute: ActivatedRoute, private utilities: Utilities, private control: MessageControlService, private appState: ApplicationState) {} @ViewChild('drawer') drawer!: MatSidenav; + subscription?: string; + subscriptions: Subscription[] = []; ngOnInit() { + this.subscriptions.push( + this.activatedRoute.paramMap.subscribe(async (params) => { + const id: any = params.get('id'); + + this.ui.clearChatMessages(); + this.relayService.unsubscribe(this.subscription!); + this.subscription = this.relayService.subscribe([{ kinds: [Kind.ChannelMessage, Kind.ChannelMuteUser, Kind.ChannelHideMessage], ['#e']: [id], limit: 500 }]).id; + + // this.ui.clearFeed(); + + // if (circle != null) { + // this.circle = Number(circle); + // this.ui.setFeedCircle(this.circle); + // } else { + // this.circle = -1; + // this.ui.setFeedCircle(this.circle); + // } + + // this.subscriptions.push( + // this.navigation.showMore$.subscribe(() => { + // this.showMore(); + // }) + // ); + }) + ); + + // debugger; + // this.subscription = this.relayService.subscribe([{ kinds: [Kind.ChannelMessage, Kind.ChannelMuteUser, Kind.ChannelHideMessage], ['#e']: [this.pubkey], limit: 500 }]).id; // this.subscription = this.service.chat.subscribe((messages) => { // this.chat = messages; @@ -72,6 +106,7 @@ export class ChatDetailComponent implements OnInit, OnDestroy { } ngOnDestroy(): void { - this.subscription?.unsubscribe(); + this.relayService.unsubscribe(this.subscription!); + this.utilities.unsubscribe(this.subscriptions); } } diff --git a/src/app/shared/chat-item/chat-item.component.html b/src/app/shared/chat-item/chat-item.component.html index 7b081d97..2e71be86 100644 --- a/src/app/shared/chat-item/chat-item.component.html +++ b/src/app/shared/chat-item/chat-item.component.html @@ -1,6 +1,11 @@ - + + {{ chat.name }} + {{ chat.about }} + + + + diff --git a/src/app/shared/chat-item/chat-item.component.ts b/src/app/shared/chat-item/chat-item.component.ts index 6a2cd920..d4137542 100644 --- a/src/app/shared/chat-item/chat-item.component.ts +++ b/src/app/shared/chat-item/chat-item.component.ts @@ -15,7 +15,7 @@ export class ChatItemComponent { constructor(private service: ChatService) {} showMessageDetail() { - this.openChatSidebar.emit(this.chat.username); + this.openChatSidebar.emit(this.chat.id); // this.service.chat.next(this.chat); } } diff --git a/src/app/shared/chat-list/chat-list.component.html b/src/app/shared/chat-list/chat-list.component.html index dc9188b7..8f8e4f86 100644 --- a/src/app/shared/chat-list/chat-list.component.html +++ b/src/app/shared/chat-list/chat-list.component.html @@ -7,7 +7,7 @@ - + @@ -18,7 +18,7 @@ --> -
{{ chat.pubkey }} : {{ chat.content }}
+ - + diff --git a/src/app/shared/chat-list/chat-list.component.ts b/src/app/shared/chat-list/chat-list.component.ts index 9c4dc833..f5f34864 100644 --- a/src/app/shared/chat-list/chat-list.component.ts +++ b/src/app/shared/chat-list/chat-list.component.ts @@ -1,6 +1,9 @@ import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; +import { Kind } from 'nostr-tools'; import { from, Observable, of } from 'rxjs'; import { ChatService } from 'src/app/services/chat.service'; +import { RelayService } from 'src/app/services/relay'; +import { UIService } from 'src/app/services/ui'; interface ChatModel { id: string; @@ -15,15 +18,21 @@ interface ChatModel { export class ChatListComponent implements OnInit { @Output() openChatSidebar: EventEmitter = new EventEmitter(); - constructor(public chatService: ChatService) {} + constructor(public chatService: ChatService, public ui: UIService, private relayService: RelayService) {} ngOnInit() { + + // this.chatService.download(); // this.chatService.uniqueChats$.subscribe((data) => { // console.log('YEEH!', data); // }); } + ngOnDestroy() { + + } + add() { // this.#chats.unshift({ id: '123', name: 'Yes!' }); } diff --git a/src/app/shared/message-bubble/message-bubble.component.html b/src/app/shared/message-bubble/message-bubble.component.html index f2307403..c5cdb477 100644 --- a/src/app/shared/message-bubble/message-bubble.component.html +++ b/src/app/shared/message-bubble/message-bubble.component.html @@ -1,10 +1,11 @@ -
+
- + +
- {{message.message}} + {{ message.content }}
diff --git a/src/app/shared/message-bubble/message-bubble.component.ts b/src/app/shared/message-bubble/message-bubble.component.ts index fe62c9c0..e03b725e 100644 --- a/src/app/shared/message-bubble/message-bubble.component.ts +++ b/src/app/shared/message-bubble/message-bubble.component.ts @@ -1,13 +1,20 @@ -import {Component, Input} from '@angular/core'; -import { MessageModel } from 'src/app/services/interfaces'; - +import { Component, Input } from '@angular/core'; +import { ApplicationState } from 'src/app/services/applicationstate'; +import { NostrEvent } from 'src/app/services/interfaces'; @Component({ selector: 'app-message-bubble', templateUrl: './message-bubble.component.html', - styleUrls: ['./message-bubble.component.scss'] + styleUrls: ['./message-bubble.component.scss'], }) export class MessageBubbleComponent { - @Input() message!: MessageModel; + @Input() message!: NostrEvent; @Input() cover!: string; + me?: string; + + constructor(private appState: ApplicationState) {} + + ngOnInit() { + this.me = this.appState.getPublicKey(); + } }