diff --git a/ngsw-config.json b/ngsw-config.json index 6c8ccf4..cda1354 100644 --- a/ngsw-config.json +++ b/ngsw-config.json @@ -1,29 +1,6 @@ { - "$schema": "./node_modules/@angular/service-worker/config/schema.json", - "index": "/index.html", - "assetGroups": [ - { - "name": "app", - "installMode": "prefetch", - "resources": { - "files": [ - "/favicon.ico", - "/index.html", - "/manifest.webmanifest", - "/*.css", - "/*.js" - ] - } - }, - { - "name": "assets", - "installMode": "lazy", - "updateMode": "prefetch", - "resources": { - "files": [ - "/**/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)" - ] - } - } - ] + "$schema": "./node_modules/@angular/service-worker/config/schema.json", + "index": "/index.html", + "assetGroups": [], + "navigationRequestStrategy": "freshness" } diff --git a/src/app/components/chat/chats/chats.component.ts b/src/app/components/chat/chats/chats.component.ts index d7b150d..a550003 100644 --- a/src/app/components/chat/chats/chats.component.ts +++ b/src/app/components/chat/chats/chats.component.ts @@ -19,7 +19,7 @@ import { ChatService } from '../chat.service'; import { Chat, Profile } from '../chat.types'; import { NewChatComponent } from '../new-chat/new-chat.component'; import { ProfileComponent } from '../profile/profile.component'; -import { AgoPipe } from 'app/shared/ago.pipe'; +import { AgoPipe } from 'app/shared/pipes/ago.pipe'; @Component({ diff --git a/src/app/components/home/home.component.html b/src/app/components/home/home.component.html index 388fa08..bd0697b 100644 --- a/src/app/components/home/home.component.html +++ b/src/app/components/home/home.component.html @@ -1,7 +1,6 @@ -
Angor Hub is a Nostr client that is customized around the Angor protocol, a decentralized crowdfunding platform. Leveraging the power of Nostr the platform allows you to explore projects that are raising funds using Angor, engage with investors, and connect directly with founders. diff --git a/src/app/services/update.service.ts b/src/app/services/update.service.ts new file mode 100644 index 0000000..12c2982 --- /dev/null +++ b/src/app/services/update.service.ts @@ -0,0 +1,78 @@ +import { Injectable, NgZone } from '@angular/core'; +import { SwUpdate } from '@angular/service-worker'; +import { Subscription, interval } from 'rxjs'; + +@Injectable({ providedIn: 'root' }) +export class NewVersionCheckerService { + isNewVersionAvailable: boolean = false; + newVersionSubscription?: Subscription; + intervalSource = interval(15 * 60 * 1000); // every 15 mins + intervalSubscription?: Subscription; + + constructor( + private swUpdate: SwUpdate, + private zone: NgZone, + ) { + this.checkForUpdateOnInterval(); + this.checkForUpdateOnLoad(); + } + + applyUpdate(): void { + // Reload the page to update to the latest version after the new version is activated + this.swUpdate + .activateUpdate() + .then(() => document.location.reload()) + .catch((error) => console.error('Failed to apply updates:', error)); + } + + checkForUpdateOnInterval(): void { + this.intervalSubscription?.unsubscribe(); + if (!this.swUpdate.isEnabled) { + return; + } + + this.zone.runOutsideAngular(() => { + this.intervalSubscription = this.intervalSource.subscribe(async () => { + if (!this.isNewVersionAvailable) { + try { + this.isNewVersionAvailable = await this.swUpdate.checkForUpdate(); + console.log(this.isNewVersionAvailable ? 'A new version is available.' : 'Already on the latest version.'); + } catch (error) { + console.error('Failed to check for updates:', error); + } + } else { + // Check for updates at interval, which will keep the + // browser updating to latest version as long as it's being kept open. + await this.swUpdate.checkForUpdate(); + } + }); + }); + } + + checkForUpdateOnLoad(): void { + this.newVersionSubscription?.unsubscribe(); + if (!this.swUpdate.isEnabled) { + console.log('Service worker updates are disabled for this app.'); + return; + } + this.newVersionSubscription = this.swUpdate.versionUpdates.subscribe((evt) => { + console.log('New version update event:'); + console.log(evt); + switch (evt.type) { + case 'VERSION_DETECTED': + console.log(`Downloading new app version: ${evt.version.hash}`); + break; + case 'VERSION_READY': + console.log(`Current app version: ${evt.currentVersion.hash}`); + console.log(`New app version ready for use: ${evt.latestVersion.hash}`); + this.isNewVersionAvailable = true; + break; + case 'VERSION_INSTALLATION_FAILED': + console.log(`Failed to install app version '${evt.version.hash}': ${evt.error}`); + break; + } + }); + + console.log('Subscribed to new version updates.'); + } +} diff --git a/src/app/shared/ago.pipe.ts b/src/app/shared/pipes/ago.pipe.ts similarity index 100% rename from src/app/shared/ago.pipe.ts rename to src/app/shared/pipes/ago.pipe.ts diff --git a/src/app/shared/pipes/safe-url.pipe.ts b/src/app/shared/pipes/safe-url.pipe.ts new file mode 100644 index 0000000..0fcf90b --- /dev/null +++ b/src/app/shared/pipes/safe-url.pipe.ts @@ -0,0 +1,14 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; + +@Pipe({ + name: 'safeResourceUrl', + standalone: true, +}) +export class SafeUrlPipe implements PipeTransform { + constructor(private readonly sanitizer: DomSanitizer) {} + + public transform(url: string): SafeResourceUrl { + return this.sanitizer.bypassSecurityTrustResourceUrl(url); + } +} diff --git a/src/app/shared/pipes/size.pipe.spec.ts b/src/app/shared/pipes/size.pipe.spec.ts new file mode 100644 index 0000000..b1b22f2 --- /dev/null +++ b/src/app/shared/pipes/size.pipe.spec.ts @@ -0,0 +1,8 @@ +import { SizePipe } from './size.pipe'; + +describe('SizePipe', () => { + it('create an instance', () => { + const pipe = new SizePipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/app/shared/pipes/size.pipe.ts b/src/app/shared/pipes/size.pipe.ts new file mode 100644 index 0000000..03c47fc --- /dev/null +++ b/src/app/shared/pipes/size.pipe.ts @@ -0,0 +1,23 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'size', + standalone: true, +}) +export class SizePipe implements PipeTransform { + transform(value: any, args?: any): any { + if (value == null) { + return ''; + } + + if (value === 0) { + return '0 Bytes'; + } + + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; + const i = Math.floor(Math.log(value) / Math.log(k)); + + return parseFloat((value / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + } +} diff --git a/src/app/shared/pipes/stripHtml.ts b/src/app/shared/pipes/stripHtml.ts new file mode 100644 index 0000000..9e4745b --- /dev/null +++ b/src/app/shared/pipes/stripHtml.ts @@ -0,0 +1,31 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'stripHtml', + standalone: true, +}) +export class StripHtmlPipe implements PipeTransform { + transform(value: string, limit: number = 200): string { + if (!value) { + return '...'; + } + + // Strip HTML tags and replace them with a space + let strippedText = value.replace(/<\/?[^>]+(>|$)/g, ' '); + + // Replace with a space + strippedText = strippedText.replace(/ /g, ' '); + + // Remove extra spaces + const normalizedText = strippedText.replace(/\s\s+/g, ' ').trim(); + + // Limit the text to the specified number of characters + let result = normalizedText; + if (normalizedText.length > limit) { + result = normalizedText.substring(0, limit); + } + + // Always add "..." at the end + return result.trim() + '...'; + } +} diff --git a/src/app/shared/timestamp.pipe.ts b/src/app/shared/pipes/timestamp.pipe.ts similarity index 100% rename from src/app/shared/timestamp.pipe.ts rename to src/app/shared/pipes/timestamp.pipe.ts diff --git a/src/app/shared/truncate.pipe.ts b/src/app/shared/pipes/truncate.pipe.ts similarity index 100% rename from src/app/shared/truncate.pipe.ts rename to src/app/shared/pipes/truncate.pipe.ts