コード例 #1
0
        private async void LoginTile_Tap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            try
            {
                var browser = new WebBrowser()
                {
                    Name = "browser", Width = 480, Height = 800
                };
                browserPopup.Child  = browser;
                browserPopup.IsOpen = true;
                //this.NavigationService.Navigate(new Uri("/Pages/FacebookLoginPage.xaml", UriKind.Relative));
                ParseUser user = await ParseFacebookUtils.LogInAsync(browser, null);

                var fb = new FacebookClient();
                fb.AccessToken = ParseFacebookUtils.AccessToken;
                var me = await fb.GetTaskAsync("me");

                if (user.IsAuthenticated)
                {
                    IDictionary <string, object> results = (IDictionary <string, object>)me;
                    ViewModelLocator.MainStatic.User.IsLogged      = true;
                    ViewModelLocator.MainStatic.User.FacebookId    = (string)results["id"];
                    ViewModelLocator.MainStatic.User.FacebookToken = ParseFacebookUtils.AccessToken;

                    ViewModelLocator.MainStatic.User.Username  = (string)results["name"];
                    ViewModelLocator.MainStatic.User.FirstName = (string)results["first_name"];
                    ViewModelLocator.MainStatic.User.LastName  = (string)results["last_name"];

                    ViewModelLocator.MainStatic.User.ObjectId = user.ObjectId.ToString();

                    ViewModelLocator.MainStatic.User.GetStats();

                    // available picture types: square (50x50), small (50xvariable height), large (about 200x variable height) (all size in pixels)
                    // for more info visit http://developers.facebook.com/docs/reference/api
                    string profilePictureUrl = string.Format("https://graph.facebook.com/{0}/picture?type={1}&access_token={2}",
                                                             ViewModelLocator.MainStatic.User.FacebookId,
                                                             "large",
                                                             ViewModelLocator.MainStatic.User.FacebookToken);
                    ViewModelLocator.MainStatic.User.UserImage = profilePictureUrl;
                }
                ;

                browserPopup.IsOpen = false;
            }
            catch {
                browserPopup.IsOpen = false;
            };
        }
コード例 #2
0
        private async void FacebookLogin_Tapped(object sender, TappedRoutedEventArgs e)
        {
            progress.IsIndeterminate = true;

            try
            {
                var user = await ParseFacebookUtils.LogInAsync(new List <string>() { "email", "public_profile" });

                if (user != null)
                {
                    FacebookClient client       = new FacebookClient(ParseFacebookUtils.AccessToken);
                    dynamic        facebookUser = await client.GetTaskAsync("me", new { fields = "id,name,email" });

                    if (!user.Keys.Contains("nameSurname"))
                    {
                        user.Add("nameSurname", facebookUser.name);
                    }
                    else
                    {
                        user["nameSurname"] = facebookUser.name;
                    }
                    if (!user.Keys.Contains("withFacebook"))
                    {
                        user.Add("withFacebook", true);
                    }
                    if (!user.Keys.Contains("email"))
                    {
                        user.Add("email", facebookUser.email);
                    }
                    else
                    {
                        user["email"] = facebookUser.email;
                    }
                    await user.SaveAsync();

                    backgroundPanel.Visibility      = Visibility.Visible;
                    successfulLoginPanel.Visibility = Visibility.Visible;
                    NameSurname.Text = ParseUser.CurrentUser["nameSurname"] as string;
                }
            }

            catch (Exception)
            {
            }

            progress.IsIndeterminate = false;
        }
