<commit_before>// @flow
import type { Node } from 'react';
import React, { Fragment, useEffect, useState } from 'react';
import { withRouter } from 'react-router';
import { createNormalizedClaimSearchKey, MATURE_TAGS } from 'lbry-redux';
import { FormField } from 'component/common/form';
import Button from 'component/button';
import moment from 'moment';
import ClaimList from 'component/claimList';
import Tag from 'component/tag';
import ClaimPreview from 'component/claimPreview';
import { toCapitalCase } from 'util/string';

const PAGE_SIZE = 20;
const TIME_DAY = 'day';
const TIME_WEEK = 'week';
const TIME_MONTH = 'month';
const TIME_YEAR = 'year';
const TIME_ALL = 'all';
const SEARCH_SORT_YOU = 'you';
const SEARCH_SORT_ALL = 'everyone';
const SEARCH_SORT_CHANNELS = 'channels';

const TYPE_TRENDING = 'trending';
const TYPE_TOP = 'top';
const TYPE_NEW = 'new';
const SEARCH_FILTER_TYPES = [SEARCH_SORT_YOU, SEARCH_SORT_CHANNELS, SEARCH_SORT_ALL];
const SEARCH_TYPES = [TYPE_TRENDING, TYPE_TOP, TYPE_NEW];
const SEARCH_TIMES = [TIME_DAY, TIME_WEEK, TIME_MONTH, TIME_YEAR, TIME_ALL];

type Props = {
  uris: Array<string>,
  subscribedChannels: Array<Subscription>,
  doClaimSearch: ({}) => void,
  injectedItem: any,
  tags: Array<string>,
  loading: boolean,
  personalView: boolean,
  doToggleTagFollow: string => void,
  meta?: Node,
  showNsfw: boolean,
  history: { action: string, push: string => void, replace: string => void },
  location: { search: string, pathname: string },
  claimSearchByQuery: {
    [string]: Array<string>,
  },
  hiddenUris: Array<string>,
};

