rxjs#merge TypeScript Examples

The following examples show how to use rxjs#merge. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: slippage-settings.component.ts    From gnosis.1inch.exchange with MIT License 6 votes vote down vote up
ngOnInit(): void {

    console.log('set=' + this.slippage);
    this.selectSlippage(this.slippage);

    const slippageInput$ = this.slippageInput.valueChanges.pipe(
      filter((x) => !this.slippageInput.errors),
      map(x => {
        // In case user input empty string, take latest selected or maximal
        return x === ''
          ? this.slippageSelect.value || '3'
          : x;
      })
    );

    const slippageSelect$ = this.slippageSelect.valueChanges.pipe(
      filter(x => x !== 'custom')
    );

    this.subscription = merge(slippageSelect$, slippageInput$).pipe(
      distinctUntilChanged(),
      tap((latest: string) => {
        this.slippage = latest;
        this.slippageChange.next(latest);
      })
    ).subscribe();
  }
Example #2
Source File: epics.ts    From anthem with Apache License 2.0 6 votes vote down vote up
highlightDataIntegrityHelpLabel: EpicSignature = action$ => {
  // Scroll to the disclaimer when it is made visible.
  const scrollToDisclaimerEpic = action$.pipe(
    filter(isActionOf(Actions.displayDataIntegrityHelpLabel)),
    delay(500),
    tap(() => {
      const disclaimer = document.getElementById(
        "help-page-data-integrity-disclaimer",
      );

      if (disclaimer) {
        disclaimer.scrollIntoView({ block: "start", behavior: "smooth" });
      }
    }),
    delay(500),
    map(() => Actions.toggleDataIntegrityHelpLabel(true)),
  );

  // Dismiss the tooltip after a delay, once it has been scrolled to.
  const dismissTooltipAfterDelay = action$.pipe(
    filter(isActionOf(Actions.toggleDataIntegrityHelpLabel)),
    pluck("payload"),
    filter(Boolean),
    delay(2500),
    map(() => Actions.toggleDataIntegrityHelpLabel(false)),
  );

  return merge(scrollToDisclaimerEpic, dismissTooltipAfterDelay);
}
Example #3
Source File: artichoke.ts    From closer-sdk.js with MIT License 6 votes vote down vote up
constructor(
    private artichokeApi: ArtichokeApi,
    private callFactory: CallFactory,
    private roomFactory: RoomFactory,
    private loggerService: LoggerService,
    private heartbeatTimeoutMultiplier: number,
    private fallbackReconnectDelayMs: number,
  ) {
    // Do not move this as a property accessor, it must be only one object to make rx `share` operator work.
    this.connection = merge(
      this.artichokeApi.connection$.pipe(
        filter(serverEvents.OutputHeartbeat.is),
        tap((ev: serverEvents.OutputHeartbeat) => this.handleHeartbeatEvent(ev)),
        ignoreElements(),
      ),
      this.artichokeApi.connection$.pipe(
        filter(serverEvents.Hello.is),
        tap(ev => this.handleHelloEvent(ev)),
      ),
    ).pipe(
      finalize(() => this.handleDisconnect()),
      // On WebSocket error
      retryWhen(errors => this.delayReconnect(errors)),
      takeUntil(this.serverUnreachableEvent),
      // On WebSocket gracefull close
      repeatWhen(attempts => this.delayReconnect(attempts)),
      // IMPORTANT
      // Share the observable, so the internal logic would behave like one consistent stream
      // Without this operator, if client subscribes two times, we would have
      // two heartbeats answers and reconnections logic
      share(),
    );
  }
Example #4
Source File: wallets-assets.service.ts    From xBull-Wallet with GNU Affero General Public License v3.0 6 votes vote down vote up
// DEPRECATED
  requestAssetDataSubscription: Subscription = merge(this.requestAssetData$, this.shouldRequestAssetInformation$)
    .pipe(mergeMap(params => {
      this.walletsAssetsStore.upsert(params._id, { lastTimeUpdated: new Date() });
      return this.getAssetExtraRecord(params)
        .pipe(switchMap(_ => {
          return this.getAssetFullRecord(params)
            .pipe(catchError(error => {
              console.error(error);
              return of(error);
            }));
        }))
        .pipe(catchError(error => {
          console.error(error);
          return of(error);
        }));
    }, 1))
    .subscribe();
