Update to latest API
This commit is contained in:
parent
7cc3803242
commit
c04f2baa3c
16 changed files with 345 additions and 167 deletions
46
lib/album_sharing_service.dart
Normal file
46
lib/album_sharing_service.dart
Normal file
|
@ -0,0 +1,46 @@
|
|||
import 'package:dio/dio.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
|
||||
class AlbumSharingService {
|
||||
final _dio = Dio();
|
||||
|
||||
AlbumSharingService._privateConstructor();
|
||||
static final AlbumSharingService instance =
|
||||
AlbumSharingService._privateConstructor();
|
||||
|
||||
Future<Map<String, bool>> getSharingStatus(String path) async {
|
||||
// TODO fetch folderID from path
|
||||
var folderID = 0;
|
||||
return _dio
|
||||
.get(
|
||||
Configuration.instance.getHttpEndpoint() + "/users",
|
||||
options:
|
||||
Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
|
||||
)
|
||||
.then((usersResponse) {
|
||||
return _dio
|
||||
.get(
|
||||
Configuration.instance.getHttpEndpoint() +
|
||||
"/folders/" +
|
||||
folderID.toString(),
|
||||
options: Options(
|
||||
headers: {"X-Auth-Token": Configuration.instance.getToken()}),
|
||||
)
|
||||
.then((sharedFoldersResponse) {
|
||||
final sharedUsers =
|
||||
(sharedFoldersResponse.data["sharedWith"] as List).toSet();
|
||||
final result = Map<String, bool>();
|
||||
(usersResponse.data as List).forEach((user) {
|
||||
if (user != Configuration.instance.getUsername()) {
|
||||
result[user] = sharedUsers.contains(user);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void shareAlbum(
|
||||
String path,
|
||||
) {}
|
||||
}
|
|
@ -15,7 +15,7 @@ class DatabaseHelper {
|
|||
static final columnUploadedFileId = 'uploaded_file_id';
|
||||
static final columnLocalId = 'local_id';
|
||||
static final columnTitle = 'title';
|
||||
static final columnPathName = 'path_name';
|
||||
static final columnDeviceFolder = 'device_folder';
|
||||
static final columnRemotePath = 'remote_path';
|
||||
static final columnIsDeleted = 'is_deleted';
|
||||
static final columnCreateTimestamp = 'create_timestamp';
|
||||
|
@ -50,7 +50,7 @@ class DatabaseHelper {
|
|||
$columnLocalId TEXT,
|
||||
$columnUploadedFileId INTEGER NOT NULL,
|
||||
$columnTitle TEXT NOT NULL,
|
||||
$columnPathName TEXT NOT NULL,
|
||||
$columnDeviceFolder TEXT NOT NULL,
|
||||
$columnRemotePath TEXT,
|
||||
$columnIsDeleted INTEGER DEFAULT 0,
|
||||
$columnCreateTimestamp TEXT NOT NULL,
|
||||
|
@ -161,12 +161,12 @@ class DatabaseHelper {
|
|||
final db = await instance.database;
|
||||
final rows = await db.query(
|
||||
table,
|
||||
columns: [columnPathName],
|
||||
columns: [columnDeviceFolder],
|
||||
distinct: true,
|
||||
);
|
||||
List<String> result = List<String>();
|
||||
for (final row in rows) {
|
||||
result.add(row[columnPathName]);
|
||||
result.add(row[columnDeviceFolder]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ class DatabaseHelper {
|
|||
final db = await instance.database;
|
||||
var rows = await db.query(
|
||||
table,
|
||||
where: '$columnPathName =?',
|
||||
where: '$columnDeviceFolder =?',
|
||||
whereArgs: [path],
|
||||
orderBy: '$columnCreateTimestamp DESC',
|
||||
limit: 1,
|
||||
|
@ -217,7 +217,7 @@ class DatabaseHelper {
|
|||
row[columnUploadedFileId] =
|
||||
photo.uploadedFileId == null ? -1 : photo.uploadedFileId;
|
||||
row[columnTitle] = photo.title;
|
||||
row[columnPathName] = photo.pathName;
|
||||
row[columnDeviceFolder] = photo.deviceFolder;
|
||||
row[columnRemotePath] = photo.remotePath;
|
||||
row[columnCreateTimestamp] = photo.createTimestamp;
|
||||
row[columnSyncTimestamp] = photo.syncTimestamp;
|
||||
|
@ -230,7 +230,7 @@ class DatabaseHelper {
|
|||
photo.localId = row[columnLocalId];
|
||||
photo.uploadedFileId = row[columnUploadedFileId];
|
||||
photo.title = row[columnTitle];
|
||||
photo.pathName = row[columnPathName];
|
||||
photo.deviceFolder = row[columnDeviceFolder];
|
||||
photo.remotePath = row[columnRemotePath];
|
||||
photo.createTimestamp = int.parse(row[columnCreateTimestamp]);
|
||||
photo.syncTimestamp = row[columnSyncTimestamp] == null
|
||||
|
|
|
@ -15,7 +15,7 @@ final logger = Logger("main");
|
|||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
SuperLogging.main(LogConfig(
|
||||
await SuperLogging.main(LogConfig(
|
||||
body: _main,
|
||||
sentryDsn: SENTRY_DSN,
|
||||
logDirPath: (await getTemporaryDirectory()).path + "/logs",
|
||||
|
|
|
@ -9,6 +9,6 @@ class FolderNameFilter implements GalleryItemsFilter {
|
|||
|
||||
@override
|
||||
bool shouldInclude(Photo photo) {
|
||||
return path.basename(photo.pathName) == folderName;
|
||||
return path.basename(photo.deviceFolder) == folderName;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ class ImportantItemsFilter implements GalleryItemsFilter {
|
|||
@override
|
||||
bool shouldInclude(Photo photo) {
|
||||
if (Platform.isAndroid) {
|
||||
final String folder = basename(photo.pathName);
|
||||
final String folder = basename(photo.deviceFolder);
|
||||
return folder == "Camera" ||
|
||||
folder == "DCIM" ||
|
||||
folder == "Download" ||
|
||||
|
|
11
lib/models/folder.dart
Normal file
11
lib/models/folder.dart
Normal file
|
@ -0,0 +1,11 @@
|
|||
class Folder {
|
||||
final int folderID;
|
||||
final String name;
|
||||
final String owner;
|
||||
final String deviceFolder;
|
||||
final List<String> sharedWith;
|
||||
final int updateTimestamp;
|
||||
|
||||
Folder(this.folderID, this.name, this.owner, this.deviceFolder,
|
||||
this.sharedWith, this.updateTimestamp);
|
||||
}
|
|
@ -10,7 +10,7 @@ class Photo {
|
|||
int uploadedFileId;
|
||||
String localId;
|
||||
String title;
|
||||
String pathName;
|
||||
String deviceFolder;
|
||||
String remotePath;
|
||||
int createTimestamp;
|
||||
int syncTimestamp;
|
||||
|
@ -18,8 +18,9 @@ class Photo {
|
|||
Photo();
|
||||
Photo.fromJson(Map<String, dynamic> json)
|
||||
: uploadedFileId = json["fileId"],
|
||||
remotePath = json["path"],
|
||||
title = json["title"],
|
||||
deviceFolder = json["deviceFolder"],
|
||||
remotePath = json["path"],
|
||||
createTimestamp = json["createTimestamp"],
|
||||
syncTimestamp = json["syncTimestamp"];
|
||||
|
||||
|
@ -29,7 +30,7 @@ class Photo {
|
|||
photo.uploadedFileId = -1;
|
||||
photo.localId = asset.id;
|
||||
photo.title = asset.title;
|
||||
photo.pathName = pathEntity.name;
|
||||
photo.deviceFolder = pathEntity.name;
|
||||
photo.createTimestamp = asset.createDateTime.microsecondsSinceEpoch;
|
||||
if (photo.createTimestamp == 0) {
|
||||
try {
|
||||
|
@ -67,7 +68,7 @@ class Photo {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Photo(generatedId: $generatedId, uploadedFileId: $uploadedFileId, localId: $localId, title: $title, pathName: $pathName, remotePath: $remotePath, createTimestamp: $createTimestamp, syncTimestamp: $syncTimestamp)';
|
||||
return 'Photo(generatedId: $generatedId, uploadedFileId: $uploadedFileId, localId: $localId, title: $title, deviceFolder: $deviceFolder, remotePath: $remotePath, createTimestamp: $createTimestamp, syncTimestamp: $syncTimestamp)';
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -79,7 +80,7 @@ class Photo {
|
|||
o.uploadedFileId == uploadedFileId &&
|
||||
o.localId == localId &&
|
||||
o.title == title &&
|
||||
o.pathName == pathName &&
|
||||
o.deviceFolder == deviceFolder &&
|
||||
o.remotePath == remotePath &&
|
||||
o.createTimestamp == createTimestamp &&
|
||||
o.syncTimestamp == syncTimestamp;
|
||||
|
@ -91,7 +92,7 @@ class Photo {
|
|||
uploadedFileId.hashCode ^
|
||||
localId.hashCode ^
|
||||
title.hashCode ^
|
||||
pathName.hashCode ^
|
||||
deviceFolder.hashCode ^
|
||||
remotePath.hashCode ^
|
||||
createTimestamp.hashCode ^
|
||||
syncTimestamp.hashCode;
|
||||
|
|
|
@ -8,7 +8,6 @@ import 'package:photos/db/db_helper.dart';
|
|||
import 'package:photos/events/user_authenticated_event.dart';
|
||||
import 'package:photos/photo_repository.dart';
|
||||
import 'package:photos/photo_provider.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
@ -110,15 +109,13 @@ class PhotoSyncManager {
|
|||
|
||||
await _getDiff(lastSyncTimestamp, _diffLimit).then((diff) async {
|
||||
if (diff != null) {
|
||||
await _downloadDiff(diff, prefs).then((_) {
|
||||
if (diff.length > 0) {
|
||||
_syncPhotos();
|
||||
}
|
||||
await _storeDiff(diff, prefs).then((_) {
|
||||
// TODO: Recursively store diff
|
||||
_uploadDiff(prefs);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
_uploadDiff(prefs);
|
||||
// TODO: Fix race conditions triggered due to concurrent syncs.
|
||||
// Add device_id/last_sync_timestamp to the upload request?
|
||||
}
|
||||
|
@ -127,32 +124,26 @@ class PhotoSyncManager {
|
|||
List<Photo> photosToBeUploaded =
|
||||
await DatabaseHelper.instance.getPhotosToBeUploaded();
|
||||
for (Photo photo in photosToBeUploaded) {
|
||||
var uploadedPhoto = await _uploadFile(photo);
|
||||
if (uploadedPhoto == null) {
|
||||
return;
|
||||
try {
|
||||
var uploadedPhoto = await _uploadFile(photo);
|
||||
if (uploadedPhoto == null) {
|
||||
return;
|
||||
}
|
||||
await DatabaseHelper.instance.updatePhoto(photo.generatedId,
|
||||
uploadedPhoto.remotePath, uploadedPhoto.syncTimestamp);
|
||||
prefs.setInt(_lastSyncTimestampKey, uploadedPhoto.syncTimestamp);
|
||||
} catch (e) {
|
||||
_logger.severe(e);
|
||||
}
|
||||
await DatabaseHelper.instance.updatePhoto(photo.generatedId,
|
||||
uploadedPhoto.remotePath, uploadedPhoto.syncTimestamp);
|
||||
prefs.setInt(_lastSyncTimestampKey, uploadedPhoto.syncTimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
Future _downloadDiff(List<Photo> diff, SharedPreferences prefs) async {
|
||||
var externalPath = (await getApplicationDocumentsDirectory()).path;
|
||||
var path = externalPath + "/photos/";
|
||||
Future _storeDiff(List<Photo> diff, SharedPreferences prefs) async {
|
||||
for (Photo photo in diff) {
|
||||
var localPath = path + basename(photo.remotePath);
|
||||
await _dio
|
||||
.download(
|
||||
Configuration.instance.getHttpEndpoint() + "/" + photo.remotePath,
|
||||
localPath)
|
||||
.catchError((e) => _logger.severe(e));
|
||||
// TODO: Save path
|
||||
photo.pathName = localPath;
|
||||
await DatabaseHelper.instance.insertPhoto(photo);
|
||||
PhotoRepository.instance.reloadPhotos();
|
||||
await prefs.setInt(_lastSyncTimestampKey, photo.syncTimestamp);
|
||||
}
|
||||
PhotoRepository.instance.reloadPhotos();
|
||||
}
|
||||
|
||||
Future<List<Photo>> _getDiff(int lastSyncTimestamp, int limit) async {
|
||||
|
@ -180,19 +171,22 @@ class PhotoSyncManager {
|
|||
var formData = FormData.fromMap({
|
||||
"file": MultipartFile.fromBytes((await localPhoto.getOriginalBytes()),
|
||||
filename: localPhoto.title),
|
||||
"deviceFolder": localPhoto.deviceFolder,
|
||||
"title": localPhoto.title,
|
||||
"createTimestamp": localPhoto.createTimestamp,
|
||||
"token": Configuration.instance.getToken(),
|
||||
});
|
||||
return _dio
|
||||
.post(
|
||||
Configuration.instance.getHttpEndpoint() + "/files",
|
||||
options: Options(
|
||||
headers: {"X-Auth-Token": Configuration.instance.getToken()}),
|
||||
data: formData,
|
||||
)
|
||||
.then((response) => Photo.fromJson(response.data))
|
||||
.catchError((e) => _logger.severe(e));
|
||||
Configuration.instance.getHttpEndpoint() + "/files",
|
||||
options:
|
||||
Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
|
||||
data: formData,
|
||||
)
|
||||
.then((response) {
|
||||
return Photo.fromJson(response.data);
|
||||
}).catchError((e) {
|
||||
_logger.severe("Error in uploading ", e);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _deletePhotos() async {
|
||||
|
|
|
@ -37,6 +37,7 @@ class _AlbumPageState extends State<AlbumPage> {
|
|||
return Scaffold(
|
||||
appBar: GalleryAppBarWidget(
|
||||
widget.album.name,
|
||||
widget.album.thumbnailPhoto.deviceFolder,
|
||||
_selectedPhotos,
|
||||
onSelectionClear: () {
|
||||
setState(() {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
|
@ -9,15 +8,18 @@ import 'package:photos/models/photo.dart';
|
|||
import 'package:photos/photo_repository.dart';
|
||||
import 'package:photos/ui/setup_page.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:photos/ui/share_album_widget.dart';
|
||||
import 'package:photos/utils/share_util.dart';
|
||||
|
||||
class GalleryAppBarWidget extends StatefulWidget
|
||||
implements PreferredSizeWidget {
|
||||
final String title;
|
||||
final String path;
|
||||
final Set<Photo> selectedPhotos;
|
||||
final Function() onSelectionClear;
|
||||
|
||||
GalleryAppBarWidget(this.title, this.selectedPhotos, {this.onSelectionClear});
|
||||
GalleryAppBarWidget(this.title, this.path, this.selectedPhotos,
|
||||
{this.onSelectionClear});
|
||||
|
||||
@override
|
||||
_GalleryAppBarWidgetState createState() => _GalleryAppBarWidgetState();
|
||||
|
@ -70,10 +72,26 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
_openSyncConfiguration(context);
|
||||
},
|
||||
));
|
||||
} else {
|
||||
actions.add(IconButton(
|
||||
icon: Icon(Icons.person_add),
|
||||
onPressed: () {
|
||||
_showShareAlbumDialog();
|
||||
},
|
||||
));
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
Future<void> _showShareAlbumDialog() async {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return ShareAlbumWidget(widget.title, widget.path);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _getPhotoActions(BuildContext context) {
|
||||
List<Widget> actions = List<Widget>();
|
||||
if (widget.selectedPhotos.isNotEmpty) {
|
||||
|
|
|
@ -53,6 +53,7 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
return Scaffold(
|
||||
appBar: GalleryAppBarWidget(
|
||||
widget.title,
|
||||
"/",
|
||||
_selectedPhotos,
|
||||
onSelectionClear: _clearSelectedPhotos,
|
||||
),
|
||||
|
|
101
lib/ui/share_album_widget.dart
Normal file
101
lib/ui/share_album_widget.dart
Normal file
|
@ -0,0 +1,101 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:photos/album_sharing_service.dart';
|
||||
import 'package:photos/ui/loading_widget.dart';
|
||||
|
||||
class ShareAlbumWidget extends StatefulWidget {
|
||||
final String title;
|
||||
final String path;
|
||||
|
||||
const ShareAlbumWidget(
|
||||
this.title,
|
||||
this.path, {
|
||||
Key key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ShareAlbumWidgetState createState() => _ShareAlbumWidgetState();
|
||||
}
|
||||
|
||||
class _ShareAlbumWidgetState extends State<ShareAlbumWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<Map<String, bool>>(
|
||||
future: AlbumSharingService.instance.getSharingStatus(widget.path),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return _getSharingDialog(snapshot.data);
|
||||
} else if (snapshot.hasError) {
|
||||
return Text(snapshot.error.toString());
|
||||
} else {
|
||||
return loadWidget;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _getSharingDialog(Map<String, bool> sharingStatus) {
|
||||
return AlertDialog(
|
||||
title: Text('Share "' + widget.title + '" with'),
|
||||
content: SingleChildScrollView(
|
||||
child: ListBody(
|
||||
children: <Widget>[
|
||||
SharingCheckboxWidget(sharingStatus),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
child: Text("Share"),
|
||||
onPressed: () {
|
||||
// TODO: AlbumSharingService.instance.shareAlbum();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SharingCheckboxWidget extends StatefulWidget {
|
||||
final Map<String, bool> sharingStatus;
|
||||
|
||||
const SharingCheckboxWidget(
|
||||
this.sharingStatus, {
|
||||
Key key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SharingCheckboxWidgetState createState() => _SharingCheckboxWidgetState();
|
||||
}
|
||||
|
||||
class _SharingCheckboxWidgetState extends State<SharingCheckboxWidget> {
|
||||
Map<String, bool> _sharingStatus;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_sharingStatus = widget.sharingStatus;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final checkboxes = List<Widget>();
|
||||
for (final user in _sharingStatus.keys) {
|
||||
checkboxes.add(Row(
|
||||
children: <Widget>[
|
||||
Checkbox(
|
||||
materialTapTargetSize: MaterialTapTargetSize.padded,
|
||||
value: _sharingStatus[user],
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_sharingStatus[user] = value;
|
||||
});
|
||||
}),
|
||||
Text(user),
|
||||
],
|
||||
));
|
||||
}
|
||||
return Column(children: checkboxes);
|
||||
}
|
||||
}
|
|
@ -16,17 +16,17 @@ class SignInWidget extends StatefulWidget {
|
|||
_SignInWidgetState createState() => _SignInWidgetState();
|
||||
}
|
||||
|
||||
enum Mode { sign_up, sign_in, unknown }
|
||||
|
||||
class _SignInWidgetState extends State<SignInWidget> {
|
||||
String _username, _password, _repeatedPassword;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
Mode mode = Mode.unknown;
|
||||
final _usernameController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
final _repeatPasswordController = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (Configuration.instance.getToken() == null) {
|
||||
// Has probably not signed up
|
||||
if (mode == Mode.sign_up) {
|
||||
return _getSignUpWidget(context);
|
||||
} else {
|
||||
return _getSignInWidget(context);
|
||||
|
@ -35,120 +35,121 @@ class _SignInWidgetState extends State<SignInWidget> {
|
|||
|
||||
Widget _getSignUpWidget(BuildContext context) {
|
||||
return Container(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 16, 0, 0),
|
||||
child: Text("Create an account to get started"),
|
||||
),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
hintText: 'username',
|
||||
contentPadding: EdgeInsets.all(20),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 16, 0, 0),
|
||||
child: Text("Create an account to get started"),
|
||||
),
|
||||
autofocus: true,
|
||||
autocorrect: false,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_username = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
hintText: 'password',
|
||||
contentPadding: EdgeInsets.all(20),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
hintText: 'username',
|
||||
contentPadding: EdgeInsets.all(20),
|
||||
),
|
||||
controller: _usernameController,
|
||||
autofocus: true,
|
||||
autocorrect: false,
|
||||
),
|
||||
autocorrect: false,
|
||||
obscureText: true,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_password = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
hintText: 'repeat password',
|
||||
contentPadding: EdgeInsets.all(20),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
hintText: 'password',
|
||||
contentPadding: EdgeInsets.all(20),
|
||||
),
|
||||
autocorrect: false,
|
||||
obscureText: true,
|
||||
controller: _passwordController,
|
||||
),
|
||||
autocorrect: false,
|
||||
obscureText: true,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_repeatedPassword = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
CupertinoButton(
|
||||
child: Text("Sign Up"),
|
||||
onPressed: () async {
|
||||
if (_password != _repeatedPassword) {
|
||||
_showPasswordMismatchDialog();
|
||||
} else {
|
||||
try {
|
||||
final userCreated = await UserAuthenticator.instance
|
||||
.create(_username, _password);
|
||||
if (userCreated) {
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
_showGenericErrorDialog();
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
hintText: 'repeat password',
|
||||
contentPadding: EdgeInsets.all(20),
|
||||
),
|
||||
autocorrect: false,
|
||||
obscureText: true,
|
||||
controller: _repeatPasswordController,
|
||||
),
|
||||
CupertinoButton(
|
||||
child: Text("Sign Up"),
|
||||
onPressed: () async {
|
||||
if (_passwordController.text != _repeatPasswordController.text) {
|
||||
_showPasswordMismatchDialog();
|
||||
} else {
|
||||
try {
|
||||
final userCreated = await UserAuthenticator.instance.create(
|
||||
_usernameController.text, _passwordController.text);
|
||||
if (userCreated) {
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
_showGenericErrorDialog();
|
||||
}
|
||||
} catch (e) {
|
||||
_showGenericErrorDialog(error: e);
|
||||
}
|
||||
} catch (e) {
|
||||
_showGenericErrorDialog(error: e);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
CupertinoButton(
|
||||
child: Text("Have an account?"),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
mode = Mode.sign_in;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
Widget _getSignInWidget(BuildContext context) {
|
||||
return Container(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
TextFormField(
|
||||
initialValue: Configuration.instance.getUsername(),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'username',
|
||||
contentPadding: EdgeInsets.all(20),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
TextFormField(
|
||||
// initialValue: Configuration.instance.getUsername(),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'username',
|
||||
contentPadding: EdgeInsets.all(20),
|
||||
),
|
||||
autofocus: true,
|
||||
autocorrect: false,
|
||||
controller: _usernameController,
|
||||
),
|
||||
autofocus: true,
|
||||
autocorrect: false,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_username = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
TextFormField(
|
||||
initialValue: Configuration.instance.getPassword(),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'password',
|
||||
contentPadding: EdgeInsets.all(20),
|
||||
TextFormField(
|
||||
// initialValue: Configuration.instance.getPassword(),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'password',
|
||||
contentPadding: EdgeInsets.all(20),
|
||||
),
|
||||
autocorrect: false,
|
||||
obscureText: true,
|
||||
controller: _passwordController,
|
||||
),
|
||||
autocorrect: false,
|
||||
obscureText: true,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_password = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
CupertinoButton(
|
||||
child: Text("Sign In"),
|
||||
onPressed: () async {
|
||||
final loggedIn =
|
||||
await UserAuthenticator.instance.login(_username, _password);
|
||||
if (loggedIn) {
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
_showAuthenticationFailedErrorDialog();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
CupertinoButton(
|
||||
child: Text("Sign In"),
|
||||
onPressed: () async {
|
||||
final loggedIn = await UserAuthenticator.instance
|
||||
.login(_usernameController.text, _passwordController.text);
|
||||
if (loggedIn) {
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
_showAuthenticationFailedErrorDialog();
|
||||
}
|
||||
},
|
||||
),
|
||||
CupertinoButton(
|
||||
child: Text("Don't have an account?"),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
mode = Mode.sign_up;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,11 @@ class UserAuthenticator {
|
|||
|
||||
Future<bool> login(String username, String password) {
|
||||
return _dio.post(
|
||||
"http://" +
|
||||
Configuration.instance.getEndpoint() +
|
||||
":8080/users/authenticate",
|
||||
data: {"username": username, "password": password}).then((response) {
|
||||
Configuration.instance.getHttpEndpoint() + "/users/authenticate",
|
||||
data: {
|
||||
"username": username,
|
||||
"password": password,
|
||||
}).then((response) {
|
||||
if (response.statusCode == 200 && response.data != null) {
|
||||
Configuration.instance.setUsername(username);
|
||||
Configuration.instance.setPassword(password);
|
||||
|
@ -36,9 +37,11 @@ class UserAuthenticator {
|
|||
}
|
||||
|
||||
Future<bool> create(String username, String password) {
|
||||
return _dio.post(
|
||||
"http://" + Configuration.instance.getEndpoint() + ":8080/users",
|
||||
data: {"username": username, "password": password}).then((response) {
|
||||
return _dio
|
||||
.post(Configuration.instance.getHttpEndpoint() + "/users", data: {
|
||||
"username": username,
|
||||
"password": password,
|
||||
}).then((response) {
|
||||
if (response.statusCode == 200 && response.data != null) {
|
||||
Configuration.instance.setUsername(username);
|
||||
Configuration.instance.setPassword(password);
|
||||
|
|
|
@ -213,7 +213,7 @@ packages:
|
|||
source: hosted
|
||||
version: "1.0.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: logging
|
||||
url: "https://pub.dartlang.org"
|
||||
|
|
|
@ -46,6 +46,7 @@ dependencies:
|
|||
archive: ^2.0.11
|
||||
flutter_email_sender: ^3.0.1
|
||||
like_button: ^0.2.0
|
||||
logging: ^0.11.4
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Loading…
Reference in a new issue