diff --git a/ui/src/app/components/form-object/form-object.component.ts b/ui/src/app/components/form-object/form-object.component.ts
index c3e0c027a..628a2f21e 100644
--- a/ui/src/app/components/form-object/form-object.component.ts
+++ b/ui/src/app/components/form-object/form-object.component.ts
@@ -95,11 +95,12 @@ export class FormObjectComponent {
expanded: true,
displayAs: displayAs ? handlebars.compile(displayAs)(newItem.value) : '',
})
+
+ pauseFor(200).then(() => {
+ const index = this.objectListInfo[key].length - 1
+ this.objectListInfo[key][index].height = this.getDocSize(key)
+ })
}
- pauseFor(200).then(() => {
- const index = this.objectListInfo[key].length - 1
- this.objectListInfo[key][index].height = this.getDocSize(key)
- })
}
toggleExpand (key: string, i: number) {
diff --git a/ui/src/app/pages/notifications/notifications.page.html b/ui/src/app/pages/notifications/notifications.page.html
index 49d58dfd5..c25b36052 100644
--- a/ui/src/app/pages/notifications/notifications.page.html
+++ b/ui/src/app/pages/notifications/notifications.page.html
@@ -24,12 +24,12 @@
style="
text-align: center;
position: absolute;
- top: 40%;
+ top: 50%;
left: 50%;
transform: translate(-50%, -50%);"
>
-
-
Inbox Empty
+
+ Inbox Empty
diff --git a/ui/src/app/services/form.service.ts b/ui/src/app/services/form.service.ts
index 03c261c46..21abc2cc3 100644
--- a/ui/src/app/services/form.service.ts
+++ b/ui/src/app/services/form.service.ts
@@ -1,6 +1,7 @@
import { Injectable } from '@angular/core'
import { AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'
-import { ConfigSpec, isValueSpecListOf, ListValueSpecNumber, ListValueSpecString, ListValueSpecUnion, ValueSpec, ValueSpecEnum, ValueSpecList, ValueSpecNumber, ValueSpecObject, ValueSpecString, ValueSpecUnion } from '../pkg-config/config-types'
+import { By } from '@angular/platform-browser'
+import { ConfigSpec, isValueSpecListOf, ListValueSpecNumber, ListValueSpecObject, ListValueSpecOf, ListValueSpecString, ListValueSpecUnion, UniqueBy, ValueSpec, ValueSpecEnum, ValueSpecList, ValueSpecNumber, ValueSpecObject, ValueSpecString, ValueSpecUnion } from '../pkg-config/config-types'
import { getDefaultString, Range } from '../pkg-config/config-utilities'
@Injectable({
@@ -213,32 +214,98 @@ export function listInRange (stringRange: string): ValidatorFn {
}
}
-export function listUnique (spec: ValueSpec): ValidatorFn {
+export function listUnique (spec: ValueSpecList): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
for (let idx = 0; idx < control.value.length; idx++) {
for (let idx2 = idx + 1; idx2 < control.value.length; idx2++) {
- if (equals(spec, control.value[idx], control.value[idx2])) {
+ if (listItemEquals(spec, control.value[idx], control.value[idx2])) {
return { listNotUnique: { value: control.value } }
- } else {
- return null
}
}
}
+ return null
}
}
-export function equals (spec: ValueSpec, val1: any, val2: any): boolean {
+function listItemEquals (spec: ValueSpecList, val1: any, val2: any): boolean {
+ switch (spec.subtype) {
+ case 'string':
+ case 'number':
+ case 'enum':
+ return val1 == val2
+ case 'object':
+ case 'union':
+ return listObjEquals(spec.spec['unique-by'], (spec.spec as ListValueSpecObject), val1, val2)
+ default:
+ return false
+ }
+}
+
+function itemEquals (spec: ValueSpec, val1: any, val2: any): boolean {
switch (spec.type) {
case 'string':
case 'number':
case 'boolean':
case 'enum':
- return val1 === val2
+ return val1 == val2
case 'object':
case 'union':
- // @TODO how to check this
- return false
+ return objEquals(spec['unique-by'], (spec as ValueSpec), val1, val2)
+ case 'list':
+ if (val1.length !== val2.length) {
+ return false
+ }
+ for (let idx = 0; idx < val1.length; idx++) {
+ if (listItemEquals(spec, val1[idx], val2[idx])) {
+ return false
+ }
+ }
+ return true
default:
return false
}
}
+
+function listObjEquals (uniqueBy: UniqueBy, spec: ListValueSpecObject, val1: any, val2: any): boolean {
+ if (uniqueBy === null) {
+ return false
+ } else if (typeof uniqueBy === 'string') {
+ return itemEquals(spec.spec[uniqueBy], val1[uniqueBy], val2[uniqueBy])
+ } else if ('any' in uniqueBy) {
+ for (let subSpec of uniqueBy.any) {
+ if (listObjEquals(subSpec, spec, val1, val2)) {
+ return true
+ }
+ }
+ return false
+ } else if ('all' in uniqueBy) {
+ for (let subSpec of uniqueBy.all) {
+ if (!listObjEquals(subSpec, spec, val1, val2)) {
+ return false
+ }
+ }
+ return true
+ }
+}
+
+function objEquals (uniqueBy: UniqueBy, spec: ValueSpec, val1: any, val2: any): boolean {
+ if (uniqueBy === null) {
+ return false
+ } else if (typeof uniqueBy === 'string') {
+ return itemEquals(spec, val1, val2)
+ } else if ('any' in uniqueBy) {
+ for (let subSpec of uniqueBy.any) {
+ if (objEquals(subSpec, spec, val1, val2)) {
+ return true
+ }
+ }
+ return false
+ } else if ('all' in uniqueBy) {
+ for (let subSpec of uniqueBy.all) {
+ if (!objEquals(subSpec, spec, val1, val2)) {
+ return false
+ }
+ }
+ return true
+ }
+}
\ No newline at end of file