Example #5
Source File: index.ts    From dbm with Apache License 2.0 6 votes vote down vote up
manifest$ = merge(
  ...Object.entries({
    "**/*.scss": stylesheets$,
    "**/*.ts*":  javascripts$
  })
    .map(([pattern, observable$]) => (
      defer(() => process.argv.includes("--watch")
        ? watch(pattern, { cwd: "src" })
        : EMPTY
      )
        .pipe(
          startWith("*"),
          switchMapTo(observable$.pipe(toArray()))
        )
    ))
)
  .pipe(
    scan((prev, mapping) => (
      mapping.reduce((next, [key, value]) => (
        next.set(key, value.replace(`${base}/`, ""))
      ), prev)
    ), new Map<string, string>()),
  )
Example #6
Source File: posts.service.ts    From FireAdmin with MIT License 6 votes vote down vote up
add(data: Post, translationId?: string) {
    const post: Post = {
      title: data.title,
      lang: data.lang,
      slug: data.slug,
      date: data.date,
      image: null,
      content: data.content,
      status: data.status,
      categories: data.categories,
      createdAt: now(), // timestamp
      updatedAt: null,
      createdBy: this.db.currentUser.id,
      updatedBy: null
    };
    if (translationId && data.image && !isFile(data.image)) {
      post.image = data.image;
    }
    return new Promise((resolve, reject) => {
      this.db.addDocument('posts', post).then((doc: any) => {
        this.uploadImage(doc.id, data.image as File).then(() => {
          this.addTranslation(data.lang, doc.id, translationId).then((translation: any) => {
            doc.set({ translationId: translationId || translation.id}, { merge: true }).then(() => {
              resolve();
            }).catch((error: Error) => {
              reject(error);
            });
          }).catch((error: Error) => {
            reject(error);
          });
        }).catch((error: Error) => {
          reject(error);
        });
      }).catch((error: Error) => {
        reject(error);
      });
    });
  }
Example #7
Source File: tab-nav-bar.component.ts    From canopy with Apache License 2.0 6 votes vote down vote up
setTabs() {
    this.tabs = this.tabQueryList.toArray();

    // Set the tab indexes and initial selected tab index
    this.tabs.forEach((tab, index: number) => {
      tab.index = index;
    });

    // Update tab link active states when active tab changes
    const tabOutputs = this.tabs.map(tab => tab.selectedTabIndexChange);

    merge(...tabOutputs)
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe((nextSelectedIndex: number) =>
        this.updateSelectedTab(nextSelectedIndex),
      );
  }
Example #8
Source File: app.component.ts    From Angular-Cookbook with MIT License 6 votes vote down vote up
startStream() {
    const streamSource = interval(1500).pipe(
      map((input) => {
        const index = input % this.combinedStreamData.length;
        return this.combinedStreamData[index];
      })
    );
    const [moviesStream, cartoonsStream] = partition(
      streamSource,
      (item) => item.type === 'movie'
    );
    this.subscription = merge(
      moviesStream.pipe(
        tap((movie) => {
          this.movies.push(movie.title);
        })
      ),
      cartoonsStream.pipe(
        tap((cartoon) => {
          this.cartoons.push(cartoon.title);
        })
      )
    ).subscribe((output) => {
      console.log(output);
    });
  }
Example #9
Source File: grocy-api.component.ts    From pantry_party with Apache License 2.0 6 votes vote down vote up
ngOnInit(): void {
    merge(
      this.form.valueChanges,
      this.forceCheck
    ).pipe(
      tap(() => this.updateValidationStatus()),
      takeUntil(this.ngUnsubscribe),
      debounceTime(250),
      filter(() =>  this.form.valid),
      tap(() => this.working = true),
      switchMap(_ => this.grocyService.getSystemInfo(
        this.formControl("url").value,
        this.formControl("apiKey").value
      ).pipe(
      map(r => ({result: "success" as "success", resp: r})),
      catchError((e: HttpErrorResponse) => of({result: "error" as "error", err: e}))
      )),
      tap(() => this.working = false)
    ).subscribe(r =>  this.renderReturn(r));

    if (this.form.valid) {
      this.forceCheck.next(true);
    }
  }
