web client support! (still buggy)
This commit is contained in:
parent
39accd5a6c
commit
c029461303
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:loris/business_logic/account/account.dart';
|
import 'package:loris/business_logic/account/account.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
@ -38,6 +39,10 @@ String _authCode = "";
|
||||||
String _url = "";
|
String _url = "";
|
||||||
App? _app;
|
App? _app;
|
||||||
|
|
||||||
|
App? getApp() {
|
||||||
|
return _app;
|
||||||
|
}
|
||||||
|
|
||||||
Response readAuthcode(Request request) {
|
Response readAuthcode(Request request) {
|
||||||
Map<String, String> params = request.url.queryParameters;
|
Map<String, String> params = request.url.queryParameters;
|
||||||
if (params.containsKey("code") && params["code"] != null) {
|
if (params.containsKey("code") && params["code"] != null) {
|
||||||
|
@ -72,20 +77,27 @@ Future<int> handleFullOauth(String url) async {
|
||||||
return response.statusCode;
|
return response.statusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
_authCode = "";
|
if (!kIsWeb) {
|
||||||
var handler = const Pipeline().addHandler(readAuthcode);
|
// if this is not a web app we can simply start a server
|
||||||
var server = await shelf_io.serve(handler, 'localhost', 1312);
|
// and listen for a code there
|
||||||
await pollCode();
|
_authCode = "";
|
||||||
server.close();
|
var handler = const Pipeline().addHandler(readAuthcode);
|
||||||
String token =
|
var server = await shelf_io.serve(handler, 'localhost', 1312);
|
||||||
await getToken(_authCode, _app!.clientId, _app!.clientSecret, _url);
|
await pollCode();
|
||||||
await saveIdentity(
|
server.close();
|
||||||
token,
|
String token =
|
||||||
url,
|
await getToken(_authCode, _app!.clientId, _app!.clientSecret, _url);
|
||||||
_app!,
|
await saveIdentity(
|
||||||
_authCode,
|
token,
|
||||||
);
|
url,
|
||||||
return 200;
|
_app!.clientId,
|
||||||
|
_app!.clientSecret,
|
||||||
|
_authCode,
|
||||||
|
);
|
||||||
|
return 200;
|
||||||
|
} else {
|
||||||
|
return 6969696969696969;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return 400;
|
return 400;
|
||||||
}
|
}
|
||||||
|
@ -94,7 +106,8 @@ Future<int> handleFullOauth(String url) async {
|
||||||
Future<bool> saveIdentity(
|
Future<bool> saveIdentity(
|
||||||
String token,
|
String token,
|
||||||
String baseurl,
|
String baseurl,
|
||||||
App app,
|
String clientId,
|
||||||
|
String clientSecret,
|
||||||
String authCode,
|
String authCode,
|
||||||
) async {
|
) async {
|
||||||
Map<String, String> headers = {"Authorization": "Bearer $token"};
|
Map<String, String> headers = {"Authorization": "Bearer $token"};
|
||||||
|
@ -111,7 +124,8 @@ Future<bool> saveIdentity(
|
||||||
final account = AccountModel.fromJson(jsonDecode(response.body));
|
final account = AccountModel.fromJson(jsonDecode(response.body));
|
||||||
await global.settings!.addNewIdentity("${account.acct}@$baseurl");
|
await global.settings!.addNewIdentity("${account.acct}@$baseurl");
|
||||||
await global.settings!.saveActiveIdentity("${account.acct}@$baseurl");
|
await global.settings!.saveActiveIdentity("${account.acct}@$baseurl");
|
||||||
await global.settings!.identities["${account.acct}@$baseurl"]!.saveApp(app);
|
await global.settings!.identities["${account.acct}@$baseurl"]!
|
||||||
|
.saveApp(clientId, clientSecret);
|
||||||
await global.settings!.identities["${account.acct}@$baseurl"]!
|
await global.settings!.identities["${account.acct}@$baseurl"]!
|
||||||
.saveAuthCode(authCode);
|
.saveAuthCode(authCode);
|
||||||
await global.settings!.identities["${account.acct}@$baseurl"]!
|
await global.settings!.identities["${account.acct}@$baseurl"]!
|
||||||
|
@ -141,7 +155,7 @@ Future<http.Response> doOauthFlow() async {
|
||||||
String url = _url;
|
String url = _url;
|
||||||
try {
|
try {
|
||||||
http.Response response = await registerApp(url);
|
http.Response response = await registerApp(url);
|
||||||
openBrowserForAuthCode(url, App.fromJson(jsonDecode(response.body)));
|
openBrowserForAuthCode(url, _app!);
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return http.Response(jsonEncode({}), 404);
|
return http.Response(jsonEncode({}), 404);
|
||||||
|
@ -151,19 +165,36 @@ Future<http.Response> doOauthFlow() async {
|
||||||
Future<http.Response> registerApp(String baseurl) async {
|
Future<http.Response> registerApp(String baseurl) async {
|
||||||
//String url = baseurl Uri."api/v1/apps";
|
//String url = baseurl Uri."api/v1/apps";
|
||||||
Uri url = Uri.https(baseurl, "/api/v1/apps");
|
Uri url = Uri.https(baseurl, "/api/v1/apps");
|
||||||
|
final Map<String, String> params = {
|
||||||
|
'client_name': global.name,
|
||||||
|
'redirect_uris': kIsWeb
|
||||||
|
? "${Uri.base.origin}/login/redirect.html"
|
||||||
|
: "http://localhost:1312",
|
||||||
|
'scopes': "read write follow push",
|
||||||
|
'website': global.website,
|
||||||
|
};
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
url,
|
url,
|
||||||
headers: global.defaultHeaders,
|
headers: global.defaultHeaders,
|
||||||
body: jsonEncode({
|
body: jsonEncode(params),
|
||||||
'client_name': global.name,
|
|
||||||
'redirect_uris': "http://localhost:1312",
|
|
||||||
'scopes': "read write follow push",
|
|
||||||
'website': global.website
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
print(response.body);
|
||||||
|
_app = App.fromJson(jsonDecode(response.body));
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Uri getAuthUrl(String baseurl, App app) {
|
||||||
|
return Uri(
|
||||||
|
scheme: "https",
|
||||||
|
path: "$baseurl/oauth/authorize",
|
||||||
|
// ignore: prefer_interpolation_to_compose_strings
|
||||||
|
query: "client_id=" +
|
||||||
|
app.clientId +
|
||||||
|
"&scope=read+write+follow+push" +
|
||||||
|
"&redirect_uri=${kIsWeb ? "${Uri.base.origin}/login/redirect.html" : "http://localhost:1312"}" +
|
||||||
|
"&response_type=code");
|
||||||
|
}
|
||||||
|
|
||||||
void openBrowserForAuthCode(String baseurl, App app) {
|
void openBrowserForAuthCode(String baseurl, App app) {
|
||||||
Uri url = Uri(
|
Uri url = Uri(
|
||||||
scheme: "https",
|
scheme: "https",
|
||||||
|
@ -172,9 +203,14 @@ void openBrowserForAuthCode(String baseurl, App app) {
|
||||||
query: "client_id=" +
|
query: "client_id=" +
|
||||||
app.clientId +
|
app.clientId +
|
||||||
"&scope=read+write+follow+push" +
|
"&scope=read+write+follow+push" +
|
||||||
"&redirect_uri=http://localhost:1312" +
|
"&redirect_uri=${kIsWeb ? "${Uri.base.origin}/login/redirect.html" : "http://localhost:1312"}" +
|
||||||
"&response_type=code");
|
"&response_type=code");
|
||||||
launchUrl(url);
|
launchUrl(
|
||||||
|
mode: LaunchMode.inAppWebView,
|
||||||
|
url,
|
||||||
|
webOnlyWindowName: "loris",
|
||||||
|
webViewConfiguration: const WebViewConfiguration(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getToken(
|
Future<String> getToken(
|
||||||
|
@ -184,21 +220,27 @@ Future<String> getToken(
|
||||||
String baseurl,
|
String baseurl,
|
||||||
) async {
|
) async {
|
||||||
Uri url = Uri.https(baseurl, "/oauth/token");
|
Uri url = Uri.https(baseurl, "/oauth/token");
|
||||||
|
final args = jsonEncode({
|
||||||
|
'grant_type': "authorization_code",
|
||||||
|
'client_id': appId,
|
||||||
|
'client_secret': clientSecret,
|
||||||
|
'redirect_uri': kIsWeb
|
||||||
|
? "${Uri.base.origin}/login/redirect.html"
|
||||||
|
: "http://localhost:1312",
|
||||||
|
'scope': "read write follow push",
|
||||||
|
'code': authCode,
|
||||||
|
});
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
url,
|
url,
|
||||||
headers: global.defaultHeaders,
|
headers: global.defaultHeaders,
|
||||||
body: jsonEncode({
|
body: args,
|
||||||
'grant_type': "authorization_code",
|
|
||||||
'client_id': appId,
|
|
||||||
'client_secret': clientSecret,
|
|
||||||
'redirect_uri': "http://localhost:1312",
|
|
||||||
'scope': "read write follow push",
|
|
||||||
'code': authCode,
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
print(args);
|
||||||
|
print(response.body);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final dec = jsonDecode(response.body);
|
final dec = jsonDecode(response.body);
|
||||||
return dec["access_token"]!;
|
return dec["access_token"]!;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import 'package:flutter/painting.dart';
|
import 'package:flutter/painting.dart';
|
||||||
import 'package:loris/themes/themes.dart';
|
import 'package:loris/themes/themes.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import '../business_logic/auth/oauth.dart' as oauth;
|
|
||||||
import './websocket.dart' as websocket;
|
import './websocket.dart' as websocket;
|
||||||
import 'package:loris/themes/themes.dart' as themes;
|
import 'package:loris/themes/themes.dart' as themes;
|
||||||
|
|
||||||
|
@ -72,11 +71,11 @@ class AccountSettings {
|
||||||
return await prefs.setString("$identity.$authCodeKey", code);
|
return await prefs.setString("$identity.$authCodeKey", code);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveApp(oauth.App app) async {
|
Future<void> saveApp(String clientSecret, String clientId) async {
|
||||||
clientId = app.clientId;
|
this.clientId = clientId;
|
||||||
clientSecret = app.clientSecret;
|
this.clientSecret = clientSecret;
|
||||||
await prefs.setString("$identity.$clientSecretKey", app.clientSecret);
|
await prefs.setString("$identity.$clientSecretKey", clientSecret);
|
||||||
await prefs.setString("$identity.$clientIdKey", app.clientId);
|
await prefs.setString("$identity.$clientIdKey", clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> saveToken(String token) async {
|
Future<bool> saveToken(String token) async {
|
||||||
|
@ -86,6 +85,10 @@ class AccountSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Settings {
|
class Settings {
|
||||||
|
static const currentClientSecretKey = "currentsecret";
|
||||||
|
late String currentClientSecret;
|
||||||
|
static const currentClientIdkey = "currentclientid";
|
||||||
|
late String currentClientId;
|
||||||
late double postWidth = 0.8;
|
late double postWidth = 0.8;
|
||||||
static const postWidthKey = "post-width";
|
static const postWidthKey = "post-width";
|
||||||
late double maxPostWidth = 1000;
|
late double maxPostWidth = 1000;
|
||||||
|
@ -108,6 +111,11 @@ class Settings {
|
||||||
Settings settings = Settings._create();
|
Settings settings = Settings._create();
|
||||||
|
|
||||||
settings.prefs = await SharedPreferences.getInstance();
|
settings.prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
settings.currentClientSecret =
|
||||||
|
settings.prefs.getString(currentClientSecretKey) ?? "";
|
||||||
|
settings.currentClientId =
|
||||||
|
settings.prefs.getString(currentClientIdkey) ?? "";
|
||||||
settings.locale = Locale(settings.prefs.getString(localeKey) ?? "en");
|
settings.locale = Locale(settings.prefs.getString(localeKey) ?? "en");
|
||||||
|
|
||||||
settings.batchSize = settings.prefs.getInt(batchSizeKey) ?? 20;
|
settings.batchSize = settings.prefs.getInt(batchSizeKey) ?? 20;
|
||||||
|
@ -152,6 +160,16 @@ class Settings {
|
||||||
return prefs.setDouble(maxPostWidthKey, width);
|
return prefs.setDouble(maxPostWidthKey, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> saveCurrentClientId(String id) async {
|
||||||
|
currentClientId = id;
|
||||||
|
return prefs.setString(currentClientIdkey, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> saveCurrentClientSecret(String secret) async {
|
||||||
|
currentClientId = secret;
|
||||||
|
return prefs.setString(currentClientSecretKey, secret);
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> savePostWidth(double width) {
|
Future<bool> savePostWidth(double width) {
|
||||||
postWidth = width;
|
postWidth = width;
|
||||||
return prefs.setDouble(postWidthKey, width);
|
return prefs.setDouble(postWidthKey, width);
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
"make-post": "make post",
|
"make-post": "make post",
|
||||||
"content-warning": "content warning",
|
"content-warning": "content warning",
|
||||||
"theme-title": "theme",
|
"theme-title": "theme",
|
||||||
"send-post": "computer, send post"
|
"send-post": "computer, send post",
|
||||||
|
"jacking-in": "jacking in..."
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,13 @@ import 'business_logic/settings.dart' as settings;
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'themes/themes.dart' as themes;
|
import 'themes/themes.dart' as themes;
|
||||||
import 'global.dart' as global;
|
import 'global.dart' as global;
|
||||||
|
import 'package:url_strategy/url_strategy.dart';
|
||||||
|
|
||||||
ThemeData theme = themes.getTheme(themes.available[1]);
|
ThemeData theme = themes.getTheme(themes.available[1]);
|
||||||
Locale activeLocale = const Locale("en_US");
|
Locale activeLocale = const Locale("en_US");
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
|
setPathUrlStrategy();
|
||||||
Intl.defaultLocale = "en_US";
|
Intl.defaultLocale = "en_US";
|
||||||
global.settings = await settings.Settings.create();
|
global.settings = await settings.Settings.create();
|
||||||
activeLocale = global.settings!.locale;
|
activeLocale = global.settings!.locale;
|
||||||
|
@ -58,10 +60,29 @@ class _LorisState extends State<Loris> {
|
||||||
LocalJsonLocalization.delegate,
|
LocalJsonLocalization.delegate,
|
||||||
],
|
],
|
||||||
initialRoute: global.settings!.identities.isEmpty ? "/login" : "/",
|
initialRoute: global.settings!.identities.isEmpty ? "/login" : "/",
|
||||||
routes: {
|
onGenerateRoute: (s) => RouterGenerator.generateRoute(s),
|
||||||
'/': (context) => const MainScaffold(),
|
|
||||||
'/login': (context) => const Login(),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RouterGenerator {
|
||||||
|
static Route<dynamic> generateRoute(RouteSettings s) {
|
||||||
|
var routingData = s.name;
|
||||||
|
switch (routingData) {
|
||||||
|
case "/login":
|
||||||
|
return MaterialPageRoute(
|
||||||
|
builder: (context) {
|
||||||
|
return const Login();
|
||||||
|
},
|
||||||
|
settings: s,
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return MaterialPageRoute(
|
||||||
|
builder: (context) {
|
||||||
|
return const MainScaffold();
|
||||||
|
},
|
||||||
|
settings: s,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:localization/localization.dart';
|
import 'package:localization/localization.dart';
|
||||||
|
import 'package:loris/pages/login/weblogin.dart';
|
||||||
import '../business_logic/auth/oauth.dart' as oauth;
|
import '../business_logic/auth/oauth.dart' as oauth;
|
||||||
|
|
||||||
class Login extends StatefulWidget {
|
class Login extends StatefulWidget {
|
||||||
|
@ -67,11 +69,20 @@ class _LoginFormState extends State<LoginForm> {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
formKey.currentState?.save();
|
formKey.currentState?.save();
|
||||||
Navigator.push(
|
if (kIsWeb) {
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => Weblogin(url: instanceUrl),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
Navigator.pushReplacement(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => AuthPage(url: instanceUrl),
|
builder: (context) => AuthPage(url: instanceUrl),
|
||||||
));
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.login),
|
icon: const Icon(Icons.login),
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:localization/localization.dart';
|
||||||
|
import 'package:universal_html/html.dart' as html;
|
||||||
|
import 'package:loris/business_logic/auth/oauth.dart' as oauth;
|
||||||
|
|
||||||
|
class Weblogin extends StatefulWidget {
|
||||||
|
const Weblogin({Key? key, required this.url}) : super(key: key);
|
||||||
|
final String url;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<Weblogin> createState() => _WebloginState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WebloginState extends State<Weblogin> {
|
||||||
|
bool doneLoading = false;
|
||||||
|
void doAuth() async {
|
||||||
|
final appresponse = await oauth.registerApp(widget.url);
|
||||||
|
if (appresponse.statusCode != 200) {
|
||||||
|
informAboutFailure(appresponse.statusCode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final app = oauth.App.fromJson(jsonDecode(appresponse.body));
|
||||||
|
|
||||||
|
var popupWin = html.window.open(
|
||||||
|
oauth.getAuthUrl(widget.url, app).toString(),
|
||||||
|
"loris",
|
||||||
|
);
|
||||||
|
|
||||||
|
html.window.onMessage.listen((event) async {
|
||||||
|
final uri = Uri.parse(event.data);
|
||||||
|
if (uri.queryParameters.containsKey("code")) {
|
||||||
|
popupWin.close();
|
||||||
|
final token = await oauth.getToken(
|
||||||
|
uri.queryParameters["code"]!,
|
||||||
|
app.clientId,
|
||||||
|
app.clientSecret,
|
||||||
|
widget.url,
|
||||||
|
);
|
||||||
|
await oauth.saveIdentity(token, widget.url, app.clientId,
|
||||||
|
app.clientSecret, uri.queryParameters["code"]!);
|
||||||
|
setState(() {
|
||||||
|
doneLoading = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void informAboutFailure(int i) {
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
.showSnackBar(SnackBar(content: Text("error: $i")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
doAuth();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
SelectableText(widget.url),
|
||||||
|
doneLoading
|
||||||
|
? TextButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pushReplacementNamed("/");
|
||||||
|
},
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.navigate_next,
|
||||||
|
),
|
||||||
|
label: Text(
|
||||||
|
"jack-in".i18n(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const LoadingIndicator(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoadingIndicator extends StatelessWidget {
|
||||||
|
const LoadingIndicator({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Wrap(
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
spacing: 24,
|
||||||
|
children: [
|
||||||
|
const CircularProgressIndicator(),
|
||||||
|
SelectableText("jacking-in".i18n()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
30
pubspec.lock
30
pubspec.lock
|
@ -266,6 +266,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.4"
|
version: "4.2.4"
|
||||||
|
routemaster:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: routemaster
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -309,7 +316,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
shared_preferences_web:
|
shared_preferences_web:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_web
|
name: shared_preferences_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
|
@ -383,6 +390,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
|
universal_html:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: universal_html
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.8"
|
||||||
|
universal_io:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: universal_io
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.4"
|
||||||
url_launcher:
|
url_launcher:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -439,6 +460,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.1"
|
||||||
|
url_strategy:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: url_strategy
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -48,6 +48,10 @@ dependencies:
|
||||||
flutter_markdown: ^0.6.10+5
|
flutter_markdown: ^0.6.10+5
|
||||||
markdown: ^5.0.0
|
markdown: ^5.0.0
|
||||||
html2md: ^1.2.6
|
html2md: ^1.2.6
|
||||||
|
routemaster: ^1.0.1
|
||||||
|
url_strategy: ^0.2.0
|
||||||
|
shared_preferences_web: ^2.0.4
|
||||||
|
universal_html: ^2.0.8
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Connexion Succeeded</title>
|
||||||
|
<meta name="description"
|
||||||
|
content="Simple, quick, standalone responsive placeholder without any additional resources">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
window.opener.postMessage(window.location.href, '*');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
Loading…
Reference in New Issue