rxjs#distinctUntilChanged TypeScript Examples

The following examples show how to use rxjs#distinctUntilChanged. 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: autocomplete.component.ts    From alauda-ui with MIT License 6 votes vote down vote up
ngAfterContentInit() {
    this.hasVisibleSuggestion$ = this.suggestions.changes.pipe(
      startWith(this.suggestions),
      switchMap((suggestions: QueryList<SuggestionComponent>) =>
        suggestions.length > 0
          ? combineLatest(suggestions.map(suggestion => suggestion.visible$))
          : of([] as boolean[]),
      ),
      map(visible => visible.some(Boolean)),
      withLatestFrom(this.directive$$),
      map(([hasVisibleSuggestion, directive]) => {
        if (hasVisibleSuggestion && directive.defaultFirstSuggestion) {
          directive.autoFocusFirstSuggestion();
        }
        return hasVisibleSuggestion;
      }),
      distinctUntilChanged(),
      debounceTime(0),
      tap(() => this.cdr.markForCheck()),
      publishRef(),
    );

    this.hasContent$ = combineLatest([
      this.hasVisibleSuggestion$,
      this.placeholder.changes.pipe(
        startWith(this.placeholder),
        map(
          (list: QueryList<AutocompletePlaceholderComponent>) => !!list.length,
        ),
      ),
    ]).pipe(
      map(
        ([hasVisibleSuggestion, hasPlaceholder]) =>
          hasVisibleSuggestion || hasPlaceholder,
      ),
    );
  }
Example #2
Source File: back-top.component.ts    From alauda-ui with MIT License 6 votes vote down vote up
isDisplayed$ = combineLatest([
    this.target$$.asObservable().pipe(
      map(target => this.getTarget(target)),
      switchMap(target =>
        fromEvent(target, 'scroll').pipe(
          // use default scheduler
          throttleTime(50, undefined, { leading: true, trailing: true }),
          map(() => this.getTargetScrollTop(target)),
        ),
      ),
    ),
    this.visibilityHeight$$,
  ]).pipe(
    map(([scrollTop, visibilityHeight]) => scrollTop >= visibilityHeight),
    distinctUntilChanged(),
  );
Example #3
Source File: tab-context.service.ts    From alauda-ui with MIT License 6 votes vote down vote up
constructor(
    @Optional()
    @SkipSelf()
    readonly _parent: TabContextService,
  ) {
    this.active$ = (
      _parent
        ? combineLatest([_parent.active$, this.active$$]).pipe(
            map(([a, b]) => a && b),
          )
        : this.active$$
    ).pipe(distinctUntilChanged());
  }
Example #4
Source File: form.directive.ts    From alauda-ui with MIT License 5 votes vote down vote up
labelWidth$: Observable<string> = this.labelWidth$$
    .asObservable()
    .pipe(distinctUntilChanged());
Example #5
Source File: form.directive.ts    From alauda-ui with MIT License 5 votes vote down vote up
labelPosition$: Observable<LabelPosition> = this.labelPosition$$
    .asObservable()
    .pipe(distinctUntilChanged());
Example #6
Source File: form.directive.ts    From alauda-ui with MIT License 5 votes vote down vote up
emptyAddon$: Observable<boolean> = this.emptyAddon$$
    .asObservable()
    .pipe(distinctUntilChanged());
Example #7
Source File: radio-group.component.ts    From alauda-ui with MIT License 5 votes vote down vote up
size$: Observable<RadioSize> = this.size$$
    .asObservable()
    .pipe(distinctUntilChanged());
Example #8
Source File: radio-group.component.ts    From alauda-ui with MIT License 5 votes vote down vote up
isPlain$: Observable<boolean> = this.isPlain$$
    .asObservable()
    .pipe(distinctUntilChanged());
Example #9
Source File: radio-group.component.ts    From alauda-ui with MIT License 5 votes vote down vote up
name$: Observable<string> = this.name$$
    .asObservable()
    .pipe(distinctUntilChanged());