Example #10
Source File: keyboard-event-dispatcher.ts    From scion-microfrontend-platform with Eclipse Public License 2.0 6 votes vote down vote up
/**
   * Subscribes to keyboard events on document-level and emits them on the {@link _keyboardEvents$} Observable.
   *
   * IMPORTANT NOTE FOR ANGULAR APPLICATIONS:
   * Always subscribe to top-level DOM events during event dispatcher construction. Event dispatchers are eagerly constructed on platform startup.
   * Typically, Angular applications connect to the platform outside of the Angular zone to avoid excessive change detection cycles for irrelevant DOM events.
   */
  private installKeyboardEventListener(): void {
    merge(fromEvent<KeyboardEvent>(document, 'keydown'), fromEvent<KeyboardEvent>(document, 'keyup'))
      .pipe(
        filter(event => event.bubbles && !!event.key),
        takeUntil(this._destroy$),
      )
      .subscribe(event => this._keyboardEvents$.next(event));
  }
Example #11
Source File: data-table-datasource.ts    From worktez with MIT License 6 votes vote down vote up
/**
   * Connect this data source to the table. The table will only update when
   * the returned stream emits new items.
   * @returns A stream of the items to be rendered.
   */
  connect(): Observable<Tasks[]> {
    if (this.paginator && this.sort) {
      // Combine everything that affects the rendered data into one update
      // stream for the data-table to consume.
      return merge(observableOf(this.data), this.paginator.page, this.sort.sortChange)
        .pipe(map(() => {
          if(this.data != undefined)
            return this.getPagedData(this.getSortedData([...this.data ]));
        }));
    } else {
      throw Error('Please set the paginator and sort on the data source before connecting.');
    }
  }
Example #12
Source File: overflow-carousel.component.ts    From nghacks with MIT License 6 votes vote down vote up
public ngAfterViewInit(): void {

    const resizedEvents: EventEmitter<ResizedEvent>[] = this._resizedDirectives.map(directive => directive.resized);

    merge(...resizedEvents)
      .pipe(
        takeUntil(this._unsubscribeAll),
        debounceTime(100)
      )
      .subscribe((ev: ResizedEvent) => {
        this._containerWidth = this._carouselContainerElementRef.nativeElement.clientWidth;
        this._contentWidth = this._carouselElementRef.nativeElement.clientWidth;
        this.carouselPosition = this._carouselPosition;
      });
  }
Example #13
Source File: anchor.directive.ts    From alauda-ui with MIT License 6 votes vote down vote up
adaptAnchorPosition(containerEl: HTMLElement, anchorEl: HTMLElement) {
    const pageContentEl = containerEl.closest('.aui-page__content');
    const anchorContentEl = anchorEl.querySelector('.aui-anchor');

    merge(observeResizeOn(containerEl), fromEvent(window, 'scroll'))
      .pipe(startWith(null as void), takeUntil(this.destroy$$))
      .subscribe(() => {
        const containerRect = containerEl.getBoundingClientRect();
        Object.assign(anchorEl.style, {
          display: !containerRect.width || !containerRect.height ? 'none' : '',
          left:
            containerRect.right -
            anchorContentEl.getBoundingClientRect().width +
            'px',
          top:
            Math.max(
              containerRect.top,
              (this.minTop ??
                (pageContentEl &&
                  +getComputedStyle(pageContentEl).paddingTop.slice(0, -2))) ||
                0,
            ) + 'px',
        });
      });

    if (this.adaptPosition) {
      observeResizeOn(anchorContentEl)
        .pipe(takeUntil(this.destroy$$))
        .subscribe(el => {
          const width = el.getBoundingClientRect().width;
          const padding = width + this.padding;
          containerEl.style.paddingRight = padding + 'px';
        });
    }
  }
Example #14
Source File: TodoListEventsController.ts    From remix-hexagonal-architecture with MIT License 6 votes vote down vote up
@Sse("/:todoListId")
  async getEvents(@Param("todoListId") todoListId: string) {
    const currentUser = await this.authenticator.currentUser();
    const heartbeat$ = interval(30_000).pipe(
      map(() => ({ type: "heartbeat", data: "_" }))
    );

    const updates$ = this.todoListEvents.events.pipe(
      filter(
        (event) =>
          event.todoListId === todoListId &&
          event.sessionId !== currentUser.sessionId
      ),
      map((event) => ({ type: "update", data: event.type } as MessageEvent))
    );

    return merge(heartbeat$, updates$);
  }
