import Data from '../Data';
import { hashPassword } from '../../lib/utils';
import Mongo from '../Mongo';
import Meteor from '../Meteor.js';
import ReactiveDict from '../ReactiveDict';
const TOKEN_KEY = 'reactnativemeteor_usertoken';
const Users = new Mongo.Collection('users');
/**
* @namespace User
* @type {object}
* @summary Represents all user/Accounts related functionality,
* that is to be available on the `Meteor` Object.
*/
const User = {
users: Users,
_reactiveDict: new ReactiveDict(),
user() {
let user_id = this._reactiveDict.get('_userIdSaved');
if (!user_id) return null;
return Users.findOne(user_id);
},
userId() {
let user_id = this._reactiveDict.get('_userIdSaved');
if (!user_id) return null;
const user = Users.findOne(user_id);
return user && user._id;
},
_isLoggingIn: true,
_isLoggingOut: false,
loggingIn() {
return this._reactiveDict.get('_loggingIn');
},
loggingOut() {
return User._isLoggingOut;
},
logout(callback) {
this._isTokenLogin = false;
User._startLoggingOut();
Meteor.call('logout', (err) => {
User.handleLogout();
Meteor.connect();
typeof callback == 'function' && callback(err);
});
},
handleLogout() {
Data._options.AsyncStorage.removeItem(TOKEN_KEY);
Data._tokenIdSaved = null;
this._reactiveDict.set('_userIdSaved', null);
User._userIdSaved = null;
User._endLoggingOut();
},
loginWithPassword(selector, password, callback) {
this._isTokenLogin = false;
if (typeof selector === 'string') {
if (selector.indexOf('@') === -1) selector = { username: selector };
else selector = { email: selector };
}
User._startLoggingIn();
Meteor.call(
'login',
{
user: selector,
password: hashPassword(password),
},
(err, result) => {
User._handleLoginCallback(err, result);
typeof callback == 'function' && callback(err);
}
);
},
loginWithPasswordAnd2faCode(selector, password, code, callback) {
this._isTokenLogin = false;
if (typeof selector === 'string') {
if (selector.indexOf('@') === -1) selector = { username: selector };
else selector = { email: selector };
}
User._startLoggingIn();
Meteor.call(
'login',
{
user: selector,
password: hashPassword(password),
code,
},
(err, result) => {
User._handleLoginCallback(err, result);
typeof callback == 'function' && callback(err);
}
);
},
logoutOtherClients(callback = () => {}) {
Meteor.call('getNewToken', (err, res) => {
if (err) return callback(err);
User._handleLoginCallback(err, res);
Meteor.call('removeOtherTokens', (err) => {
callback(err);
});
});
},
_login(user, callback) {
User._startLoggingIn();
Meteor.call('login', user, (err, result) => {
User._handleLoginCallback(err, result);
typeof callback == 'function' && callback(err);
});
},
_startLoggingIn() {
this._reactiveDict.set('_loggingIn', true);
Data.notify('loggingIn');
},
_startLoggingOut() {
User._isLoggingOut = true;
Data.notify('loggingOut');
},
_endLoggingIn() {
this._reactiveDict.set('_loggingIn', false);
Data.notify('loggingIn');
},
_endLoggingOut() {
User._isLoggingOut = false;
Data.notify('loggingOut');
},
_handleLoginCallback(err, result) {
if (!err) {
Meteor.isVerbose &&
console.info(
'User._handleLoginCallback::: token:',
result.token,
'id:',
result.id
);
Data._options.AsyncStorage.setItem(TOKEN_KEY, result.token);
Data._tokenIdSaved = result.token;
this._reactiveDict.set('_userIdSaved', result.id);
User._userIdSaved = result.id;
User._endLoggingIn();
this._isTokenLogin = false;
Data.notify('onLogin');
} else {
Meteor.isVerbose &&
console.info('User._handleLoginCallback::: error:', err);
if (this._isTokenLogin) {
setTimeout(() => {
if (User._userIdSaved) {
return;
}
this._timeout *= 2;
if (Meteor.user()) {
return;
}
User._loginWithToken(User._userIdSaved);
}, this._timeout);
}
// Signify we aren't logginging in any more after a few seconds
if (this._timeout > 2000) {
User._endLoggingIn();
}
User._endLoggingIn();
// we delegate the error to enable better logging
Data.notify('onLoginFailure', err);
}
Data.notify('change');
},
_timeout: 50,
_isTokenLogin: false,
_isCallingLogin: false,
_loginWithToken(value) {
if (!value) {
Meteor.isVerbose &&
console.info(
'User._loginWithToken::: parameter value is null, will not save as token.'
);
} else {
Data._tokenIdSaved = value;
}
if (value !== null) {
this._isTokenLogin = true;
Meteor.isVerbose && console.info('User._loginWithToken::: token:', value);
if (this._isCallingLogin) {
return;
}
this._isCallingLogin = true;
User._startLoggingIn();
Meteor.call('login', { resume: value }, (err, result) => {
this._isCallingLogin = false;
if (err?.error == 'too-many-requests') {
Meteor.isVerbose &&
console.info(
'User._handleLoginCallback::: too many requests retrying:',
err
);
let time = err.details?.timeToReset || err.timeToReset;
setTimeout(() => {
if (User._userIdSaved) {
return;
}
this._loadInitialUser();
}, time + 100);
} else if (err?.error === 403) {
User.logout();
} else {
User._handleLoginCallback(err, result);
}
});
} else {
Meteor.isVerbose && console.info('User._loginWithToken::: token is null');
User._endLoggingIn();
}
},
getAuthToken() {
return Data._tokenIdSaved;
},
async _loadInitialUser() {
this._timeout = 500;
User._startLoggingIn();
var value = null;
try {
value = await Data._options.AsyncStorage.getItem(TOKEN_KEY);
} catch (error) {
console.warn('AsyncStorage error: ' + error.message);
} finally {
User._loginWithToken(value);
}
},
};
export default User;