<commit_before>import { Observable } from 'rxjs';

import { ReadOnlyProperty, Property, Command } from '../../../WebRx';
import { BaseViewModel, RoutingStateHandler } from '../../React';

export interface SearchRequest {
  filter: string;
  regex: RegExp;
}

export interface SearchRoutingState {
  filter?: string;
}

export class SearchViewModel extends BaseViewModel implements RoutingStateHandler<SearchRoutingState> {
  public static displayName = 'SearchViewModel';

  public readonly filter: Property<string>;
  public readonly requests: ReadOnlyProperty<SearchRequest>;
  public readonly searchPending: ReadOnlyProperty<boolean>;

  public readonly search: Command<any>;
  public readonly clear: Command<any>;

  constructor(private readonly liveSearchTimeout = 500, private readonly isCaseInsensitive = true) {
    super();

    this.filter = this.wx.property('');

    // search is executed when a search is to be performed
    this.search = this.wx.command();

    this.clear = this.wx.command();

    this.requests = this.wx
      // project search executions into filter values
      .whenAny(this.search.results, () => this.filter.value)
      // debounce a little extra to protect against too many search invocations
      .debounceTime(250)
      // then map into a search request with regex
      .map(filter => (<SearchRequest>{
        filter,
        regex: this.createRegex(filter),
      }))
      .toProperty();

    this.searchPending = Observable
      .merge(
        Observable
          .merge(
            this.filter.changed
              .map(() => true),
            this.search.results
              .map(() => true),
          ),
        this.requests.changed
          .map(() => false),
      )
      .toProperty(false);

    this.addSubscription(
      this.wx
        .whenAny(this.clear, x => x)
        .subscribe(() => {
          this.filter.value = '';
        }),
    );

    if (this.liveSearchTimeout > 0) {
      this.addSubscription(
        this.filter.changed
          // debounce on the live search timeout
          .debounceTime(this.liveSearchTimeout)
          .invokeCommand(this.search),
      );
    }

    this.addSubscription(
      this.wx
        .whenAny(this.search, x => x)
        .subscribe(x => {
          this.notifyChanged(x);
        }),
    );
  }

  isRoutingStateHandler() {
    return true;
  }

  createRoutingState(): SearchRoutingState {<commit_msg>updating search to generate state change notifications only on requests (rather than search results)<commit_after>import { Observable } from 'rxjs';

import { ReadOnlyProperty, Property, Command } from '../../../WebRx';
import { BaseViewModel, RoutingStateHandler } from '../../React';

export interface SearchRequest {
  filter: string;
  regex: RegExp;
}

export interface SearchRoutingState {
  filter?: string;
}

export class SearchViewModel extends BaseViewModel implements RoutingStateHandler<SearchRoutingState> {
  public static displayName = 'SearchViewModel';

  public readonly filter: Property<string>;
  public readonly requests: ReadOnlyProperty<SearchRequest>;
  public readonly searchPending: ReadOnlyProperty<boolean>;

  public readonly search: Command<any>;
  public readonly clear: Command<any>;

  constructor(private readonly liveSearchTimeout = 500, private readonly isCaseInsensitive = true) {
    super();

    this.filter = this.wx.property('');

    // search is executed when a search is to be performed
    this.search = this.wx.command();

    this.clear = this.wx.command();

    this.requests = this.wx
      // project search executions into filter values
      .whenAny(this.search.results, () => this.filter.value)
      // debounce a little extra to protect against too many search invocations
      .debounceTime(250)
      // then map into a search request with regex
      .map(filter => (<SearchRequest>{
        filter,
        regex: this.createRegex(filter),
      }))
      .toProperty();

    this.searchPending = Observable
      .merge(
        Observable
          .merge(
            this.filter.changed
              .map(() => true),
            this.search.results
              .map(() => true),
          ),
        this.requests.changed
          .map(() => false),
      )
      .toProperty(false);

    this.addSubscription(
      this.wx
        .whenAny(this.clear, x => x)
        .subscribe(() => {
          this.filter.value = '';
        }),
    );

    if (this.liveSearchTimeout > 0) {
      this.addSubscription(
        this.filter.changed
          // debounce on the live search timeout
          .debounceTime(this.liveSearchTimeout)
          .invokeCommand(this.search),
      );
    }

    this.addSubscription(
      this.wx
        .whenAny(this.requests, x => x)
        .subscribe(x => {
          this.notifyChanged(x);
        }),
    );
  }

  isRoutingStateHandler() {
    return true;
  }

  createRoutingState(): SearchRoutingState {
{'fraction_non_alphanumeric': 0.11400848999393572, 'fraction_numerical': 0.0028299979785728724, 'mean_word_length': 2.4553072625698324, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2394620', 'n_tokens_mistral': 1341, 'n_tokens_neox': 1309, 'n_words': 345}