mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 22:39:46 +00:00
begin popover for UI launch select
This commit is contained in:
committed by
Aiden McClelland
parent
cb7790ccba
commit
ee9c328606
@@ -6,8 +6,8 @@ import { SharedPipesModule } from '@start9labs/shared'
|
|||||||
import {
|
import {
|
||||||
AppInterfacesItemComponent,
|
AppInterfacesItemComponent,
|
||||||
AppInterfacesPage,
|
AppInterfacesPage,
|
||||||
AddressTypePipe,
|
|
||||||
} from './app-interfaces.page'
|
} from './app-interfaces.page'
|
||||||
|
import { UiPipeModule } from 'src/app/pipes/ui/ui.module'
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@@ -22,11 +22,8 @@ const routes: Routes = [
|
|||||||
IonicModule,
|
IonicModule,
|
||||||
RouterModule.forChild(routes),
|
RouterModule.forChild(routes),
|
||||||
SharedPipesModule,
|
SharedPipesModule,
|
||||||
|
UiPipeModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [AppInterfacesPage, AppInterfacesItemComponent],
|
||||||
AppInterfacesPage,
|
|
||||||
AppInterfacesItemComponent,
|
|
||||||
AddressTypePipe,
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class AppInterfacesPageModule {}
|
export class AppInterfacesPageModule {}
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
import {
|
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||||
ChangeDetectionStrategy,
|
|
||||||
Component,
|
|
||||||
Input,
|
|
||||||
Pipe,
|
|
||||||
PipeTransform,
|
|
||||||
} from '@angular/core'
|
|
||||||
import { ActivatedRoute } from '@angular/router'
|
import { ActivatedRoute } from '@angular/router'
|
||||||
import { ModalController, ToastController } from '@ionic/angular'
|
import { ModalController, ToastController } from '@ionic/angular'
|
||||||
import { getPkgId, copyToClipboard } from '@start9labs/shared'
|
import { getPkgId, copyToClipboard } from '@start9labs/shared'
|
||||||
@@ -80,31 +74,3 @@ export class AppInterfacesItemComponent {
|
|||||||
await toast.present()
|
await toast.present()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Pipe({
|
|
||||||
name: 'addressType',
|
|
||||||
})
|
|
||||||
export class AddressTypePipe implements PipeTransform {
|
|
||||||
transform(address: string): string {
|
|
||||||
if (isValidIpv4(address)) return 'IPv4'
|
|
||||||
if (isValidIpv6(address)) return 'IPv6'
|
|
||||||
|
|
||||||
const hostname = new URL(address).hostname
|
|
||||||
if (hostname.endsWith('.onion')) return 'Tor'
|
|
||||||
if (hostname.endsWith('.local')) return 'Local'
|
|
||||||
|
|
||||||
return 'Custom'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isValidIpv4(address: string): boolean {
|
|
||||||
const regexExp =
|
|
||||||
/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
|
|
||||||
return regexExp.test(address)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isValidIpv6(address: string): boolean {
|
|
||||||
const regexExp =
|
|
||||||
/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/gi
|
|
||||||
return regexExp.test(address)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -25,10 +25,35 @@
|
|||||||
slot="end"
|
slot="end"
|
||||||
fill="clear"
|
fill="clear"
|
||||||
color="primary"
|
color="primary"
|
||||||
(click)="launchUi($event, installed['address-info'])"
|
(click)="openPopover($event)"
|
||||||
[disabled]="status !== 'running'"
|
[disabled]="status !== 'running'"
|
||||||
>
|
>
|
||||||
<ion-icon slot="icon-only" name="open-outline"></ion-icon>
|
<ion-icon slot="icon-only" name="open-outline"></ion-icon>
|
||||||
</ion-button>
|
</ion-button>
|
||||||
|
<ion-popover
|
||||||
|
#popover
|
||||||
|
[isOpen]="isPopoverOpen"
|
||||||
|
(didDismiss)="isPopoverOpen = false"
|
||||||
|
mode="ios"
|
||||||
|
type="event"
|
||||||
|
>
|
||||||
|
<ng-template>
|
||||||
|
<ion-content>
|
||||||
|
<ion-item-group>
|
||||||
|
<ng-container
|
||||||
|
*ngFor="let uiAddress of installed['address-info'] | uiAddresses"
|
||||||
|
>
|
||||||
|
<ion-item-divider>{{ uiAddress.name }}</ion-item-divider>
|
||||||
|
<ion-item button *ngFor="let address of uiAddress.addresses">
|
||||||
|
<ion-label>
|
||||||
|
<h2>{{ address | addressType }}</h2>
|
||||||
|
<p>{{ address }}</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ng-container>
|
||||||
|
</ion-item-group>
|
||||||
|
</ion-content>
|
||||||
|
</ng-template>
|
||||||
|
</ion-popover>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ion-popover {
|
||||||
|
--min-width: 300px;
|
||||||
|
}
|
||||||
@@ -1,21 +1,25 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
|
||||||
import {
|
import {
|
||||||
InstalledPackageInfo,
|
ChangeDetectionStrategy,
|
||||||
PackageMainStatus,
|
Component,
|
||||||
} from 'src/app/services/patch-db/data-model'
|
Input,
|
||||||
|
ViewChild,
|
||||||
|
} from '@angular/core'
|
||||||
|
import { PackageMainStatus } from 'src/app/services/patch-db/data-model'
|
||||||
import { PkgInfo } from 'src/app/util/get-package-info'
|
import { PkgInfo } from 'src/app/util/get-package-info'
|
||||||
import { UiLauncherService } from 'src/app/services/ui-launcher.service'
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-list-pkg',
|
selector: 'app-list-pkg',
|
||||||
templateUrl: 'app-list-pkg.component.html',
|
templateUrl: 'app-list-pkg.component.html',
|
||||||
|
styleUrls: ['app-list-pkg.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class AppListPkgComponent {
|
export class AppListPkgComponent {
|
||||||
|
@ViewChild('popover') popover!: HTMLIonPopoverElement
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
pkg!: PkgInfo
|
pkg!: PkgInfo
|
||||||
|
|
||||||
constructor(private readonly launcherService: UiLauncherService) {}
|
isPopoverOpen = false
|
||||||
|
|
||||||
get status(): PackageMainStatus {
|
get status(): PackageMainStatus {
|
||||||
return (
|
return (
|
||||||
@@ -23,9 +27,10 @@ export class AppListPkgComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
launchUi(e: Event, addressInfo: InstalledPackageInfo['address-info']): void {
|
openPopover(e: Event): void {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.launcherService.launch(addressInfo)
|
this.popover.event = e
|
||||||
|
this.isPopoverOpen = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { UiPipe } from './ui.pipe'
|
import { UiPipe, UiAddressesPipe, AddressTypePipe } from './ui.pipe'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [UiPipe],
|
declarations: [UiPipe, UiAddressesPipe, AddressTypePipe],
|
||||||
exports: [UiPipe],
|
exports: [UiPipe, UiAddressesPipe, AddressTypePipe],
|
||||||
})
|
})
|
||||||
export class UiPipeModule {}
|
export class UiPipeModule {}
|
||||||
|
|||||||
@@ -10,3 +10,47 @@ export class UiPipe implements PipeTransform {
|
|||||||
return hasUi(addressInfo)
|
return hasUi(addressInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'uiAddresses',
|
||||||
|
})
|
||||||
|
export class UiAddressesPipe implements PipeTransform {
|
||||||
|
transform(
|
||||||
|
addressInfo: InstalledPackageInfo['address-info'],
|
||||||
|
): { name: string; addresses: string[] }[] {
|
||||||
|
return Object.values(addressInfo)
|
||||||
|
.filter(info => info.ui)
|
||||||
|
.map(info => ({
|
||||||
|
name: info.name,
|
||||||
|
addresses: info.addresses,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'addressType',
|
||||||
|
})
|
||||||
|
export class AddressTypePipe implements PipeTransform {
|
||||||
|
transform(address: string): string {
|
||||||
|
if (isValidIpv4(address)) return 'IPv4'
|
||||||
|
if (isValidIpv6(address)) return 'IPv6'
|
||||||
|
|
||||||
|
const hostname = new URL(address).hostname
|
||||||
|
if (hostname.endsWith('.onion')) return 'Tor'
|
||||||
|
if (hostname.endsWith('.local')) return 'Local'
|
||||||
|
|
||||||
|
return 'Custom'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidIpv4(address: string): boolean {
|
||||||
|
const regexExp =
|
||||||
|
/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
|
||||||
|
return regexExp.test(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidIpv6(address: string): boolean {
|
||||||
|
const regexExp =
|
||||||
|
/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/gi
|
||||||
|
return regexExp.test(address)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user