コード例 #3
0
        /// <summary>
        /// Constructeur pour l'objet Application.
        /// </summary>
        public App()
        {
            // App.xaml initialization
            ParseClient.Initialize("KB0XBMX06SVCiUnSUKKgA52v2pee75nSGexrh0wT", "oiclVXauxBf2mVR4fjs7VE8wAJzaVEKEAR3TrCFp");
            ParseFacebookUtils.Initialize("603572813098360");
            // Other initialization


            //// You can setup a event handler to be called back when the authentication has finished
            //Session.OnFacebookAuthenticationFinished += OnFacebookAuthenticationFinished;

            //// This line is from the above step
            //RootFrame.UriMapper = new FacebookUriMapper();

            // Gestionnaire global pour les exceptions non interceptées.
            UnhandledException += Application_UnhandledException;

            // Initialisation du XAML standard
            InitializeComponent();

            // Initialisation spécifique au téléphone
            InitializePhoneApplication();

            // Initialisation de l'affichage de la langue
            InitializeLanguage();

            // Affichez des informations de profilage graphique lors du débogage.
            if (Debugger.IsAttached)
            {
                // Affichez les compteurs de fréquence des trames actuels.
                Application.Current.Host.Settings.EnableFrameRateCounter = true;

                // Affichez les zones de l'application qui sont redessinées dans chaque frame.
                //Application.Current.Host.Settings.EnableRedrawRegions = true;

                // Activez le mode de visualisation d'analyse hors production,
                // qui montre les zones d'une page sur lesquelles une accélération GPU est produite avec une superposition colorée.
                //Application.Current.Host.Settings.EnableCacheVisualization = true;

                // Empêche l'écran de s'éteindre lorsque le débogueur est utilisé en désactivant
                // la détection de l'état inactif de l'application.
                // Attention :- À utiliser uniquement en mode de débogage. Les applications qui désactivent la détection d'inactivité de l'utilisateur continueront de s'exécuter
                // et seront alimentées par la batterie lorsque l'utilisateur ne se sert pas du téléphone.
                PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
            }
            //RootFrame.UriMapper = new FacebookUriMapper();
        }
コード例 #4
0
 public override bool FinishedLaunching(UIApplication app, NSDictionary options)
 {
     DependencyService.Register <ToastNotificatorImplementation>();
     ToastNotificatorImplementation.Init();
     global::Xamarin.Forms.Forms.Init();
     ImageCircleRenderer.Init();
     new SfAutoCompleteRenderer();
     ParseClient.Initialize(new ParseClient.Configuration
     {
         ApplicationId = "myAppId",
         Server        = "http://bigbreakers.herokuapp.com/parse/"          // trailing slash is important
     });
     ParseFacebookUtils.Initialize("1790522011216423");
     LoadApplication(new App());
     Xamarin.FormsGoogleMaps.Init("AIzaSyC6MmgY1vrl6B71IRKJKL9cLdfVBJssHfA");
     return(base.FinishedLaunching(app, options));
 }
コード例 #5
0
        public override void ViewDidAppear(bool animated)
        {
            base.ViewDidAppear(animated);
            var auth = new OAuth2Authenticator(
                clientId: "1790522011216423",                                                 // your OAuth2 client id
                scope: "email",                                                               // the scopes for the particular API you're accessing, delimited by "+" symbols
                authorizeUrl: new Uri("https://www.facebook.com/v2.8/dialog/oauth"),          // the auth URL for the service
                redirectUrl: new Uri("https://www.facebook.com/connect/login_success.html")); // the redirect URL for the service

            auth.Completed += async(sender, eventArgs) =>
            {
                count++;
                // We presented the UI, so it's up to us to dimiss it on iOS.
                //App.SuccessfulLoginAction.Invoke();

                if (eventArgs.IsAuthenticated)
                {
                    DismissViewController(true, null);
                    var accessToken = eventArgs.Account.Properties["access_token"].ToString();
                    var expiresIn   = Convert.ToDouble(eventArgs.Account.Properties["expires_in"]);
                    var expireDate  = DateTime.Now + TimeSpan.FromSeconds(expiresIn);

                    var request  = new OAuth2Request("GET", new Uri("https://graph.facebook.com/me?fields=email,first_name,last_name,gender"), null, eventArgs.Account);
                    var response = await request.GetResponseAsync();

                    var obj = JObject.Parse(response.GetResponseText());
                    var id  = obj["id"].ToString().Replace("\"", "");
                    await ParseFacebookUtils.LogInAsync(id, accessToken, expireDate);

                    var session = await ParseSession.GetCurrentSessionAsync();

                    var token = session.SessionToken;
                    App.SuccessfulLoginAction.Invoke();
                    App.SuccessFacebookLogin(token, obj);
                }
                else
                {
                    // The user cancelled
                }
            };
            if (count <= 1)
            {
                PresentViewController(auth.GetUI(), true, null);
            }
        }
