<commit_before>import { delay } from 'redux-saga';
import { take, call, apply, put, race, select } from 'redux-saga/effects';
import * as Keychain from 'react-native-keychain';
import { REHYDRATE } from 'redux-persist/constants';
import {
  LOGIN_REQUEST,
  LOGIN_SUCCESS,
  LOGIN_ERROR,
  LOGOUT,
  login,
  successLogin,
  failedLogin,
  logout,
  doneRehydrate,
} from '../actions/auth';
import { addError } from '../actions/error';
import pixiv from '../helpers/apiClient';
import { getAuthUser } from '../selectors';

export function* authorize(email, password) {
  // use apply instead of call to pass this to function
  const loginResponse = yield apply(pixiv, pixiv.login, [email, password]);
  yield call(Keychain.setGenericPassword, email, password);
  yield put(successLogin(loginResponse));
  return loginResponse;
}

export function* authAndRefreshTokenOnExpiry(email, password) {
  const loginResponse = yield call(authorize, email, password);
  while (true) {
    const credentials = yield call(Keychain.getGenericPassword);
    if (credentials.username && credentials.password) {
      // refresh token 5 min before expire
      // convert expires in to milisecond
      const delayMilisecond = (loginResponse.expires_in - 300) * 1000;
      yield call(delay, delayMilisecond);
      try {
        yield call(authorize, credentials.username, credentials.password);
      } catch (err) {
        yield put(logout());
      }
    }
  }
}

export function* handleLogout() {
  yield [call(Keychain.resetGenericPassword), apply(pixiv, pixiv.logout)];
}

export function* watchLoginRequest() {
  // yield takeEvery(LOGIN_REQUEST, loginFlow)
  while (true) {
    try {
      const action = yield take(LOGIN_REQUEST);
      const { email, password } = action.payload;
      yield race([
        take(LOGOUT),
        call(authAndRefreshTokenOnExpiry, email, password),
      ]);
      yield call(handleLogout);
      // user logged out, next while iteration will wait for the
      // next LOGIN_REQUEST action
    } catch (err) {
      const errMessage = err.errors &&
        err.errors.system &&
        err.errors.system.message
        ? err.errors.system.message
        : '';
      yield put(failedLogin());
      yield put(addError(errMessage));
    }
  }
}

// wait rehydrate complete, login if authUser exist, then run other api
export function* watchRehydrate() {
  while (true) {
    try {
      yield take(REHYDRATE);
      const user = yield select(getAuthUser);
      if (user) {
        console.log('watchRehydrate login');
        const credentials = yield call(Keychain.getGenericPassword);
        yield put(login(credentials.username, credentials.password));
        yield take([LOGIN_SUCCESS, LOGIN_ERROR]);
      }
      // todo put sync complete
      yield put(doneRehydrate());
    } catch (err) {
      // todo logout user
      console.log('err in watchRehydrate ', err);
    }
  }
}<commit_msg>better auth watchRehydrate saga handling<commit_after>import { delay } from 'redux-saga';
import { take, call, apply, put, race, select } from 'redux-saga/effects';
import * as Keychain from 'react-native-keychain';
import { REHYDRATE } from 'redux-persist/constants';
import {
  LOGIN_REQUEST,
  LOGIN_SUCCESS,
  LOGIN_ERROR,
  LOGOUT,
  login,
  successLogin,
  failedLogin,
  logout,
  doneRehydrate,
} from '../actions/auth';
import { addError } from '../actions/error';
import pixiv from '../helpers/apiClient';
import { getAuthUser } from '../selectors';

export function* authorize(email, password) {
  // use apply instead of call to pass this to function
  const loginResponse = yield apply(pixiv, pixiv.login, [email, password]);
  yield call(Keychain.setGenericPassword, email, password);
  yield put(successLogin(loginResponse));
  return loginResponse;
}

export function* authAndRefreshTokenOnExpiry(email, password) {
  const loginResponse = yield call(authorize, email, password);
  while (true) {
    const credentials = yield call(Keychain.getGenericPassword);
    if (credentials.username && credentials.password) {
      // refresh token 5 min before expire
      // convert expires in to milisecond
      const delayMilisecond = (loginResponse.expires_in - 300) * 1000;
      yield call(delay, delayMilisecond);
      try {
        yield call(authorize, credentials.username, credentials.password);
      } catch (err) {
        yield put(logout());
      }
    }
  }
}

export function* handleLogout() {
  yield [call(Keychain.resetGenericPassword), apply(pixiv, pixiv.logout)];
}

export function* watchLoginRequest() {
  // yield takeEvery(LOGIN_REQUEST, loginFlow)
  while (true) {
    try {
      const action = yield take(LOGIN_REQUEST);
      const { email, password } = action.payload;
      yield race([
        take(LOGOUT),
        call(authAndRefreshTokenOnExpiry, email, password),
      ]);
      yield call(handleLogout);
      // user logged out, next while iteration will wait for the
      // next LOGIN_REQUEST action
    } catch (err) {
      const errMessage = err.errors &&
        err.errors.system &&
        err.errors.system.message
        ? err.errors.system.message
        : '';
      yield put(failedLogin());
      yield put(addError(errMessage));
    }
  }
}

// wait rehydrate complete, login if authUser exist, then run other api
export function* watchRehydrate() {
  while (true) {
    try {
      yield take(REHYDRATE);
      const user = yield select(getAuthUser);
      if (user) {
        console.log('watchRehydrate login');
        const credentials = yield call(Keychain.getGenericPassword);
        if (credentials) {
          yield put(login(credentials.username, credentials.password));
        } else {
          yield put(logout());
        }
        yield take([LOGIN_SUCCESS, LOGIN_ERROR, LOGOUT]);
      }
      // todo put sync complete
      yield put(doneRehydrate());
    } catch (err) {
      // todo logout user
      console.log('err in watchRehydrate ', err);
    }
  }
}
{'fraction_non_alphanumeric': 0.11400869274490137, 'fraction_numerical': 0.0026746907388833165, 'mean_word_length': 3.1007539410555176, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '6506005', 'n_tokens_mistral': 1780, 'n_tokens_neox': 1735, 'n_words': 472}