diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index de2e763..36bdbba 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -27,6 +27,7 @@ import { FeedPrivateComponent } from './feed-private/feed-private'; import { ConnectKeyComponent } from './connect/key/key'; import { EditorComponent } from './editor/editor'; import { ArticleComponent } from './article/article'; +import { RelaysManagementComponent } from './relays/relays'; const routes: Routes = [ { @@ -221,6 +222,14 @@ const routes: Routes = [ data: LoadingResolverService, }, }, + { + path: 'relays', + component: RelaysManagementComponent, + canActivate: [AuthGuard], + resolve: { + data: LoadingResolverService, + }, + }, { path: 'development', component: DevelopmentComponent, diff --git a/src/app/app.html b/src/app/app.html index faf9ecd..4defedc 100644 --- a/src/app/app.html +++ b/src/app/app.html @@ -111,9 +111,9 @@ construction Development --> - - settings - Settings + + dns + Relays chevron_left @@ -171,6 +171,10 @@ edit Edit profile + + settings + Settings + help_outline About diff --git a/src/app/app.module.ts b/src/app/app.module.ts index b68ba67..167049d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -124,6 +124,7 @@ import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { ArticleComponent } from './article/article'; import { LabelPipe } from './shared/label.pipe'; import { LabelComponent } from './shared/label/label'; +import { RelaysManagementComponent } from './relays/relays'; @NgModule({ declarations: [ @@ -201,6 +202,7 @@ import { LabelComponent } from './shared/label/label'; ArticleComponent, LabelComponent, LabelPipe, + RelaysManagementComponent ], imports: [ AboutModule, diff --git a/src/app/relays/relays.css b/src/app/relays/relays.css new file mode 100644 index 0000000..2392e7e --- /dev/null +++ b/src/app/relays/relays.css @@ -0,0 +1,87 @@ +.example-action-buttons { + padding-bottom: 20px; +} + +.example-headers-align .mat-expansion-panel-header-description { + justify-content: space-between; + align-items: center; +} + +.example-headers-align .mat-mdc-form-field + .mat-mdc-form-field { + margin-left: 8px; +} + +.online { + margin-left: 0.2em; + margin-bottom: -0.2em; +} + +.relay-status-0 { + color: silver; +} + +.relay-status-1 { + color: green; +} + +.relay-status-2 { + color: orange; +} + +.relay-status-3 { + color: red; +} + +.relay-status-4 { + color: rgb(49, 49, 210); +} + +.relay-read-disabled { + color: rgb(49, 49, 210) !important; +} + +.relay-disabled { + color: rgb(234, 136, 9) !important; +} + +.primary-relay { + color: rgb(198, 3, 181); +} + +.relay-options { + margin-top: 0.4em; + margin-bottom: 0.2em; +} + +.settings-action-buttons { + padding-top: 0.8em; + padding-bottom: 1em; +} + +.settings-action-buttons button { + margin-bottom: 1em; + margin-right: 1em; +} + +/* When changing the sidenav-content to flex, the toolbar does not render properly, so a minor hack is needed. */ +@media only screen and (max-width: 599px) { + .settings-action-buttons button { + width: 100%; + margin-right: 0; + } + + .mat-expansion-panel-header-title { + flex-grow: 2 !important; + } + + .mat-expansion-panel-header-description { + flex-grow: 1 !important; + } +} + +.relay-button { + margin-top: 0.8em; +} +.options-slider { + margin-left: 1em; +} diff --git a/src/app/relays/relays.html b/src/app/relays/relays.html new file mode 100644 index 0000000..3042542 --- /dev/null +++ b/src/app/relays/relays.html @@ -0,0 +1,25 @@ +
+
+
+ + + + Options + + + +
+ + +
+

+ +
+
+
+
+ + +
+
diff --git a/src/app/relays/relays.ts b/src/app/relays/relays.ts new file mode 100644 index 0000000..64bd906 --- /dev/null +++ b/src/app/relays/relays.ts @@ -0,0 +1,163 @@ +import { Component, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { MatAccordion } from '@angular/material/expansion'; +import { Relay } from 'nostr-tools'; +import { ApplicationState } from '../services/applicationstate'; +import { StorageService } from '../services/storage'; +import { EventService } from '../services/event'; +import { NostrRelay } from '../services/interfaces'; +import { ProfileService } from '../services/profile'; +import { RelayService } from '../services/relay'; +import { ThemeService } from '../services/theme'; +import { AddRelayDialog, AddRelayDialogData } from '../shared/add-relay-dialog/add-relay-dialog'; +import { OptionsService } from '../services/options'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { DataService } from '../services/data'; +import { NostrService } from '../services/nostr'; +import { UploadService } from '../services/upload'; + +@Component({ + selector: 'app-relays-management', + templateUrl: './relays.html', + styleUrls: ['./relays.css'], +}) +export class RelaysManagementComponent { + @ViewChild(MatAccordion) accordion!: MatAccordion; + + wiped = false; + wipedNonFollow = false; + wipedNotes = false; + open = false; + + constructor( + public uploadService: UploadService, + private nostr: NostrService, + public optionsService: OptionsService, + public relayService: RelayService, + public dialog: MatDialog, + public appState: ApplicationState, + private profileService: ProfileService, + public theme: ThemeService, + public db: StorageService, + private snackBar: MatSnackBar, + public dataService: DataService + ) {} + + toggle() { + if (this.open) { + this.open = false; + this.accordion.closeAll(); + } else { + this.open = true; + this.accordion.openAll(); + } + } + + openMediaPlayer() { + this.optionsService.values.showMediaPlayer = true; + } + + async primaryRelay(relay: NostrRelay) { + this.optionsService.values.primaryRelay = relay.url; + this.optionsService.save(); + } + + // async deleteRelay(relay: Relay) { + // await this.relayService.deleteRelay(relay.url); + // } + + async deleteRelays() { + await this.relayService.deleteRelays([]); + } + + async clearProfileCache() { + // await this.profileService.wipeNonFollow(); + this.wipedNonFollow = true; + } + + // async onRelayChanged(relay: NostrRelay) { + // if (relay.metadata.enabled && relay.metadata.read) { + // await relay.connect(); + // } else if (!relay.metadata.read) { + // await relay.close(); + // } else { + // await relay.close(); + // } + + // await this.relayService.putRelayMetadata(relay.metadata); + // } + + async clearNotesCache() { + // await this.feedService.wipe(); + this.wipedNotes = true; + } + + async getDefaultRelays() { + // Append the default relays. + await this.relayService.appendRelays(this.nostr.defaultRelays); + } + + // private getPublicPublicKeys() { + // console.log(this.profileService.following); + // const items: string[] = []; + + // for (let i = 0; i < this.circleService.circles.length; i++) { + // const circle = this.circleService.circles[i]; + + // if (circle.public) { + // const profiles = this.getFollowingInCircle(circle.id); + // const pubkeys = profiles.map((p) => p.pubkey); + // items.push(...pubkeys); + // } + // } + + // return items; + // } + + async getRelays() { + const relays = await this.nostr.relays(); + + // Append the default relays. + await this.relayService.appendRelays(relays); + } + + ngOnInit() { + this.appState.updateTitle('Relays'); + this.appState.showBackButton = false; + this.appState.actions = [ + { + icon: 'add_circle', + tooltip: 'Add Relay', + click: () => { + this.addRelay(); + }, + }, + ]; + } + + registerHandler(protocol: string, parameter: string) { + // navigator.registerProtocolHandler(protocol, `./index.html?${parameter}=%s`); + navigator.registerProtocolHandler(protocol, `/?${parameter}=%s`); + } + + addRelay(): void { + const dialogRef = this.dialog.open(AddRelayDialog, { + data: { read: true, write: true }, + maxWidth: '100vw', + panelClass: 'full-width-dialog', + }); + + dialogRef.afterClosed().subscribe(async (result: AddRelayDialogData) => { + if (!result) { + return; + } + + // Append the Web Socket prefix if missing. + if (result.url.indexOf('://') === -1) { + result.url = 'wss://' + result.url; + } + + await this.relayService.appendRelay(result.url, result.read, result.write); + }); + } +}