Example #15
Source File: auth.state.ts    From auth0-angular with MIT License 6 votes vote down vote up
/**
   * Trigger used to pull User information from the Auth0Client.
   * Triggers when an event occurs that needs to retrigger the User Profile information.
   * Events: Login, Access Token change and Logout
   */
  private readonly isAuthenticatedTrigger$ = this.isLoading$.pipe(
    filter((loading) => !loading),
    distinctUntilChanged(),
    switchMap(() =>
      // To track the value of isAuthenticated over time, we need to merge:
      //  - the current value
      //  - the value whenever the access token changes. (this should always be true of there is an access token
      //    but it is safer to pass this through this.auth0Client.isAuthenticated() nevertheless)
      //  - the value whenever refreshState$ emits
      merge(
        defer(() => this.auth0Client.isAuthenticated()),
        this.accessTokenTrigger$.pipe(
          mergeMap(() => this.auth0Client.isAuthenticated())
        ),
        this.refresh$.pipe(mergeMap(() => this.auth0Client.isAuthenticated()))
      )
    )
  );
Example #16
Source File: page-tabs.component.ts    From t3mpl-editor with MIT License 6 votes vote down vote up
public ngOnInit() {
		merge(
			this.stateService.onStateChanged,
			this.stateService.onPageChanged,
			this.stateService.onConfigurationChanged,
			this.stateService.onPreviewModeChanged
		)
		.pipe(debounceTime(50))
		.subscribe(() => this.reload());
	}
Example #17
Source File: auth.ts    From webapp with MIT License 6 votes vote down vote up
merge(onLogin$, onLogout$).subscribe(address => {
  const stringForm = address == false ? "" : address;
  try {
    vxm.ethBancor.onAuthChange(stringForm);
    vxm.ethWallet.setLoggedInAccount(stringForm);
  } catch (e) {
    console.error(e);
  }
});
Example #18
Source File: BleTransport.ts    From Elastos.Essentials.App with MIT License 6 votes vote down vote up
// TODO we probably will do this at end of open
  async inferMTU() {
    let { mtu } = this.device;
    await this.exchangeAtomicImpl(async () => {
      Logger.log(TAG, "inferMTU exchangeAtomicImpl");
      try {
        mtu =
          (await merge(
            this.notifyObservable.pipe(
              first((buffer) => buffer.readUInt8(0) === 0x08),
              map((buffer) => buffer.readUInt8(5))
            ),
            defer(() => from(this.write(Buffer.from([0x08, 0, 0, 0, 0])))).pipe(
              ignoreElements()
            )
          ).toPromise()) + 3;
      } catch (e: any) {
        Logger.log(TAG, "inferMTU got error:", String(e));
        await bleManager.disconnect(this.id).catch(() => {}); // but we ignore if disconnect worked.

        throw remapError(e);
      }
    });

    if (mtu > 23) {
      const mtuSize = mtu - 3;
      this.mtuSize = mtuSize;
    }

    return this.mtuSize;
  }
Example #19
Source File: pan.ts    From ble with Apache License 2.0 6 votes vote down vote up
resize: Epic = (action$, { store, app }) => {
	return merge(
		action$.pipe(
			ofType('hydrate'),
		),
		fromEvent(app.renderer, 'resize'),
	).pipe(
		tap(() => {
			const box = app.view.getBoundingClientRect();
			store.editor.setScreenSize(box);
		}),
		ignoreElements(),
	);
}
Example #20
Source File: color-canvas.component.ts    From angular-material-components with MIT License 6 votes vote down vote up
ngOnInit() {

    const rgbaCtrl$ = merge(this.rCtrl.valueChanges, this.gCtrl.valueChanges,
      this.bCtrl.valueChanges, this.aCtrl.valueChanges);
    rgbaCtrl$.pipe(takeUntil(this._destroyed), debounceTime(400), distinctUntilChanged())
      .subscribe(_ => {
        const color = new Color(Number(this.rCtrl.value),
          Number(this.gCtrl.value), Number(this.bCtrl.value), Number(this.aCtrl.value));
        this.emitChange(color);
      });

    const hexCtrl$ = this.hexCtrl.valueChanges;
    hexCtrl$.pipe(takeUntil(this._destroyed), debounceTime(400), distinctUntilChanged())
      .subscribe(hex => {
        const obj = stringInputToObject(hex);
        if (obj != null) {
          const color = new Color(obj.r, obj.g, obj.b, obj.a);
          this.emitChange(color);
        }
      })
  }
