text: // @flow
  import * as ACTIONS from 'constants/action_types';
  import { SIDEBAR_SUBS_DISPLAYED } from 'constants/subscriptions';
  import REWARDS from 'rewards';
  import { Lbryio } from 'lbryinc';
  import { doClaimSearch } from 'redux/actions/claims';
  import { doClaimRewardType } from 'redux/actions/rewards';
  import { getChannelFromClaim } from 'util/claim';
  import { parseURI } from 'util/lbryURI';
  import { doAlertWaitingForSync } from 'redux/actions/app';
  import { doToast } from 'redux/actions/notifications';
  import { selectSubscriptions } from 'redux/selectors/subscriptions';
  
  type SubscriptionArgs = {
    channelName: string,
    uri: string,
    notificationsDisabled?: boolean,
  };
  
  const FETCH_LAST_ACTIVE_SUBS_MIN_INTERVAL_MS = 5 * 60 * 1000;
  let activeSubsLastFetchedTime = 0;
  
  export function doToggleSubscription(
    subscription: SubscriptionArgs,
    followToast: boolean,
    isSubscribed: boolean = false
  ) {
    return async (dispatch: Dispatch, getState: GetState) => {
      const {
        settings: { daemonSettings },
        sync: { prefsReady: ready },
      } = getState();
  
      if (!ready) {
        return dispatch(doAlertWaitingForSync());
      }
  
      const { share_usage_data: shareSetting } = daemonSettings;
      const isSharingData = shareSetting || IS_WEB;
  
      if (!isSubscribed) {
        const subscriptionUri = subscription.uri;
        if (!subscriptionUri.startsWith('lbry://')) {
          throw Error(`Subscription uris must include the "lbry://" prefix.\nTried to subscribe to ${subscriptionUri}`);
        }
      }
  
      dispatch({
        type: !isSubscribed ? ACTIONS.CHANNEL_SUBSCRIBE : ACTIONS.CHANNEL_UNSUBSCRIBE,
        data: subscription,
      });
  
      // if the user isn't sharing data, keep the subscriptions entirely in the app
      if (isSharingData || IS_WEB) {
        const { channelClaimId } = parseURI(subscription.uri);
  
        if (!isSubscribed) {
          // They are sharing data, we can store their subscriptions in our internal database
          Lbryio.call('subscription', 'new', {
            channel_name: subscription.channelName,
            claim_id: channelClaimId,
            notifications_disabled: subscription.notificationsDisabled,
          });
  
          dispatch(doClaimRewardType(REWARDS.TYPE_SUBSCRIPTION, { failSilently: true }));
        } else {
          Lbryio.call('subscription', 'delete', {
            claim_id: channelClaimId,
          });
        }
      }
      if (followToast) {
        dispatch(
          doToast({
            message: __(!isSubscribed ? 'You followed %CHANNEL_NAME%!' : 'Unfollowed %CHANNEL_NAME%.', {
              CHANNEL_NAME: subscription.channelName,
            }),
          })
        );
      }
  
      // Reset last-fetch counter
      activeSubsLastFetchedTime = 0;
    };
  }
  
  export function doChannelSubscribe(subscription: SubscriptionArgs, followToast: boolean = true) {
    return (dispatch: Dispatch) => {
      return dispatch(doToggleSubscription(subscription, followToast));
    };
  }
  
  export function doChannelUnsubscribe(subscription: SubscriptionArgs, followToast: boolean = true) {
    return (dispatch: Dispatch) => {
      return dispatch(doToggleSubscription(subscription, followToast, true));
    };
  }
  
  export function doFetchLastActiveSubs(forceFetch: boolean = false, count: number = SIDEBAR_SUBS_DISPLAYED) {
    function parseIdFromUri(uri) {
      try {
        const { channelClaimId } = parseURI(uri);
        return channelClaimId;
      } catch {
        return '';
      }
    }
  
    return (dispatch: Dispatch, getState: GetState) => {
      const now = Date.now();
      if (!forceFetch && now - activeSubsLastFetchedTime < FETCH_LAST_ACTIVE_SUBS_MIN_INTERVAL_MS) {
        dispatch({ type: ACTIONS.FETCH_LAST_ACTIVE_SUBS_SKIP });
        return;
      }
  
      const state = getState();
      const subscriptions = selectSubscriptions(state);
      const channelIds = subscriptions.map((sub) => parseIdFromUri(sub.uri));
      activeSubsLastFetchedTime = now;
  
      if (channelIds.length === 0) {
        dispatch({
          type: ACTIONS.FETCH_LAST_ACTIVE_SUBS_DONE,
          data: [],
        });
        return;
      }
  
      const searchOptions = {
        limit_claims_per_channel: 1,
        channel_ids: channelIds,
        claim_type: ['stream', 'repost'],
        page: 1,
        page_size: count,
        no_totals: true,
        order_by: ['release_time'],
      };
  
      dispatch(doClaimSearch(searchOptions))
        .then((results) => {
          const values = Object.values(results || {});
          dispatch({
            type: ACTIONS.FETCH_LAST_ACTIVE_SUBS_DONE,
            // $FlowFixMe https://github.com/facebook/flow/issues/2221
            data: values.map((v) => getChannelFromClaim(v.stream)),
          });
        })
        .catch(() => {
          dispatch({ type: ACTIONS.FETCH_LAST_ACTIVE_SUBS_FAIL });
        });
    };
  }
  