Skip to content
Snippets Groups Projects
app.dart 6.19 KiB
Newer Older
igorwork's avatar
igorwork committed
// app.dart
Markin Igor's avatar
Markin Igor committed
import '../config.dart';
import 'error-alert.dart';
Markin Igor's avatar
Markin Igor committed
import 'screens/splashscreen.dart';
igorwork's avatar
igorwork committed
import 'package:flutter/material.dart';
import 'screens/home.dart';
import 'package:flutter/services.dart';
import 'dart:async';
import 'dart:developer';
Markin Igor's avatar
Markin Igor committed
import 'package:shared_preferences/shared_preferences.dart';
Markin Igor's avatar
Markin Igor committed
import 'package:flutter_web_browser/flutter_web_browser.dart';
import 'dart:convert';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_app_auth_wrapper/flutter_app_auth_wrapper.dart';
import 'dart:io';

import 'package:uni_links/uni_links.dart';

enum AppMode {
  Default,
  Authorization,
}

enum Screen {
  App,
  OAuth,
  Dashboard
}

Markin Igor's avatar
Markin Igor committed
class App extends StatelessWidget {
Markin Igor's avatar
Markin Igor committed
  Future<Widget> initApplication() async {
    Uri initialUri;

    try {
      initialUri = await getInitialUri();
    } on PlatformException {
      initialUri = null;
    } on FormatException {
      initialUri = null;
    }

Markin Igor's avatar
Markin Igor committed
    final prefs = await SharedPreferences.getInstance();
    final host = prefs.getString('host');

    return new MainApp(initialUri: initialUri, initialHost: host);
  @override
Markin Igor's avatar
Markin Igor committed
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Vereign',
        theme: ThemeData(
            primaryColor: Color(0xFFd51d32),
            fontFamily: "Arial",
            textTheme: TextTheme(
                button: TextStyle(color: Colors.white, fontSize: 18.0),
                title: TextStyle(color: Colors.red))),
        home: new SplashScreen(
Markin Igor's avatar
Markin Igor committed
          navigateAfterFuture: initApplication,
Markin Igor's avatar
Markin Igor committed
        )
    );
  }
}

class MainApp extends StatefulWidget {
Markin Igor's avatar
Markin Igor committed
  MainApp({ @required this.initialUri, this.initialHost });
Markin Igor's avatar
Markin Igor committed
  final Uri initialUri;
Markin Igor's avatar
Markin Igor committed
  final String initialHost;
Markin Igor's avatar
Markin Igor committed
  @override
  _MainAppState createState() => _MainAppState();
Markin Igor's avatar
Markin Igor committed
class _MainAppState extends State<MainApp> {
  StreamSubscription _sub;

  /// "Default" when you open app, "Authorization" when app initiated
  /// from third party app
  AppMode _appMode;
  // Url of the app which invoked OAuth
  String _invokerURL;

Markin Igor's avatar
Markin Igor committed
  String _host = Config.appFlavor == Flavor.DEVELOPMENT ? Config.HOSTS[0] : Config.DEFAULT_APP_HOST;

  Screen _currentScreen = Screen.App;

Markin Igor's avatar
Markin Igor committed
  bool _buttonsHidden = true;

