You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datalab.apache.org by hs...@apache.org on 2022/09/20 11:40:35 UTC

[incubator-datalab] 01/01: [DATALAB-3008] implemented logyc by request for input dropdown

This is an automated email from the ASF dual-hosted git repository.

hshpak pushed a commit to branch feat/DATALAB-3008/support-autocomplete-for-sharing-dialog
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit 14fb48ab2cb3c2494cf5513fdaff5893cdd13e53
Author: Hennadii_Shpak <bo...@gmail.com>
AuthorDate: Thu Sep 15 15:08:55 2022 +0300

    [DATALAB-3008] implemented logyc by request for input dropdown
---
 .../app/core/directives/click-outside.directive.ts |   2 +-
 .../app/core/services/user-images-page.service.ts  |  22 ++-
 .../image-action-dialog.component.html             |   4 +-
 .../image-action-dialog.component.scss             |   4 -
 .../image-action-dialog.component.ts               |  15 ++-
 .../image-action-dialog.module.ts                  |   4 +-
 .../image-action-dialog/image-action.model.ts      |   5 +
 .../share-dialog/share-dialog.component.html       |  96 ++++++-------
 .../share-dialog/share-dialog.component.scss       |  40 ++++--
 .../share-dialog/share-dialog.component.ts         | 148 +++++++++++----------
 .../share-dialog/share-dialog.service.ts           |  91 +++++++++++++
 .../share-user-data/share-user-data.component.html |   2 +-
 .../share-user-data/share-user-data.component.ts   |   4 +-
 .../un-share-warning.component.html                |   2 +-
 .../page-filter/page-filter.component.html         |  32 ++---
 .../page-filter/page-filter.component.ts           |  20 +--
 .../src/app/resources/images/images.component.html |   4 +-
 .../src/app/resources/images/images.component.ts   |  16 +--
 .../src/app/resources/images/images.config.ts      |   5 +
 .../src/app/resources/images/images.model.ts       |   2 +-
 .../src/app/resources/images/images.service.ts     |  90 ++++++-------
 21 files changed, 367 insertions(+), 241 deletions(-)

diff --git a/services/self-service/src/main/resources/webapp/src/app/core/directives/click-outside.directive.ts b/services/self-service/src/main/resources/webapp/src/app/core/directives/click-outside.directive.ts
index ad34ab242..e19f0cd0c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/directives/click-outside.directive.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/directives/click-outside.directive.ts
@@ -76,4 +76,4 @@ export class ClickOutsideDirective implements OnInit, OnDestroy {
     }
     return false;
   }
-}
\ No newline at end of file
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/user-images-page.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/user-images-page.service.ts
index 5811202da..919af15a6 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/user-images-page.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/user-images-page.service.ts
@@ -25,11 +25,11 @@ import {  ErrorUtils } from '../util';
 import { ApplicationServiceFacade } from './applicationServiceFacade.service';
 import {
   ImageActionType,
-  ImageFilterFormValue, ImageModel,
+  ImageFilterFormValue,
   ProjectImagesInfo,
-  ImageParams
+  ImageParams, URL_Chunk
 } from '../../resources/images';
-import { UserData } from '../../resources/exploratory/image-action-dialog/image-action.model';
+import { ShareDialogData, UserData } from '../../resources/exploratory/image-action-dialog/image-action.model';
 
 @Injectable()
 export class UserImagesPageService {
@@ -69,9 +69,19 @@ export class UserImagesPageService {
       );
   }
 
