diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c962e4bc..372de081 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -53,7 +53,9 @@ import { EntityComponent, EntityDetailComponent, FooterComponent, + GridComponent, GridElementComponent, + GroupElementComponent, InstitutionComponent, NavbarComponent, PersonComponent, @@ -87,6 +89,7 @@ import { // Pipes import { FilesizePipe, ReplayHasValuePipe, SafePipe } from './pipes'; +import { CountUniqueGroupMembersPipe } from './components/elements/group-element/count-unique-group-members.pipe'; // Dialogs import { @@ -94,16 +97,15 @@ import { EditEntityDialogComponent, EntityRightsDialogComponent, EntitySettingsDialogComponent, - ExploreCompilationDialogComponent, - ExploreEntityDialogComponent, + ForgotPasswordDialogComponent, + ForgotUsernameDialogComponent, GroupMemberDialogComponent, PasswordProtectedDialogComponent, RegisterDialogComponent, + ResetPasswordDialogComponent, UploadApplicationDialogComponent, + ViewerDialogComponent, } from './dialogs'; -import { ResetPasswordDialogComponent } from './dialogs/reset-password-dialog/reset-password-dialog.component'; -import { ForgotUsernameDialogComponent } from './dialogs/forgot-username-dialog/forgot-username-dialog.component'; -import { ForgotPasswordDialogComponent } from './dialogs/forgot-password-dialog/forgot-password-dialog.component'; // Interceptors import { HttpOptionsInterceptor } from './services/interceptors/http-options-interceptor'; @@ -148,12 +150,10 @@ const INTERCEPTORS: Provider[] = [ AnnotateComponent, CollaborateComponent, AboutComponent, - ExploreEntityDialogComponent, UploadApplicationDialogComponent, ProfilePageHelpComponent, ActionbarComponent, AnimatedImageComponent, - ExploreCompilationDialogComponent, EditEntityDialogComponent, AdminPageComponent, CompilationDetailComponent, @@ -170,6 +170,10 @@ const INTERCEPTORS: Provider[] = [ ForgotUsernameDialogComponent, ForgotPasswordDialogComponent, ReplayHasValuePipe, + GroupElementComponent, + CountUniqueGroupMembersPipe, + GridComponent, + ViewerDialogComponent, ], imports: [ CommonModule, diff --git a/src/app/components/elements/grid/grid.component.html b/src/app/components/elements/grid/grid.component.html new file mode 100644 index 00000000..6dbc7430 --- /dev/null +++ b/src/app/components/elements/grid/grid.component.html @@ -0,0 +1 @@ + diff --git a/src/app/components/elements/grid/grid.component.scss b/src/app/components/elements/grid/grid.component.scss new file mode 100644 index 00000000..8cf06f5a --- /dev/null +++ b/src/app/components/elements/grid/grid.component.scss @@ -0,0 +1,5 @@ +:host { + display: grid; + grid-template-columns: repeat(var(--items-per-row, 4), 1fr); + gap: var(--gap-size, 8px); +} diff --git a/src/app/components/elements/grid/grid.component.ts b/src/app/components/elements/grid/grid.component.ts new file mode 100644 index 00000000..df882db7 --- /dev/null +++ b/src/app/components/elements/grid/grid.component.ts @@ -0,0 +1,20 @@ +import { Component, HostBinding, Input } from '@angular/core'; + +@Component({ + selector: 'app-grid', + templateUrl: './grid.component.html', + styleUrls: ['./grid.component.scss'], +}) +export class GridComponent { + @Input('itemsPerRow') itemsPerRow = 4; + + @Input('gapSizePx') gapSizePx = 8; + + @HostBinding('style.--items-per-row') get itemsPerRowStyle() { + return this.itemsPerRow; + } + + @HostBinding('style.--gap-size') get gapSizeStyle() { + return this.gapSizePx + 'px'; + } +} diff --git a/src/app/components/elements/group-element/count-unique-group-members.pipe.ts b/src/app/components/elements/group-element/count-unique-group-members.pipe.ts new file mode 100644 index 00000000..d936b382 --- /dev/null +++ b/src/app/components/elements/group-element/count-unique-group-members.pipe.ts @@ -0,0 +1,12 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { IGroup } from '~common/interfaces'; + +@Pipe({ + name: 'countUniqueGroupMembers', +}) +export class CountUniqueGroupMembersPipe implements PipeTransform { + transform({ members, owners }: IGroup): unknown { + const uniqueIds = new Set([...members, ...owners].map(({ _id }) => _id)); + return Array.from(uniqueIds).length; + } +} diff --git a/src/app/components/elements/group-element/group-element.component.html b/src/app/components/elements/group-element/group-element.component.html new file mode 100644 index 00000000..6c7d0ef7 --- /dev/null +++ b/src/app/components/elements/group-element/group-element.component.html @@ -0,0 +1,11 @@ + +

+ {{group.name}}
+ by {{group.creator.fullname}} +

+ + +
diff --git a/src/app/components/elements/group-element/group-element.component.scss b/src/app/components/elements/group-element/group-element.component.scss new file mode 100644 index 00000000..f469fc03 --- /dev/null +++ b/src/app/components/elements/group-element/group-element.component.scss @@ -0,0 +1,17 @@ +:host { + display: inline-flex; + flex-direction: column; + padding: 24px; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.2); + border-radius: 4px; + gap: 16px; + min-width: 240px; + + * { + margin: 0; + } + + button { + align-self: flex-start; + } +} diff --git a/src/app/components/elements/group-element/group-element.component.ts b/src/app/components/elements/group-element/group-element.component.ts new file mode 100644 index 00000000..676ef9fc --- /dev/null +++ b/src/app/components/elements/group-element/group-element.component.ts @@ -0,0 +1,28 @@ +import { Component, Input } from '@angular/core'; +import { IGroup } from '~common/interfaces'; +import { combineLatest, map, ReplaySubject } from 'rxjs'; +import { AccountService } from '~services'; +import { GroupMemberDialogComponent } from '~dialogs'; +import { MatDialog } from '@angular/material/dialog'; + +@Component({ + selector: 'app-group-element', + templateUrl: './group-element.component.html', + styleUrls: ['./group-element.component.scss'], +}) +export class GroupElementComponent { + @Input('group') set group(group: IGroup) { + this.group$.next(group); + } + + public group$ = new ReplaySubject(1); + public isUserOwner$ = combineLatest([this.group$, this.account.user$]).pipe( + map(([group, user]) => group?.creator._id === user._id), + ); + + public openMemberList(group: IGroup) { + this.dialog.open(GroupMemberDialogComponent, { data: group }); + } + + constructor(private account: AccountService, private dialog: MatDialog) {} +} diff --git a/src/app/components/grid-element/grid-element.component.ts b/src/app/components/grid-element/grid-element.component.ts index 44523756..2d2ae04d 100644 --- a/src/app/components/grid-element/grid-element.component.ts +++ b/src/app/components/grid-element/grid-element.component.ts @@ -1,18 +1,17 @@ -import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { MatMenu } from '@angular/material/menu'; -import { MatDialog } from '@angular/material/dialog'; import { + ICompilation, + IEntity, isAnnotation, isCompilation, isEntity, isResolvedEntity, - ICompilation, - IEntity, ObjectId, } from 'src/common'; import { environment } from 'src/environments/environment'; -import { ExploreEntityDialogComponent, ExploreCompilationDialogComponent } from 'src/app/dialogs'; +import { DialogHelperService } from '~services'; @Component({ selector: 'app-grid-element', @@ -52,7 +51,7 @@ export class GridElementComponent { @Output() public updateSelectedObject = new EventEmitter(); - constructor(private dialog: MatDialog) {} + constructor(private dialogHelper: DialogHelperService) {} get tooltipContent() { let description = ''; @@ -125,23 +124,17 @@ export class GridElementComponent { public openExploreDialog(element: IEntity | ICompilation) { if (!element) return; - if (isCompilation(element)) { - // tslint:disable-next-line:no-non-null-assertion - const eId = (Object.values(element.entities)[0] as IEntity)._id; - - this.dialog.open(ExploreCompilationDialogComponent, { - data: { - collectionId: element._id, - entityId: eId, - }, - id: 'explore-compilation-dialog', - }); - } else { - this.dialog.open(ExploreEntityDialogComponent, { - data: element._id, - id: 'explore-entity-dialog', - }); - } + console.log(element); + const compilation = isCompilation(element) ? element._id.toString() : undefined; + const entity = compilation + ? Object.keys((element as ICompilation).entities)[0].toString() + : element._id.toString(); + + this.dialogHelper.openViewerDialog({ + compilation, + entity, + mode: 'explore', + }); } public selectObject(id: string | ObjectId) { diff --git a/src/app/components/index.ts b/src/app/components/index.ts index 1c70c8fb..fba34057 100644 --- a/src/app/components/index.ts +++ b/src/app/components/index.ts @@ -15,3 +15,5 @@ export { SidenavListComponent } from './navigation/sidenav-list/sidenav-list.com export { NavbarComponent } from './navigation/navbar/navbar.component'; export { FooterComponent } from './navigation/footer/footer.component'; export { UploadComponent } from './upload/upload.component'; +export { GroupElementComponent } from './elements/group-element/group-element.component'; +export { GridComponent } from './elements/grid/grid.component'; diff --git a/src/app/dialogs/explore-compilation-dialog/explore-compilation-dialog.component.html b/src/app/dialogs/explore-compilation-dialog/explore-compilation-dialog.component.html deleted file mode 100644 index 704cecd2..00000000 --- a/src/app/dialogs/explore-compilation-dialog/explore-compilation-dialog.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/dialogs/explore-compilation-dialog/explore-compilation-dialog.component.scss b/src/app/dialogs/explore-compilation-dialog/explore-compilation-dialog.component.scss deleted file mode 100644 index 75a80552..00000000 --- a/src/app/dialogs/explore-compilation-dialog/explore-compilation-dialog.component.scss +++ /dev/null @@ -1,6 +0,0 @@ -#close-explore-compilation-button { - margin-top: -15px; - margin-right: -10px; - margin-bottom: 10px; - background-color: #ddd; -} diff --git a/src/app/dialogs/explore-compilation-dialog/explore-compilation-dialog.component.ts b/src/app/dialogs/explore-compilation-dialog/explore-compilation-dialog.component.ts deleted file mode 100644 index 18d95e78..00000000 --- a/src/app/dialogs/explore-compilation-dialog/explore-compilation-dialog.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Component, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; - -import { environment } from 'src/environments/environment'; - -@Component({ - selector: 'app-explore-compilation-dialog', - templateUrl: './explore-compilation-dialog.component.html', - styleUrls: ['./explore-compilation-dialog.component.scss'], -}) -export class ExploreCompilationDialogComponent { - public viewerUrl: string; - - constructor( - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) - public data: { entityId: string; collectionId: string }, - ) { - // tslint:disable-next-line:max-line-length - this.viewerUrl = `${environment.viewer_url}?compilation=${data.collectionId}&entity=${data.entityId}&mode=explore`; - } -} diff --git a/src/app/dialogs/explore-entity/explore-entity-dialog.component.html b/src/app/dialogs/explore-entity/explore-entity-dialog.component.html deleted file mode 100644 index 704cecd2..00000000 --- a/src/app/dialogs/explore-entity/explore-entity-dialog.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/dialogs/explore-entity/explore-entity-dialog.component.scss b/src/app/dialogs/explore-entity/explore-entity-dialog.component.scss deleted file mode 100644 index 531f2ac9..00000000 --- a/src/app/dialogs/explore-entity/explore-entity-dialog.component.scss +++ /dev/null @@ -1,6 +0,0 @@ -#close-explore-entity-button { - margin-top: -15px; - margin-right: -10px; - margin-bottom: 10px; - background-color: #ddd; -} diff --git a/src/app/dialogs/explore-entity/explore-entity-dialog.component.ts b/src/app/dialogs/explore-entity/explore-entity-dialog.component.ts deleted file mode 100644 index a272750e..00000000 --- a/src/app/dialogs/explore-entity/explore-entity-dialog.component.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Component, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; - -import { environment } from 'src/environments/environment'; - -@Component({ - selector: 'app-explore-entity-dialog', - templateUrl: './explore-entity-dialog.component.html', - styleUrls: ['./explore-entity-dialog.component.scss'], -}) -export class ExploreEntityDialogComponent { - public viewerUrl: string; - - constructor( - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public id: string, - ) { - this.viewerUrl = `${environment.viewer_url}?entity=${this.id}&mode=explore`; - } -} diff --git a/src/app/dialogs/index.ts b/src/app/dialogs/index.ts index 64c0d738..d9a1349b 100644 --- a/src/app/dialogs/index.ts +++ b/src/app/dialogs/index.ts @@ -2,8 +2,6 @@ export { ConfirmationDialogComponent } from './confirmation-dialog/confirmation- export { EditEntityDialogComponent } from './edit-entity-dialog/edit-entity-dialog.component'; export { EntityRightsDialogComponent } from './entity-rights-dialog/entity-rights-dialog.component'; export { EntitySettingsDialogComponent } from './entity-settings-dialog/entity-settings-dialog.component'; -export { ExploreCompilationDialogComponent } from './explore-compilation-dialog/explore-compilation-dialog.component'; -export { ExploreEntityDialogComponent } from './explore-entity/explore-entity-dialog.component'; export { GroupMemberDialogComponent } from './group-member-dialog/group-member-dialog.component'; export { PasswordProtectedDialogComponent } from './password-protected-dialog/password-protected-dialog.component'; export { RegisterDialogComponent } from './register-dialog/register-dialog.component'; @@ -11,3 +9,4 @@ export { UploadApplicationDialogComponent } from './upload-application-dialog/up export { ResetPasswordDialogComponent } from './reset-password-dialog/reset-password-dialog.component'; export { ForgotUsernameDialogComponent } from './forgot-username-dialog/forgot-username-dialog.component'; export { ForgotPasswordDialogComponent } from './forgot-password-dialog/forgot-password-dialog.component'; +export { ViewerDialogComponent, ViewerDialogData } from './viewer-dialog/viewer-dialog.component' diff --git a/src/app/dialogs/viewer-dialog/viewer-dialog.component.html b/src/app/dialogs/viewer-dialog/viewer-dialog.component.html new file mode 100644 index 00000000..f1fe4db8 --- /dev/null +++ b/src/app/dialogs/viewer-dialog/viewer-dialog.component.html @@ -0,0 +1,4 @@ + + + + diff --git a/src/app/dialogs/viewer-dialog/viewer-dialog.component.scss b/src/app/dialogs/viewer-dialog/viewer-dialog.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/dialogs/viewer-dialog/viewer-dialog.component.ts b/src/app/dialogs/viewer-dialog/viewer-dialog.component.ts new file mode 100644 index 00000000..115c8b94 --- /dev/null +++ b/src/app/dialogs/viewer-dialog/viewer-dialog.component.ts @@ -0,0 +1,31 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { environment } from '~environments'; +import { ReplaySubject } from 'rxjs'; + +export type ViewerDialogData = { + entity?: string; + compilation?: string; + mode?: 'upload' | 'explore' | 'edit' | 'annotation' | 'open'; +}; + +@Component({ + selector: 'app-viewer-dialog', + templateUrl: './viewer-dialog.component.html', + styleUrls: ['./viewer-dialog.component.scss'], +}) +export class ViewerDialogComponent { + public viewerUrl$ = new ReplaySubject(0); + + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) private data: ViewerDialogData, + ) { + const url = new URL(environment.viewer_url); + for (const [key, value] of Object.entries(data)) { + if (!value) continue; + url.searchParams.set(key, value); + } + this.viewerUrl$.next(url.toString()); + } +} diff --git a/src/app/pages/admin-page/admin-page.component.html b/src/app/pages/admin-page/admin-page.component.html index a67522bf..4314c4ff 100644 --- a/src/app/pages/admin-page/admin-page.component.html +++ b/src/app/pages/admin-page/admin-page.component.html @@ -1,4 +1,4 @@ -
+

User Administration

Waiting for admin data

diff --git a/src/app/pages/annotate/annotate.component.html b/src/app/pages/annotate/annotate.component.html index 22314989..30075e5b 100644 --- a/src/app/pages/annotate/annotate.component.html +++ b/src/app/pages/annotate/annotate.component.html @@ -1,60 +1,71 @@ -
+

Annotate

-
-

What are annotations?

+

What are annotations?

-

Annotations are multimedia containers that allow you to attach descriptions or references to points in space from - a - chosen camera perspective on 3D objects in Kompakkt. Through the selection of different perspectives on - consecutive - annotations you can generate a walk-through. Annotations can contain text, images, AV media, URLs and more. You - can - annotate your own objects with default annotations visible to everyone else. You can also create your own private - annotations or annotate other people’s objects by creating a collection and then annotating the objects you curate - into this collection.

+

Annotations are multimedia containers that allow you to attach descriptions or references to points in space from + a + chosen camera perspective on 3D objects in Kompakkt. Through the selection of different perspectives on + consecutive + annotations you can generate a walk-through. Annotations can contain text, images, AV media, URLs and more. You + can + annotate your own objects with default annotations visible to everyone else. You can also create your own private + annotations or annotate other people’s objects by creating a collection and then annotating the objects you curate + into this collection.

-

How to annotate objects in Kompakkt?

+

How to annotate objects in Kompakkt?

-

You can start annotating objects when you switch to Annotate mode via the Edit menu within your own objects or - when - you access Annotate mode from the Collection’s page Edit menu.

+

You can start annotating objects when you switch to Annotate mode via the Edit menu within your own objects or + when + you access Annotate mode from the Collection’s page Edit menu.

- -
+ -
-

Your annotations

+

Your annotations

- -

You must be logged in to see your annotations.

-
- - + +

You must be logged in to see your annotations.

+
+ + +
+
- - -

You have created annotations within the following objects and/or collections:

+ + + +

You have created annotations within the following objects and/or collections:

- - - - -
- -
+ + + + + +
- -

You don’t have any annotations yet. Start creating annotations by accessing your existing objects and - collections, or creating new ones:

- - - - -
-
-
+ + +

You don’t have any annotations yet. Start creating annotations by accessing your existing objects and + collections, or creating new ones:

+
+ + + +
+
+
diff --git a/src/app/pages/annotate/annotate.component.scss b/src/app/pages/annotate/annotate.component.scss index 6f4f7ae5..4f2b0fcd 100644 --- a/src/app/pages/annotate/annotate.component.scss +++ b/src/app/pages/annotate/annotate.component.scss @@ -1,8 +1,11 @@ -:host { - display: flex; - align-items: center; +p { + margin: 12px 0; } -div.container { - max-width: 1024px; +p:has(b) { + margin-top: 24px; +} + +button { + margin-top: 12px; } diff --git a/src/app/pages/annotate/annotate.component.ts b/src/app/pages/annotate/annotate.component.ts index d5be01e3..8101e33e 100644 --- a/src/app/pages/annotate/annotate.component.ts +++ b/src/app/pages/annotate/annotate.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { Meta, Title } from '@angular/platform-browser'; import { IAnnotation, ICompilation, IEntity } from 'src/common'; -import { AccountService, BackendService } from 'src/app/services'; +import { AccountService, BackendService, DialogHelperService } from 'src/app/services'; import { map } from 'rxjs/operators'; import { ReplaySubject } from 'rxjs'; @@ -13,11 +13,13 @@ import { ReplaySubject } from 'rxjs'; }) export class AnnotateComponent implements OnInit { public entitiesAndCompilations$ = new ReplaySubject>(0); + constructor( private titleService: Title, private metaService: Meta, private account: AccountService, private backend: BackendService, + public dialogHelper: DialogHelperService, ) {} get isAuthenticated$() { diff --git a/src/app/pages/collaborate/collaborate.component.html b/src/app/pages/collaborate/collaborate.component.html index 10d30c1d..7b0b8155 100644 --- a/src/app/pages/collaborate/collaborate.component.html +++ b/src/app/pages/collaborate/collaborate.component.html @@ -1,54 +1,60 @@ -
+

Collaborate

-
-

What are groups?

-

You can easily collaborate with your team or your students when managing annotations and collections of objects if - you create a dedicated group. Groups allow you to manage access rights across multiple registered users. Regular - group members receive annotation access for objects/collections associated with the group. Group owners receive - annotation access and can add/remove other group members. Groups can also be helpful when managing viewing access to - private objects, which remain unpublished to other Kompakkt users.

- -

How do groups work with collections?

-

When you create a collection of Kompakkt objects you can annotate them regardless of their owners. This can be an - effective way to collaborate as you can invite either other individual Kompakkt users, or non-registred users (via - password-protection) to gain access rights to specific collections. If you need to continuously give access and/or - annotation rights to a number of individuals to different objects / collections, creating a group will just make - this process more efficient and will enable you to manage access rights centrally by managing the group membership. - Note that groups do require all their members to be registered users.

-
- - -
-

Your groups

- - -

You must be logged in to see your groups. -

+

What are groups?

+

You can easily collaborate with your team or your students when managing annotations and collections of objects if + you create a dedicated group. Groups allow you to manage access rights across multiple registered users. Regular + group members receive annotation access for objects/collections associated with the group. Group owners receive + annotation access and can add/remove other group members. Groups can also be helpful when managing viewing access to + private objects, which remain unpublished to other Kompakkt users.

+ +

How do groups work with collections?

+

When you create a collection of Kompakkt objects you can annotate them regardless of their owners. This can be an + effective way to collaborate as you can invite either other individual Kompakkt users, or non-registred users (via + password-protection) to gain access rights to specific collections. If you need to continuously give access and/or + annotation rights to a number of individuals to different objects / collections, creating a group will just make + this process more efficient and will enable you to manage access rights centrally by managing the group membership. + Note that groups do require all their members to be registered users.

+ +

Your groups

+ + +

You must be logged in to see your groups.

+ +
+ + +
+
+ + + + - - - - - - - -

You are either the owner or a member of one of the following groups:

+ + +

You are either the owner or a member of one of the following groups:

-
-
{{ group.name }}
-
-
+ + +
- -

You are not a member of any groups yet. -

+
+ +

You are not a member of any groups yet. +

- - -
+
+ + +
-
- +
diff --git a/src/app/pages/collaborate/collaborate.component.scss b/src/app/pages/collaborate/collaborate.component.scss index 6f4f7ae5..4f2b0fcd 100644 --- a/src/app/pages/collaborate/collaborate.component.scss +++ b/src/app/pages/collaborate/collaborate.component.scss @@ -1,8 +1,11 @@ -:host { - display: flex; - align-items: center; +p { + margin: 12px 0; } -div.container { - max-width: 1024px; +p:has(b) { + margin-top: 24px; +} + +button { + margin-top: 12px; } diff --git a/src/app/pages/collaborate/collaborate.component.ts b/src/app/pages/collaborate/collaborate.component.ts index 6815cf90..dd917e53 100644 --- a/src/app/pages/collaborate/collaborate.component.ts +++ b/src/app/pages/collaborate/collaborate.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit } from '@angular/core'; import { Meta, Title } from '@angular/platform-browser'; -import { AccountService, BackendService } from 'src/app/services'; -import { ReplaySubject } from 'rxjs'; -import { ICompilation, IGroup } from '~common/interfaces'; +import { AccountService, BackendService, DialogHelperService } from 'src/app/services'; +import { map, Observable, of, switchMap } from 'rxjs'; +import { IGroup } from '~common/interfaces'; @Component({ selector: 'app-collaborate', @@ -11,14 +11,27 @@ import { ICompilation, IGroup } from '~common/interfaces'; styleUrls: ['./collaborate.component.scss'], }) export class CollaborateComponent implements OnInit { - public userInGroups$ = new ReplaySubject(0); - public userInCompilations$ = new ReplaySubject(0); + public groups$: Observable> = this.account.userData$.pipe( + switchMap(user => { + if (!user) return of({ user, groups: [] }); + return this.backend.findUserInGroups().then(groups => ({ user, groups })); + }), + map(({ user, groups }) => { + if (!user) return []; + return groups.map(group => ({ + ...group, + userOwned: + group.creator._id === user._id || group.owners.some(owner => owner._id === user._id), + })); + }), + ); constructor( private account: AccountService, private backend: BackendService, private titleService: Title, private metaService: Meta, + public dialogHelper: DialogHelperService, ) {} get isAuthenticated$() { @@ -31,15 +44,5 @@ export class CollaborateComponent implements OnInit { name: 'description', content: 'Work collaboratively.', }); - - this.userInGroups$.next([]); - this.userInCompilations$.next([]); - - this.account.user$.subscribe(() => { - this.backend.findUserInGroups().then(groups => this.userInGroups$.next(groups)); - this.backend - .findUserInCompilations() - .then(compilations => this.userInCompilations$.next(compilations)); - }); } } diff --git a/src/app/pages/profile-page/profile-page.component.html b/src/app/pages/profile-page/profile-page.component.html index 80772b90..ba2a9e15 100644 --- a/src/app/pages/profile-page/profile-page.component.html +++ b/src/app/pages/profile-page/profile-page.component.html @@ -1,6 +1,6 @@ -
+

User Profile

@@ -14,7 +14,7 @@

No data available for the current user.

-
+

User Profile

@@ -193,81 +193,17 @@

No matches

- -
-

You have not created any groups

-
- -
-
-

{{ group.name }}

-

- Members: {{ group.members.length }} | Owners: - {{ group.owners.length }} -

- - - - - - - - - -
+ +
+

You are not associated with any groups

- - -
-

You are not partaking in any group

-
-
- - {{ group.name }} - Members: {{ group.members.length }} | Owners: - {{ group.owners.length }} - - - - - -
-
+ + + + +
@@ -278,26 +214,6 @@

{{ group.name }}

> Create a new group - -
- - My groups - - Groups I partake in - -
diff --git a/src/app/pages/static-pages/consortium/consortium.component.html b/src/app/pages/static-pages/consortium/consortium.component.html index 5124252d..7347686e 100644 --- a/src/app/pages/static-pages/consortium/consortium.component.html +++ b/src/app/pages/static-pages/consortium/consortium.component.html @@ -1,4 +1,4 @@ -
+

Kompakkt Developer Consortium

diff --git a/src/app/pages/static-pages/contact/contact.component.html b/src/app/pages/static-pages/contact/contact.component.html index 3b133ccb..c3c4861a 100644 --- a/src/app/pages/static-pages/contact/contact.component.html +++ b/src/app/pages/static-pages/contact/contact.component.html @@ -1,4 +1,4 @@ -

+

Contact

Kompakkt is being developed at:

diff --git a/src/app/pages/static-pages/privacy/privacy.component.html b/src/app/pages/static-pages/privacy/privacy.component.html index b605f617..eb235ca8 100644 --- a/src/app/pages/static-pages/privacy/privacy.component.html +++ b/src/app/pages/static-pages/privacy/privacy.component.html @@ -1,4 +1,4 @@ -
+

Privacy Policy

diff --git a/src/app/services/dialog-helper.service.ts b/src/app/services/dialog-helper.service.ts index e58768dc..64b792bf 100644 --- a/src/app/services/dialog-helper.service.ts +++ b/src/app/services/dialog-helper.service.ts @@ -4,13 +4,19 @@ import { MatDialog } from '@angular/material/dialog'; import { EventsService } from './'; import { ICompilation, IEntity } from 'src/common'; import { AuthDialogComponent } from 'src/app/components'; -import { AddCompilationWizardComponent, AddEntityWizardComponent } from 'src/app/wizards'; +import { + AddCompilationWizardComponent, + AddEntityWizardComponent, + AddGroupWizardComponent, +} from 'src/app/wizards'; import { ConfirmationDialogComponent, - RegisterDialogComponent, EditEntityDialogComponent, EntitySettingsDialogComponent, PasswordProtectedDialogComponent, + RegisterDialogComponent, + ViewerDialogComponent, + ViewerDialogData, } from 'src/app/dialogs'; @Injectable({ @@ -47,6 +53,22 @@ export class DialogHelperService { }); } + public openEntityWizard() { + return this.dialog.open(AddEntityWizardComponent, { + disableClose: true, + }); + } + + public openGroupWizard() { + return this.dialog.open(AddGroupWizardComponent, { + disableClose: true, + }); + } + + public openViewerDialog(data: ViewerDialogData) { + return this.dialog.open(ViewerDialogComponent, { data, id: 'viewer-dialog' }); + } + public editCompilation(element: ICompilation | undefined) { if (!element) return; const dialogRef = this.dialog.open(AddCompilationWizardComponent, { diff --git a/src/styles.scss b/src/styles.scss index 6987fb9a..c4685375 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -8,10 +8,12 @@ @import url('./assets/fonts/OpenSans.css'); @import url('./assets/fonts/MaterialIcons.css'); +@import 'utility'; + :root { --font-stack: 'Open Sans', Frutiger, 'Frutiger Linotype', Univers, Calibri, 'Gill Sans', - 'Gill Sans MT', 'Myriad Pro', Myriad, 'DejaVu Sans Condensed', 'Liberation Sans', - 'Nimbus Sans L', Tahoma, Geneva, 'Helvetica Neue', Helvetica, Arial, sans-serif; + 'Gill Sans MT', 'Myriad Pro', Myriad, 'DejaVu Sans Condensed', 'Liberation Sans', + 'Nimbus Sans L', Tahoma, Geneva, 'Helvetica Neue', Helvetica, Arial, sans-serif; --drop-shadow: drop-shadow(0 0.1rem 0.1rem rgba(0, 0, 0, 0.25)); --brand-color: #00afe7; } @@ -241,9 +243,10 @@ iframe { /* !important to overwrite default mat-card rule */ transition: border 280ms cubic-bezier(0.4, 0, 0.2, 1), - box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1) !important; + box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1) !important; --color: var(--brand-color); + &.error { --color: red; } @@ -412,6 +415,7 @@ app-actionbar { height: 100%; > * { + display: flex; flex-direction: column !important; } } @@ -422,7 +426,8 @@ app-annotate { #edit-entity-dialog, #explore-entity-dialog, -#explore-compilation-dialog { +#explore-compilation-dialog, +#viewer-dialog { height: 100vh; width: 100vw; @@ -433,7 +438,8 @@ app-annotate { } #explore-compilation-dialog, -#explore-entity-dialog { +#explore-entity-dialog, +#viewer-dialog{ padding: 0 !important; border-radius: 1rem !important; background: #111; @@ -447,7 +453,7 @@ app-annotate { grid-gap: 1em; padding-bottom: 10px; - grid-template-columns: repeat(5, 1fr); + grid-template-columns: repeat(4, 1fr); @include queries.small { grid-template-columns: repeat(1, 1fr); @@ -458,11 +464,11 @@ app-annotate { } @include queries.large { - grid-template-columns: repeat(5, 1fr); + grid-template-columns: repeat(4, 1fr); } @include queries.huge { - grid-template-columns: repeat(6, 1fr); + grid-template-columns: repeat(4, 1fr); } } @@ -502,6 +508,24 @@ app-annotate { } } +button { + &.rounded { + border-radius: 2em; + } + .mat-button-wrapper { + display: flex; + justify-content: center; + align-items: center; + gap: 4px; + } +} + +.button-row { + display: flex; + align-items: center; + gap: 8px; +} + #actionbar .mat-slide-toggle.mat-checked:not(.mat-disabled) { .mat-slide-toggle-bar { background-color: #aaa; @@ -546,6 +570,7 @@ app-edit-entity-dialog { .can-be-disabled { transition: filter 500ms; + &.disabled { filter: opacity(0.5); pointer-events: none; @@ -558,6 +583,7 @@ app-edit-entity-dialog { div.block { margin-bottom: 0 !important; + & + div.block { margin-top: 1rem !important; } diff --git a/src/utility.scss b/src/utility.scss new file mode 100644 index 00000000..d2102074 --- /dev/null +++ b/src/utility.scss @@ -0,0 +1,36 @@ +.centered-content { + box-sizing: border-box; + width: min(1440px, 100%); + max-width: 1440px; + align-self: center; + padding: 16px 32px; +} + +.flex { + display: flex; + align-items: flex-start; +} + +.flex-column { + display: flex; + flex-direction: column; + align-items: flex-start; +} + +.gap-12 { + gap: 12px; +} + +.gap-24 { + gap: 24px; +} + +.flex-text-margin { + h1, h2, h3, h4, h5, h6, p { + margin: 0; + } + + h1 + p, h2 + p, h3 + p, h4 + p, h5 + p, h6 + p, p + p { + margin-top: 1em; + } +}