Example #21
Source File: datetime-picker.component.ts    From ngx-mat-datetime-picker with MIT License 6 votes vote down vote up
/** Create the popup. */
  private _createPopup(): void {
    const overlayConfig = new OverlayConfig({
      positionStrategy: this._createPopupPositionStrategy(),
      hasBackdrop: this._hasBackdrop,
      backdropClass: 'mat-overlay-transparent-backdrop',
      direction: this._dir,
      scrollStrategy: this._scrollStrategy(),
      panelClass: 'mat-datepicker-popup',
    });

    this._popupRef = this._overlay.create(overlayConfig);
    this._popupRef.overlayElement.setAttribute('role', 'dialog');

    merge(
      this._popupRef.backdropClick(),
      this._popupRef.detachments(),
      this._popupRef.keydownEvents().pipe(filter(event => {
        // Closing on alt + up is only valid when there's an input associated with the datepicker.
        return event.keyCode === ESCAPE ||
          (this._datepickerInput && event.altKey && event.keyCode === UP_ARROW);
      }))
    ).subscribe(event => {
      if (event) {
        event.preventDefault();
      }

      (this._hasBackdrop && event) ? this.cancel() : this.close();

    });
  }
Example #22
Source File: click-out-side.component.ts    From ng-ant-admin with MIT License 6 votes vote down vote up
ngAfterViewInit(): void {
    this.targetHtmlClick$ = fromEvent(this.targetHtml.nativeElement, 'click').pipe(tap(e => {
      fnStopMouseEvent(<MouseEvent>e);
      this.text = '刀斩肉身';
    }));
    this.winClick$ = fromEvent(this.doc, 'click').pipe(tap(() => {
      this.text = '心斩灵魂'
    }));
    merge(this.targetHtmlClick$, this.winClick$).subscribe(res => {
        this.cdr.markForCheck();
    })
  }
Example #23
Source File: BindQueryParamsManager.ts    From bind-query-params with MIT License 6 votes vote down vote up
onInit() {
    this.handleInitialURLSync();

    this.updateControl(
      this.defs,
      { emitEvent: true },
      (def) => !!(def.syncInitialQueryParamValue ?? this.createOptions?.syncInitialQueryParamValue)
    );

    const controls = this.defs.map((def) => {
      return this.group.get(def.path)!.valueChanges.pipe(
        map((value) => ({
          def,
          value,
        }))
      );
    });

    // Could be a several changes in the same tick,
    // for example when we use reset() or patchValue.
    // We need to aggregate the changes and apply them once
    // because the router navigates in micro task
    let buffer: ResolveParamsOption[] = [];

    merge(...controls)
      .pipe(
        map((result) => buffer.push(result)),
        auditTime(0),
        takeUntil(this.$destroy)
      )
      .subscribe(() => {
        this.updateQueryParams(resolveParams(buffer));
        buffer = [];
      });
  }
Example #24
Source File: dia-backend-asset-repository.service.ts    From capture-lite with GNU General Public License v3.0 6 votes vote down vote up
getPostCaptureById$(id: string) {
    return merge(
      this.postCapturesCache$.pipe(
        map(postCaptures => postCaptures.results.find(p => p.id === id)),
        isNonNullable()
      ),
      this.fetchById$(id)
    );
  }
Example #25
Source File: rtc.component.ts    From onchat-web with Apache License 2.0 6 votes vote down vote up
ngOnInit() {
    super.ngOnInit();

    merge(
      this.socket.on(SocketEvent.RtcHangUp),
      this.socket.on(SocketEvent.RtcBusy).pipe(
        tap(({ data: { senderId } }) => senderId === this.target.id && this.busy())
      ),
    ).pipe(
      takeUntil(this.destroyer),
      filter(({ data: { senderId } }) => senderId === this.target.id)
    ).subscribe(() => this.dismiss());

    // 如果自己是申请人,自己先准备好 RTC
    this.isRequester && this.prepare().subscribe();

    this.feedbackService.audio(AudioName.Ring).play();
    this.globalData.rtcing = true;

    // 如果三分钟后还没接通,客户端主动挂断
    this.timer = this.window.setTimeout(() => {
      if (this.waiting) {
        this.isRequester && this.busy();
        this.hangUp();
      }
    }, 60000 * 3);
  }