コード例 #6
0
 protected override void OnCreate(Bundle bundle)
 {
     TabLayoutResource = Resource.Layout.Tabbar;
     ToolbarResource   = Resource.Layout.Toolbar;
     base.OnCreate(bundle);
     Xamarin.FormsGoogleMaps.Init(this, bundle);
     ImageCircleRenderer.Init();
     DependencyService.Register <ToastNotificatorImplementation>();
     ToastNotificatorImplementation.Init(this);
     global::Xamarin.Forms.Forms.Init(this, bundle);
     ParseClient.Initialize(new ParseClient.Configuration
     {
         ApplicationId = "myAppId",
         Server        = "http://bigbreakers.herokuapp.com/parse/"          // trailing slash is important
     });
     ParseFacebookUtils.Initialize("1790522011216423");
     LoadApplication(new App());
 }
コード例 #7
0
ファイル: App.xaml.cs プロジェクト: lhanneman/windows-phone
        // Do not add any additional code to this method
        private void InitializePhoneApplication()
        {
            if (phoneApplicationInitialized)
            {
                return;
            }

            // Create the frame but don't set it as RootVisual yet; this allows the splash
            // screen to remain active until the application is ready to render.
            RootFrame            = new PhoneApplicationFrame();
            RootFrame.Navigated += CompleteInitializePhoneApplication;

            // Assign the URI-mapper class to the application frame.
            RootFrame.UriMapper = new UriMapper();

            // Handle navigation failures
            RootFrame.NavigationFailed += RootFrame_NavigationFailed;

            // Handle reset requests for clearing the backstack
            RootFrame.Navigated += CheckForResetNavigation;

            // Ensure we don't initialize again
            phoneApplicationInitialized = true;

            // ------------------------------------------------------------------
            //
            // CUSTOM STUFF
            //
            //-------------------------------------------------------------------

            (App.Current.Resources["PhoneAccentBrush"] as SolidColorBrush).Color     = Color.FromArgb(255, 223, 179, 0);
            (App.Current.Resources["PhoneForegroundBrush"] as SolidColorBrush).Color = Colors.Black;
            (App.Current.Resources["PhoneBackgroundBrush"] as SolidColorBrush).Color = Color.FromArgb(255, 241, 190, 0);// orange

            // initailize parse so we can make requests:
            ParseClient.Initialize(Constants.Parse_ApplicationID, Constants.Parse_DotNetKey);
            ParseFacebookUtils.Initialize(Constants.FacebookAppId);

            if (ParseUser.CurrentUser != null)
            {
                ParseUser.CurrentUser.FetchAsync();
            }
        }
コード例 #8
0
ファイル: ParseService.cs プロジェクト: XamarinGuru/DigiCache
        public static async Task <string> FacebookSignUp(string fbUserID, string accessToken, DateTime expiration)
        {
            try
            {
                ParseUser user = await ParseFacebookUtils.LogInAsync(fbUserID, accessToken, expiration);

                if (user == null)
                {
                    return(Constants.STR_STATUS_FAIL);
                }
                else
                {
                    return(Constants.STR_STATUS_SUCCESS);
                }
            }
            catch (Exception e)
            {
                return(e.Message);
            }
        }
コード例 #9
0
        /// <summary>
        /// Constructor for the Application object.
        /// </summary>
        public App()
        {
            // Global handler for uncaught exceptions.
            UnhandledException += Application_UnhandledException;

            // Standard XAML initialization
            InitializeComponent();

            // Phone-specific initialization
            InitializePhoneApplication();

            ThemeManager.ToLightTheme();
            //AccentColor color = new AccentColor();
            //ThemeManager.SetAccentColor(color);


            // Language display initialization
            InitializeLanguage();

            // Show graphics profiling information while debugging.
            if (Debugger.IsAttached)
            {
                // Display the current frame rate counters.
                Application.Current.Host.Settings.EnableFrameRateCounter = true;

                // Show the areas of the app that are being redrawn in each frame.
                //Application.Current.Host.Settings.EnableRedrawRegions = true;

                // Enable non-production analysis visualization mode,
                // which shows areas of a page that are handed off to GPU with a colored overlay.
                //Application.Current.Host.Settings.EnableCacheVisualization = true;

                // Prevent the screen from turning off while under the debugger by disabling
                // the application's idle detection.
                // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run
                // and consume battery power when the user is not using the phone.
                PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
            }
            ParseClient.Initialize("bthlugArbHCrcDgbuHPS5Uiz3lZbWvmU2EIjBgaV", "SPE35IPobltfR4wHXipwa8uIFZuNoiI2tY0QHpCV");
            ParseFacebookUtils.Initialize("690850497593331");
        }
