mirror of
https://github.com/block-core/angor-hub-old.git
synced 2025-12-17 01:44:19 +01:00
Add NewVersionCheckerService
This commit is contained in:
@@ -1,29 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "./node_modules/@angular/service-worker/config/schema.json",
|
"$schema": "./node_modules/@angular/service-worker/config/schema.json",
|
||||||
"index": "/index.html",
|
"index": "/index.html",
|
||||||
"assetGroups": [
|
"assetGroups": [],
|
||||||
{
|
"navigationRequestStrategy": "freshness"
|
||||||
"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)"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { ChatService } from '../chat.service';
|
|||||||
import { Chat, Profile } from '../chat.types';
|
import { Chat, Profile } from '../chat.types';
|
||||||
import { NewChatComponent } from '../new-chat/new-chat.component';
|
import { NewChatComponent } from '../new-chat/new-chat.component';
|
||||||
import { ProfileComponent } from '../profile/profile.component';
|
import { ProfileComponent } from '../profile/profile.component';
|
||||||
import { AgoPipe } from 'app/shared/ago.pipe';
|
import { AgoPipe } from 'app/shared/pipes/ago.pipe';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<div class="flex h-full w-full flex-col items-center justify-center">
|
<div class="flex h-full w-full flex-col items-center justify-center p-10">
|
||||||
<div class="w-full max-w-3xl">
|
<div class="w-full max-w-3xl">
|
||||||
<div class="prose prose-sm mx-auto max-w-none">
|
<div class="prose prose-sm mx-auto max-w-none">
|
||||||
<img class="w-20" src="images/logo/logo.svg" alt="Angor Hub" />
|
|
||||||
<h1>Welcome to Angor Hub</h1>
|
<h1>Welcome to Angor Hub</h1>
|
||||||
<p>
|
<p>
|
||||||
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.
|
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.
|
||||||
|
|||||||
78
src/app/services/update.service.ts
Normal file
78
src/app/services/update.service.ts
Normal file
@@ -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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/app/shared/pipes/safe-url.pipe.ts
Normal file
14
src/app/shared/pipes/safe-url.pipe.ts
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/app/shared/pipes/size.pipe.spec.ts
Normal file
8
src/app/shared/pipes/size.pipe.spec.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { SizePipe } from './size.pipe';
|
||||||
|
|
||||||
|
describe('SizePipe', () => {
|
||||||
|
it('create an instance', () => {
|
||||||
|
const pipe = new SizePipe();
|
||||||
|
expect(pipe).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
23
src/app/shared/pipes/size.pipe.ts
Normal file
23
src/app/shared/pipes/size.pipe.ts
Normal file
@@ -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];
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/app/shared/pipes/stripHtml.ts
Normal file
31
src/app/shared/pipes/stripHtml.ts
Normal file
@@ -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() + '...';
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user