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:34 UTC

[incubator-datalab] branch feat/DATALAB-3008/support-autocomplete-for-sharing-dialog created (now 14fb48ab2)

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

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


      at 14fb48ab2 [DATALAB-3008] implemented logyc by request for input dropdown

This branch includes the following new commits:

     new 14fb48ab2 [DATALAB-3008] implemented logyc by request for input dropdown

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



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


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

Posted by hs...@apache.org.
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