コード例 #10
0
    private IEnumerator ParseLogin()
    {
        if (FB.IsLoggedIn)
        {
            // Logging in with Parse
            // print("______________________" + FB.UserId + " " + FB.AccessToken + " " + DateTime.Now);

            System.Threading.CancellationToken s = new System.Threading.CancellationToken();

            var aToken = Facebook.Unity.AccessToken.CurrentAccessToken;

            var loginTask = ParseFacebookUtils.LogInAsync(aToken.UserId,
                                                          aToken.TokenString,
                                                          DateTime.Now, s);
            while (!loginTask.IsCompleted)
            {
                yield return(null);
            }
            // Login completed, check results
            if (loginTask.IsFaulted || loginTask.IsCanceled)
            {
                // There was an error logging in to Parse
                foreach (var e in loginTask.Exception.InnerExceptions)
                {
                    ParseException parseException = (ParseException)e;
                    Debug.Log("ParseLogin: error message " + parseException.Message);
                    Debug.Log("ParseLogin: error code: " + parseException.Code);
                }
            }
            else
            {
                // Log in to Parse successful
                // Get user info
                FB.API("/me", HttpMethod.GET, FBAPICallback);
                // Display current profile info
                UpdateProfile();
            }
        }
    }
コード例 #11
0
ファイル: FBLogin.cs プロジェクト: gabrielamboss/Ulkoa
    private void AuthCallback(ILoginResult result)
    {
        if (FB.IsLoggedIn)
        {
            var accessToken = Facebook.Unity.AccessToken.CurrentAccessToken;
            GlobalVariables.facebookLogin = true;

            Debug.Log("Facebook Acess token User ID: " + accessToken.UserId);
            Debug.Log("Permissions:");
            foreach (string perm in accessToken.Permissions)
            {
                Debug.Log(perm);
            }

            Task <ParseUser> logInTask = ParseFacebookUtils.LogInAsync(accessToken.UserId, accessToken.TokenString, accessToken.ExpirationTime);

            SceneManager.LoadScene(SceneBook.MAIN_MENU_NAME);
        }
        else
        {
            Debug.Log("Login cancelled.");
        }
    }
コード例 #12
0
ファイル: ParseExampleManager.cs プロジェクト: polats/meta
    private void updateFBLinkStatus()
    {
        // no parse user found
        if (ParseUser.CurrentUser == null)
        {
            loginPanel.profileFBID.text = "no user found";
        }

        else
        {
            bool showFBLinkButton = true;

            // user is linked to FB
            if (ParseFacebookUtils.IsLinked(ParseUser.CurrentUser))
            {
                try
                {
                    showFBLinkButton            = false;
                    loginPanel.profileFBID.text = getFBIDFromParseUser();
                }
                catch (Exception e)
                {
                    // fallback to using FB
                    AddLog(e.ToString());
                    AddLog("unable to get from Parse User, will get from FB");
                    loginPanel.profileFBID.text = AccessToken.CurrentAccessToken.UserId;
                }
            }
            else
            {
                loginPanel.profileFBID.text = "FB not linked";
            }

            loginPanel.toggleFBLinkButton(showFBLinkButton);
        }
    }