Example #26
Source File: mockApiRx.ts    From guardian with Apache License 2.0 6 votes vote down vote up
MockApiRx = of({
  ...acalaRpc,
  consts: {
    prices: { stableCurrencyFixedPrice: 1e18 },
    currencies: { getNativeCurrencyId: ACA },
    cdpEngine: {
      getStableCurrencyId: AUSD,
      collateralCurrencyIds: COLLATERAL_CURRENCY_IDS
    }
  },
  query: {
    auctionManager: {
      collateralAuctions: {
        entries: () => of([[collateralAuctionsKey(0), COLLATERAL_AUCTION]])
      }
    },
    auction: {
      auctionsIndex: () => of(registry.createType('AuctionId', 1)),
      auctions: () => of(AUCTION)
    },
    dex: {
      liquidityPool: () => of(LP)
    },
    loans: {
      positions: () => of(POSITION)
    },
    cdpEngine: {
      debitExchangeRate: () => of(EXCHANGE_RATE)
    },
    acalaOracle: {
      values: () => {
        return merge([of(PRICE), timer(1000).pipe(mapTo(PRICE_UPDATED))]).pipe(concatAll(), share());
      }
    }
  },
  createType: (type: string, value: any) => registry.createType(type, value)
})
Example #27
Source File: settings.effects.ts    From enterprise-ng-2020-workshop with MIT License 6 votes vote down vote up
updateRouteAnimationType = createEffect(
    () =>
      merge(
        INIT,
        this.actions$.pipe(
          ofType(
            actionSettingsChangeAnimationsElements,
            actionSettingsChangeAnimationsPage
          )
        )
      ).pipe(
        withLatestFrom(
          combineLatest([
            this.store.pipe(select(selectPageAnimations)),
            this.store.pipe(select(selectElementsAnimations))
          ])
        ),
        tap(([action, [pageAnimations, elementsAnimations]]) =>
          this.animationsService.updateRouteAnimationType(
            pageAnimations,
            elementsAnimations
          )
        )
      ),
    { dispatch: false }
  );
Example #28
Source File: trade-logs.effects.ts    From zorro-fire-log with MIT License 6 votes vote down vote up
onTradeLogUpdate$ = createEffect(() =>
    iif(
      () => this.useMockData,
      of(
        addTradeLogs({
          tradeLogs: mockTradeLogs,
        })
      ).pipe(
        delay(500),
        tap(() => console.warn('Using MOCKED data'))
      ),
      this.store.select(selectLatestEntryTime).pipe(
        map(
          (latest) =>
            latest && new Timestamp(latest.seconds, latest.nanoseconds).toDate()
        ),
        take(1),
        switchMap((latest) =>
          merge(
            ...environment.tradeLogs.map((tl) =>
              this.firestore
                .collection<TradeLogEntry>(tl, (ref) =>
                  (latest ? ref.where('close', '>', latest) : ref).orderBy(
                    'close'
                  )
                )
                .valueChanges({ idField: 'id' })
                .pipe(
                  map((entries) => entries.map((e) => ({ ...e, alias: tl })))
                )
            )
          ).pipe(
            tap(() => console.warn('Using LIVE data')),
            map((tradeLogs) => addTradeLogs({ tradeLogs }))
          )
        )
      )
    )
  );
Example #29
Source File: tracker.component.ts    From Covinex with MIT License 6 votes vote down vote up
ngOnInit(): void {
    merge(
      this.service.getDateWiseData().pipe(
        map(result => {
          this.dateWiseData = result;
        })
      ),
      this.service.getGlobalData().pipe(map(result => {
        this.data = result;
        this.data.forEach(cs => {
          this.countries.push(cs.country)
        })
      }))
    ).subscribe(
      {
        complete: () => {
          this.updateValues('India')
          this.loading = false;
        }
      }
    )



  }