  @override
  initState() {
    super.initState();
Markin Igor's avatar
Markin Igor committed

    // Set up app host
    if (widget.initialHost != null) {
      setState(() {
        _host = widget.initialHost;
      });
    }

    // Subscribe to authorization events
    FlutterAppAuthWrapper.eventStream().listen((data) async {
      var token = json.decode(data.toString())["access_token"];
      setScreen(Screen.App);

      try {
        await launch("$_invokerURL?token=$token&host=$_host");
      } catch (e) {
Markin Igor's avatar
Markin Igor committed
        showErrorAlert(context, 'Unable to open URL $_invokerURL', openVereign);
Markin Igor's avatar
Markin Igor committed
      revealButtons(1);
Markin Igor's avatar
Markin Igor committed
    }, onError: (error) {
      log("Err $error");

      String errorMessage = error.message;
      String errorDetails  = "";

      try {
        errorDetails = json.decode(error.details)["errorDescription"];
      } catch (e) {
        errorDetails = error.details;
      }
Markin Igor's avatar
Markin Igor committed

      if (
        // Handle cancellation for Android
        errorDetails == 'User cancelled flow' ||
Markin Igor's avatar
Markin Igor committed
        // Handle cancellation for iOS
        errorMessage.toLowerCase().contains("the operation couldn")
      ) {
        if (Platform.isAndroid) {
Markin Igor's avatar
Markin Igor committed
          // Reveal after three seconds
          revealButtons(3);

          // Open only for android, because iOS will show the same alert as
Markin Igor's avatar
Markin Igor committed
          // was for Auth request
          openVereign();
        } else {
          setScreen(Screen.App);
Markin Igor's avatar
Markin Igor committed
          revealButtons(0);
Markin Igor's avatar
Markin Igor committed
        }
      } else {
        showErrorAlert(context, Platform.isAndroid ? errorDetails : errorMessage, openVereign);
        setScreen(Screen.App);
Markin Igor's avatar
Markin Igor committed
        revealButtons(0);
Markin Igor's avatar
Markin Igor committed
    // Show buttons after timeout
    revealButtons(3);


    initUniLinks();
  }

  @override
  dispose() {
    if (_sub != null) _sub.cancel();
    super.dispose();
  }

  Future<Null> initUniLinks() async {
Markin Igor's avatar
Markin Igor committed
    handleLinkChange(widget.initialUri);

    _sub = getUriLinksStream().listen((Uri uri) {
Markin Igor's avatar
Markin Igor committed
      handleLinkChange(uri);
    }, onError: (err) {
      log('got err: $err');
    });
  }

  hideButtons() {
    if (_revealTimer != null && _revealTimer.isActive) {
      _revealTimer.cancel();
    }


    setState(() {
      _buttonsHidden = true;
    });
  }

  Timer _revealTimer;
Markin Igor's avatar
Markin Igor committed
  revealButtons(delay) {
    _revealTimer = Timer(
Markin Igor's avatar
Markin Igor committed
        Duration(seconds: delay),
            () {
          setState(() {
            _buttonsHidden = false;
          });
        }
    );
  }

Markin Igor's avatar
Markin Igor committed
  handleLinkChange(Uri uri) {
igorwork's avatar
igorwork committed
    if (uri?.path == "/authorize") {
      setState(() {
igorwork's avatar
igorwork committed
        _invokerURL = uri.queryParameters["invokerUrl"];
Markin Igor's avatar
Markin Igor committed

      startOAuth();
    } else {
      setState(() {
Markin Igor's avatar
Markin Igor committed

      openVereign();
Markin Igor's avatar
Markin Igor committed
  setScreen(Screen screen) {
    setState(() {
      _currentScreen = screen;
    });
  }

  openVereign() {
    setScreen(Screen.Dashboard);

    FlutterWebBrowser.openWebPage(url: _host, androidToolbarColor: Color(0xFFd51d32));
  }

  startOAuth() {
    if (_currentScreen == Screen.OAuth) {
      return;
    }

Markin Igor's avatar
Markin Igor committed
    // Hide buttons so they wont blink after we close or finish oauth
    hideButtons();
Markin Igor's avatar
Markin Igor committed

Markin Igor's avatar
Markin Igor committed
    setScreen(Screen.OAuth);

    var params = Config.getOAuthParams(host: _host);

    FlutterAppAuthWrapper.startAuth(
      AuthConfig(
        clientId: params["clientId"],
        clientSecret: params["clientSecret"],
        redirectUrl: params["redirectUrl"],
        state: "login",
        prompt: "consent",
        endpoint: AuthEndpoint(
            auth: params["authEndpoint"], token: params["tokenEndpoint"]),
        scopes: [
          "user_account_status",
          "user_territory",
          "user_profile"
        ],
      ),
    );
  }

  setHost(String host) {
    setState(() {
      _host = host;
    });
  }

igorwork's avatar
igorwork committed
  @override
  Widget build(BuildContext context) {
Markin Igor's avatar
Markin Igor committed
    return new Scaffold(
      appBar: new AppBar(title: Text("Vereign")),
      body: Home(
        mode: _appMode,
Markin Igor's avatar
Markin Igor committed
        host: _host,
        setHost: setHost,
        openDashboardClick: openVereign,
        authorizeClick: startOAuth,
Markin Igor's avatar
Markin Igor committed
        buttonsHidden: _buttonsHidden
Markin Igor's avatar
Markin Igor committed
      )
igorwork's avatar
igorwork committed
    );
  }
}