add loginpage, load videos, load preview

This commit is contained in:
lukas committed 2021-12-11 17:17:38 +01:00
1 parent d1f6f02fcc
commit af5db6242e
11 files changed
+293 -50

No files matched your search

+1
View File
@@ -1,5 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="eu.heili.openmediacentermobile">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="openmediacentermobile"
android:name="${applicationName}"
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+31
View File
@@ -0,0 +1,31 @@
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:openmediacentermobile/api/token.dart';
import 'package:openmediacentermobile/log/log.dart';
class API {
static Future<String> query(
String apinode, String action, Object payload) async {
final Completer<String> cmpl = Completer();
final t = await Token.getInstance().getToken();
Log.d(t);
if (t != null) {
final resp = await http.post(
Uri.parse(t.domain + '/api/$apinode/$action'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
'Token': t.token
},
body: jsonEncode(payload),
);
cmpl.complete(resp.body);
} else {
cmpl.complete("");
}
return cmpl.future;
}
}
+22 -15
View File
@@ -5,43 +5,50 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import '../log/log.dart';
class TokenT {
String token;
String domain;
TokenT(this.token, this.domain);
}
class Token {
static final Token _token = Token._();
final _storage = const FlutterSecureStorage();
String _tokenval = "";
String _domain = "";
static Token getInstance() {
return _token;
}
Future<String?> _readToken() async {
Log.d("reading token store");
WidgetsFlutterBinding.ensureInitialized();
return _storage.read(key: 'jwt');
}
Future<TokenT?> getToken() async {
var completer = Completer<TokenT?>();
Future<String> getToken() async {
var completer = Completer<String>();
if (_tokenval == "") {
final token = await _readToken();
if (_tokenval == "" || _domain == "") {
Log.d("reading token store");
WidgetsFlutterBinding.ensureInitialized();
final token = await _storage.read(key: 'jwt');
final domain = await _storage.read(key: 'domain');
// check if value is defined in phone store
if (token != null) {
completer.complete(token);
if (token != null && domain != null) {
completer.complete(TokenT(token, domain));
} else {
Log.d("no token defined");
completer.complete("");
completer.complete(null);
}
} else {
completer.complete(_tokenval);
completer.complete(TokenT(_tokenval, _domain));
}
return completer.future;
}
void setToken(String token) {
void setToken(String token, String domain) {
_tokenval = token;
_domain = domain;
_storage.write(key: 'jwt', value: token);
_storage.write(key: 'domain', value: domain);
}
Token._();
+5 -8
View File
@@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:logger/logger.dart';
import 'package:openmediacentermobile/log/log.dart';
import 'package:openmediacentermobile/login/login_screen.dart';
import 'api/token.dart';
import 'login/logincontext.dart';
import 'video_feed.dart';
@@ -18,15 +19,10 @@ class App extends StatelessWidget {
Widget build(BuildContext context) {
var loginctx = LoginContext.of(context);
Logger().d("We are logged in: ${loginctx.LoggedIn}");
Log.d("We are logged in: ${loginctx.LoggedIn}");
if (!loginctx.LoggedIn) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text("Login"),
),
body: LoginScreen()));
return const MaterialApp(home: LoginScreen());
} else {
return MaterialApp(
home: Scaffold(
@@ -37,6 +33,7 @@ class App extends StatelessWidget {
ElevatedButton(
onPressed: () {
loginctx.onLoggin(false);
Token.getInstance().setToken("", "");
},
child: Text("logout"))
],
+149 -10
View File
@@ -1,23 +1,162 @@
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'logincontext.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:openmediacentermobile/api/token.dart';
import 'package:openmediacentermobile/log/log.dart';
import 'package:openmediacentermobile/login/logincontext.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({Key? key, this.onLogin}) : super(key: key);
final onLogin;
const LoginScreen({Key? key}) : super(key: key);
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final TextEditingController _domainTextController = TextEditingController();
final TextEditingController _passwordTextController = TextEditingController();
String error = "";
Future<String> login(String password, String domain) async {
Log.i("logging in...");
final compl = Completer<String>();
final resp = await http.post(
Uri.parse(domain + '/api/login/login'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'Password': password,
}),
);
if (resp.statusCode != 200) {
compl.complete(resp.body);
} else {
final json = jsonDecode(resp.body);
final token = json["Token"];
Token.getInstance().setToken(token, domain);
LoginContext.of(context).onLoggin(true);
compl.complete("");
}
// LoginContext.of(context).onLoggin(true);
return compl.future;
}
@override
void dispose() {
_domainTextController.dispose();
_passwordTextController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
LoginContext.of(context).onLoggin(true);
},
child: Text("klick meee"));
return Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/login.png'), fit: BoxFit.cover),
),
child: Scaffold(
backgroundColor: Colors.transparent,
body: Stack(
children: [
Container(
padding: const EdgeInsets.only(left: 35, top: 90),
child: const Text(
'Welcome\nBack',
style: TextStyle(color: Colors.white, fontSize: 33),
),
),
SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(
top: MediaQuery.of(context).size.height * 0.5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.only(left: 35, right: 35),
child: Column(
children: [
TextField(
controller: _domainTextController,
style: const TextStyle(color: Colors.black),
decoration: InputDecoration(
fillColor: Colors.grey.shade100,
filled: true,
hintText: "Domain",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
)),
),
const SizedBox(
height: 30,
),
TextField(
controller: _passwordTextController,
style: const TextStyle(),
obscureText: true,
decoration: InputDecoration(
fillColor: Colors.grey.shade100,
filled: true,
hintText: "Password",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
)),
),
const SizedBox(
height: 40,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Sign in',
style: TextStyle(
fontSize: 27, fontWeight: FontWeight.w700),
),
CircleAvatar(
radius: 30,
backgroundColor: const Color(0xff4c505b),
child: IconButton(
color: Colors.white,
onPressed: () {
final pwd =
_passwordTextController.value.text;
final domain =
_domainTextController.value.text;
login(pwd, domain).then((value) {
if (value != "") {
setState(() {
error = value;
});
}
});
},
icon: const Icon(
Icons.arrow_forward,
)),
)
],
),
error != "" ? Text(error) : const Text("")
],
),
)
],
),
),
),
],
),
),
);
}
}
+1 -3
View File
@@ -17,14 +17,12 @@ class _LoginContainerState extends State<LoginContainer> {
@override
void initState() {
// TODO: implement initState
super.initState();
final token = Token.getInstance();
token.getToken().then((value) {
// todo this context call might occur before app rendered correctly!
Log.i("The token value is $value");
if (value != "") {
if (value != null) {
setState(() {
loggedIn = true;
loading = false;
+38 -8
View File
@@ -2,24 +2,54 @@ import 'dart:convert';
import 'package:flutter/material.dart';
class VideoPreview {
String thumbnail;
String title;
int id;
import 'api/api.dart';
VideoPreview(this.thumbnail, this.title, this.id);
class VideoT {
int id;
String title;
double Ratio;
VideoT(this.title, this.id, this.Ratio);
factory VideoT.fromJson(dynamic json) {
return VideoT(json['MovieName'] as String, json['MovieId'] as int,
(json['Ratio'] as num).toDouble());
}
}
class PreviewTile extends StatelessWidget {
class PreviewTile extends StatefulWidget {
const PreviewTile({Key? key, required this.dta}) : super(key: key);
final VideoT dta;
final VideoPreview dta;
@override
_PreviewTileState createState() => _PreviewTileState();
}
class _PreviewTileState extends State<PreviewTile> {
String prev = "";
@override
void initState() {
super.initState();
API.query("video", "readThumbnail", {'Movieid': widget.dta.id}).then(
(value) {
setState(() {
prev = value.substring(23);
});
});
}
@override
Widget build(BuildContext context) {
return InkWell(
child: Column(
children: [Text(dta.title), Image.memory(base64Decode(dta.thumbnail))],
children: [
Text(widget.dta.title),
prev != ""
? Image.memory(base64Decode(prev))
: const CircularProgressIndicator()
],
),
);
}
+28 -6
View File
@@ -1,16 +1,38 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:openmediacentermobile/api/api.dart';
import 'log/log.dart';
import 'preview_tile.dart';
class VideoFeed extends StatelessWidget {
VideoFeed({Key? key}) : super(key: key);
class VideoFeed extends StatefulWidget {
const VideoFeed({Key? key}) : super(key: key);
static const String testprev =
"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 truncated
@override
State<StatefulWidget> createState() {
return VideoFeedState();
}
}
final List<VideoPreview> _vids =
List.generate(6000, (index) => VideoPreview(testprev, "title4", index));
class VideoFeedState extends State<VideoFeed> {
List<VideoT> _vids = [];
@override
void initState() {
super.initState();
API.query("video", "getMovies", {'Tag': 1, 'Sort': 0}).then((value) {
final d = jsonDecode(value);
List<VideoT> dta =
(d['Videos'] as List).map((e) => VideoT.fromJson(e)).toList();
setState(() {
_vids = dta;
});
});
}
@override
Widget build(BuildContext context) {
+14
View File
@@ -121,6 +121,20 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
http:
dependency: "direct main"
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.4"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
js:
dependency: transitive
description:
+4
View File
@@ -36,6 +36,7 @@ dependencies:
cupertino_icons: ^1.0.2
flutter_secure_storage: ^5.0.2
logger: ^1.1.0
http: ^0.13.4
dev_dependencies:
flutter_test:
@@ -63,6 +64,9 @@ flutter:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
assets:
- assets/images/logo_circle.png
- assets/images/login.png
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.