sdk.js

import Auth from "./managers/auth";
import Http from "./http";
import {Storage} from "./storage";
import BanInfo from "./models/BanInfo";
import AuthResponse from "./models/AuthResponse";
import Thrift from "./_thrift";

import "./thrift/Hades";
import "./thrift/exceptions_types";

import CurrentUser from './CurrentUser';

window.Thrift = Thrift;

/**
 * @class GetSocial
 */
export default class GetSocial {
    /**
     * Initializes the SDK.
     *
     * @memberof GetSocial
     * @param {Object} config
     * @param {string} config.appId - GetSocial App ID
     * @param {string} config.appName - App Name
     * @param {string} [config.appVersion] - App Version
     * @param {string} [config.language="en"] - SDK language
     * @returns {Promise<AuthResponse|null>} Promise with the authenticated user and referral information (if any)
     */
    static init (config) {
        GetSocial._config = config || {};
        GetSocial._baseUrl = GetSocial._config.baseUrl || SERVICE_URL;
        // Smart Invites service url.
        //
        // Smart Banners and Smart Widget work directly from SI,
        // they are not part of the HTTP API.
        GetSocial._siUrl = GetSocial._config.siUrl || SI_URL;
        GetSocial._http = Http;
        GetSocial._sessionId = "";
        GetSocial._appId = GetSocial._config.appId;

        // Store click ID, if any.
        let cid = GetSocial.getQueryParam("_gs_cid");
        if (cid !== "") {
            GetSocial._cid = cid;
            Storage.storeClickID(cid);
        }

        // Set super properties to be used in some requests
        const nav = window.navigator || {};
        let tz = (new Date()).getTimezoneOffset() * -1 / 60 * 100;
        tz = Math.abs(tz) < 1000
            ? (tz < 0
                ? `-0${Math.abs(tz)}`
                : `+0${tz}`)
            : (tz < 0
                ? tz
                : `+${tz}`);

        GetSocial.superProps = new THSuperProperties({
            appName: config.appName || null,
            appVersionPublic: config.appVersion || null,
            sdkVersion: '7.12.7',
            sdkLanguage: config.language || 'en',
            deviceTimezone: 'GMT' + tz,
            deviceOs: THDeviceOs.WEB_DESKTOP,
            deviceOsVersion: nav.appVersion,
            deviceLanguage: nav.language,
            deviceModel: nav.platform
        })

        // Init Hades Client
        let transport = new Thrift.TXHRTransport(HADES_URL);
        let protocol = new Thrift.TJSONProtocol(transport);
        GetSocial._hades = new HadesClient(protocol);

        GetSocial._initialized = true;

        const auth = Storage.getAuth();

        if (!auth || !auth.sessionId ||
            !auth.expiresAt || auth.expiresAt < new Date()) {
            if (GetSocial.onInitListener) {
                GetSocial.onInitListener();
            }

            Auth.logout();

            return Promise.resolve(null);
        } else {
            GetSocial.onAuth(auth, Storage.getProviders());

            return GetSocial.getCurrentUser().refresh()
                .then(() => {
                    // Process app open if click ID is set
                    if (cid) {
                        return GetSocial.request(
                            'processAppOpen',
                            new THProcessAppOpenRequest({
                                referrerData: {
                                    [THAppOpenSource.API]: {
                                        [THAppOpenKey.Url]: cid
                                    }
                                }
                            })
                        ).then((tokenInfo) => {
                            // Persist referral data
                            GetSocial._referral = tokenInfo;
                            return {
                                user: GetSocial.getCurrentUser(),
                                referral: tokenInfo
                            }
                        });
                    } else {
                        return {
                            user: GetSocial.getCurrentUser(),
                            referral: null
                        }
                    }
                })
                .catch((e) => {
                    console.error(e);
                    Auth.logout();
                    return null;
                })
                .finally(() => {
                    if (GetSocial.onInitListener) {
                        GetSocial.onInitListener();
                    }
                });
        }

    }

    /**
     * Initializes the SDK.
     *
     * @memberof GetSocial
     * @param {string} appId - GetSocial App ID
     * @returns {Promise<AuthResponse|null>} Promise with the authenticated user and referral information (if any)
     */
     static initWithAppId(appId) {
        return GetSocial.init({ appId });
    }

    /**
     * Notifies callers when SDK finished initialization.
     *
     * @memberof GetSocial
     * @param {function} onInit - Function to be invoked.
     */
    static addOnInitializedListener(onInit) {
        GetSocial.onInitListener = onInit || null;
    }

    /**
     * Returns if SDK is initialized or not.
     *
     * @memberof GetSocial
     * @return {boolean} true, if initialized, otherwise false.
     */
    static isInitialized() {
        return GetSocial._initialized;
    }

    /**
     * Returns current language of GetSocial plugin.
     *
     * @memberof GetSocial
     * @return {string} currently used language.
     */
    static getLanguage() {
        return GetSocial.superProps
            ? GetSocial.superProps.sdkLanguage
            : 'en';
    }

    /**
     * Sets the language of GetSocial plugin.
     * If provided value is incorrect, sets the default language.
     *
     * @memberof GetSocial
     * @param {string} language
     * @return {Promise<string>} language code or error
     */
    static setLanguage(language) {
        if (!GetSocial.superProps) {
            GetSocial.superProps = new THSuperProperties();
        }

        GetSocial.superProps.sdkLanguage = language;

        const idt = Storage.getIdentity();
        if (idt) {
            // Re-authenticate user to update sdkLanguage in session
            return Auth.authenticate({
                identity_type: idt.providerId,
                token: idt.accessToken
            })
                .then(() => GetSocial.superProps.sdkLanguage);
        } else {
            return Promise.resolve(GetSocial.superProps.sdkLanguage);
        }
    }