コード例 #13
0
ファイル: ParseExampleManager.cs プロジェクト: polats/meta
    protected void HandleFBLoginResult(IResult result)
    {
        bool loggedInSuccessfully = false;

        if (result == null)
        {
            AddLog("Null Response from FB Login");
            loginPanel.setMode(LoginPanel.Mode.LOGGED_OUT);
            return;
        }

        // Some platforms return the empty string instead of null.
        if (!string.IsNullOrEmpty(result.Error))
        {
            AddLog("Error Response:\n" + result.Error);
        }
        else if (result.Cancelled)
        {
            AddLog("Cancelled Response:\n" + result.RawResult);
        }
        else if (!string.IsNullOrEmpty(result.RawResult))
        {
            AddLog("Success Response:\n" + result.RawResult);
            loggedInSuccessfully = true;

            AccessToken uat = AccessToken.CurrentAccessToken;
            AddLog("FB User Id: " + uat.UserId);

            GameObject loginPanelObject = loginPanel.gameObject;

            ParseFacebookUtils.LogInAsync(uat.UserId, uat.TokenString, uat.ExpirationTime).ContinueWith(t =>
            {
                // check for errors
                if (t.IsFaulted)
                {
                    Exception ex = t.Exception;
                    bufferedLog.Push(ex.ToString());
                }
                else if (t.IsCanceled)
                {
                    bufferedLog.Push("operation cancelled");
                }
                else
                {
                    bufferedLog.Push("[User " + ParseUser.CurrentUser.Username +
                                     " created] id = " + ParseUser.CurrentUser.ObjectId);

                    checkIfParseUserFbIdSaved(uat.UserId);
                }
                objectsToEnable.Push(loginPanelObject);
            });
        }
        else
        {
            AddLog("Empty Response\n");
        }

        // AddLog(result.ToString());

        if (!loggedInSuccessfully)
        {
            AddLog("FB Login unsuccessful, set panel to logged out");
            loginPanel.setMode(LoginPanel.Mode.LOGGED_OUT);
        }
    }
コード例 #14
0
        public async Task <UserModel> FacebookLogin()
        {
            var user = await ParseFacebookUtils.LogInAsync(new List <string>() { "email", "public_profile" });

            return(null);
        }
