[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[reclaim-ui] 02/459: Migrated from internal devops without history
From: |
gnunet |
Subject: |
[reclaim-ui] 02/459: Migrated from internal devops without history |
Date: |
Fri, 11 Jun 2021 23:21:34 +0200 |
This is an automated email from the git hooks/post-receive script.
martin-schanzenbach pushed a commit to branch master
in repository reclaim-ui.
commit 253233a36dc736070c5ab870e3c36d8133b0a00c
Author: Schanzenbach, Martin <mschanzenbach@posteo.de>
AuthorDate: Tue Nov 20 15:23:41 2018 +0100
Migrated from internal devops without history
---
Dockerfile | 35 ++
docker-entrypoint.sh | 12 +
package.json | 41 ++
proxy.conf.json | 10 +
src/app/app-routing.module.ts | 14 +
src/app/app.component.html | 2 +
src/app/app.component.scss | 19 +
src/app/app.component.ts | 10 +
src/app/app.module.ts | 44 +++
src/app/attribute.ts | 5 +
src/app/config.service.ts | 19 +
src/app/config.ts | 3 +
src/app/gns-response.ts | 3 +
src/app/gns.service.ts | 17 +
src/app/gnu-net-response.ts | 3 +
src/app/identity-list/identity-list.component.html | 214 ++++++++++
src/app/identity-list/identity-list.component.scss | 46 +++
src/app/identity-list/identity-list.component.ts | 435 +++++++++++++++++++++
src/app/identity.service.ts | 29 ++
src/app/identity.ts | 4 +
src/app/namestore-response.ts | 3 +
src/app/namestore.service.ts | 17 +
src/app/open-id.service.ts | 52 +++
src/app/reclaim.service.ts | 34 ++
src/app/record.ts | 5 +
src/app/rxjs.ts | 1 +
src/app/ticket.ts | 5 +
src/assets/Audiowide-Regular.ttf | Bin 0 -> 69916 bytes
src/assets/config.json | 3 +
src/assets/reclaim_icon.png | Bin 0 -> 7834 bytes
src/assets/reclaim_small.png | Bin 0 -> 65046 bytes
src/environments/environment.prod.ts | 3 +
src/environments/environment.ts | 8 +
src/index.html | 17 +
src/main.ts | 12 +
src/polyfills.ts | 79 ++++
src/styles.scss | 57 +++
src/tsconfig.app.json | 13 +
src/typings.d.ts | 5 +
tsconfig.json | 19 +
tslint.json | 154 ++++++++
41 files changed, 1452 insertions(+)
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..7814a8d
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,35 @@
+FROM node:8-alpine AS build
+
+LABEL maintainer="Christian Banse <christian.banse@aisec.fraunhofer.de>"
+
+EXPOSE 80
+
+WORKDIR /tmp
+
+# this should hopefully trigger Docker to only update yarn if dependencies
have changed
+ADD *.json ./
+ADD *.lock ./
+RUN yarn install
+
+# add the rest of the files
+ADD . .
+
+# set environment to production
+ENV NODE_ENV production
+
+# lint
+#RUN yarn lint
+
+# build everything for production
+RUN yarn run build --prod
+
+FROM nginx:alpine
+
+# copy to nginx
+COPY --from=build /tmp/dist /usr/share/nginx/html/
+
+ADD ./docker-entrypoint.sh /
+ENTRYPOINT ["/bin/sh", "/docker-entrypoint.sh"]
+
+EXPOSE 80
+CMD ["nginx"]
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
new file mode 100644
index 0000000..b55d7d7
--- /dev/null
+++ b/docker-entrypoint.sh
@@ -0,0 +1,12 @@
+#/bin/bash
+set -a
+
+if [ -n "$API_URL" ]; then
+ echo {\"apiUrl\": \"${API_URL}\"} > /usr/share/nginx/html/assets/config.json
+fi
+
+if [ "$1" = 'nginx' ]; then
+ exec nginx -g "daemon off;"
+fi
+
+exec "$@"
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..9d30681
--- /dev/null
+++ b/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "reclaim-ui",
+ "version": "0.0.0",
+ "license": "MIT",
+ "scripts": {
+ "ng": "ng",
+ "start": "ng serve --proxy-config proxy.conf.json",
+ "build": "ng build --prod",
+ "test": "ng test",
+ "lint": "ng lint",
+ "e2e": "ng e2e"
+ },
+ "private": true,
+ "dependencies": {
+ "@angular/animations": "^5.2.0",
+ "@angular/common": "^5.2.0",
+ "@angular/compiler": "^5.2.0",
+ "@angular/core": "^5.2.0",
+ "@angular/forms": "^5.2.0",
+ "@angular/http": "^5.2.0",
+ "@angular/platform-browser": "^5.2.0",
+ "@angular/platform-browser-dynamic": "^5.2.0",
+ "@angular/router": "^5.2.0",
+ "bootstrap": "4",
+ "core-js": "^2.4.1",
+ "font-awesome": "^4.7.0",
+ "jquery": "^3.3.1",
+ "popper.js": "^1.12.9",
+ "rxjs": "^5.5.6",
+ "zone.js": "^0.8.19"
+ },
+ "devDependencies": {
+ "@angular/cli": "~1.7.2",
+ "@angular/compiler-cli": "^5.2.0",
+ "@angular/language-service": "^5.2.0",
+ "codelyzer": "^4.2.1",
+ "rxjs-tslint-rules": "^3.14.0",
+ "tslint": "^5.9.1",
+ "typescript": "~2.5.3"
+ }
+}
diff --git a/proxy.conf.json b/proxy.conf.json
new file mode 100644
index 0000000..a277703
--- /dev/null
+++ b/proxy.conf.json
@@ -0,0 +1,10 @@
+{
+ "/api": {
+ "target": "http://localhost:7776",
+ "secure": false,
+ "pathRewrite": {
+ "^/api": ""
+ },
+ "changeOrigin": true
+ }
+}
\ No newline at end of file
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
new file mode 100644
index 0000000..9b75a7e
--- /dev/null
+++ b/src/app/app-routing.module.ts
@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { IdentityListComponent } from
'./identity-list/identity-list.component';
+
+const routes: Routes = [
+ { path: 'identities', component: IdentityListComponent },
+ { path: '', redirectTo: '/identities', pathMatch: 'full' },
+];
+
+@NgModule({
+ imports: [RouterModule.forRoot(routes, { useHash: true })],
+ exports: [RouterModule]
+})
+export class AppRoutingModule { }
diff --git a/src/app/app.component.html b/src/app/app.component.html
new file mode 100644
index 0000000..183735d
--- /dev/null
+++ b/src/app/app.component.html
@@ -0,0 +1,2 @@
+<div class="logo"><img src="assets/reclaim_icon.png"/></div>
+<router-outlet></router-outlet>
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
new file mode 100644
index 0000000..7a249b0
--- /dev/null
+++ b/src/app/app.component.scss
@@ -0,0 +1,19 @@
+
+@font-face {
+ font-family: 'Audiowide';
+ src: url('assets/Audiowide-Regular.ttf');
+ font-weight: normal;
+ font-style: normal;
+}
+
+.logo {
+ font-family: Audiowide;
+ font-size: 32px;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.125);
+ background-color: #fff;
+ text-align: center;
+}
+
+.logo img {
+ width: 200px;
+}
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
new file mode 100644
index 0000000..7d943bc
--- /dev/null
+++ b/src/app/app.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-root',
+ templateUrl: './app.component.html',
+ styleUrls: ['./app.component.scss']
+})
+export class AppComponent {
+ title = 'app';
+}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
new file mode 100644
index 0000000..3e988e5
--- /dev/null
+++ b/src/app/app.module.ts
@@ -0,0 +1,44 @@
+import { BrowserModule } from '@angular/platform-browser';
+import { APP_INITIALIZER, NgModule } from '@angular/core';
+import { HttpClientModule } from '@angular/common/http';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+import { IdentityListComponent } from
'./identity-list/identity-list.component';
+import { AppRoutingModule } from './app-routing.module';
+import { IdentityService } from './identity.service';
+import { ReclaimService } from './reclaim.service';
+import { NamestoreService } from './namestore.service';
+import { GnsService } from './gns.service';
+import { ConfigService } from './config.service';
+
+import './rxjs';
+import { OpenIdService } from './open-id.service';
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ IdentityListComponent,
+ ],
+ imports: [
+ BrowserModule,
+ AppRoutingModule,
+ HttpClientModule,
+ FormsModule
+ ],
+ providers: [
+ IdentityService,
+ ReclaimService,
+ NamestoreService,
+ GnsService,
+ OpenIdService,
+ ConfigService,
+ {
+ provide: APP_INITIALIZER,
+ useFactory: (config: ConfigService) => () => config.load(),
+ deps: [ConfigService], multi: true
+ }
+ ],
+ bootstrap: [AppComponent]
+})
+export class AppModule { }
diff --git a/src/app/attribute.ts b/src/app/attribute.ts
new file mode 100644
index 0000000..8f17fb8
--- /dev/null
+++ b/src/app/attribute.ts
@@ -0,0 +1,5 @@
+export class Attribute {
+ constructor(public name: string,
+ public value: string,
+ public type: string) { }
+}
diff --git a/src/app/config.service.ts b/src/app/config.service.ts
new file mode 100644
index 0000000..c0eefd0
--- /dev/null
+++ b/src/app/config.service.ts
@@ -0,0 +1,19 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+import { Config } from './config';
+
+@Injectable()
+export class ConfigService {
+ config: Config;
+
+ constructor(private http: HttpClient) { }
+
+ get() {
+ return this.config;
+ }
+
+ async load() {
+ this.config = await
this.http.get<Config>('assets/config.json').toPromise();
+ }
+}
diff --git a/src/app/config.ts b/src/app/config.ts
new file mode 100644
index 0000000..7885b88
--- /dev/null
+++ b/src/app/config.ts
@@ -0,0 +1,3 @@
+export class Config {
+ constructor(public apiUrl: string) { }
+}
diff --git a/src/app/gns-response.ts b/src/app/gns-response.ts
new file mode 100644
index 0000000..ae2b2f7
--- /dev/null
+++ b/src/app/gns-response.ts
@@ -0,0 +1,3 @@
+export class GnuNetResponse {
+ constructor(public data: any) { }
+}
diff --git a/src/app/gns.service.ts b/src/app/gns.service.ts
new file mode 100644
index 0000000..1f0be69
--- /dev/null
+++ b/src/app/gns.service.ts
@@ -0,0 +1,17 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs/Observable';
+
+import { ConfigService } from './config.service';
+
+@Injectable()
+export class GnsService {
+
+ constructor(private http: HttpClient, private config: ConfigService) { }
+
+ getClientName(client_id) {
+ return this.http.get<any[]>(this.config.get().apiUrl
+ + '/gns/+.'+client_id+'?record_type=RECLAIM_OIDC_CLIENT');
+ }
+
+}
diff --git a/src/app/gnu-net-response.ts b/src/app/gnu-net-response.ts
new file mode 100644
index 0000000..ae2b2f7
--- /dev/null
+++ b/src/app/gnu-net-response.ts
@@ -0,0 +1,3 @@
+export class GnuNetResponse {
+ constructor(public data: any) { }
+}
diff --git a/src/app/identity-list/identity-list.component.html
b/src/app/identity-list/identity-list.component.html
new file mode 100644
index 0000000..29d5a52
--- /dev/null
+++ b/src/app/identity-list/identity-list.component.html
@@ -0,0 +1,214 @@
+<div *ngIf="inOpenIdFlow()" class="alert alert-primary alert-dismissible fade
show" role="alert">
+ <span class="fa fa-openid"></span> <strong>"{{ clientName }}"</strong> asks
you to share personal information.<br/>
+ Choose an identity to let it access the following information:
+ <ul>
+ <li *ngFor="let attribute of getScopes()"><strong>{{attribute}}</strong>
+ </ul>
+ <button class="btn btn-danger">
+ <span class="fa fa-ban"></span> Do not share information and return to
website
+ </button>
+</div>
+<div *ngIf="0 == identities.length && !isAddIdentity()" style="text-align:
center;" class="alert alert-secondary alert-dismissible fade show" role="alert">
+ You don't have any identities yet.<br/><br/>
+ <button class="btn btn-primary" (click)="addIdentity()">
+ <span class="fa fa-plus"></span> Add your first identity!
+ </button>
+</div>
+
+<div class="card" *ngIf="(null != identityInEdit) && !isAddIdentity()">
+ <div class="card-avatar card-img-top">
+ <div class="card-avatar-character text-dark">
+ Manage identity: {{ identityInEdit.name }}
+ </div>
+ </div>
+ <div class="card-body">
+ <div>
+
+ <h6 class="card-subtitle mb-2">Attributes:
+ </h6>
+
+
+ <table class="table pb-1" *ngIf="isAttributeMissing(identityInEdit)">
+ <tbody>
+ <tr [class.openid]="inOpenIdFlow()"
[class.alert-danger]="newAttribute.name === missing.name" class="text-primary"
*ngFor="let missing of missingAttributes[identityInEdit.pubkey]">
+ <td><div style="min-width: 15em">{{missing.name}}</div></td>
+ <td>
+ <input placeholder="Value" [(ngModel)]="missing.value">
+ </td>
+ <td>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="table pb-1" style="">
+ <tbody>
+ <tr [class.openid]="inOpenIdFlow()"
[class.text-primary]="isRequested(identityInEdit, attribute)"
[class.alert-danger]="newAttribute.name === attribute.name" *ngFor="let
attribute of attributes[identityInEdit.pubkey]">
+ <td><div style="min-width: 15em">{{attribute.name}}</div></td>
+ <td>
+ <input placeholder="Value" [(ngModel)]="attribute.value">
+ </td>
+ <td>
+ <button class="btn btn-primary"
(click)="deleteAttribute(attribute)">
+ <span class="fa fa-trash"></span>
+ </button>
+ </td>
+ </tr>
+ <tr [class.alert-danger]="isInConflict(identityInEdit,newAttribute)">
+ <td>
+ <input
[class.text-danger]="!attributeNameValid(identityInEdit,newAttribute)"
placeholder="Attribute" [(ngModel)]="newAttribute.name">
+ </td>
+ <td>
+ <input placeholder="Value"
[class.text-danger]="!attributeValueValid(newAttribute)"
[(ngModel)]="newAttribute.value">
+ </td>
+ <td>
+ <button
[disabled]="!canAddAttribute(identityInEdit,newAttribute)" class="btn
btn-primary" (click)="addAttribute(newAttribute)">
+ <span class="fa fa-plus"></span>
+ </button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div *ngIf="!attributeNameValid(identityInEdit,newAttribute) ||
!attributeValueValid(newAttribute)" class="alert alert-primary
alert-dismissible fade show" role="alert">
+ <span class="fa fa-warning"></span> Note:
+ <ul>
+ <li>Only use alphanumeric attribute names.</li>
+ <li>You cannot define the same name twice.</li>
+ <li>Attribute values may not be empty!</li>
+ </ul>
+ </div>
+
+ <div style="margin-top: 1.5em;">
+ <h6 class="card-subtitle mb-2" *ngIf="identityInEdit ==
showTicketsIdentity">Authorized Entities:
+ </h6>
+
+ <table class="table pb-1" *ngIf="identityInEdit == showTicketsIdentity">
+ <tbody>
+ <tr *ngFor="let ticket of tickets[identityInEdit.pubkey]">
+ <td><div style="min-width:
15em">{{identityNameMapper[ticket.audience]}}</div></td>
+ <td>
+ <button class="btn btn-primary"
(click)="revokeTicket(identityInEdit, ticket)">
+ <span class="fa fa-unlink"></span> Revoke
+ </button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div>
+ <button class="btn btn-primary"
(click)="saveIdentityAttributes(identityInEdit)"
[disabled]="!canSaveIdentity(identityInEdit)">
+ <span class="fa fa-save"></span> Save and Back
+ </button>
+ <button *ngIf="(0 < tickets[identityInEdit.pubkey]?.length) &&
!inOpenIdFlow()" class="btn btn-primary"
(click)="toggleShowTickets(identityInEdit)" [style.float]="'right'">
+ <span class="fa fa-openid"></span><span *ngIf="identityInEdit ==
showTicketsIdentity"> Hide</span><span *ngIf="identityInEdit !=
showTicketsIdentity"> Show</span> Authorizations
+ </button>
+ </div>
+ </div>
+
+</div>
+<div class="card-columns p-2" *ngIf="(null == identityInEdit) &&
!isAddIdentity()">
+ <div class="card" *ngFor="let identity of identities">
+ <div class="card-avatar card-img-top">
+ <div class="card-avatar-character text-dark">
+ <div class="icon m-1 text-uppercase"
[style.background-color]="intToRGB(identity.pubkey)">{{ identity.name[0]}}</div>
+ <span class="m-1" styl="display: inline-block">{{
identity.name}}</span>
+ <button class="btn btn-primary" *ngIf="showConfirmDelete != identity"
(click)="confirmDelete(identity)">
+ <span class="fa fa-trash"></span>
+ </button>
+ <button class="btn btn-primary" *ngIf="showConfirmDelete !=
identity" (click)="editIdentity(identity)">
+ <span class="fa fa-edit"></span>
+ </button>
+ </div>
+ </div>
+ <div class="alert alert-danger fade show" *ngIf="showConfirmDelete ==
identity">
+ Do you really want to delete this identity?<br/><br/>
+ <button class="btn btn-primary" (click)="deleteIdentity(identity)">
+ <span class="fa fa-trash"></span> Delete
+ </button>
+ <button class="btn btn-primary" (click)="hideConfirmDelete()">
+ <span class="fa fa-close"></span> Cancel
+ </button>
+ </div>
+ <div class="alert alert-secondary fade show"
*ngIf="!hasAttributes(identity)">
+ This identity has no attributes. Maybe try <a class="buttonlink"
(click)="editIdentity(identity)">adding some?</a>
+ </div>
+ <div *ngIf="isAttributeMissing(identity)" class="alert alert-danger
alert-dismissible fade show" role="alert">
+ <span class="fa fa-openid"></span> This identity cannot be used because
it's missing requested attributes:
+ <ul>
+ <li *ngFor="let attr of getMissing(identity)">{{attr}}</li>
+ </ul>
+ <button class="btn btn-primary" (click)="editIdentity(identity)">
+ <span class="fa fa-plus"></span> Add
+ </button>
+ </div>
+ <div class="card-body">
+ <div>
+ <h6 class="card-subtitle mb-2" *ngIf="hasAttributes(identity)">
+ <strong>Attributes:</strong>
+ </h6>
+ <h6 class="card-subtitle mb-2" *ngIf="!hasAttributes(identity)">
+ This identity has no attributes
+ </h6>
+ <table class="table pb-1">
+ <tbody>
+ <tr [class.openid]="inOpenIdFlow()"
[class.text-primary]="isRequested(identity, attribute)"
+ [class.alert-danger]="newAttribute.name === attribute.name"
*ngFor="let attribute of attributes[identity.pubkey]">
+ <td>
+ <div style="min-width: 15em">{{attribute.name}}</div>
+ </td>
+ <td>
+ <div style="min-width: 15em">{{attribute.value}}</div>
+ </td>
+ <td>
+ <div style="min-width: 8em;">
+ <span *ngIf="isRequested(identity, attribute)"
[style.float]="'right'" class="fa fa-openid" ></span>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div style="margin-bottom: 1em" *ngIf="(0 <
attributes[identity.pubkey]?.length) && inOpenIdFlow()" class="text-primary">
+ Only attributes marked with <span class="fa fa-openid" ></span> will
be shared.
+ </div>
+
+ <div>
+ <button *ngIf="canAuthorize(identity)" [disabled]="!inOpenIdFlow() ||
isAttributeMissing(identity)" (click)="loginIdentity(identity)" class="btn
btn-primary mr-1 openid-login">
+ <span class="fa fa-openid"></span> Share from this identity</button>
+ </div>
+ </div>
+ </div>
+
+
+</div>
+<div *ngIf="isAddIdentity()" class="card" style="text-align:center;">
+ <div class="card-avatar card-img-top">
+ <div class="card-avatar-character text-dark">
+ New Identity </div>
+ </div>
+ <div *ngIf="'' !== newIdentity.name && !canSave()" class="alert alert-danger
alert-dismissible fade show" role="alert">
+ Only Alphanumeric input. No spaces or special characters allowed.
+ </div>
+ <div *ngIf="'' === newIdentity.name || canSave()" class="alert
alert-secondary alert-dismissible fade show" role="alert">
+ Enter a username for your new identity
+ </div>
+ <div class="card-body">
+ <input [class.text-danger]="'' !== newIdentity.name && !canSave()"
style="text-align: center; border: 1px solid #111;"
(keyup.enter)="saveIdentity()" pattern="^[a-zA-Z0-9-]+" placeholder="Username"
title="Only Alphanumeric input. No spaces or special characters allowed."
class="mr-2" [(ngModel)]="newIdentity.name" autofocus>
+ <br/>
+ <br/>
+ <button [disabled]="!canSave()" [style.inactive]="!canSave()" class="btn
btn-primary" (click)="saveIdentity()">
+ <span class="fa fa-save"></span> Save
+ </button>
+ <button class="btn btn-danger" (click)="cancelAddIdentity()">
+ <span class="fa fa-close"></span> Cancel
+ </button>
+ </div>
+</div>
+<div *ngIf="0 != identities.length && !isAddIdentity() && (null ==
identityInEdit)" style="margin-top: 1em; text-align: center;">
+ <button class="btn btn-primary" (click)="addIdentity()">
+ <span class="fa fa-plus"></span> Add identity
+ </button>
+</div>
+
+
diff --git a/src/app/identity-list/identity-list.component.scss
b/src/app/identity-list/identity-list.component.scss
new file mode 100644
index 0000000..5cba5ef
--- /dev/null
+++ b/src/app/identity-list/identity-list.component.scss
@@ -0,0 +1,46 @@
+.card table {
+ table-layout: fixed;
+}
+
+.card table input {
+ width: 100%;
+}
+
+div.card-avatar {
+ text-align: center;
+ border-bottom: 2px solid rgba(0, 0, 0, 0.125);
+ //min-height: 50px;
+}
+
+div.card-avatar-character {
+ font-size: 2em;
+ border: 1px solid white;
+}
+
+div.card-avatar-id {
+ font-size: 1em;
+}
+
+.card.selected {
+ box-shadow: 0px 1px 2px -1px rgba(0, 0, 0, 0.5);
+ //border-color: #343a40;
+}
+
+.card tr.openid.text-dimmed {
+ color: #eee;
+}
+
+.card-avatar .btn-primary {
+ border: none;
+ background-color: rgba(0,0,0,0);
+ color: #555;
+ font-size: 0.75em;
+ float: right;
+}
+
+.card-avatar div.icon {
+ border-radius: 5em;
+ width: 1.5em;
+ color: white;
+ display: inline-block;
+}
diff --git a/src/app/identity-list/identity-list.component.ts
b/src/app/identity-list/identity-list.component.ts
new file mode 100644
index 0000000..96ac308
--- /dev/null
+++ b/src/app/identity-list/identity-list.component.ts
@@ -0,0 +1,435 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { IdentityService } from '../identity.service';
+import { OpenIdService } from '../open-id.service';
+import { Identity } from '../identity';
+import { Attribute } from '../attribute';
+import { Ticket } from '../ticket';
+import { ReclaimService } from '../reclaim.service';
+import { NamestoreService } from '../namestore.service';
+import { GnsService } from '../gns.service';
+import { Observable } from 'rxjs/Rx'
+import 'rxjs/add/observable/forkJoin';
+
+@Component({
+ selector: 'app-identity-list',
+ templateUrl: './identity-list.component.html',
+ styleUrls: ['./identity-list.component.scss']
+})
+export class IdentityListComponent implements OnInit {
+
+ requestedAttributes: any;
+ missingAttributes: any;
+ attributes: any;
+ tickets: any;
+ clientName: any;
+ identities: Identity[];
+ newIdentity: Identity;
+ newAttribute: Attribute;
+ identityInEdit: Identity;
+ identityInEditName: string;
+ identityNameMapper: any;
+ showTicketsIdentity: Identity;
+ showConfirmDelete: any;
+
+ constructor(private route: ActivatedRoute,
+ private router: Router,
+ private oidcService: OpenIdService,
+ private identityService: IdentityService,
+ private reclaimService: ReclaimService,
+ private namestoreService: NamestoreService,
+ private gnsService: GnsService) { }
+
+ ngOnInit() {
+ this.attributes = {};
+ this.tickets = {};
+ this.identities = [];
+ this.showConfirmDelete = null;
+ this.newAttribute = new Attribute('', '', 'STRING');
+ this.requestedAttributes = {};
+ this.missingAttributes = {};
+ this.oidcService.parseRouteParams(this.route.snapshot.queryParams);
+ this.getClientName();
+ //this.newIdentity = new Identity('', '', {});
+ this.identityInEditName = "";
+ this.identityNameMapper = {};
+ this.updateIdentities();
+ }
+
+ confirmDelete(identity) {
+ this.showConfirmDelete = identity;
+ }
+
+ hideConfirmDelete() {
+ this.showConfirmDelete = null;
+ }
+
+ getClientName() {
+ if (!this.inOpenIdFlow()) {
+ this.clientName = "-";
+ return;
+ }
+ this.clientName = this.oidcService.getClientId();
+ this.gnsService.getClientName(this.oidcService.getClientId()).subscribe
(records => {
+ console.log (records);
+ for (var i = 0; i < records.length; i++) {
+ if (records[i].record_type !== "RECLAIM_OIDC_CLIENT")
+ continue;
+ this.clientName = records[i].value;
+ break;
+ }
+ });
+ }
+
+ intToRGB(i) {
+ i = this.hashCode(i);
+ const c = (i & 0x00FFFFFF)
+ .toString(16)
+ .toUpperCase();
+
+ return '#' + '00000'.substring(0, 6 - c.length) + c;
+ }
+
+ hashCode(str) {
+ let hash = 0;
+ for (let i = 0; i < str.length; i++) {
+ hash = str.charCodeAt(i) + ((hash << 5) - hash);
+ }
+ return hash;
+ }
+
+ isAddIdentity() {
+ return null != this.newIdentity;
+ }
+
+ canSave() {
+ if (this.newIdentity.name == null) {
+ return false;
+ }
+ if (this.newIdentity.name === '') {
+ return false;
+ }
+ if (!/^[a-zA-Z0-9-]+$/.test(this.newIdentity.name)) {
+ return false;
+ }
+ return true;
+ }
+
+ addIdentity() {
+ this.newIdentity = new Identity ('','');
+ }
+
+ editIdentity(identity) {
+ this.identityInEdit = identity;
+ this.showTicketsIdentity = null;
+ }
+
+ isInEdit(identity) {
+ return this.identityInEdit == identity;
+ }
+
+ saveIdentityAttributes(identity) {
+ this.storeAttributes(identity)
+ .finally(() => this.updateAttributes(identity))
+ .subscribe(
+ res => console.log(res),
+ error => {return Observable.empty()},
+ () => {
+ this.identityInEdit = null;
+ this.updateAttributes(identity);
+ }
+ );
+ this.newAttribute.name = '';
+ this.newAttribute.value = '';
+ this.newAttribute.type = "STRING";
+ this.identityInEdit = null;
+ }
+
+ deleteAttribute(attribute) {
+ this.namestoreService.deleteName(this.identityInEdit,
attribute.name).subscribe (data => {
+ this.updateAttributes(this.identityInEdit);
+ });
+ }
+ getMissingAttributes(identity) {
+ let scopes = this.getScopes();
+ var i;
+ for (i = 0; i < this.requestedAttributes[identity.pubkey].length; i++) {
+ const j =
scopes.indexOf(this.requestedAttributes[identity.pubkey][i].name);
+ if (j >= 0) {
+ scopes.splice(j, 1);
+ }
+ }
+ this.missingAttributes[identity.pubkey] = [];
+ for (i = 0; i < scopes.length; i++) {
+ let attribute = new Attribute('', '', 'STRING');
+ attribute.name = scopes[i];
+ this.missingAttributes[identity.pubkey].push(attribute);
+ }
+ }
+
+ private updateTickets(identity) {
+ this.reclaimService.getTickets(identity).subscribe(tickets => {
+ this.tickets[identity.pubkey] = [];
+ if (tickets === null) {
+ return;
+ }
+ this.tickets[identity.pubkey] = tickets;
+ tickets.forEach((ticket) => {
+ this.gnsService.getClientName(ticket.audience).subscribe(records => {
+ for (var i = 0; i < records.length; i++) {
+ if (records[i].type !== "RECLAIM_OIDC_CLIENT")
+ continue;
+ this.identityNameMapper[ticket.audience] = records[i].value;
+ break;
+ }
+ });
+ });
+ });
+ }
+
+ toggleShowTickets(identity) {
+ if (this.showTicketsIdentity == identity) {
+ this.showTicketsIdentity = null;
+ return;
+ }
+ this.showTicketsIdentity = identity;
+ }
+
+ revokeTicket(identity, ticket) {
+ this.reclaimService.revokeTicket(ticket).subscribe(data => {
+ this.updateTickets(identity);
+ });
+ }
+
+
+ private updateAttributes(identity) {
+ this.reclaimService.getAttributes(identity).subscribe(attributes => {
+ this.attributes[identity.pubkey] = [];
+ this.requestedAttributes[identity.pubkey] = [];
+ if (attributes === null) {
+ this.getMissingAttributes(identity);
+ return;
+ }
+ var i;
+ for (i = 0; i < attributes.length; i++) {
+ this.attributes[identity.pubkey].push(attributes[i]);
+ if (this.oidcService.getScope().includes(attributes[i].name)) {
+ this.requestedAttributes[identity.pubkey].push(attributes[i]);
+ }
+
+ }
+ this.getMissingAttributes(identity);
+ });
+
+ }
+
+ saveAttribute(identity, attribute) {
+ return this.reclaimService.addAttribute(identity, attribute).subscribe
(data => {
+ //this.updateAttributes(identity);
+ });
+ }
+
+ private storeAttributes(identity) {
+ var promises = [];
+ var i;
+ for (i = 0; i < this.missingAttributes[identity.pubkey].length; i++) {
+ if (this.missingAttributes[identity.pubkey][i].value === "") {
+ continue;
+ }
+ promises.push(this.saveAttribute(identity,
this.missingAttributes[identity.pubkey][i]));
+ }
+ for (i = 0; i < this.attributes[identity.pubkey].length; i++) {
+ promises.push(this.saveAttribute(identity,
this.attributes[identity.pubkey][i]));
+ }
+ if (this.newAttribute.value !== "") {
+ promises.push(this.saveAttribute(identity, this.newAttribute));
+ }
+ return Observable.forkJoin(promises)
+ }
+
+ addAttribute(attribute) {
+ this.storeAttributes(this.identityInEdit)
+ .finally(() => this.updateAttributes(this.identityInEdit))
+ .subscribe(
+ res => console.log(res),
+ error => {return Observable.empty()},
+ () => {
+ this.newAttribute.name = '';
+ this.newAttribute.value = '';
+ this.newAttribute.type = "STRING";
+ this.updateAttributes(this.identityInEdit);
+ }
+ );
+ this.newAttribute.name = '';
+ this.newAttribute.value = '';
+ }
+
+ cancelAddIdentity() {
+ this.newIdentity = null;
+ }
+
+ saveIdentity() {
+ if (!this.canSave()) {
+ return;
+ }
+ this.identityInEditName = this.newIdentity.name;
+ this.identityService.addIdentity(this.newIdentity).subscribe(data => {
+ this.newIdentity.name = '';
+ this.updateIdentities();
+ this.cancelAddIdentity();
+ });
+ }
+
+ deleteIdentity(identity) {
+ this.showConfirmDelete = false;
+ this.identityInEdit = null;
+ this.identityService.deleteIdentity(identity.pubkey).subscribe(data => {
+ this.updateIdentities();
+ });
+ }
+
+
+ loginIdentity(identity) {
+ this.oidcService.login(identity).subscribe(data => {
+ console.log('Successfully logged in');
+ this.authorize();
+ });
+ }
+
+ authorize() {
+ this.oidcService.authorize();
+ }
+
+ inOpenIdFlow() {
+ return this.oidcService.inOpenIdFlow();
+ }
+
+ canAddAttribute(identity,attribute) {
+ if ((attribute.name === "") || (attribute.value == "")) {
+ return false;
+ }
+ if (attribute.name.indexOf(" ") >= 0) {
+ return false;
+ }
+ return !this.isInConflict(identity,attribute);
+ }
+
+ attributeNameValid(identity, attribute) {
+ if (attribute.name === "" && attribute.value === "") {
+ return true;
+ }
+ if (attribute.name.indexOf(" ") >= 0) {
+ return false;
+ }
+ if (!/^[a-zA-Z0-9-]+$/.test(attribute.name)) {
+ return false;
+ }
+ return !this.isInConflict(identity,attribute);
+ }
+
+ attributeValueValid(attribute) {
+ if (attribute.value === "") {
+ return attribute.name === "";
+ }
+ return true;
+ }
+
+ canSaveIdentity(identity) {
+ if (this.canAddAttribute(identity,this.newAttribute)) {
+ return true;
+ }
+ return ((this.newAttribute.name === "") && (this.newAttribute.value ===
"")) && !this.isInConflict(identity,this.newAttribute);
+ }
+
+ isInConflict(identity,attribute) {
+ var i;
+ if (undefined !== this.missingAttributes[identity.pubkey]) {
+ for (i = 0; i < this.missingAttributes[identity.pubkey].length; i++) {
+ if (attribute.name ===
this.missingAttributes[identity.pubkey][i].name) {
+ return true;
+ }
+ }
+ }
+ if (undefined !== this.attributes[identity.pubkey]) {
+ for (i = 0; i < this.attributes[identity.pubkey].length; i++) {
+ if (attribute.name === this.attributes[identity.pubkey][i].name) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ getScopes() {
+ return this.oidcService.getScope();
+ }
+
+ getScopesPretty() {
+ return this.getScopes().join(", ");
+ }
+ getMissing(identity) {
+ var arr = [];
+ var i = 0;
+ for (i = 0; i < this.missingAttributes[identity.pubkey].length; i++) {
+ arr.push(this.missingAttributes[identity.pubkey][i].name);
+ }
+ return arr;
+ }
+ getMissingPretty(identity) {
+ return this.getMissing(identity).join(", ");
+ }
+ canAuthorize(identity) {
+ return this.inOpenIdFlow() && !this.isInEdit(identity);
+ }
+ isRequested(identity, attribute) {
+ if (undefined === this.requestedAttributes[identity.pubkey]) {
+ return false;
+ } else {
+ return -1 !==
this.requestedAttributes[identity.pubkey].indexOf(attribute);
+ }
+ }
+
+ isAttributeMissing(identity) {
+ if (!this.inOpenIdFlow()) {
+ return false;
+ }
+ if (undefined === this.requestedAttributes[identity.pubkey]) {
+ return false;
+ }
+ return this.getScopes().length !==
this.requestedAttributes[identity.pubkey].length;
+ }
+
+ hasAttributes(identity) {
+ if (undefined === this.attributes[identity.pubkey]) {
+ return false;
+ }
+ return 0 !== this.attributes[identity.pubkey].length
+ }
+
+ private updateIdentities() {
+ this.identityService.getIdentities().subscribe(identities => {
+ this.identities = [];
+ var i;
+ this.identityNameMapper = {};
+ for (i = 0; i < identities.length; i++) {
+ //"reclaim" is the reclaim UI and API namespace!
+ this.identityNameMapper[identities[i].pubkey] = identities[i].name;
+ if ("reclaim" === identities[i].name) {
+ continue;
+ }
+ this.identities.push(identities[i]);
+ if (this.identityInEditName === identities[i].name) {
+ this.editIdentity(this.identities[this.identities.length-1]);
+ this.identityInEditName = "";
+ }
+ }
+
+ identities.forEach(identity => {
+ if ("id" !== identity.name && "io" !== identity.name) {
+ this.updateAttributes(identity);
+ this.updateTickets(identity);
+ }
+ });
+ });
+ }
+}
diff --git a/src/app/identity.service.ts b/src/app/identity.service.ts
new file mode 100644
index 0000000..f03f716
--- /dev/null
+++ b/src/app/identity.service.ts
@@ -0,0 +1,29 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs/Observable';
+
+import { ConfigService } from './config.service';
+import { Identity } from './identity';
+import { GnuNetResponse } from './gnu-net-response';
+
+@Injectable()
+export class IdentityService {
+ constructor(private http: HttpClient, private config: ConfigService) { }
+
+ getIdentities(): Observable<Identity[]> {
+ return this.http.get<any[]>(this.config.get().apiUrl + '/identity/all');
+ }
+
+ getIdentity(identityId: string) {
+ return this.http.get(this.config.get().apiUrl + '/identity/pubkey/' +
identityId);
+ }
+
+ addIdentity(identity: Identity) {
+ var obj = {'name': identity.name};
+ return this.http.post(this.config.get().apiUrl + '/identity/', obj);
+ }
+
+ deleteIdentity(identityId: string) {
+ return this.http.delete(this.config.get().apiUrl + '/identity/pubkey/' +
identityId);
+ }
+}
diff --git a/src/app/identity.ts b/src/app/identity.ts
new file mode 100644
index 0000000..1a0a6f8
--- /dev/null
+++ b/src/app/identity.ts
@@ -0,0 +1,4 @@
+export class Identity {
+ constructor(public pubkey: string,
+ public name: string) { }
+}
diff --git a/src/app/namestore-response.ts b/src/app/namestore-response.ts
new file mode 100644
index 0000000..ae2b2f7
--- /dev/null
+++ b/src/app/namestore-response.ts
@@ -0,0 +1,3 @@
+export class GnuNetResponse {
+ constructor(public data: any) { }
+}
diff --git a/src/app/namestore.service.ts b/src/app/namestore.service.ts
new file mode 100644
index 0000000..21a4939
--- /dev/null
+++ b/src/app/namestore.service.ts
@@ -0,0 +1,17 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs/Observable';
+
+import { Identity } from './identity';
+import { ConfigService } from './config.service';
+
+@Injectable()
+export class NamestoreService {
+
+ constructor(private http: HttpClient, private config: ConfigService) { }
+
+ deleteName(identity: Identity, name: string) {
+ return this.http.delete(this.config.get().apiUrl + '/namestore/?label=' +
name + "&name=" + identity.name);
+ }
+
+}
diff --git a/src/app/open-id.service.ts b/src/app/open-id.service.ts
new file mode 100644
index 0000000..fce807d
--- /dev/null
+++ b/src/app/open-id.service.ts
@@ -0,0 +1,52 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Identity } from './identity';
+import { ConfigService } from './config.service';
+import { Router } from '@angular/router';
+
+@Injectable()
+export class OpenIdService {
+ params: any;
+
+ constructor(private http: HttpClient,
+ private config: ConfigService,
+ private router: Router) { }
+
+ login(identity: Identity) {
+ return this.http.post(this.config.get().apiUrl + '/openid/login', {
'identity': identity.pubkey});
+ }
+
+ parseRouteParams(params: any): any {
+ if (undefined !== this.params) {
+ return;
+ }
+ this.params = params;
+ }
+
+ authorize(): any {
+ window.location.href = this.config.get().apiUrl +
'/openid/authorize?client_id=' + this.params['client_id'] +
+ '&redirect_uri=' + this.params['redirect_uri'] +
+ '&response_type=' + this.params['response_type'] +
+ '&scope=' + this.params['scope'] +
+ '&state=' + this.params['state'] +
+ '&nonce=' + this.params['nonce'];
+ }
+
+ inOpenIdFlow(): any {
+ return this.params['redirect_uri'] !== undefined;
+ }
+
+ getClientId(): any {
+ return this.params['client_id'];
+ }
+
+ getScope(): any {
+ if (!this.inOpenIdFlow()) {
+ return [];
+ }
+ var scopes = this.params['scope'].split(" ");
+ const i = scopes.indexOf("openid");
+ scopes.splice(i, 1);
+ return scopes;
+ }
+}
diff --git a/src/app/reclaim.service.ts b/src/app/reclaim.service.ts
new file mode 100644
index 0000000..31b9ccb
--- /dev/null
+++ b/src/app/reclaim.service.ts
@@ -0,0 +1,34 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs/Observable';
+
+import { ConfigService } from './config.service';
+import { Identity } from './identity';
+import { Attribute } from './attribute';
+import { Ticket } from './ticket';
+import { GnuNetResponse } from './gnu-net-response';
+
+@Injectable()
+export class ReclaimService {
+
+ constructor(private http: HttpClient, private config: ConfigService) { }
+
+ getAttributes(identity: Identity): Observable<Attribute[]> {
+ return this.http.get<Attribute[]>(this.config.get().apiUrl +
'/reclaim/attributes/'
+ + identity.name);
+ }
+
+ addAttribute(identity: Identity, attribute: Attribute) {
+ return this.http.post<Attribute>(this.config.get().apiUrl +
'/reclaim/attributes/' + identity.name, attribute);
+ }
+
+ getTickets(identity: Identity): Observable<Ticket[]> {
+ return this.http.get<Ticket[]>(this.config.get().apiUrl +
'/reclaim/tickets/'
+ + identity.name);
+ }
+
+ revokeTicket(ticket: Ticket) {
+ return this.http.post<Ticket>(this.config.get().apiUrl +
'/reclaim/revoke', ticket);
+ }
+
+}
diff --git a/src/app/record.ts b/src/app/record.ts
new file mode 100644
index 0000000..e7a5199
--- /dev/null
+++ b/src/app/record.ts
@@ -0,0 +1,5 @@
+export class Record {
+ constructor(public value: string,
+ public expiration_time: string,
+ public label) { }
+}
diff --git a/src/app/rxjs.ts b/src/app/rxjs.ts
new file mode 100644
index 0000000..ea16726
--- /dev/null
+++ b/src/app/rxjs.ts
@@ -0,0 +1 @@
+import 'rxjs/add/operator/map';
diff --git a/src/app/ticket.ts b/src/app/ticket.ts
new file mode 100644
index 0000000..03527b9
--- /dev/null
+++ b/src/app/ticket.ts
@@ -0,0 +1,5 @@
+export class Ticket {
+ constructor(public identity: string,
+ public audience: string,
+ public rnd: string) { }
+}
diff --git a/src/assets/Audiowide-Regular.ttf b/src/assets/Audiowide-Regular.ttf
new file mode 100755
index 0000000..8b50bed
Binary files /dev/null and b/src/assets/Audiowide-Regular.ttf differ
diff --git a/src/assets/config.json b/src/assets/config.json
new file mode 100644
index 0000000..d8dc2d0
--- /dev/null
+++ b/src/assets/config.json
@@ -0,0 +1,3 @@
+{
+ "apiUrl": "https://api.reclaim"
+}
diff --git a/src/assets/reclaim_icon.png b/src/assets/reclaim_icon.png
new file mode 100644
index 0000000..4e23a1d
Binary files /dev/null and b/src/assets/reclaim_icon.png differ
diff --git a/src/assets/reclaim_small.png b/src/assets/reclaim_small.png
new file mode 100644
index 0000000..ffc0f54
Binary files /dev/null and b/src/assets/reclaim_small.png differ
diff --git a/src/environments/environment.prod.ts
b/src/environments/environment.prod.ts
new file mode 100644
index 0000000..3612073
--- /dev/null
+++ b/src/environments/environment.prod.ts
@@ -0,0 +1,3 @@
+export const environment = {
+ production: true
+};
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
new file mode 100644
index 0000000..b7f639a
--- /dev/null
+++ b/src/environments/environment.ts
@@ -0,0 +1,8 @@
+// The file contents for the current environment will overwrite these during
build.
+// The build system defaults to the dev environment which uses
`environment.ts`, but if you do
+// `ng build --env=prod` then `environment.prod.ts` will be used instead.
+// The list of which env maps to which file can be found in
`.angular-cli.json`.
+
+export const environment = {
+ production: false
+};
diff --git a/src/index.html b/src/index.html
new file mode 100644
index 0000000..c4fc08f
--- /dev/null
+++ b/src/index.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html lang="en">
+
+<head>
+ <meta charset="utf-8">
+ <title>re:claimID</title>
+ <base href="/">
+
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link rel="icon" type="image/x-icon" href="favicon.ico">
+</head>
+
+<body>
+ <app-root></app-root>
+</body>
+
+</html>
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..91ec6da
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,12 @@
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+ enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule)
+ .catch(err => console.log(err));
diff --git a/src/polyfills.ts b/src/polyfills.ts
new file mode 100644
index 0000000..af84770
--- /dev/null
+++ b/src/polyfills.ts
@@ -0,0 +1,79 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are
sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded
before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions
of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55
(including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/** IE9, IE10 and IE11 requires all of the following polyfills. **/
+// import 'core-js/es6/symbol';
+// import 'core-js/es6/object';
+// import 'core-js/es6/function';
+// import 'core-js/es6/parse-int';
+// import 'core-js/es6/parse-float';
+// import 'core-js/es6/number';
+// import 'core-js/es6/math';
+// import 'core-js/es6/string';
+// import 'core-js/es6/date';
+// import 'core-js/es6/array';
+// import 'core-js/es6/regexp';
+// import 'core-js/es6/map';
+// import 'core-js/es6/weak-map';
+// import 'core-js/es6/set';
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+// import 'classlist.js'; // Run `npm install --save classlist.js`.
+
+/** IE10 and IE11 requires the following for the Reflect API. */
+// import 'core-js/es6/reflect';
+
+
+/** Evergreen browsers require these. **/
+// Used for reflect-metadata in JIT. If you use AOT (and only Angular
decorators), you can remove.
+import 'core-js/es7/reflect';
+
+
+/**
+ * Required to support Web Animations `@angular/platform-browser/animations`.
+ * Needed for: All but Chrome, Firefox and Opera.
http://caniuse.com/#feat=web-animation
+ **/
+// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following
flags
+ */
+
+ // (window as any).__Zone_disable_requestAnimationFrame = true; // disable
patch requestAnimationFrame
+ // (window as any).__Zone_disable_on_property = true; // disable patch
onProperty such as onclick
+ // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll',
'mousemove']; // disable patch specified eventNames
+
+ /*
+ * in IE/Edge developer tools, the addEventListener will also be wrapped by
zone.js
+ * with the following flag, it will bypass `zone.js` patch for IE/Edge
+ */
+// (window as any).__Zone_enable_cross_context_check = true;
+
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js/dist/zone'; // Included with Angular CLI.
+
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
diff --git a/src/styles.scss b/src/styles.scss
new file mode 100644
index 0000000..8d871af
--- /dev/null
+++ b/src/styles.scss
@@ -0,0 +1,57 @@
+@import "node_modules/bootstrap/scss/bootstrap";
+$fa-font-path: "../node_modules/font-awesome/fonts";
+@import "node_modules/font-awesome/scss/font-awesome.scss";
+
+html {
+ /*font-family: Roboto, Helvetica, Arial, sans-serif;*/
+ margin: 0 auto;
+ font-size: 14px;
+}
+
+body {
+ background-color: #fafafa;
+}
+
+
+div.card {
+ //box-shadow: 0px 1px 2px -1px rgba(0, 0, 0, 0.5);
+ border-radius: 0;
+}
+
+.btn:disabled {
+ background-color: lightgray;
+}
+
+.btn {
+ border-radius: 0.25rem;
+}
+
+.btn-primary {
+ background-color: #fff;
+ color: #555;
+ border: 1px solid #ddd;
+}
+
+.btn-primary:hover {
+ background-color: #777;
+ border: 1px solid #777;
+}
+
+.btn-primary:disabled {
+ border: 1px solid #ccc;
+}
+
+.btn-primary:disabled:hover {
+ background-color: lightgrey;
+}
+
+.alert a.buttonlink {
+ text-decoration: underline;
+ cursor: pointer;
+ color: #777;
+}
+
+.alert {
+ border-radius: 0;
+ margin-bottom: 0;
+}
diff --git a/src/tsconfig.app.json b/src/tsconfig.app.json
new file mode 100644
index 0000000..39ba8db
--- /dev/null
+++ b/src/tsconfig.app.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../out-tsc/app",
+ "baseUrl": "./",
+ "module": "es2015",
+ "types": []
+ },
+ "exclude": [
+ "test.ts",
+ "**/*.spec.ts"
+ ]
+}
diff --git a/src/typings.d.ts b/src/typings.d.ts
new file mode 100644
index 0000000..ef5c7bd
--- /dev/null
+++ b/src/typings.d.ts
@@ -0,0 +1,5 @@
+/* SystemJS module definition */
+declare var module: NodeModule;
+interface NodeModule {
+ id: string;
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..a6c016b
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compileOnSave": false,
+ "compilerOptions": {
+ "outDir": "./dist/out-tsc",
+ "sourceMap": true,
+ "declaration": false,
+ "moduleResolution": "node",
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "target": "es5",
+ "typeRoots": [
+ "node_modules/@types"
+ ],
+ "lib": [
+ "es2017",
+ "dom"
+ ]
+ }
+}
diff --git a/tslint.json b/tslint.json
new file mode 100644
index 0000000..ecf0d49
--- /dev/null
+++ b/tslint.json
@@ -0,0 +1,154 @@
+{
+ "rulesDirectory": [
+ "node_modules/codelyzer",
+ "node_modules/rxjs-tslint-rules/dist/rules"
+ ],
+ "rules": {
+ "rxjs-add": {
+ "options": [
+ {
+ "allowElsewhere": false,
+ "allowUnused": false,
+ "file": "./src/app/rxjs.ts"
+ }
+ ],
+ "severity": "error"
+ },
+ "arrow-return-shorthand": true,
+ "callable-types": true,
+ "class-name": true,
+ "comment-format": [
+ true,
+ "check-space"
+ ],
+ "curly": true,
+ "deprecation": {
+ "severity": "warn"
+ },
+ "eofline": true,
+ "forin": true,
+ "import-blacklist": [
+ true,
+ "rxjs",
+ "rxjs/Rx"
+ ],
+ "import-spacing": true,
+ "indent": [
+ true,
+ "spaces"
+ ],
+ "interface-over-type-literal": true,
+ "label-position": true,
+ "max-line-length": [
+ true,
+ 140
+ ],
+ "member-access": false,
+ "member-ordering": [
+ true,
+ {
+ "order": [
+ "static-field",
+ "instance-field",
+ "static-method",
+ "instance-method"
+ ]
+ }
+ ],
+ "no-arg": true,
+ "no-bitwise": false,
+ "no-console": [
+ true,
+ "debug",
+ "info",
+ "time",
+ "timeEnd",
+ "trace"
+ ],
+ "no-construct": true,
+ "no-debugger": true,
+ "no-duplicate-super": true,
+ "no-empty": false,
+ "no-empty-interface": true,
+ "no-eval": true,
+ "no-inferrable-types": [
+ true,
+ "ignore-params"
+ ],
+ "no-misused-new": true,
+ "no-non-null-assertion": true,
+ "no-shadowed-variable": true,
+ "no-string-literal": false,
+ "no-string-throw": true,
+ "no-switch-case-fall-through": true,
+ "no-trailing-whitespace": true,
+ "no-unnecessary-initializer": true,
+ "no-unused-expression": true,
+ "no-use-before-declare": true,
+ "no-var-keyword": true,
+ "object-literal-sort-keys": false,
+ "one-line": [
+ true,
+ "check-open-brace",
+ "check-catch",
+ "check-else",
+ "check-whitespace"
+ ],
+ "prefer-const": true,
+ "quotemark": [
+ true,
+ "single"
+ ],
+ "radix": true,
+ "semicolon": [
+ true,
+ "always"
+ ],
+ "triple-equals": [
+ true,
+ "allow-null-check"
+ ],
+ "typedef-whitespace": [
+ true,
+ {
+ "call-signature": "nospace",
+ "index-signature": "nospace",
+ "parameter": "nospace",
+ "property-declaration": "nospace",
+ "variable-declaration": "nospace"
+ }
+ ],
+ "unified-signatures": true,
+ "variable-name": false,
+ "whitespace": [
+ true,
+ "check-branch",
+ "check-decl",
+ "check-operator",
+ "check-separator",
+ "check-type"
+ ],
+ "directive-selector": [
+ true,
+ "attribute",
+ "app",
+ "camelCase"
+ ],
+ "component-selector": [
+ true,
+ "element",
+ "app",
+ "kebab-case"
+ ],
+ "no-output-on-prefix": true,
+ "use-input-property-decorator": true,
+ "use-output-property-decorator": true,
+ "use-host-property-decorator": true,
+ "no-input-rename": true,
+ "no-output-rename": true,
+ "use-life-cycle-interface": true,
+ "use-pipe-transform-interface": true,
+ "component-class-suffix": true,
+ "directive-class-suffix": true
+ }
+}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [reclaim-ui] 15/459: See previous commit, (continued)
- [reclaim-ui] 15/459: See previous commit, gnunet, 2021/06/11
- [reclaim-ui] 16/459: Update README.md, gnunet, 2021/06/11
- [reclaim-ui] 01/459: Initial commit, gnunet, 2021/06/11
- [reclaim-ui] 07/459: readme update, gnunet, 2021/06/11
- [reclaim-ui] 11/459: resolved #1, gnunet, 2021/06/11
- [reclaim-ui] 04/459: add yarn lock, gnunet, 2021/06/11
- [reclaim-ui] 14/459: towards cancel ng, gnunet, 2021/06/11
- [reclaim-ui] 18/459: hash tag not needed for this image, gnunet, 2021/06/11
- [reclaim-ui] 19/459: update, gnunet, 2021/06/11
- [reclaim-ui] 20/459: Merge branch 'master' of gitlab.com:reclaimid/ui, gnunet, 2021/06/11
- [reclaim-ui] 02/459: Migrated from internal devops without history,
gnunet <=
- [reclaim-ui] 17/459: add CI, gnunet, 2021/06/11
- [reclaim-ui] 29/459: update icon again, gnunet, 2021/06/11
- [reclaim-ui] 30/459: clear local storage, gnunet, 2021/06/11
- [reclaim-ui] 22/459: changes, gnunet, 2021/06/11
- [reclaim-ui] 33/459: bugfix, gnunet, 2021/06/11
- [reclaim-ui] 39/459: border, gnunet, 2021/06/11
- [reclaim-ui] 28/459: updates merge, gnunet, 2021/06/11
- [reclaim-ui] 26/459: changes, gnunet, 2021/06/11
- [reclaim-ui] 32/459: merge master, gnunet, 2021/06/11
- [reclaim-ui] 21/459: make webex ready!, gnunet, 2021/06/11