-  getImageShareInfo(image: ImageParams): Observable<UserData[]> {
-    const { imageName, projectName, endpoint} = image;
-    const url = `/sharing_info/${imageName}/${projectName}/${endpoint}/`;
+  getImageShareInfo(imageInfo: ImageParams): Observable<ShareDialogData> {
+    const { imageName, projectName, endpoint} = imageInfo;
+    const url = `/${URL_Chunk.sharingInfo}/${imageName}/${projectName}/${endpoint}/`;
+    return this.applicationServiceFacade
+      .buildGetImageShareInfo(url)
+      .pipe(
+        catchError(ErrorUtils.handleServiceError)
+      );
+  }
+
+  getUserDataForShareDropdown(imageInfo: ImageParams, userData: string): Observable<UserData[]> {
+    const { imageName, projectName, endpoint} = imageInfo;
+    const url = `/${URL_Chunk.autocomplete}/${imageName}/${projectName}/${endpoint}?value=${userData}`;
     return this.applicationServiceFacade
       .buildGetImageShareInfo(url)
       .pipe(
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html
index ea72dcaf3..c531d5423 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html
@@ -33,10 +33,10 @@
     <div [hidden]="activeTabConfig && activeTabConfig.shareWith" class="text-center m-top-30 m-bott-10">
       <button type="button" class="butt mat-raised-button" (click)="dialogRef.close()">No</button>
       <button
-        [disabled]="isApplyBtnDisabled"
+        [disabled]="isShareBtnDisabled"
         type="button"
         class="butt butt-success mat-raised-button"
-        (click)="dialogRef.close(responseObj)">
+        (click)="dialogRef.close(sharingDataList)">
         {{confirmBtnName}}
       </button>
     </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.scss
index 80ef60e42..ec94d5903 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.scss
@@ -17,10 +17,6 @@
 * under the License.
 */
 
-.dialog__wrapper {
-  height: 385px;
-}
-
 .content {
   max-height: 75vh;
   margin: 0;
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts
index 79f7049b3..d07f7f47c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts
@@ -22,6 +22,11 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
 import { ImageActions, ImageActionModalData } from '../../images';
 import { DialogWindowTabConfig, UserData } from './image-action.model';
 
+const CONFIRM_BUTTON_CONFIG = {
+  share: 'Share',
+  terminate: 'Yes'
+};
+
 @Component({
   selector: 'datalab-image-action-dialog',
   templateUrl: './image-action-dialog.component.html',
@@ -29,8 +34,8 @@ import { DialogWindowTabConfig, UserData } from './image-action.model';
 })
 export class ImageActionDialogComponent implements OnInit {
   @Input() activeTabConfig: DialogWindowTabConfig;
-  @Input() isApplyBtnDisabled: Boolean;
-  @Input() responseObj: UserData[] = [];
+  @Input() isShareBtnDisabled: Boolean;
+  @Input() sharingDataList: UserData[] = [];
 
   readonly actionType: typeof ImageActions = ImageActions;
 
@@ -46,10 +51,6 @@ export class ImageActionDialogComponent implements OnInit {
   }
 
   private createConfirmBtnName(): void {
-    const btnNameObj = {
-      share: 'Share',
-      terminate: 'Yes'
-    };
-    this.confirmBtnName = btnNameObj[this.data.actionType];
+    this.confirmBtnName = CONFIRM_BUTTON_CONFIG[this.data.actionType];
   }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.module.ts
index fc43e341d..a8e5b976e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.module.ts
@@ -23,7 +23,7 @@ import { ImageActionDialogComponent } from './image-action-dialog.component';
 import { MaterialModule } from '../../../shared/material.module';
 import { TerminateDialogComponent } from './terminate-dialog/terminate-dialog.component';
 import { ShareDialogComponent } from './share-dialog/share-dialog.component';
-import { FormsModule } from '@angular/forms';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { ShareUserDataComponent } from './share-user-data/share-user-data.component';
 import { UnShareWarningComponent } from './unshare-warning/un-share-warning.component';
 
@@ -37,7 +37,7 @@ import { UnShareWarningComponent } from './unshare-warning/un-share-warning.comp
     ShareUserDataComponent,
     UnShareWarningComponent
   ],
-  imports: [ CommonModule, MaterialModule, FormsModule ],
+  imports: [ CommonModule, MaterialModule, FormsModule, ReactiveFormsModule ],
   entryComponents: [TerminateDialogComponent , ShareDialogComponent, UnShareWarningComponent],
   exports: [ ImageActionDialogComponent ]
 })
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action.model.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action.model.ts
index 8c16eecd5..42de04e41 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action.model.ts
@@ -27,5 +27,10 @@ export interface UserData {
   type: UserDataType;
 }
 
+export interface ShareDialogData {
+  canBeSharedWith: UserData[];
+  sharedWith: UserData[];
+}
+
 export type UserDataType = 'USER' | 'GROUP';
 
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.html
index fc2d6e7d1..e91d48890 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.html
@@ -19,11 +19,14 @@
 
 <datalab-image-action-dialog
   [activeTabConfig]="activeTabConfig"
-  [responseObj]="responseObj"
-  [isApplyBtnDisabled]="isApplyBtnDisabled"
+  [sharingDataList]="sharingDataList"
+  [isShareBtnDisabled]="!isShareBtnDisabled"
 >
 
-  <ng-container *ngIf="($getUserListData | async)"></ng-container>
+<!--  Component subscription-->
+  <ng-container *ngIf="(getUserListDataSubscription$ | async)"></ng-container>
+  <ng-container *ngIf="(getUserListDataOnChangeSubscription$ | async)"></ng-container>
+  <ng-container *ngIf="(onInputSubscription$ | async)"></ng-container>
 
   <div datalab-share-dialog class="dialog__wrapper">
     <ul class="title__list">
@@ -53,62 +56,61 @@
 
     <div *ngIf="activeTabConfig.shareImage" class="search-input__wrapper">
 
-<!--      TODO REMOVE IT WHEN REMOVING THE RADIO BUTTON FROM APP-->
-
-      <div class="radio__wrapper">
-        <div class="radio-input__wrapper">
-          <label>
-            user
-            <input
-              name="user-radio"
-              type="radio"
-              [(ngModel)]="userNameOrGroup"
-              [value]="userDataTypeConfig.user">
-          </label>
-          <label>
-            user group
-            <input
-              name="group-radio"
-              type="radio"
-              [(ngModel)]="userNameOrGroup"
-              [value]="userDataTypeConfig.group">
-          </label>
-        </div>
-        <span [hidden]="!isSearchUserTouched" class="error">Select with whom to share the image</span>
-      </div>
       <div class="input__wrapper">
         <input
+          type="text"
           class="search-input"
+          panelClass="scrolling"
           [placeholder]="placeholder.groupOrNameSearchInput"
-          [(ngModel)]="searchInput"
-          #searchUser="ngModel"
-          (keydown.enter)="onAddUser()"
-          required
-        >
-        <button
-          class="add-person__btn"
-          mat-icon-button
-          [disabled]="isAddUserDataBtnDisabled"
-          (click)="onAddUser()"
-        >
-          <mat-icon> person_add </mat-icon>
-        </button>
-        <span [hidden]="!isUserInputEmpty" class="error">This field is required</span>
-        <span [hidden]="!isLongInputMessage" class="error">Length can`t be more than 25 symbols</span>
+          [formControl]="addUserDataControl"
+          (change)="onChange()"
+          (input)="onInputKeyDown()"
+          matInput
+          [matAutocomplete]="auto"
+          #searchUserData
+        />
+        <mat-autocomplete #auto="matAutocomplete">
+          <mat-option
+            *ngFor="let user of (searchUserDataDropdownList$ | async)"
+            (click)="addUserToTemporaryList(user)"
+            [value]="user.value">
+            <mat-icon class="user-data__icon">{{user.type === 'GROUP' ? 'people' : 'person'}}</mat-icon>
+            {{user.value}}
+          </mat-option>
+          <mat-option *ngIf="!(searchUserDataDropdownList$ | async)?.length" class="multiple-select ml-10" disabled>
+            No results found
+          </mat-option>
+        </mat-autocomplete>
+        <span [hidden]="!isUserDataListEmpty" class="error">
+          Please enter user login or group name
+        </span>
       </div>
       <div class="user-list__wrapper scrolling">
-        <ul class="user-list">
-          <li *ngFor="let entity of temporaryUserDataList" class="user-list__item">
-            <datalab-share-user-data (removeUserData)="onRemoveUserData($event)" [userData]="entity"></datalab-share-user-data>
+        <ul class="user-list user-list__temporary">
+          <li
+            *ngFor="let entity of (temporaryUserDataList$ | async)"
+            class="user-list__item"
+          >
+            <datalab-share-user-data
+              (removeUserData)="onRemoveUserData($event)"
+              [userData]="entity"
+            >
+            </datalab-share-user-data>
           </li>
         </ul>
       </div>
     </div>
 
     <div *ngIf="activeTabConfig.shareWith" class="share-with__wrapper">
-      <div *ngIf="userDataList.length; else emptyUserList" class="user-list__wrapper scrolling">
-        <ul class="user-list">
-          <li *ngFor="let entity of userDataList" class="user-list__item">
+      <div
+        *ngIf="(userDataList$ | async)?.length; else emptyUserList"
+        class="user-list__wrapper scrolling"
+      >
+        <ul class="user-list user-list__shared">
+          <li
+            *ngFor="let entity of (userDataList$ | async)"
+            class="user-list__item"
+          >
             <datalab-share-user-data (removeUserData)="unShare($event)" [userData]="entity"></datalab-share-user-data>
           </li>
         </ul>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.scss
index 5d5aca78b..62748a486 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.scss
@@ -51,7 +51,13 @@
   outline: none;
 
   &::placeholder {
+    font-size: 16px;
     color: #c6bcbc;
+    font-weight: 300;
+  }
+
+  &__wrapper {
+    height: 149px;
   }
 }
 
@@ -73,9 +79,16 @@
 .user-list {
   display: flex;
   flex-wrap: wrap;
-  height: 80px;
   padding: 10px 0;
 
+  &__temporary {
+    height: 80px;
+  }
+
+  &__shared {
+    height: 160px;
+  }
+
   &__wrapper{
     overflow-y: scroll;
   }
@@ -103,19 +116,18 @@
   color: #35afd5;
 }
 
-// TODO REMOVE IT WHEN REMOVING THE RADIO BUTTON FROM APP
-//start
-.radio__wrapper {
-  margin-bottom: 20px;
-  padding-left: 10px;
-  text-align: start;
-}
-input[name="user-radio"] {
-  margin-right: 20px;
+.share-with {
+  &__wrapper {
+    height: 225px;
+  }
 }
 
-label {
-  font-weight: 300;
+.user-data__icon {
+  width: 30px;
+  height: 30px;
+  padding-top: 2px;
+  text-align: center;
+  border-radius: 50%;
+  background-color: #E6E6E6;
+  color: white;
 }
-//end
-
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.ts
index 08711f5b8..0cbbc035e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.ts
@@ -17,66 +17,94 @@
  * under the License.
  */
 
-import { ChangeDetectionStrategy, Component, Inject, OnInit, ViewChild } from '@angular/core';
-import { SharePlaceholder, TabName, UserDataTypeConfig } from '../image-action.config';
-import { DialogWindowTabConfig, UserData, UserDataType } from '../image-action.model';
-import { NgModel } from '@angular/forms';
+import { Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
+import { SharePlaceholder, TabName } from '../image-action.config';
+import { DialogWindowTabConfig, ShareDialogData, UserData } from '../image-action.model';
+import { FormControl } from '@angular/forms';
 import { ImagesService } from '../../../images/images.service';
 import { MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog';
-import { ImageActionModalData, ImageParams, ModalTitle, ProjectImagesInfo, Toaster_Message, UnShareModal } from '../../../images';
+import {
+  ImageActionModalData,
+  ModalTitle,
+  Toaster_Message,
+  UnShareModal,
+} from '../../../images';
 import { switchMap, tap } from 'rxjs/operators';
-import { EMPTY, Observable } from 'rxjs';
+import { Observable, timer } from 'rxjs';
 import { UnShareWarningComponent } from '../unshare-warning/un-share-warning.component';
 import { ToastrService } from 'ngx-toastr';
+import { ShareDialogService } from './share-dialog.service';
 
 @Component({
   selector: 'datalab-share-dialog',
   templateUrl: './share-dialog.component.html',
   styleUrls: ['./share-dialog.component.scss'],
-  changeDetection: ChangeDetectionStrategy.OnPush
 })
-export class ShareDialogComponent implements OnInit {
-  @ViewChild('searchUser') searchUser: NgModel;
+
+export class ShareDialogComponent implements OnInit, OnDestroy {
+  @ViewChild('searchUserData') searchUserData: ElementRef;
 
   readonly placeholder: typeof SharePlaceholder = SharePlaceholder;
   readonly tabsName: typeof TabName = TabName;
-  readonly userDataTypeConfig: typeof UserDataTypeConfig = UserDataTypeConfig;
 
-  userDataList: UserData[] = [];
-  temporaryUserDataList: UserData[] = [];
-  userNameOrGroup: UserDataType;
+  searchUserDataDropdownList$: Observable<UserData[]>;
+  userDataList$!: Observable<UserData[]>;
+  temporaryUserDataList$!: Observable<UserData[]>;
+  sharingDataList: UserData[] = [];
   activeTabConfig: DialogWindowTabConfig = {
     shareImage: true,
     shareWith: false
   };
-  searchInput = '';
+  addUserDataControl: FormControl = new FormControl('');
 
-  $getUserListData: Observable<UserData[]>;
+  onInputSubscription$: Observable<UserData[]>;
+  getUserListDataSubscription$: Observable<ShareDialogData>;
+  getUserListDataOnChangeSubscription$: Observable<UserData[]>;
 
   constructor(
+    public toastr: ToastrService,
     private imagesService: ImagesService,
     @Inject(MAT_DIALOG_DATA) public data: ImageActionModalData,
     private dialog: MatDialog,
-    public toastr: ToastrService,
+    private shareDialogService: ShareDialogService
   ) {
   }
 
   ngOnInit(): void {
-    this.initUserList();
+    this.getImageParams();
+    this.getUserListDataSubscription$ = this.shareDialogService.getImageShareInfo();
+    this.initUserData();
+    this.initSearchDropdownList();
+    this.initTemporaryUserDataList();
+  }
+
+  ngOnDestroy(): void {
+    this.shareDialogService.clearTemporaryList();
+  }
+
+  onInputKeyDown(): void {
+    this.onInputSubscription$ = timer(300).pipe(
+      switchMap(() => this.shareDialogService.getUserDataForShareDropdown(this.addUserDataControl.value))
+    );
+  }
+
+  onChange(): void {
+    this.addUserDataControl.setValue('');
+    // We need a timer because the click event cannot have time to select userData from the dropdown list.
+    this.getUserListDataOnChangeSubscription$ = timer(300).pipe(
+      switchMap(() => this.shareDialogService.getUserDataForShareDropdown())
+    );
   }
 
-  onAddUser(): void {
-    if (!this.searchInput) {
+  addUserToTemporaryList(user: UserData): void {
+    if (!user) {
       return;
     }
-    const newUserEntity: UserData = {
-      value: this.searchInput.trim(),
-      type: this.userNameOrGroup
-    };
-    this.temporaryUserDataList = [...this.temporaryUserDataList, newUserEntity]
-      .sort((a, b) => a.value < b.value ? 1 : -1)
-      .sort((a, b) => a.type > b.type ? 1 : -1);
-    this.searchInput = '';
+    this.shareDialogService.addToTemporaryList(user);
+    this.addUserDataControl.setValue('');
+    this.searchUserData.nativeElement.blur();
+    this.sharingDataList = this.shareDialogService.getSharingUserDataList();
+    this.shareDialogService.filterSearchDropdown();
   }
 
   onTabTitle(tabName: keyof DialogWindowTabConfig): void {
@@ -84,75 +112,51 @@ export class ShareDialogComponent implements OnInit {
     this.activeTabConfig = {...this.activeTabConfig, [tabName]: true};
   }
 
-  onRemoveUserData(userName: string): void {
-    this.temporaryUserDataList = this.temporaryUserDataList.filter(({value}) => value !== userName);
+  onRemoveUserData(userData: UserData): void {
+    this.shareDialogService.removeUserFromTemporaryList(userData);
+    this.shareDialogService.filterSearchDropdown();
   }
 
-  unShare(userName: string): void {
+  unShare(userData: UserData): void {
     const data: UnShareModal = {
-      userName,
+      userData,
       title: ModalTitle.unShare
     };
-    const filteredList = this.userDataList.filter(({value}) => value !== userName);
+    const filteredList = this.shareDialogService.filterSharingList(userData);
     const imageInfo = this.imagesService.createImageRequestInfo(this.data.image, filteredList);
 
-    this.$getUserListData = this.dialog.open(UnShareWarningComponent, {
+    this.getUserListDataSubscription$ = this.dialog.open(UnShareWarningComponent, {
       data,
       panelClass: 'modal-sm'
     }).afterClosed()
       .pipe(
-        switchMap((isShare) => this.sendShareRequest(isShare, imageInfo)),
-        switchMap(() =>  this.getSharingUserList()),
+        switchMap((isShare) => this.shareDialogService.sendShareRequest(isShare, imageInfo)),
+        switchMap(() =>  this.shareDialogService.getImageShareInfo()),
         tap(() => this.toastr.success(Toaster_Message.successUnShare, Toaster_Message.successTitle))
       );
   }
 
-  private sendShareRequest(flag: boolean, imageInfo: ImageParams): Observable<ProjectImagesInfo> {
-    if (!flag) {
-      return EMPTY;
-    }
-    return  this.imagesService.shareImageAllUsers(imageInfo);
-  }
-
-  private initUserList(): void {
-    this.$getUserListData = this.getSharingUserList();
-  }
-
-  private getSharingUserList(): Observable<UserData[]> {
-    const { name, project, endpoint} = this.data.image;
-    const imageParams = {
-      imageName: name,
-      projectName: project,
-      endpoint
-    };
-    return this.imagesService.getImageShareInfo(imageParams).pipe(
-      tap(userListData => this.userDataList = userListData)
-    );
-  }
-
-  get responseObj(): UserData[] {
-    return [...this.temporaryUserDataList, ...this.userDataList];
+  private getImageParams(): void {
+    this.shareDialogService.imageInfo = this.imagesService.createImageRequestInfo(this.data.image);
   }
 
-  get isApplyBtnDisabled(): boolean {
-    return this.searchInput.length > 25 || !Boolean(this.temporaryUserDataList.length);
+  private initUserData(): void {
+    this.userDataList$ = this.shareDialogService.userDataList$;
   }
 
-  get isAddUserDataBtnDisabled(): boolean {
-    return !Boolean(this.userNameOrGroup) || this.searchInput.length > 25 || !Boolean(this.searchInput.length);
+  private initSearchDropdownList(): void {
+    this.searchUserDataDropdownList$ = this.shareDialogService.searchUserDataDropdownList$;
   }
 
-  get isSearchUserTouched(): boolean {
-    return this.searchUser?.control.touched && !Boolean(this.userNameOrGroup);
+  private initTemporaryUserDataList(): void {
+    this.temporaryUserDataList$ = this.shareDialogService.temporaryUserDataList$;
   }
 
-  get isUserInputEmpty(): boolean {
-    return this.searchUser?.control.touched
-      && !Boolean(this.searchInput.length)
-      && !Boolean(this.temporaryUserDataList.length);
+  get isShareBtnDisabled(): boolean {
+    return !this.shareDialogService.isTemporaryListEmpty();
   }
 
-  get isLongInputMessage() {
-    return this.searchInput.length > 25;
+  get isUserDataListEmpty(): boolean {
+    return this.addUserDataControl.touched && this.shareDialogService.isTemporaryListEmpty();
   }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.service.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.service.ts
new file mode 100644
index 000000000..40a8aabf4
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.service.ts
@@ -0,0 +1,91 @@
+import { Injectable } from '@angular/core';
+import { BehaviorSubject, EMPTY, Observable } from 'rxjs';
+import { take, tap } from 'rxjs/operators';
+
+import isEqual from 'lodash.isequal';
+
+import { ImagesService } from '../../../images/images.service';
+import { ImageActionDialogModule } from '../image-action-dialog.module';
+import { ImageParams, ProjectImagesInfo } from '../../../images';
+import { ShareDialogData, UserData } from '../image-action.model';
+import { UserImagesPageService } from '../../../../core/services';
+
+@Injectable({
+  providedIn: ImageActionDialogModule
+})
+export class ShareDialogService {
+  private searchUserDataDropdownList$$: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
+  private userDataList$$: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
+  private temporaryUserDataList$$: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
+  private cashedUserDataDropdownList: UserData[];
+
+  searchUserDataDropdownList$: Observable<UserData[]> = this.searchUserDataDropdownList$$.asObservable();
+  userDataList$: Observable<UserData[]> = this.userDataList$$.asObservable();
+  temporaryUserDataList$: Observable<UserData[]> = this.temporaryUserDataList$$.asObservable();
+  imageInfo: ImageParams;
+
+  constructor(
+    private imagesService: ImagesService,
+    private userImagesPageService: UserImagesPageService
+  ) { }
+
+  getUserDataForShareDropdown(userData: string = ''): Observable<UserData[]> {
+    return this.userImagesPageService.getUserDataForShareDropdown(this.imageInfo, userData)
+      .pipe(
+        tap(response => this.filterSearchDropdown(response))
+      );
+  }
+
+  sendShareRequest(flag: boolean, imageInfo: ImageParams): Observable<ProjectImagesInfo> {
+    if (!flag) {
+      return EMPTY;
+    }
+    return  this.imagesService.shareImageAllUsers(imageInfo);
+  }
+
+  getImageShareInfo(): Observable<ShareDialogData> {
+    return this.userImagesPageService.getImageShareInfo(this.imageInfo).pipe(
+      tap(({canBeSharedWith, sharedWith}) => {
+        this.cashedUserDataDropdownList = canBeSharedWith;
+        this.searchUserDataDropdownList$$.next(canBeSharedWith);
+        this.userDataList$$.next(sharedWith);
+      }),
+      take(1)
+    );
+  }
+
+  filterSharingList(userData: UserData): UserData[] {
+    return this.userDataList$$.value.filter(item => !isEqual(item, userData));
+  }
+
+  getSharingUserDataList(): UserData[] {
+    return [...this.userDataList$$.value, ...this.temporaryUserDataList$$.value];
+  }
+
+  addToTemporaryList(user: UserData): void {
+    const filteredList = [...this.temporaryUserDataList$$.value, user]
+      .sort((a, b) => a.value < b.value ? 1 : -1)
+      .sort((a, b) => a.type > b.type ? 1 : -1);
+    this.temporaryUserDataList$$.next(filteredList);
+  }
+
+  isTemporaryListEmpty(): boolean {
+    return !Boolean(this.temporaryUserDataList$$.value.length);
+  }
+
+  clearTemporaryList(): void {
+    this.temporaryUserDataList$$.next([]);
+  }
+
+  removeUserFromTemporaryList(userData: UserData): void {
+    const filteredList = this.temporaryUserDataList$$.value.filter(item => !isEqual(item, userData));
+    this.temporaryUserDataList$$.next(filteredList);
+  }
+
+  filterSearchDropdown(userData: UserData[] = this.cashedUserDataDropdownList): void {
+   const filteredDropdownList = userData.filter(item => !this.getSharingUserDataList()
+     .some(temporaryDataItem => isEqual(item, temporaryDataItem))
+   );
+    this.searchUserDataDropdownList$$.next(filteredDropdownList);
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.html
index aa647e824..f8c8a580b 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.html
@@ -20,6 +20,6 @@
 <div class="user-data__wrapper">
   <span><mat-icon class="user-data__icon">{{userData.type === 'GROUP' ? 'people' : 'person'}}</mat-icon></span>
   <span class="user-data__name">{{userData.value}}
-    <button type="button" (click)="removeUser(userData.value)" class="close-btn">&times;</button>
+    <button type="button" (click)="removeUser(userData)" class="close-btn">&times;</button>
 </span>
 </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.ts
index 9468d21ed..ba52a47fd 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.ts
@@ -28,9 +28,9 @@ import { UserData } from '../image-action.model';
 export class ShareUserDataComponent {
   @Input() userData: UserData;
 
-  @Output() removeUserData: EventEmitter<string> = new EventEmitter<string>();
+  @Output() removeUserData: EventEmitter<UserData> = new EventEmitter<UserData>();
 
-  removeUser(userData: string): void {
+  removeUser(userData: UserData): void {
     this.removeUserData.emit(userData);
   }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/unshare-warning/un-share-warning.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/unshare-warning/un-share-warning.component.html
index 073d20067..b1c9cfabd 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/unshare-warning/un-share-warning.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/unshare-warning/un-share-warning.component.html
@@ -24,7 +24,7 @@
     <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
   </header>
   <section class="content">
-    <p class="modal__text">User <span class="user__name">{{data.userName}}</span> will no longer have an access to the image.</p>
+    <p class="modal__text"><span>{{data.userData.type | titlecase}} </span> <span class="user__name">{{data.userData.value}}</span> will no longer have an access to the image.</p>
     <p class="question center">
       Do you want proceed?
     </p>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.html
index 818a8a5b2..62fce4380 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.html
@@ -19,8 +19,8 @@
 
 <div class="dialog-content selection">
 
-  <ng-container *ngIf="$setFilterValueObservable | async"></ng-container>
-  <ng-container *ngIf="$changeIsApplyBtnDisabledObservable | async"></ng-container>
+  <ng-container *ngIf="setFilterValueObservable$ | async"></ng-container>
+  <ng-container *ngIf="changeIsApplyBtnDisabledObservable$ | async"></ng-container>
 
   <div id="scrolling" class="mat-reset">
     <form class="filter-table__wrapper" [formGroup]="filterForm">
@@ -35,7 +35,7 @@
           [matAutocomplete]="auto"
         />
         <mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">
-          <mat-option *ngFor="let option of ($filterDropdownData | async).imageNames" [value]="option">
+          <mat-option *ngFor="let option of (filterDropdownData$ | async).imageNames" [value]="option">
             {{option}}
           </mat-option>
         </mat-autocomplete>
@@ -57,7 +57,7 @@
                     {{statuses.value | normalizeDropdownMultiValue: selectAllValue | titlecase}}
                   </mat-select-trigger>
                   <mat-option
-                    *ngIf="($filterDropdownData | async).statuses.length > 1"
+                    *ngIf="(filterDropdownData$ | async).statuses.length > 1"
                     #allStatusesSelected
                     [value]="selectAllValue"
                     (click)="onClickAll(statuses, allStatusesSelected, dropdownFieldNames.statuses)"
@@ -65,8 +65,8 @@
                    All
                   </mat-option>
                   <mat-option
-                    *ngFor="let status of ($filterDropdownData | async).statuses"
-                    [ngClass]="{'single-option': ($filterDropdownData | async).statuses.length === 1}"
+                    *ngFor="let status of (filterDropdownData$ | async).statuses"
+                    [ngClass]="{'single-option': (filterDropdownData$ | async).statuses.length === 1}"
                     [value]="status"
                   >
                     {{ status | titlecase }}
@@ -96,7 +96,7 @@
                     {{endpoints.value | normalizeDropdownMultiValue: selectAllValue}}
                   </mat-select-trigger>
                   <mat-option
-                    *ngIf="($filterDropdownData | async).endpoints.length > 1"
+                    *ngIf="(filterDropdownData$ | async).endpoints.length > 1"
                     #allEndpointsSelected
                     [value]="selectAllValue"
                     (click)="onClickAll(endpoints, allEndpointsSelected, dropdownFieldNames.endpoints)"
@@ -104,8 +104,8 @@
                    All
                   </mat-option>
                   <mat-option
-                    *ngFor="let endpoint of ($filterDropdownData | async).endpoints"
-                    [ngClass]="{'single-option': ($filterDropdownData | async).statuses.length === 1}"
+                    *ngFor="let endpoint of (filterDropdownData$ | async).endpoints"
+                    [ngClass]="{'single-option': (filterDropdownData$ | async).statuses.length === 1}"
                     [value]="endpoint"
                   >
                     {{ endpoint }}
@@ -135,7 +135,7 @@
                     {{templateNames.value | normalizeDropdownMultiValue: selectAllValue}}
                   </mat-select-trigger>
                   <mat-option
-                    *ngIf="($filterDropdownData | async).templateNames.length > 1"
+                    *ngIf="(filterDropdownData$ | async).templateNames.length > 1"
                     #allTemplatesSelected
                     [value]="selectAllValue"
                     (click)="onClickAll(templateNames, allTemplatesSelected, dropdownFieldNames.templateNames)"
@@ -143,8 +143,8 @@
                    All
                   </mat-option>
                   <mat-option
-                    *ngFor="let template of ($filterDropdownData | async).templateNames"
-                    [ngClass]="{'single-option': ($filterDropdownData | async).statuses.length === 1}"
+                    *ngFor="let template of (filterDropdownData$ | async).templateNames"
+                    [ngClass]="{'single-option': (filterDropdownData$ | async).statuses.length === 1}"
                     [value]="template"
                   >
                     {{ template }}
@@ -174,7 +174,7 @@
                     {{sharingStatuses.value | normalizeDropdownMultiValue: selectAllValue | titlecase}}
                   </mat-select-trigger>
                   <mat-option
-                    *ngIf="($filterDropdownData | async).sharingStatuses.length > 1"
+                    *ngIf="(filterDropdownData$ | async).sharingStatuses.length > 1"
                     #allSharingStatusesSelected
                     [value]="selectAllValue"
                     (click)="onClickAll(sharingStatuses, allSharingStatusesSelected, dropdownFieldNames.sharingStatuses)"
@@ -182,8 +182,8 @@
                    All
                   </mat-option>
                   <mat-option
-                    *ngFor="let status of ($filterDropdownData | async).sharingStatuses"
-                    [ngClass]="{'single-option': ($filterDropdownData | async).statuses.length === 1}"
+                    *ngFor="let status of (filterDropdownData$ | async).sharingStatuses"
+                    [ngClass]="{'single-option': (filterDropdownData$ | async).statuses.length === 1}"
                     [value]="status"
                   >
                     {{ status | titlecase }}
@@ -201,7 +201,7 @@
         <button type="button" class="butt" mat-raised-button (click)="cancelFilter()" >Cancel</button>
         <button
           type="button"
-          [disabled]="($isApplyBtnDisabled | async)"
+          [disabled]="(isApplyBtnDisabled$ | async)"
           class="butt butt-success"
           mat-raised-button
           (click)="confirmFilter()"
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.ts
index 0c7961786..5a5803b1c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.ts
@@ -40,8 +40,8 @@ import {
   styleUrls: ['./page-filter.component.scss']
 })
 export class PageFilterComponent implements OnInit {
-  @Input() $filterDropdownData: Observable<ImageFilterFormDropdownData>;
-  @Input() $filterFormStartValue: Observable<ImageFilterFormValue>;
+  @Input() filterDropdownData$: Observable<ImageFilterFormDropdownData>;
+  @Input() filterFormStartValue$: Observable<ImageFilterFormValue>;
 
   @Output() filterFormValue: EventEmitter<ImageFilterFormValue> = new EventEmitter<ImageFilterFormValue>();
   @Output() closeFilter: EventEmitter<any> = new EventEmitter<any>();
@@ -53,13 +53,13 @@ export class PageFilterComponent implements OnInit {
   readonly controlNames: typeof FilterFormControlNames = FilterFormControlNames;
   readonly selectAllValue = DropdownSelectAllValue;
 
-  private $$isApplyBtnDisabled: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
+  private isApplyBtnDisabled$$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
   private filterFormStartValue: ImageFilterFormValue;
 
   filterForm: FormGroup;
-  $setFilterValueObservable: Observable<ImageFilterFormValue>;
-  $changeIsApplyBtnDisabledObservable: Observable<ImageFilterFormValue>;
-  $isApplyBtnDisabled: Observable<boolean> = this.$$isApplyBtnDisabled.asObservable();
+  setFilterValueObservable$: Observable<ImageFilterFormValue>;
+  changeIsApplyBtnDisabledObservable$: Observable<ImageFilterFormValue>;
+  isApplyBtnDisabled$: Observable<boolean> = this.isApplyBtnDisabled$$.asObservable();
 
   constructor(
     private fb: FormBuilder
@@ -93,7 +93,7 @@ export class PageFilterComponent implements OnInit {
 
   onClickAll(control: FormControl, allSelected: MatOption, key: DropdownFieldNames ): void {
     if (allSelected.selected) {
-      this.$filterDropdownData.pipe(
+      this.filterDropdownData$.pipe(
         tap(value => control.patchValue([...value[key], this.selectAllValue])),
         take(1)
       ).subscribe();
@@ -113,7 +113,7 @@ export class PageFilterComponent implements OnInit {
   }
 
   private setFilterValue(): void {
-    this.$setFilterValueObservable = this.$filterFormStartValue.pipe(
+    this.setFilterValueObservable$ = this.filterFormStartValue$.pipe(
       tap(value => this.updateFilterForm(value))
       );
   }
@@ -124,8 +124,8 @@ export class PageFilterComponent implements OnInit {
   }
 
   private isFilterFormChanged(): void {
-    this.$changeIsApplyBtnDisabledObservable = this.filterForm.valueChanges.pipe(
-      tap(formValue => this.$$isApplyBtnDisabled.next(isEqual(formValue, this.filterFormStartValue)))
+    this.changeIsApplyBtnDisabledObservable$ = this.filterForm.valueChanges.pipe(
+      tap(formValue => this.isApplyBtnDisabled$$.next(isEqual(formValue, this.filterFormStartValue)))
     );
   }
 
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html
index 508e3c1b2..12321aba0 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html
@@ -107,8 +107,8 @@
 
         <div *ngIf="isFilterOpened | async" class="filer__component--wrapper">
           <datalab-page-filter
-            [$filterDropdownData]="$filterDropdownData"
-            [$filterFormStartValue]="$filterFormValue"
+            [filterDropdownData$]="$filterDropdownData"
+            [filterFormStartValue$]="$filterFormValue"
             (filterFormValue)="onFilterApplyClick($event)"
             (closeFilter)="onFilterCancelClick()"
             (onValueChanges)="onControlChanges(dropdownFieldNames.imageNames, $event)"
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts
index 3ab7dd033..66f2425f2 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts
@@ -264,7 +264,7 @@ export class ImagesComponent implements OnInit, OnDestroy {
   }
 
   private initFilteredColumnState(): void {
-    this.$filteredColumnState = this.imagesService.$filteredColumnState;
+    this.$filteredColumnState = this.imagesService.filteredColumnState$;
   }
 
   private getUserImagePageInfo(): void {
@@ -276,8 +276,8 @@ export class ImagesComponent implements OnInit, OnDestroy {
   }
 
   private initImageTable(): void {
-    this.dataSource = this.imagesService.$imageList;
-    this.projectSource = this.imagesService.$projectList;
+    this.dataSource = this.imagesService.imageList$;
+    this.projectSource = this.imagesService.projectList$;
   }
 
   private getProjectList(imagePageList: ProjectModel[]): void {
@@ -296,22 +296,22 @@ export class ImagesComponent implements OnInit, OnDestroy {
   }
 
   private initFilterBtn(): void {
-    this.isFilterOpened = this.imagesService.$isFilterOpened;
+    this.isFilterOpened = this.imagesService.isFilterOpened$;
   }
 
   private getDropdownList(): void {
-    this.$filterDropdownData = this.imagesService.$filterDropdownData;
+    this.$filterDropdownData = this.imagesService.filterDropdownData$;
   }
 
   private getFilterFormValue(): void {
-    this.$filterFormValue = this.imagesService.$filterFormValue;
+    this.$filterFormValue = this.imagesService.filterFormValue$;
   }
 
   private getIsProjectListEmpty(): void {
-    this.$isProjectListEmpty = this.imagesService.$isProjectListEmpty;
+    this.$isProjectListEmpty = this.imagesService.isProjectListEmpty$;
   }
 
   private initIsImageListFiltered(): void {
-    this.$isFiltered = this.imagesService.$isImageListFiltered;
+    this.$isFiltered = this.imagesService.isImageListFiltered$;
   }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts
index 82c4a65e3..fb5e0860e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts
@@ -124,3 +124,8 @@ export enum ModalTitle {
   terminate = 'Terminate image',
   unShare = '! Warning'
 }
+
+export enum URL_Chunk {
+  sharingInfo = 'sharing_info',
+  autocomplete = 'share_autocomplete'
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
index 25eaa7f72..4f874ecc5 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
@@ -128,6 +128,6 @@ export interface FilteredColumnList {
 export type FilterFormItemType = [string, string[] | string];
 
 export interface UnShareModal {
-  userName: string;
+  userData: UserData;
   title: ModalTitle;
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts
index 469086713..f37650960 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts
@@ -25,26 +25,26 @@ import { UserData } from '../exploratory/image-action-dialog/image-action.model'
   providedIn: 'root'
 })
 export class ImagesService {
-  private $$projectList: BehaviorSubject<ProjectModel[]> = new BehaviorSubject<ProjectModel[]>([]);
-  private $$isProjectListEmpty: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
-  private $$cashedProjectList: BehaviorSubject<ProjectModel[]> = new BehaviorSubject<ProjectModel[]>([]);
-  private $$imageList: BehaviorSubject<ImageModel[]> = new BehaviorSubject<ImageModel[]>([]);
-  private $$isFilterOpened: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
+  private projectList$$: BehaviorSubject<ProjectModel[]> = new BehaviorSubject<ProjectModel[]>([]);
+  private isProjectListEmpty$$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
+  private cashedProjectList$$: BehaviorSubject<ProjectModel[]> = new BehaviorSubject<ProjectModel[]>([]);
+  private imageList$$: BehaviorSubject<ImageModel[]> = new BehaviorSubject<ImageModel[]>([]);
+  private isFilterOpened$$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
   // tslint:disable-next-line:max-line-length
-  private $$filterDropdownData: BehaviorSubject<ImageFilterFormDropdownData> = new BehaviorSubject<ImageFilterFormDropdownData>({} as ImageFilterFormDropdownData);
-  private $$filterFormValue: BehaviorSubject<ImageFilterFormValue> = new BehaviorSubject<ImageFilterFormValue>(FilterFormInitialValue);
-  private $$filteredColumnState: BehaviorSubject<FilteredColumnList> = new BehaviorSubject<FilteredColumnList>(ChangedColumnStartValue);
-  private $$isImageListFiltered: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
+  private filterDropdownData$$: BehaviorSubject<ImageFilterFormDropdownData> = new BehaviorSubject<ImageFilterFormDropdownData>({} as ImageFilterFormDropdownData);
+  private filterFormValue$$: BehaviorSubject<ImageFilterFormValue> = new BehaviorSubject<ImageFilterFormValue>(FilterFormInitialValue);
+  private filteredColumnState$$: BehaviorSubject<FilteredColumnList> = new BehaviorSubject<FilteredColumnList>(ChangedColumnStartValue);
+  private isImageListFiltered$$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
   private dropdownStartValue: ImageFilterFormDropdownData;
 
-  $projectList = this.$$projectList.asObservable();
-  $isProjectListEmpty = this.$$isProjectListEmpty.asObservable();
-  $imageList = this.$$imageList.asObservable();
-  $isFilterOpened = this.$$isFilterOpened.asObservable();
-  $filterDropdownData = this.$$filterDropdownData.asObservable();
-  $filterFormValue = this.$$filterFormValue.asObservable();
-  $filteredColumnState = this.$$filteredColumnState.asObservable();
-  $isImageListFiltered = this.$$isImageListFiltered.asObservable();
+  projectList$ = this.projectList$$.asObservable();
+  isProjectListEmpty$ = this.isProjectListEmpty$$.asObservable();
+  imageList$ = this.imageList$$.asObservable();
+  isFilterOpened$ = this.isFilterOpened$$.asObservable();
+  filterDropdownData$ = this.filterDropdownData$$.asObservable();
+  filterFormValue$ = this.filterFormValue$$.asObservable();
+  filteredColumnState$ = this.filteredColumnState$$.asObservable();
+  isImageListFiltered$ = this.isImageListFiltered$$.asObservable();
 
   constructor(
     private applicationServiceFacade: ApplicationServiceFacade,
@@ -82,19 +82,15 @@ export class ImagesService {
       );
   }
 
-  getImageShareInfo(imageInfo: ImageParams): Observable<UserData[]> {
-    return this.userImagesPageService.getImageShareInfo(imageInfo);
-  }
-
   getActiveProject(projectName: string): void {
-    const projectList = this.$$cashedProjectList.getValue();
+    const projectList = this.cashedProjectList$$.getValue();
     if (!projectName) {
       this.updateProjectList(projectList);
-      this.$$isProjectListEmpty.next(this.isProjectListEmpty(projectList));
+      this.isProjectListEmpty$$.next(this.isProjectListEmpty(projectList));
     } else {
       const currentProject = projectList.find(({project}) => project === projectName);
       this.updateProjectList([currentProject]);
-      this.$$isProjectListEmpty.next(this.isProjectListEmpty([currentProject]));
+      this.isProjectListEmpty$$.next(this.isProjectListEmpty([currentProject]));
     }
   }
 
@@ -103,37 +99,37 @@ export class ImagesService {
   }
 
   updateImageList(imageList: ImageModel[]): void {
-    this.$$imageList.next(imageList);
+    this.imageList$$.next(imageList);
   }
 
   updateProjectList(projectList: ProjectModel[]): void {
-    this.$$projectList.next(projectList);
+    this.projectList$$.next(projectList);
   }
 
   changeCheckboxValue(value: boolean): void {
-    const updatedImageList = this.$$imageList.value.map(image => {
+    const updatedImageList = this.imageList$$.value.map(image => {
       image.isSelected = value;
       return image;
     });
-    this.$$imageList.next(updatedImageList);
+    this.imageList$$.next(updatedImageList);
   }
 
   openFilter(): void {
-    this.$$isFilterOpened.next(true);
+    this.isFilterOpened$$.next(true);
   }
 
   closeFilter(): void {
-    this.$$isFilterOpened.next(false);
+    this.isFilterOpened$$.next(false);
   }
 
   filterDropdownField(field: keyof ImageFilterFormDropdownData, value: string, ): void {
     const filteredDropdownList = this.dropdownStartValue[field].filter(item => item.toLowerCase().includes(value));
-    this.addFilterDropdownData({...this.$$filterDropdownData.value, imageNames: filteredDropdownList});
+    this.addFilterDropdownData({...this.filterDropdownData$$.value, imageNames: filteredDropdownList});
   }
 
   resetFilterField(field: keyof ImageFilterFormValue, exceptionValue: string = ''): void {
     const droppedFieldValue = this.getDroppedFieldValue(field);
-    const updatedFilterFormValue = {...this.$$filterFormValue.value, [field]: droppedFieldValue};
+    const updatedFilterFormValue = {...this.filterFormValue$$.value, [field]: droppedFieldValue};
     const normalizeFormValue = this.normalizeFilterFormValue(updatedFilterFormValue, exceptionValue);
     this.setFilterFormValue(updatedFilterFormValue);
     this.updateFilterColumnState(normalizeFormValue);
@@ -142,15 +138,15 @@ export class ImagesService {
   }
 
   setFilterFormValue(value: ImageFilterFormValue): void {
-    this.$$filterFormValue.next(value);
+    this.filterFormValue$$.next(value);
   }
 
   showImage(flag: boolean, field: keyof ImageModel, comparedValue: string): void {
-    const projectList = this.$$cashedProjectList.getValue();
+    const projectList = this.cashedProjectList$$.getValue();
     if (flag) {
       this.updateProjectList(projectList);
     } else {
-      const filteredImageList = this.filterByCondition(this.$$projectList.getValue(), field, comparedValue);
+      const filteredImageList = this.filterByCondition(this.projectList$$.getValue(), field, comparedValue);
       this.updateProjectList(filteredImageList);
     }
   }
@@ -167,28 +163,32 @@ export class ImagesService {
     const columnStateList = (<any>Object).entries(filterFormValue)
       .reduce((acc, fieldItem) => this.checkColumnState(acc, fieldItem), <FilteredColumnList>{});
 
-    this.$$filteredColumnState.next(columnStateList);
+    this.filteredColumnState$$.next(columnStateList);
   }
 
   getDroppedFieldValue(field: keyof ImageFilterFormValue): string | [] {
-    return typeof this.$$filterFormValue.value[field] === 'string'
+    return typeof this.filterFormValue$$.value[field] === 'string'
       ? ''
       : [];
   }
 
   checkIsPageFiltered(): void {
-    const isImageListFiltered = (<any>Object).values(this.$$filteredColumnState.value).some(item => Boolean(item));
-    this.$$isImageListFiltered.next(isImageListFiltered);
+    const isImageListFiltered = (<any>Object).values(this.filteredColumnState$$.value).some(item => Boolean(item));
+    this.isImageListFiltered$$.next(isImageListFiltered);
   }
 
-  createImageRequestInfo(image: ImageModel, userDataList: UserData[]): ImageParams {
+  createImageRequestInfo(image: ImageModel, userDataList?: UserData[]): ImageParams {
     const { name, project, endpoint } = image;
-    return {
+    const imageParams = {
       imageName: name,
       projectName: project,
       endpoint: endpoint,
-      sharedWith: userDataList
     };
+
+    if (userDataList) {
+      imageParams['sharedWith'] = userDataList;
+    }
+    return imageParams;
   }
 
   createActionDialogConfig(image: ImageModel, actionType: ImageActionType): ImageActionModalData {
@@ -259,7 +259,7 @@ export class ImagesService {
   }
 
   private updateCashedProjectList(projectList: ProjectModel[]): void {
-    this.$$cashedProjectList.next(projectList);
+    this.cashedProjectList$$.next(projectList);
   }
 
   private getDropdownDataList(dropdownList: ImageFilterFormDropdownData): void {
@@ -268,7 +268,7 @@ export class ImagesService {
   }
 
   private addFilterDropdownData(data: ImageFilterFormDropdownData): void {
-    this.$$filterDropdownData.next(data);
+    this.filterDropdownData$$.next(data);
   }
 
   private getImagePageData(imagePageData: ProjectModel[]): void {
@@ -276,7 +276,7 @@ export class ImagesService {
     this.updateProjectList(imagePageData);
     this.updateImageList(imageList);
     this.updateCashedProjectList(imagePageData);
-    this.$$isProjectListEmpty.next(this.isProjectListEmpty(imagePageData));
+    this.isProjectListEmpty$$.next(this.isProjectListEmpty(imagePageData));
   }
 
   private isProjectListEmpty(imagePageData: ProjectModel[]): boolean {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org