コード例 #15
0
ファイル: ParsePlayer.cs プロジェクト: polats/meta
    public void Start()
    {
        // update user parse task
        UpdateCommand.Subscribe(
            _ =>
        {
            ParseUser user = ParseUser.CurrentUser;

            String oldUsername = user.Username;
            String oldEmail    = user.Email;

            String newUsername = ProfileUsername.Value;
            String newPassword = ProfilePassword.Value;
            String newEmail    = ProfileEmail.Value;

            if (newUsername != "")
            {
                user.Username = newUsername;
            }
            if (newPassword != "")
            {
                user.Password = newPassword;
            }
            if (newEmail != "")
            {
                user.Email = newEmail;
            }

            AsyncSubject <Task> asyncSubject = new AsyncSubject <Task>();

            user.SaveAsync().ContinueWith(
                x =>
            {
                bool updateSuccessful = false;

                if (x.IsFaulted)
                {
                    LogParseError(x.Exception);
                    asyncSubject.OnError(x.Exception);
                }
                if (x.IsCompleted && !x.IsCanceled)
                {
                    if (!x.IsFaulted)
                    {
                        UserData.OnNext(ParseUser.CurrentUser);
                        updateSuccessful = true;
                    }

                    asyncSubject.OnNext(x);
                    asyncSubject.OnCompleted();
                }
                if (x.IsCanceled)
                {
                    asyncSubject.OnError(new OperationCanceledException());         // was TaskCanceledException(x))
                }

                if (!updateSuccessful)
                {
                    user.Username = oldUsername;
                    user.Email    = oldEmail;
                    asyncSubject.OnCompleted();
                }
            });

            return
            (UniRx.Observable.AsUnitObservable(asyncSubject).ObserveOnMainThread());
        });


        // login parse task
        LoginCommand.Subscribe(
            _ =>
        {
            var parseLoginStream = TaskObservableExtensions.ToObservable(
                ParseUser.LogInAsync(LoginUsername.Value, LoginPassword.Value));

            parseLoginStream.Subscribe(
                pu =>
            {
                UserData.OnNext(pu);
            },
                error =>
            {
                LogParseError(error);
            }
                );

            return
            (UniRx.Observable.AsUnitObservable(parseLoginStream).ObserveOnMainThread());
        });

        // logout task
        LogoutCommand.Subscribe(
            _ =>
        {
            AsyncSubject <Task> asyncSubject = new AsyncSubject <Task>();

            ParseUser.LogOutAsync().ContinueWith(
                x =>
            {
                if (x.IsFaulted)
                {
                    LogParseError(x.Exception);
                    asyncSubject.OnError(x.Exception);
                }
                if (x.IsCompleted && !x.IsCanceled)
                {
                    if (!x.IsFaulted)
                    {
                        UserData.OnNext(ParseUser.CurrentUser);
                    }

                    asyncSubject.OnNext(x);
                    asyncSubject.OnCompleted();
                }
                if (x.IsCanceled)
                {
                    asyncSubject.OnError(new OperationCanceledException());         // was TaskCanceledException(x))
                }
            });

            return(UniRx.Observable.AsUnitObservable(asyncSubject).ObserveOnMainThread());
        });

        // signup parse task
        SignupCommand.Subscribe(
            _ =>
        {
            ParseUser newUser = new ParseUser();

            newUser.Username = LoginUsername.Value;
            newUser.Password = LoginPassword.Value;

            AsyncSubject <Task> asyncSubject = new AsyncSubject <Task>();

            newUser.SignUpAsync().ContinueWith(
                x =>
            {
                if (x.IsFaulted)
                {
                    LogParseError(x.Exception);
                    asyncSubject.OnError(x.Exception);
                }
                if (x.IsCompleted && !x.IsCanceled)
                {
                    if (!x.IsFaulted)
                    {
                        UserData.OnNext(ParseUser.CurrentUser);
                    }

                    asyncSubject.OnNext(x);
                    asyncSubject.OnCompleted();
                }
                if (x.IsCanceled)
                {
                    asyncSubject.OnError(new OperationCanceledException());         // was TaskCanceledException(x))
                }
            });

            return(UniRx.Observable.AsUnitObservable(asyncSubject).ObserveOnMainThread());
        });

        // Facebook login task
        FBLoginCommand.Subscribe(
            _ =>
        {
            // we cascade this asyncSubject through 3 steps: fblogin, parseuserlogin, and parse fb id update
            AsyncSubject <FacebookDelegate <ILoginResult> > asyncSubject = new AsyncSubject <FacebookDelegate <ILoginResult> >();

            FacebookDelegate <ILoginResult> handleFBLoginResult =
                result =>
            {
                bool additionalOperation = false;

                if (result == null)
                {
                    Debug.Log("Null Response from FB Login");
                }
                // Some platforms return the empty string instead of null.
                if (!string.IsNullOrEmpty(result.Error))
                {
                    Debug.Log("Error Response:\n" + result.Error);
                }

                else if (result.Cancelled)
                {
                    Debug.Log("Cancelled Response:\n" + result.RawResult);
                }
                else if (!string.IsNullOrEmpty(result.RawResult))
                {
                    additionalOperation = true;
                    Debug.Log("Success Response:\n" + result.RawResult);

                    AccessToken uat = AccessToken.CurrentAccessToken;
                    Debug.Log("FB User Id: " + uat.UserId);

                    var parseLoginStream = TaskObservableExtensions.ToObservable(
                        ParseFacebookUtils.LogInAsync(uat.UserId, uat.TokenString, uat.ExpirationTime));

                    parseLoginStream.Subscribe(
                        pu =>
                    {
                        // if the user doesn't have an fbId field, we do another parse update call to add fbId field
                        if (!pu.ContainsKey(KEY_FBID))
                        {
                            Debug.Log("no fbId found: updating user");
                            pu["fbId"] = uat.UserId;
                            pu.SaveAsync().ContinueWith(t =>
                            {
                                if (t.IsFaulted)
                                {
                                    Exception ex = t.Exception;
                                    LogParseError(ex);
                                    asyncSubject.OnError(ex);
                                }
                                else if (t.IsCanceled)
                                {
                                    Debug.Log("user update cancelled");
                                }
                                else
                                {
                                    Debug.Log("[User " + pu.Username + " updated] id = " + pu.ObjectId);
                                    UserData.OnNext(pu);
                                }

                                asyncSubject.OnCompleted();
                            });
                        }
                        else
                        {
                            UserData.OnNext(pu);
                            asyncSubject.OnCompleted();
                        }
                    },
                        error =>
                    {
                        LogParseError(error);
                        asyncSubject.OnError(error);
                    }
                        );
                }
                else
                {
                    Debug.Log("Empty Response\n");
                }

                if (!additionalOperation)
                {
                    asyncSubject.OnCompleted();
                }
            };


            FB.LogInWithReadPermissions(new List <string>()
            {
                "public_profile", "email", "user_friends"
            }, handleFBLoginResult);

            return(UniRx.Observable.AsUnitObservable(asyncSubject).ObserveOnMainThread());
        });


        // facebook link
        FBLinkCommand.Subscribe(
            _ =>
        {
            ParseUser user = ParseUser.CurrentUser;

            // user is already linked to FB
            if (ParseFacebookUtils.IsLinked(user))
            {
                AsyncSubject <Task> asyncSubject = new AsyncSubject <Task>();

                ParseFacebookUtils.UnlinkAsync(user).ContinueWith(t =>
                {
                    // check for errors
                    if (t.IsFaulted)
                    {
                        LogParseError(t.Exception);
                        asyncSubject.OnError(t.Exception);
                    }
                    else if (t.IsCanceled)
                    {
                        Debug.Log("operation cancelled");
                    }
                    else
                    {
                        Debug.Log("[User " + user.Username + " FB unlinked]");
                        user.Remove("fbId");

                        // user unlinked, now update parse user
                        user.SaveAsync().ContinueWith(
                            x =>
                        {
                            if (x.IsFaulted)
                            {
                                LogParseError(x.Exception);
                                asyncSubject.OnError(x.Exception);
                            }
                            if (x.IsCompleted && !x.IsCanceled)
                            {
                                if (!x.IsFaulted)
                                {
                                    UserData.OnNext(ParseUser.CurrentUser);
                                }

                                asyncSubject.OnNext(x);
                                asyncSubject.OnCompleted();
                            }
                            if (x.IsCanceled)
                            {
                                asyncSubject.OnError(new OperationCanceledException());             // was TaskCanceledException(x))
                            }
                        });
                    }
                });

                return
                (UniRx.Observable.AsUnitObservable(asyncSubject).ObserveOnMainThread());
            }

            // user has not linked yet
            else
            {
                // we cascade this asyncSubject through 3 steps: fblogin, parseuserlink, and parse fb id update
                AsyncSubject <FacebookDelegate <ILoginResult> > asyncSubject = new AsyncSubject <FacebookDelegate <ILoginResult> >();

                FacebookDelegate <ILoginResult> handleFBLoginResult =
                    result =>
                {
                    bool additionalOperation = false;

                    if (result == null)
                    {
                        Debug.Log("Null Response from FB Login");
                    }
                    // Some platforms return the empty string instead of null.
                    if (!string.IsNullOrEmpty(result.Error))
                    {
                        Debug.Log("Error Response:\n" + result.Error);
                    }

                    else if (result.Cancelled)
                    {
                        Debug.Log("Cancelled Response:\n" + result.RawResult);
                    }
                    else if (!string.IsNullOrEmpty(result.RawResult))
                    {
                        additionalOperation = true;
                        Debug.Log("Success Response:\n" + result.RawResult);

                        AccessToken uat = AccessToken.CurrentAccessToken;
                        Debug.Log("FB User Id: " + uat.UserId);

                        ParseFacebookUtils.LinkAsync(user, uat.UserId, uat.TokenString, uat.ExpirationTime)
                        .ContinueWith(
                            t =>
                        {
                            if (t.IsFaulted)
                            {
                                Exception ex = t.Exception;
                                LogParseError(ex);
                                asyncSubject.OnError(ex);
                            }
                            else if (t.IsCanceled)
                            {
                                Debug.Log("user update cancelled");
                            }
                            else
                            {
                                // link success, now we add the fb field to user
                                user["fbId"] = uat.UserId;
                                user.SaveAsync().ContinueWith(
                                    t2 =>
                                {
                                    if (t2.IsFaulted)
                                    {
                                        Exception ex = t2.Exception;
                                        LogParseError(ex);
                                        asyncSubject.OnError(ex);
                                    }
                                    else if (t2.IsCanceled)
                                    {
                                        Debug.Log("user update cancelled");
                                    }
                                    else
                                    {
                                        Debug.Log("[User " + user.Username + " updated] id = " + user.ObjectId);
                                        UserData.OnNext(user);
                                    }

                                    asyncSubject.OnCompleted();
                                });
                            }
                        }
                            );
                    }
                    else
                    {
                        Debug.Log("Empty Response\n");
                    }

                    if (!additionalOperation)
                    {
                        asyncSubject.OnCompleted();
                    }
                };


                FB.LogInWithReadPermissions(new List <string>()
                {
                    "public_profile", "email", "user_friends"
                }, handleFBLoginResult);

                return
                (UniRx.Observable.AsUnitObservable(asyncSubject).ObserveOnMainThread());
            }
        });

        // check if user logged in
        UserData.OnNext(ParseUser.CurrentUser);
    }