    /**
     * Returns version of GetSocial SDK.
     *
     * @memberof GetSocial
     * @return {string} : version as string.
     */
    static getSdkVersion() {
        return GetSocial.superProps
            ? GetSocial.superProps.sdkVersion
            : null;
    }

    /**
     * Current user.
     *
     * @memberof GetSocial
     * @return {CurrentUser} current user or null if SDK is not initialized.
     */
    static getCurrentUser() {
        return GetSocial._currentUser || null;
    }

    /**
     * Makes an HTTP request to the API using the Hades client.
     *
     * @ignore
     * @memberof GetSocial
     * @param {string} endpoint - Method defined on Hades client, e.g <code>/authenticateSdk</code>
     * @param {object} parameters - Parameters to pass onto the endpoint
     * @return {Promise} - Promise from Hades client
     */
    static request(endpoint, ...parameters) {
        const guestEndpoints = [
            'authenticateSdkAllInOne'
        ];

        if (guestEndpoints.indexOf(endpoint) < 0) {
            if (!GetSocial._sessionId) {
                return Promise.reject(new Error('User not authenticated'));
            } else if (parameters[0] && parameters[0].sessionId !== undefined) {
                parameters[0].sessionId = GetSocial._sessionId;
            } else {
                parameters.unshift(GetSocial._sessionId);
            }
        }

        if (!GetSocial._hades) {
            return Promise.reject(new Error('SDK not initalized'));
        }

        if (!GetSocial._hades[endpoint]) {
            return Promise.reject(new Error('Endpoint doesn\'t exist'));
        }

        return new Promise ((resolve, reject) => {
            GetSocial._hades[endpoint].apply(GetSocial._hades, parameters)
                .then(resolve)
                .catch((res) => {
                    if (res.errors && Array.isArray(res.errors)) {
                        res.errors = res.errors.pop();
                    }

                    if (res.errors &&
                        res.errors.errorCode === THErrorCode.InvalidSession) {
                        // Logout
                        Auth.logout();

                        if (Storage.getIdentity()) {
                            // Try to reauthenticate
                            const params = Storage.getIdentity().getParams();
                            Auth.authenticate(params)
                                .then(() => {
                                    // Attempt to make the request again
                                    GetSocial.request(endpoint, parameters)
                                        .then(resolve, reject);
                                })
                                .catch(reject);
                        } else {
                            reject(new Error('User not authenticated'));
                        }
                    } else {
                        reject(res && res.errors
                            ? res.errors
                            : res);
                    }
                })
        });
    }

    /**
     * Makes an HTTP request to the given API path with the given options.
     *
     * @ignore
     * @param {string} path - Request URI, e.g <code>/authenticate/user</code>
     * @param {object} options - Request options like method, query params etc.
     * @return {Promise<Http>|undefined} - Promise of an XMLHttpRequest.
     */
    static rawRequest(path, options) {
        options = options || {};
        options.method = options.method || "GET";

        let uri;
        try {
            uri = new URL(options.url || options.isSI ? `${GetSocial._siUrl}${path}` : `${GetSocial._baseUrl}${path}`);
        } catch (e) {
            console.log(e);
            return;
        }

        if (GetSocial._accessToken !== "") {
            options.accessToken = GetSocial._accessToken;
        }
        if (typeof options.queryParams === "object") {
            /** @var {Object} options.queryParams */
            for (let key in options.queryParams) {
                if (options.queryParams.hasOwnProperty(key)) {
                    uri.searchParams.set(key, options.queryParams[key]);
                }
            }
        }
        if (options.useAppId) {
            uri.searchParams.set("app_id", GetSocial._appId);
        }

        options.url = uri.toString();
        return GetSocial._http(options);
    }

    /**
     * Returns the query string parameter with the given name, or an empty string.
     *
     * @ignore
     * @param {string} name - Name of the parameter
     * @return {string}
     */
    static getQueryParam(name) {
        name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
        const regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
        const results = regex.exec(window.location.href);
        return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
    }

    /**
     * Sets the User's session ID to the given value.
     *
     * @ignore
     * @param {string} sessionId
     */
    static setSessionId(sessionId) {
        GetSocial._sessionId = sessionId;
    }

    /**
     * Set internal props related to auth and providers
     *
     * @ignore
     */
    static onAuth(auth, inviteProviders) {
        const user = auth.user;

        GetSocial._sessionId = auth.sessionId;
        GetSocial._banInfo = user.internalPrivateProperties.ban_expiry
            ? new BanInfo({
                expiration: user.internalPrivateProperties.ban_expiry,
                reason: user.internalPrivateProperties.ban_reason
            })
            : null;
        GetSocial._currentUser = CurrentUser.create({
            ...user,
            verified: user.internalPublicProperties &&
                !!user.internalPublicProperties.verified
        });

        GetSocial._defaultInviteContent = inviteProviders
            .defaultInviteContent;
        GetSocial._inviteProviders = inviteProviders.providers;
        GetSocial._appInfo = auth.applicationInfo;
        GetSocial._uploadEndpoint = auth.uploadEndpoint;
        GetSocial._uploadChunkSize = auth.uploadChunkSize;
        GetSocial._uploadFileSizeLimit = auth.uploadFileSizeLimit;
    }
}