function ClaimListDiscover(props: Props) {
  const {
    doClaimSearch,
    claimSearchByQuery,
    tags,
    loading,
    personalView,
    injectedItem,
    meta,
    subscribedChannels,
    showNsfw,
    history,
    location,
    hiddenUris,
  } = props;
  const didNavigateForward = history.action === 'PUSH';
  const { search } = location;
  const urlParams = new URLSearchParams(search);
  const personalSort = urlParams.get('sort') || SEARCH_SORT_YOU;
  const typeSort = urlParams.get('type') || TYPE_TRENDING;
  const timeSort = urlParams.get('time') || TIME_WEEK;
  const [page, setPage] = useState(1);
  const tagsInUrl = urlParams.get('t') || '';
  const options: {
    page_size: number,
    page: number,
    no_totals: boolean,
    any_tags: Array<string>,
    channel_ids: Array<string>,
    not_channel_ids: Array<string>,
    not_tags: Array<string>,
    order_by: Array<string>,
    release_time?: string,
  } = {
    page_size: PAGE_SIZE,
    page,
    // no_totals makes it so the sdk doesn't have to calculate total number pages for pagination
    // it's faster, but we will need to remove it if we start using total_pages
    no_totals: true,
    any_tags: (personalView && personalSort === SEARCH_SORT_YOU) || !personalView ? tags : [],
    channel_ids: personalSort === SEARCH_SORT_CHANNELS ? subscribedChannels.map(sub => sub.uri.split('#')[1]) : [],
    not_channel_ids: hiddenUris && hiddenUris.length ? hiddenUris.map(hiddenUri => hiddenUri.split('#')[1]) : [],
    not_tags: !showNsfw ? MATURE_TAGS : [],
    order_by:
      typeSort === TYPE_TRENDING
        ? ['trending_global', 'trending_mixed']
        : typeSort === TYPE_NEW
        ? ['release_time']
        : ['effective_amount'], // Sort by top
  };

  if (typeSort === TYPE_TOP && timeSort !== TIME_ALL) {
    options.release_time = `>${Math.floor(
      moment()
        .subtract(1, timeSort)
        .unix()
    )}`;
  }
  const hasContent =
    (personalSort === SEARCH_SORT_CHANNELS && subscribedChannels.length) ||
    (personalSort === SEARCH_SORT_YOU && !!tags.length) ||
    personalSort === SEARCH_SORT_ALL;
  const claimSearchCacheQuery = createNormalizedClaimSearchKey(options);
  const uris = (hasContent && claimSearchByQuery[claimSearchCacheQuery]) || [];
  const shouldPerformSearch =
    hasContent &&
    (uris.length === 0 ||
      didNavigateForward ||
      (!loading && uris.length < PAGE_SIZE * page && uris.length % PAGE_SIZE === 0));
  // Don't use the query from createNormalizedClaimSearchKey for the effect since that doesn't include page & release_time
  const optionsStringForEffect = JSON.stringify(options);

  const noChannels = (
    <div>
      <p>{__("You're not following any channels.")}</p>
      <Button button={'link'} navigate={'/?type=trending&sort=everyone'}>
        {__("Look what's trending for everyone")}
      </Button>{' '}
      {__('or')}{' '}
      <Button button={'link'} navigate={'/$/following'}>
        {__('Discover some channels!')}
      </Button>
    </div>
  );

  const emptyState = personalSort === SEARCH_SORT_CHANNELS ? noChannels : false;

  function getSearch() {
    let search = `?`;
    if (!personalView) {
      search += `t=${tagsInUrl}&`;
    }

    return search;
  }

  function handleTypeSort(newTypeSort) {
    let url = `${getSearch()}type=${newTypeSort}&sort=${personalSort}`;
    if (newTypeSort === TYPE_TOP) {
      url += `&time=${timeSort}`;
    }
    history.push(url);
  }

  function handlePersonalSort(newPersonalSort) {
    history.push(`${getSearch()}type=${typeSort}&sort=${newPersonalSort}`);
  }

  function handleTimeSort(newTimeSort) {
    history.push(`${getSearch()}type=${typeSort}&sort=${personalSort}&time=${newTimeSort}`);
  }

  function handleScrollBottom() {
    if (!loading) {
      setPage(page + 1);
    }
  }
<commit_msg>reset page on filter change (lost in rebase)<commit_after>// @flow
import type { Node } from 'react';
import React, { Fragment, useEffect, useState } from 'react';
import { withRouter } from 'react-router';
import { createNormalizedClaimSearchKey, MATURE_TAGS } from 'lbry-redux';
import { FormField } from 'component/common/form';
import Button from 'component/button';
import moment from 'moment';
import ClaimList from 'component/claimList';
import Tag from 'component/tag';
import ClaimPreview from 'component/claimPreview';
import { toCapitalCase } from 'util/string';

const PAGE_SIZE = 20;
const TIME_DAY = 'day';
const TIME_WEEK = 'week';
const TIME_MONTH = 'month';
const TIME_YEAR = 'year';
const TIME_ALL = 'all';
const SEARCH_SORT_YOU = 'you';
const SEARCH_SORT_ALL = 'everyone';
const SEARCH_SORT_CHANNELS = 'channels';

const TYPE_TRENDING = 'trending';
const TYPE_TOP = 'top';
const TYPE_NEW = 'new';
const SEARCH_FILTER_TYPES = [SEARCH_SORT_YOU, SEARCH_SORT_CHANNELS, SEARCH_SORT_ALL];
const SEARCH_TYPES = [TYPE_TRENDING, TYPE_TOP, TYPE_NEW];
const SEARCH_TIMES = [TIME_DAY, TIME_WEEK, TIME_MONTH, TIME_YEAR, TIME_ALL];

type Props = {
  uris: Array<string>,
  subscribedChannels: Array<Subscription>,
  doClaimSearch: ({}) => void,
  injectedItem: any,
  tags: Array<string>,
  loading: boolean,
  personalView: boolean,
  doToggleTagFollow: string => void,
  meta?: Node,
  showNsfw: boolean,
  history: { action: string, push: string => void, replace: string => void },
  location: { search: string, pathname: string },
  claimSearchByQuery: {
    [string]: Array<string>,
  },
  hiddenUris: Array<string>,
};

function ClaimListDiscover(props: Props) {
  const {
    doClaimSearch,
    claimSearchByQuery,
    tags,
    loading,
    personalView,
    injectedItem,
    meta,
    subscribedChannels,
    showNsfw,
    history,
    location,
    hiddenUris,
  } = props;
  const didNavigateForward = history.action === 'PUSH';
  const { search } = location;
  const urlParams = new URLSearchParams(search);
  const personalSort = urlParams.get('sort') || SEARCH_SORT_YOU;
  const typeSort = urlParams.get('type') || TYPE_TRENDING;
  const timeSort = urlParams.get('time') || TIME_WEEK;
  const [page, setPage] = useState(1);
  const tagsInUrl = urlParams.get('t') || '';
  const options: {
    page_size: number,
    page: number,
    no_totals: boolean,
    any_tags: Array<string>,
    channel_ids: Array<string>,
    not_channel_ids: Array<string>,
    not_tags: Array<string>,
    order_by: Array<string>,
    release_time?: string,
  } = {
    page_size: PAGE_SIZE,
    page,
    // no_totals makes it so the sdk doesn't have to calculate total number pages for pagination
    // it's faster, but we will need to remove it if we start using total_pages
    no_totals: true,
    any_tags: (personalView && personalSort === SEARCH_SORT_YOU) || !personalView ? tags : [],
    channel_ids: personalSort === SEARCH_SORT_CHANNELS ? subscribedChannels.map(sub => sub.uri.split('#')[1]) : [],
    not_channel_ids: hiddenUris && hiddenUris.length ? hiddenUris.map(hiddenUri => hiddenUri.split('#')[1]) : [],
    not_tags: !showNsfw ? MATURE_TAGS : [],
    order_by:
      typeSort === TYPE_TRENDING
        ? ['trending_global', 'trending_mixed']
        : typeSort === TYPE_NEW
        ? ['release_time']
        : ['effective_amount'], // Sort by top
  };

  if (typeSort === TYPE_TOP && timeSort !== TIME_ALL) {
    options.release_time = `>${Math.floor(
      moment()
        .subtract(1, timeSort)
        .unix()
    )}`;
  }
  const hasContent =
    (personalSort === SEARCH_SORT_CHANNELS && subscribedChannels.length) ||
    (personalSort === SEARCH_SORT_YOU && !!tags.length) ||
    personalSort === SEARCH_SORT_ALL;
  const claimSearchCacheQuery = createNormalizedClaimSearchKey(options);
  const uris = (hasContent && claimSearchByQuery[claimSearchCacheQuery]) || [];
  const shouldPerformSearch =
    hasContent &&
    (uris.length === 0 ||
      didNavigateForward ||
      (!loading && uris.length < PAGE_SIZE * page && uris.length % PAGE_SIZE === 0));
  // Don't use the query from createNormalizedClaimSearchKey for the effect since that doesn't include page & release_time
  const optionsStringForEffect = JSON.stringify(options);

  const noChannels = (
    <div>
      <p>{__("You're not following any channels.")}</p>
      <Button button={'link'} navigate={'/?type=trending&sort=everyone'}>
        {__("Look what's trending for everyone")}
      </Button>{' '}
      {__('or')}{' '}
      <Button button={'link'} navigate={'/$/following'}>
        {__('Discover some channels!')}
      </Button>
    </div>
  );

  const emptyState = personalSort === SEARCH_SORT_CHANNELS ? noChannels : false;

  function getSearch() {
    let search = `?`;
    if (!personalView) {
      search += `t=${tagsInUrl}&`;
    }

    return search;
  }

  function handleTypeSort(newTypeSort) {
    let url = `${getSearch()}type=${newTypeSort}&sort=${personalSort}`;
    if (newTypeSort === TYPE_TOP) {
      url += `&time=${timeSort}`;
    }

    setPage(1);
    history.push(url);
  }

  function handlePersonalSort(newPersonalSort) {
    setPage(1);
    history.push(`${getSearch()}type=${typeSort}&sort=${newPersonalSort}`);
  }

  function handleTimeSort(newTimeSort) {
    setPage(1);
    history.push(`${getSearch()}type=${typeSort}&sort=${personalSort}&time=${newTimeSort}`);
  }

  function handleScrollBottom() {
    if (!loading) {
      setPage(page + 1);
    }
  }

{'fraction_non_alphanumeric': 0.14271310724106326, 'fraction_numerical': 0.001924839596700275, 'mean_word_length': 4.005045871559633, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 53, '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': '7048801', 'n_tokens_mistral': 3612, 'n_tokens_neox': 3546, 'n_words': 814}