Example #10
Source File: fixed-size-table-virtual-scroll-strategy.ts    From alauda-ui with MIT License 5 votes vote down vote up
scrolledIndexChange = this._indexChange.pipe(distinctUntilChanged());
Example #11
Source File: option.component.ts    From alauda-ui with MIT License 5 votes vote down vote up
constructor(
    private readonly cdr: ChangeDetectorRef,
    @Inject(forwardRef(() => BaseSelect))
    select: any, // FIXME: workaround temporarily
  ) {
    this.isMulti = select.isMulti;
    this.select = select;
    this.selected$ = combineLatest([this.select.values$, this.value$$]).pipe(
      map(([selectValue, selfValue]) =>
        selectValue
          ?.map(this.select.trackFn)
          .includes(this.select.trackFn(selfValue)),
      ),
      distinctUntilChanged(),
      tap(selected => {
        this.selected = selected;
      }),
      publishRef(),
    );
    this.size$ = this.select.size$.pipe(
      tap(size => {
        this.size = size;
      }),
    );
    this.visible$ = combineLatest([
      this.select.filterString$,
      combineLatest([this.label$, this.value$, this.labelContext$]).pipe(
        map(([label, value, labelContext]) => ({ label, value, labelContext })),
      ),
    ]).pipe(
      map(([filterString, option]) =>
        this.select.filterFn(filterString, option),
      ),
      distinctUntilChanged(),
      tap(visible => {
        this.visible = visible;
      }),
      publishRef(),
    );
  }
Example #12
Source File: select.component.ts    From alauda-ui with MIT License 5 votes vote down vote up
override ngAfterContentInit() {
    super.ngAfterContentInit();

    this.selectedOption$ = combineLatest([
      (
        this.contentOptions.changes as Observable<QueryList<OptionComponent<T>>>
      ).pipe(
        startWith(this.contentOptions),
        switchMap(options =>
          combineLatest(options.map(option => option.selected$)).pipe(
            startWith(null as void),
            map(() => options.find(option => option.selected)),
            distinctUntilChanged(),
            switchMap(option =>
              option
                ? combineLatest([
                    option.value$,
                    option.label$,
                    option.labelContext$,
                  ]).pipe(
                    map(([value, label, labelContext]) => ({
                      value,
                      label,
                      labelContext,
                    })),
                  )
                : of(null as void),
            ),
          ),
        ),
      ),
      this.model$,
    ]).pipe(
      map(([option, value]) =>
        option
          ? {
              label:
                option.label ||
                this.labelFn?.(option.value) ||
                coerceString(this.trackFn(option.value)),
              // https://github.com/angular/angular/issues/24515
              labelContext: {
                ...(option.labelContext as Record<string, unknown>),
              },
            }
          : {
              label: this.labelFn?.(value) || coerceString(this.trackFn(value)),
            },
      ),
      publishRef(),
    );

    this.hasSelected$ = this.selectedOption$.pipe(
      map(({ label }) => !!label),
      publishRef(),
    );
  }
Example #13
Source File: theme.service.ts    From alauda-ui with MIT License 5 votes vote down vote up
readonly currentTheme$ = this.currentAppTheme$$
    .asObservable()
    .pipe(distinctUntilChanged());
Example #14
Source File: index.ts    From platyplus with MIT License 5 votes vote down vote up
createRxHasura = async (
  name: string,
  url: string,
  password?: string
): Promise<Database> => {
  addRxPlugin(RxDBReplicationGraphQLPlugin)
  addRxPlugin(RxDBAjvValidatePlugin)
  addRxPlugin(RxHasuraPlugin)
  addPouchPlugin(require('rxdb/plugins/migration'))
  addRxPlugin(require('rxdb/plugins/leader-election'))
  addRxPlugin(require('rxdb/plugins/update'))
  addPouchPlugin(require('rxdb/plugins/query-builder'))

  if (process.env.NODE_ENV === 'development')
    addPouchPlugin(require('rxdb/plugins/dev-mode'))

  // * IMPORTANT: Do not use addRxPlugin to add pouchdb adapter, instead use addPouchPlugin
  if (persist) addPouchPlugin(require('pouchdb-adapter-idb'))
  else addPouchPlugin(require('pouchdb-adapter-memory'))

  const settings: RxDatabaseCreator = {
    name,
    password,
    multiInstance: false, // ! Causes errors when set to true. See notice in https://rxdb.info/leader-election.html
    eventReduce: true, // <- eventReduce (optional, default: true))
    options: {
      url
    },
    storage: getRxStoragePouch(persist ? 'idb' : 'memory')
  }

  const db = (await createRxDatabase<DatabaseCollections>(
    settings
  )) as unknown as Database

  debug('db', `created: ${settings.name}`)
  if (process.env.NODE_ENV === 'development' || process.env.DEBUG)
    window['db'] = db // write to window for debugging

  // * When being connected, browse the roles and create table info accordingly
  db.isAuthenticated$
    .pipe(
      distinctUntilChanged(),
      filter((status) => status),
      switchMap(() => db.isConfigReady$)
    )
    .subscribe(async (ready) => {
      debug('db', 'first time authenticated. Is ready?', ready)
      if (ready) addTableInfoCollection(db)
      else await initConfigCollections(db)
    })

  db.isReady$
    .pipe(
      filter((ready) => ready && !!db.collections[TABLE_INFO_TABLE]),
      switchMap(() => db.collections[TABLE_INFO_TABLE].find().$)
    )
    .subscribe((tables) => createContentsCollections(db, tables))

  // * runs when db becomes leader
  db.waitForLeadership().then(() => {
    debug('db', 'took the leadership')
  })

  return db
}
Example #15
Source File: state.ts    From platyplus with MIT License 5 votes vote down vote up
addStateToDatabasePrototype = (proto: Database) => {
  proto.isConfigReady$ = new Observable((subscriber) => {
    readyTables.subscribe((tables) =>
      subscriber.next(CONFIG_TABLES.every((name) => tables.includes(name)))
    )
  }).pipe(distinctUntilChanged<boolean>())
  proto.isReady$ = new Observable((subscriber) => {
    readyTables.subscribe((tables) =>
      subscriber.next(PLATYPLUS_TABLES.every((name) => tables.includes(name)))
    )
  }).pipe(distinctUntilChanged<boolean>())
  proto.jwt$ = new BehaviorSubject<string | null>(null)
  proto.isAdmin$ = new BehaviorSubject<boolean>(false)
  proto.isAuthenticated$ = new BehaviorSubject<boolean>(false)
  proto.setAuthStatus = (
    status: boolean | null,
    newJwt: string | null,
    admin: boolean
  ) => {
    debug('db', 'setAuthStatus', status, !!newJwt)
    if (typeof status === 'boolean') {
      proto.isAuthenticated$.next(status)
      proto.jwt$.next(newJwt)
      proto.isAdmin$.next(status ? admin : false)
    }
  }
  proto.isConnected$ = merge(
    of(null),
    fromEvent(window, 'online'),
    fromEvent(window, 'offline')
  ).pipe(map(() => navigator.onLine))

  proto.stop = async function (this: Database) {
    this.setAuthStatus(false, null, false)
    readyTables.next([])
    for (const collection of Object.values(this.collections)) {
      await collection.destroy()
    }
  }
}
Example #16
Source File: replicator.ts    From platyplus with MIT License 4 votes vote down vote up
createReplicator = async <T>(
  collection: RxCollection<T, unknown> & { replicator: Replicator<T> },
  options: ReplicatorOptions<T>
): Promise<Replicator<T>> => {
  const db = collection.database as unknown as Database
  const headers = () =>
    createHeaders(options.role, db.jwt$.getValue(), options.substituteRole)
  const resetWs = () => {
    if (wsClient) {
      wsClient.unsubscribeAll()
      wsClient.close()
    }
  }
  let state: RxGraphQLReplicationState<T> | undefined
  let wsClient: SubscriptionClient | undefined
  let jwtSubscription: Subscription | undefined
  let errorSubscription: Subscription | undefined

  const setupGraphQLReplication = async () => {
    const replicationState = collection.syncGraphQL({
      url: options.url,
      headers: headers(),
      pull: {
        queryBuilder: options.pull.queryBuilder,
        modifier: options.pull.modifier
      },
      push: {
        batchSize: options.batchSize || DEFAULT_BATCH_SIZE,
        queryBuilder: options.push?.queryBuilder,
        modifier: options.push?.modifier
      },
      live: true,
      liveInterval: 1000 * 60 * 10, // 10 minutes
      deletedFlag: DELETED_COLUMN,
      waitForLeadership: true // defaults to true
    })

    // replicationState.initialReplicationComplete$.subscribe((active) =>
    //   active
    //     ? setReplicationReady(collection.name)
    //     : setReplicationBusy(collection.name)
    // )

    replicationState.canceled$.subscribe(() => {
      debug(collection.name, `replication cancelled`)
      jwtSubscription?.unsubscribe()
      errorSubscription?.unsubscribe()
      startOption?.()
      resetWs()
    })
    return replicationState
  }

  const initWsSubscription = () => {
    debug(collection.name, `initGraphQLSubscription`)
    resetWs()
    wsClient = new SubscriptionClient(httpUrlToWebSockeUrl(options.url), {
      reconnect: true,
      connectionParams: {
        headers: headers()
      },
      timeout: 1000 * 60,
      reconnectionAttempts: 10000,
      inactivityTimeout: 10 * 1000,
      lazy: true
    })

    const request = wsClient.request({
      query: options.subscription.query,
      variables: options.subscription.variables?.()
    })

    request.subscribe({
      next: ({ data, ...rest }) => {
        if (data) {
          const contents = Object.values(data)[0]
          debug(collection.name, `WS request emitted`, contents)
          options.onWsReceive?.(contents)
          state?.run()
        } else {
          debug(collection.name, `WS request emitted, but no data`, rest)
        }
      },
      error: (error) => {
        warn(collection.name, `WS request error`, error)
      }
    })

    wsClient.onReconnected(() => {
      info(collection.name, `WS reconnected`)
    })
    wsClient.onConnected(() => {
      debug(collection.name, `WS connected`)
    })
    wsClient.onDisconnected(() => {
      debug(collection.name, `WS disconnected`)
    })
    wsClient.onReconnecting(() => {
      info(collection.name, `WS reconnecting`)
    })
    wsClient.onError((err) => {
      info(collection.name, ` WS error`, err)
    })
  }
  let startOption: undefined | (() => void)

  const start = async (): Promise<void> => {
    state = await setupGraphQLReplication()

    startOption = options.onStart?.()
    errorSubscription = state.error$.subscribe((data) => {
      warn(collection.name, `replication error`, data)
    })

    jwtSubscription = db.jwt$.subscribe((jwt) => {
      debug(collection.name, `new jwt received`)
      if (jwt) {
        state.setHeaders(headers())
        initWsSubscription()
      } else debug(collection.name, `new jwt received was null`)
    })

    state.awaitInitialReplication().then(() => {
      debug(collection.name, `awaitInitialReplication OK`)
    })
  }

  const stop = async (): Promise<void> => {
    debug(collection.name, `stop replication`)
    await state?.cancel()
  }

  const destroy = async (): Promise<void> => {
    // TODO check if it is really solved: https://gitter.im/pubkey/rxdb?at=61391ec763dca818914c246e
    //  this should be part of _prepare() in https://github.com/pubkey/rxdb/blob/master/src/plugins/replication-graphql/index.ts
    await setLastPullDocument(state.collection, state.endpointHash, null)
    await setLastPushSequence(state.collection, state.endpointHash, 0)
    await stop()
  }

  combineLatest([db.isConnected$, db.isAuthenticated$])
    .pipe(
      map(([connected, authenticated]) => connected && authenticated),
      distinctUntilChanged<boolean>()
    )
    .subscribe(async (ok) => {
      debug(collection.name, `auth/connection status change`, ok)
      if (ok) await start()
      else await stop()
    })
  collection.replicator = { start, stop, destroy, state }
  return collection.replicator
}