.
This commit is contained in:
@@ -1,29 +0,0 @@
|
||||
import 'package:nextcloud/nextcloud.dart';
|
||||
import 'package:nextcloud/provisioning_api.dart';
|
||||
|
||||
Future<String> login(String uri, String username, String password) async {
|
||||
try {
|
||||
|
||||
final client = NextcloudClient(
|
||||
Uri.parse(uri),
|
||||
loginName: username,
|
||||
password: password,
|
||||
);
|
||||
|
||||
|
||||
final response =
|
||||
await client.provisioningApi.users.getUser(userId: username);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
print(response.body);
|
||||
return "successful|$response.body.ocs.data.";
|
||||
} else {
|
||||
return "failed|$response.body.ocs.data";
|
||||
}
|
||||
} catch (e) {
|
||||
print("");
|
||||
print("$e");
|
||||
print("");
|
||||
return "$e";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:http/io_client.dart';
|
||||
import 'package:nextcloud/nextcloud.dart';
|
||||
|
||||
|
||||
extension NextcloudClientExtension on NextcloudClient {
|
||||
//
|
||||
static final userAgent = 'NextCloud App'
|
||||
'(${Platform.operatingSystem}) ';
|
||||
// 'Dart/${Platform.version.split(' ').first}';
|
||||
|
||||
//
|
||||
static IOClient newHttpClient() => IOClient(
|
||||
HttpClient()..userAgent = userAgent,
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_web_auth_2/flutter_web_auth_2.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:nextcloud/core.dart';
|
||||
import 'package:nextcloud/nextcloud.dart';
|
||||
import 'package:nextcloudapp/app/extension.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class SaberLoginFlow {
|
||||
SaberLoginFlow.start({
|
||||
required this.serverUrl,
|
||||
}) {
|
||||
// NcHttpOverrides.tempAcceptBadCertificateFrom(serverUrl);
|
||||
unawaited(_run());
|
||||
}
|
||||
|
||||
final Uri serverUrl;
|
||||
|
||||
final log = Logger('SaberLoginFlow');
|
||||
Timer? _pollTimer;
|
||||
|
||||
final completer = Completer<LoginFlowV2Credentials>();
|
||||
late final future = completer.future;
|
||||
|
||||
LoginFlowV2? init;
|
||||
|
||||
Future<void> openLoginUrl() async {
|
||||
final init = this.init;
|
||||
if (init == null) return;
|
||||
|
||||
if (Platform.isMacOS || Platform.isIOS || Platform.isAndroid) {
|
||||
// Use FlutterWebAuth2 which returns to Saber when authenticated
|
||||
log.info('Opening login link in-app: ${init.login}');
|
||||
await FlutterWebAuth2.authenticate(
|
||||
url: init.login,
|
||||
callbackUrlScheme: 'nc',
|
||||
);
|
||||
} else {
|
||||
// Use url_launcher to open the link in the default browser
|
||||
log.info('Opening login link in browser: ${init.login}');
|
||||
final loginLink = Uri.parse(init.login);
|
||||
await launchUrl(loginLink);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _run() async {
|
||||
_catchHttpError(() async {
|
||||
final client = NextcloudClient(serverUrl,
|
||||
httpClient: NextcloudClientExtension.newHttpClient());
|
||||
final flowClient = client.core.clientFlowLoginV2;
|
||||
init = await flowClient.init().then((response) => response.body);
|
||||
log.info('init: $init');
|
||||
|
||||
openLoginUrl();
|
||||
|
||||
_pollTimer?.cancel();
|
||||
_pollTimer = Timer.periodic(
|
||||
const Duration(seconds: 1),
|
||||
(_) => _catch404Error(() async {
|
||||
// Throws 404 if not logged in yet
|
||||
final poll = await flowClient.poll(
|
||||
$body: ClientFlowLoginV2PollRequestApplicationJson(
|
||||
(b) => b..token = init!.poll.token,
|
||||
),
|
||||
);
|
||||
|
||||
_pollTimer?.cancel();
|
||||
if (!completer.isCompleted) completer.complete(poll.body);
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<T?> _catchHttpError<T>(Future<T> Function() fn) async {
|
||||
try {
|
||||
return await fn();
|
||||
} on http.ClientException catch (error, stackTrace) {
|
||||
log.severe('Error while polling the login flow.', error, stackTrace);
|
||||
if (!completer.isCompleted) completer.completeError(error, stackTrace);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<T?> _catch404Error<T>(Future<T> Function() fn) async {
|
||||
try {
|
||||
return await fn();
|
||||
} on DynamiteStatusCodeException catch (error) {
|
||||
if (error.statusCode != 404) rethrow;
|
||||
|
||||
log.fine('Login flow not found or completed yet, will repoll');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_pollTimer?.cancel();
|
||||
if (!completer.isCompleted) {
|
||||
completer.completeError(TimeoutException('Login flow was disposed'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:nextcloudapp/components/nextcloud_routes.dart';
|
||||
import 'package:nextcloudapp/service/navigation_service.dart';
|
||||
|
||||
class NextcloudApp extends StatelessWidget {
|
||||
NextcloudApp({
|
||||
super.key,
|
||||
});
|
||||
|
||||
final NextcloudAppRoutes _nextcloudAppRoutes = NextcloudAppRoutes.instance;
|
||||
final NavigationService _navigationService = NavigationService.instance;
|
||||
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Nextcloud App',
|
||||
navigatorKey: _navigationService.navigatorKey,
|
||||
theme: ThemeData(
|
||||
primaryColor: Color.fromARGB(255, 152, 120, 57),
|
||||
scaffoldBackgroundColor: Colors.white,
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: Color.fromARGB(255, 152, 120, 57),
|
||||
shape: const StadiumBorder(),
|
||||
maximumSize: const Size(double.infinity, 56),
|
||||
minimumSize: const Size(double.infinity, 56),
|
||||
),
|
||||
),
|
||||
// inputDecorationTheme: const InputDecorationTheme(
|
||||
// filled: true,
|
||||
// fillColor: primaryLightColor,
|
||||
// iconColor: primaryColor,
|
||||
// prefixIconColor: primaryColor,
|
||||
// contentPadding: EdgeInsets.symmetric(
|
||||
// horizontal: defaultPadding, vertical: defaultPadding),
|
||||
// border: OutlineInputBorder(
|
||||
// borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
// borderSide: BorderSide.none,
|
||||
// ),
|
||||
// ),
|
||||
fontFamily: 'Poppins',
|
||||
),
|
||||
routes: _nextcloudAppRoutes.routes,
|
||||
initialRoute: "login",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// import 'package:nextcloud/nextcloud.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
// import 'package:http/http.dart' as http;
|
||||
|
||||
|
||||
|
||||
const primaryColor = Color.fromARGB(255, 26, 26, 74);
|
||||
const primaryLightColor = Color.fromARGB(150, 238, 224, 173);
|
||||
const double defaultPadding = 15.0;
|
||||
const timeout = 3;
|
||||
// var client = http.Client();
|
||||
// var client = NextcloudClient()
|
||||
|
||||
|
||||
setSharedPref(String key, var value) async {
|
||||
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
|
||||
|
||||
switch (value.runtimeType.toString()) {
|
||||
case 'String':
|
||||
sharedPreferences.setString(key, value);
|
||||
break;
|
||||
|
||||
case 'double':
|
||||
sharedPreferences.setDouble(key, value);
|
||||
break;
|
||||
|
||||
case 'int':
|
||||
sharedPreferences.setInt(key, value);
|
||||
break;
|
||||
|
||||
case 'bool':
|
||||
sharedPreferences.setBool(key, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
getSharedPref(var key, var type) async {
|
||||
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
|
||||
|
||||
switch (type) {
|
||||
case 'String':
|
||||
return sharedPreferences.getString(key);
|
||||
|
||||
case 'double':
|
||||
return sharedPreferences.getDouble(key);
|
||||
|
||||
case 'int':
|
||||
return sharedPreferences.getInt(key);
|
||||
|
||||
case 'bool':
|
||||
return sharedPreferences.getBool(key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'dart:math';
|
||||
|
||||
class FileSize {
|
||||
static FileSize instance = FileSize();
|
||||
|
||||
String getFileSizeString({required int bytes, int decimals = 0}) {
|
||||
const suffixes = ["Bytes", "KB", "MB", "GB", "TB"];
|
||||
if (bytes == 0) return '0 ${suffixes[0]}';
|
||||
|
||||
var i = (log(bytes) / log(1024)).floor();
|
||||
return "${(bytes / pow(1024, i)).toStringAsFixed(
|
||||
i == 0 ? 0 : decimals,
|
||||
)} ${suffixes[i]}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MimetypeIcon {
|
||||
static MimetypeIcon instance = MimetypeIcon();
|
||||
|
||||
IconData getIconForMimeType(String mimeType) {
|
||||
if (mimeType.startsWith('image/')) {
|
||||
return Icons.image;
|
||||
} else if (mimeType.startsWith('video/')) {
|
||||
return Icons.video_file;
|
||||
} else if (mimeType.startsWith('audio/')) {
|
||||
return Icons.audiotrack;
|
||||
} else if (mimeType == 'application/pdf') {
|
||||
return Icons.picture_as_pdf;
|
||||
} else if (mimeType.startsWith('text/')) {
|
||||
return Icons.text_snippet;
|
||||
} else if (mimeType.contains("application/octet-stream")) {
|
||||
return Icons.disc_full_rounded;
|
||||
} else if (mimeType.contains("application/zip")) {
|
||||
return Icons.folder_zip;
|
||||
} else {
|
||||
return Icons.folder; // Default icon
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:nextcloudapp/screens/login_screen.dart';
|
||||
import 'package:nextcloudapp/screens/home_screen.dart';
|
||||
|
||||
class NextcloudAppRoutes {
|
||||
//
|
||||
static NextcloudAppRoutes instance = NextcloudAppRoutes();
|
||||
|
||||
Map<String, WidgetBuilder> get routes => {
|
||||
"login": (BuildContext _context) => LoginScreen(),
|
||||
"home": (BuildContext _context) => HomeSceen(),
|
||||
};
|
||||
}
|
||||
+6
-43
@@ -1,48 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:nextcloudapp/components/default_values.dart';
|
||||
import 'package:nextcloudapp/screens/login_screen.dart';
|
||||
import 'package:nextcloudapp/app/nextcloud_app.dart';
|
||||
|
||||
void main() => runApp(
|
||||
const MyApp(),
|
||||
);
|
||||
void main() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
runApp(
|
||||
NextcloudApp(),
|
||||
);
|
||||
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Nextcloud',
|
||||
theme: ThemeData(
|
||||
primaryColor: primaryColor,
|
||||
scaffoldBackgroundColor: Colors.white,
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: primaryColor,
|
||||
shape: const StadiumBorder(),
|
||||
maximumSize: const Size(double.infinity, 56),
|
||||
minimumSize: const Size(double.infinity, 56),
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: const InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: primaryLightColor,
|
||||
iconColor: primaryColor,
|
||||
prefixIconColor: primaryColor,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: defaultPadding, vertical: defaultPadding),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
),
|
||||
fontFamily: 'Poppins',
|
||||
),
|
||||
home: const LoginScreen(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:nextcloud/nextcloud.dart';
|
||||
|
||||
class ServerDetails {
|
||||
String name;
|
||||
String description;
|
||||
|
||||
ServerDetails({
|
||||
required this.name,
|
||||
required this.description,
|
||||
});
|
||||
|
||||
factory ServerDetails.fromTheme(Map _themeValues) {
|
||||
//
|
||||
DynamiteResponse icon = _themeValues['icon'];
|
||||
DynamiteResponse theme = _themeValues['systemtheme'];
|
||||
|
||||
return ServerDetails(name: '', description: '');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'package:nextcloud/webdav.dart';
|
||||
|
||||
class Files {
|
||||
String? path;
|
||||
String? name;
|
||||
String? resourceType;
|
||||
String? contentType;
|
||||
DateTime? creationDate;
|
||||
DateTime? modifiedDate;
|
||||
int? size;
|
||||
|
||||
Files({
|
||||
required this.path,
|
||||
required this.name,
|
||||
required this.resourceType,
|
||||
required this.contentType,
|
||||
required this.creationDate,
|
||||
required this.modifiedDate,
|
||||
required this.size,
|
||||
});
|
||||
|
||||
factory Files.fromWebDavResponse(WebDavResponse _response) {
|
||||
//
|
||||
var _name;
|
||||
var _resourceType;
|
||||
var _size;
|
||||
|
||||
var _tempPath = _response.href!.split("/");
|
||||
var _length = _tempPath.length;
|
||||
var _collection = _response.propstats[0].prop.davResourcetype!.collection;
|
||||
if (_collection == null) {
|
||||
_resourceType = 'file';
|
||||
_name = _tempPath[_length - 1];
|
||||
_size = _response.propstats[0].prop.davGetcontentlength;
|
||||
} else {
|
||||
_resourceType = 'folder';
|
||||
_name = _tempPath[_length - 2].contains("dav")
|
||||
? ".."
|
||||
: _tempPath[_length - 2];
|
||||
_size = _response.propstats[0].prop.davQuotaUsedBytes;
|
||||
}
|
||||
|
||||
return Files(
|
||||
path: _response.href ?? '',
|
||||
name: Uri.decodeFull(_name),
|
||||
contentType: _response.propstats[0].prop.davGetcontenttype ?? '',
|
||||
creationDate: DateTime.now(),
|
||||
modifiedDate: _response.propstats[0].prop.davGetlastmodified,
|
||||
size: _size,
|
||||
resourceType: _resourceType,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:nextcloud/core.dart';
|
||||
import 'package:nextcloud/nextcloud.dart';
|
||||
import 'package:nextcloud/provisioning_api.dart';
|
||||
import 'package:nextcloud/theming.dart';
|
||||
import 'package:nextcloud/webdav.dart';
|
||||
import 'package:nextcloudapp/app/extension.dart';
|
||||
import 'package:nextcloudapp/models/details.dart';
|
||||
import 'package:nextcloudapp/models/files.dart';
|
||||
|
||||
import 'package:nextcloudapp/service/navigation_service.dart';
|
||||
import 'package:nextcloudapp/service/snackbar_service.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
enum AuthStatus {
|
||||
notAuthenticated,
|
||||
authenticated,
|
||||
authenticating,
|
||||
userNotFound,
|
||||
error,
|
||||
}
|
||||
|
||||
class NextcloudProvider extends ChangeNotifier {
|
||||
//
|
||||
//
|
||||
|
||||
static NextcloudProvider instance = NextcloudProvider();
|
||||
final NavigationService _navigationService = NavigationService.instance;
|
||||
|
||||
late NextcloudClient client;
|
||||
final SharedPreferencesAsync _sharedPreferences = SharedPreferencesAsync();
|
||||
// SharedPreferences? _sharedPreferences;
|
||||
|
||||
AuthStatus? status;
|
||||
UserDetails? userDetails;
|
||||
|
||||
NextcloudProvider() {
|
||||
// _init();
|
||||
_checkLogin();
|
||||
}
|
||||
|
||||
// void _init() async {
|
||||
// _sharedPreferences = SharedPreferencesAsync();
|
||||
// }
|
||||
|
||||
void _checkLogin() async {
|
||||
await Future.delayed(Duration(milliseconds: 300));
|
||||
try {
|
||||
String? _appPassword = await _sharedPreferences.getString('apppassword');
|
||||
print(_appPassword);
|
||||
String? _uri = await _sharedPreferences.getString('uri');
|
||||
if (_appPassword != null && _uri != null) {
|
||||
client = NextcloudClient(
|
||||
Uri.parse(_uri),
|
||||
appPassword: _appPassword,
|
||||
httpClient: NextcloudClientExtension.newHttpClient(),
|
||||
);
|
||||
|
||||
var _user = await client.provisioningApi.users.getCurrentUser();
|
||||
userDetails = _user.body.ocs.data;
|
||||
|
||||
status = AuthStatus.authenticated;
|
||||
_navigationService.navigateToReplacement("home");
|
||||
} else {
|
||||
status = AuthStatus.notAuthenticated;
|
||||
}
|
||||
} on Exception catch (e) {
|
||||
status = AuthStatus.notAuthenticated;
|
||||
SnackBarService.instance.showSnackBarError("$e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<ServerDetails> envData() async {
|
||||
Map _themeValues = {};
|
||||
|
||||
_themeValues['icon'] = await client.theming.icon.getFavicon();
|
||||
_themeValues['systemtheme'] = await client.theming.theming.getManifest();
|
||||
|
||||
return ServerDetails.fromTheme(_themeValues);
|
||||
}
|
||||
|
||||
Future<void> login(String _uri, String _username, String _password,
|
||||
bool _savePassword) async {
|
||||
notifyListeners();
|
||||
status = AuthStatus.error;
|
||||
try {
|
||||
status = AuthStatus.authenticating;
|
||||
client = NextcloudClient(
|
||||
Uri.parse(_uri),
|
||||
loginName: _username,
|
||||
password: _password,
|
||||
httpClient: NextcloudClientExtension.newHttpClient(),
|
||||
);
|
||||
var _user = await client.provisioningApi.users.getCurrentUser();
|
||||
userDetails = _user.body.ocs.data;
|
||||
client.core.appPassword.getAppPassword().then((_response) async {
|
||||
await _sharedPreferences.setString(
|
||||
'apppassword', _response.body.ocs.data.apppassword);
|
||||
await _sharedPreferences.setString('uri', _uri);
|
||||
await _sharedPreferences.setString('username', _username);
|
||||
if (_savePassword) {
|
||||
await _sharedPreferences.setString('password', _password);
|
||||
}
|
||||
});
|
||||
|
||||
status = AuthStatus.authenticated;
|
||||
SnackBarService.instance.showSnackBarSuccess('successfully logged in');
|
||||
_navigationService.navigateToReplacement("home");
|
||||
} on ClientException catch (e) {
|
||||
SnackBarService.instance.showSnackBarError("$e");
|
||||
status = AuthStatus.error;
|
||||
} on Exception catch (e) {
|
||||
status = AuthStatus.error;
|
||||
SnackBarService.instance.showSnackBarError("$e");
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void logout() async {
|
||||
try {
|
||||
var _resp = await client.core.appPassword.deleteAppPassword();
|
||||
if (_resp.statusCode == 200) {
|
||||
_sharedPreferences.remove('apppassword');
|
||||
SnackBarService.instance.showSnackBarSuccess("succssfully loggedout");
|
||||
_navigationService.navigateToReplacement("login");
|
||||
} else {
|
||||
SnackBarService.instance
|
||||
.showSnackBarError(_resp.body.ocs.meta.message!);
|
||||
}
|
||||
} on Exception catch (e) {
|
||||
SnackBarService.instance.showSnackBarError("$e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Files>> listDirectory(String _cd) async {
|
||||
List<Files> _fileList = [];
|
||||
|
||||
try {
|
||||
WebDavClient _client = client.webdav;
|
||||
var _returnVal = await _client.propfind(
|
||||
PathUri.parse(Uri.decodeFull(_cd)),
|
||||
prop: WebDavPropWithoutValues(),
|
||||
depth: WebDavDepth.one,
|
||||
);
|
||||
|
||||
_fileList = _returnVal.responses.map((_value) {
|
||||
return Files.fromWebDavResponse(_value);
|
||||
}).toList();
|
||||
|
||||
_fileList[0].name = "..";
|
||||
_fileList[0].size = 0;
|
||||
_fileList[0].resourceType = 'folder';
|
||||
List<Files> filteredList = List.from(_fileList)..removeAt(0);
|
||||
filteredList.sort((a, b) => b.resourceType!.compareTo(a.resourceType!));
|
||||
filteredList.insert(0, _fileList[0]);
|
||||
return filteredList;
|
||||
} on Exception catch (e) {
|
||||
print(e.toString());
|
||||
return Future.delayed(Duration.zero);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getIt() async {
|
||||
Directory? storageDirectory = await getApplicationDocumentsDirectory();
|
||||
String _sdPath = storageDirectory.path;
|
||||
var d = Directory(_sdPath);
|
||||
if (!d.existsSync()) {
|
||||
d.createSync(recursive: true);
|
||||
File _fileTemp = File("$_sdPath/.nomedia");
|
||||
_fileTemp.writeAsString('');
|
||||
}
|
||||
|
||||
// File temp = File('$_sdPath/temp.png');
|
||||
|
||||
// await _client.getFile(
|
||||
// PathUri.parse("/files/${userDetails!.id}/Nextcloud.png"), temp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:nextcloud/webdav.dart';
|
||||
import 'package:nextcloudapp/components/file_size.dart';
|
||||
import 'package:nextcloudapp/models/files.dart';
|
||||
import 'package:nextcloudapp/provider/nextcloud_provider.dart';
|
||||
import 'package:nextcloudapp/service/snackbar_service.dart';
|
||||
import 'package:nextcloudapp/components/mime_type.dart';
|
||||
import 'package:file_icon/file_icon.dart';
|
||||
|
||||
class HomeSceen extends StatefulWidget {
|
||||
const HomeSceen({super.key});
|
||||
|
||||
@override
|
||||
State<HomeSceen> createState() => _HomeSceenState();
|
||||
}
|
||||
|
||||
class _HomeSceenState extends State<HomeSceen> {
|
||||
//
|
||||
var _deviceHeight;
|
||||
var _deviceWidth;
|
||||
var _percentage;
|
||||
var _usedMemory;
|
||||
var _totalMemory;
|
||||
var _cwd;
|
||||
|
||||
List<String> _temp = [];
|
||||
List<Files>? _data = [];
|
||||
|
||||
final MimetypeIcon _mimetypeIcon = MimetypeIcon.instance;
|
||||
final FileSize _fileSize = FileSize.instance;
|
||||
final NextcloudProvider _provider = NextcloudProvider.instance;
|
||||
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
_HomeSceenState() {
|
||||
_cwd = "";
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_deviceHeight = MediaQuery.of(context).size.height;
|
||||
_deviceWidth = MediaQuery.of(context).size.width;
|
||||
SnackBarService.instance.buildContext = context;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Container(
|
||||
height: _deviceHeight * 0.10,
|
||||
width: _deviceWidth * 0.20,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
image: DecorationImage(
|
||||
image: AssetImage('assets/images/nextcloud_logo.png'),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text("NextCloud"),
|
||||
],
|
||||
),
|
||||
),
|
||||
drawer: _appDrawerUI(),
|
||||
body: _appBodyUI(),
|
||||
floatingActionButton: _floatingButton());
|
||||
}
|
||||
|
||||
Widget _floatingButton() {
|
||||
return SpeedDial(
|
||||
closeManually: true,
|
||||
animatedIcon: AnimatedIcons.menu_arrow,
|
||||
children: [
|
||||
SpeedDialChild(
|
||||
label: "File Upload",
|
||||
child: Icon(Icons.upload),
|
||||
),
|
||||
SpeedDialChild(
|
||||
label: "Folder Upload",
|
||||
child: Icon(Icons.upload),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _appDrawerUI() {
|
||||
_totalMemory = _provider.userDetails!.quota.total;
|
||||
_usedMemory = _provider.userDetails!.quota.used;
|
||||
_percentage = (_usedMemory / _totalMemory);
|
||||
return NavigationDrawer(
|
||||
backgroundColor: Color.fromARGB(255, 72, 160, 232),
|
||||
children: [
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: _deviceHeight * 0.05,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Text(_provider.userDetails!.displayName),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
_provider.logout();
|
||||
},
|
||||
icon: Icon(Icons.logout_sharp),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: _deviceHeight * 0.20,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Text(
|
||||
'Memory Usage',
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
LinearProgressIndicator(
|
||||
value: _percentage,
|
||||
backgroundColor: Colors.grey[300],
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
_percentage > 0.80 ? Colors.red : Colors.green,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Text(
|
||||
'${(_percentage * 100).toStringAsFixed(2)}% used',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
Text(
|
||||
'${_fileSize.getFileSizeString(
|
||||
bytes: _usedMemory,
|
||||
decimals: 1,
|
||||
)} of ${_fileSize.getFileSizeString(
|
||||
bytes: _totalMemory,
|
||||
decimals: 1,
|
||||
)}',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _appBodyUI() {
|
||||
return SizedBox(
|
||||
height: _deviceHeight * 0.80,
|
||||
child: FutureBuilder(
|
||||
future: _provider.listDirectory(_cwd),
|
||||
builder: (context, _snapshot) {
|
||||
_data = _snapshot.data;
|
||||
if (_data != null) {
|
||||
// WidgetsBinding.instance.addPostFrameCallback(
|
||||
// (_) {
|
||||
// if (_scrollController.hasClients) {
|
||||
// _scrollController
|
||||
// .jumpTo(_scrollController.position.minScrollExtent);
|
||||
// }
|
||||
// },
|
||||
// );
|
||||
|
||||
return Scrollbar(
|
||||
controller: _scrollController,
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
itemCount: _data!.length,
|
||||
itemBuilder: (BuildContext _context, int _index) {
|
||||
return ListTile(
|
||||
onLongPress: () {},
|
||||
onTap: () {
|
||||
setState(() {
|
||||
if (_data![_index].resourceType! == "folder") {
|
||||
if (_index == 0) {
|
||||
_temp.removeAt(_temp.length - 2);
|
||||
_cwd = _temp
|
||||
.join("/")
|
||||
.replaceAll(webdavBase.toString(), '');
|
||||
} else {
|
||||
_cwd = _data![_index]
|
||||
.path!
|
||||
.replaceAll(webdavBase.toString(), '');
|
||||
_temp = _data![_index].path!.split("/");
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
leading: _data![_index].resourceType! == 'folder'
|
||||
? Icon(_mimetypeIcon
|
||||
.getIconForMimeType(_data![_index].contentType!))
|
||||
: FileIcon(
|
||||
_data![_index].name!,
|
||||
size: 32.0,
|
||||
),
|
||||
title: Text(
|
||||
_data![_index].name!,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 15),
|
||||
),
|
||||
subtitle: Text(
|
||||
_index == 0
|
||||
? ''
|
||||
: DateFormat('dd MMM, yyyy hh:mm a')
|
||||
.format(_data![_index].modifiedDate!),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
trailing: Text(
|
||||
_index != 0
|
||||
? _fileSize.getFileSizeString(
|
||||
bytes: _data![_index].size!,
|
||||
decimals: 2,
|
||||
)
|
||||
: '',
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
} else if (_snapshot.hasError) {
|
||||
return Align(
|
||||
child: Text("No Data"),
|
||||
);
|
||||
} else if (_snapshot.connectionState == ConnectionState.waiting) {
|
||||
return Align(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
} else {
|
||||
return Align(
|
||||
child: Text("Undecided"),
|
||||
);
|
||||
;
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+101
-77
@@ -1,8 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:nextcloudapp/api/login_api.dart';
|
||||
import 'package:nextcloudapp/components/default_values.dart';
|
||||
import 'package:nextcloudapp/screens/welcome_screen.dart';
|
||||
import 'package:nextcloudapp/provider/nextcloud_provider.dart';
|
||||
import 'package:nextcloudapp/service/snackbar_service.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
const LoginScreen({super.key});
|
||||
@@ -14,44 +13,55 @@ class LoginScreen extends StatefulWidget {
|
||||
class _LoginScreenState extends State<LoginScreen> {
|
||||
var _deviceHeight;
|
||||
var _deviceWidth;
|
||||
var urlController;
|
||||
var usernameController;
|
||||
var passwordController;
|
||||
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
|
||||
var checked = false;
|
||||
var _url;
|
||||
var _username;
|
||||
var _password;
|
||||
var enableInput = true;
|
||||
var savePassword = false;
|
||||
var obscureText = true;
|
||||
|
||||
NextcloudProvider _provider = NextcloudProvider.instance;
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SnackBarService.instance.buildContext = context;
|
||||
_deviceHeight = MediaQuery.of(context).size.height;
|
||||
_deviceWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
return Scaffold(
|
||||
body: Align(
|
||||
child: Container(
|
||||
height: _deviceHeight * 0.60,
|
||||
padding: EdgeInsets.symmetric(horizontal: _deviceWidth * 0.05),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_inputField(),
|
||||
_passwordSaveField(),
|
||||
_signInField(),
|
||||
],
|
||||
),
|
||||
child: ChangeNotifierProvider<NextcloudProvider>.value(
|
||||
value: _provider,
|
||||
child: _loginPage(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _inputField() {
|
||||
Widget _loginPage() {
|
||||
return Builder(builder: (BuildContext _context) {
|
||||
SnackBarService.instance.buildContext = _context;
|
||||
_provider = Provider.of<NextcloudProvider>(_context);
|
||||
return Container(
|
||||
height: _deviceHeight * 0.80,
|
||||
padding: EdgeInsets.symmetric(horizontal: _deviceWidth * 0.05),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_inputForm(),
|
||||
_passwordSaveField(),
|
||||
_signInButton(_context),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _inputForm() {
|
||||
return SizedBox(
|
||||
height: _deviceHeight * 0.25,
|
||||
height: _deviceHeight * 0.27,
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
onChanged: () {
|
||||
@@ -63,64 +73,84 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextFormField(
|
||||
initialValue: "https://storage.theonasanyas.com",
|
||||
enabled: enableInput,
|
||||
keyboardType: TextInputType.url,
|
||||
textInputAction: TextInputAction.next,
|
||||
cursorColor: Colors.white,
|
||||
cursorColor: Colors.black,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
),
|
||||
onSaved: (_input) {
|
||||
setState(() {
|
||||
urlController = _input!;
|
||||
_url = _input!;
|
||||
});
|
||||
},
|
||||
validator: (_input) {
|
||||
return null;
|
||||
return _input!.isNotEmpty ? null : "nnnns";
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
decoration: InputDecoration(
|
||||
hintText: "hosting URL",
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.white)
|
||||
),
|
||||
borderSide: BorderSide(color: Colors.white)),
|
||||
prefixIcon: Padding(
|
||||
padding: EdgeInsets.all(defaultPadding),
|
||||
padding: EdgeInsets.all(15.0),
|
||||
child: Icon(Icons.computer_outlined),
|
||||
),
|
||||
),
|
||||
),
|
||||
TextFormField(
|
||||
initialValue: "fluttertest",
|
||||
enabled: enableInput,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
textInputAction: TextInputAction.next,
|
||||
cursorColor: Colors.black,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
),
|
||||
onSaved: (_input) {
|
||||
if (_input == null) {}
|
||||
setState(() {
|
||||
usernameController = _input!;
|
||||
_username = _input!;
|
||||
});
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
validator: (_input) {
|
||||
return _input!.isNotEmpty ? null : "nnnns";
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: "username",
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.white)),
|
||||
prefixIcon: Padding(
|
||||
padding: EdgeInsets.all(defaultPadding),
|
||||
padding: EdgeInsets.all(15.0),
|
||||
child: Icon(Icons.person),
|
||||
),
|
||||
),
|
||||
),
|
||||
TextFormField(
|
||||
initialValue: "9tWx8XAv545+ijnBxOrh",
|
||||
enabled: enableInput,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
textInputAction: TextInputAction.done,
|
||||
cursorColor: Colors.black,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
),
|
||||
obscureText: obscureText,
|
||||
onSaved: (_input) {
|
||||
setState(() {
|
||||
passwordController = _input!;
|
||||
_password = _input!;
|
||||
});
|
||||
},
|
||||
cursorColor: primaryColor,
|
||||
validator: (_input) {
|
||||
return _input!.isNotEmpty ? null : "nnnns";
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: "Password",
|
||||
hintText: "password",
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.white)),
|
||||
prefixIcon: const Padding(
|
||||
padding: EdgeInsets.all(defaultPadding),
|
||||
padding: EdgeInsets.all(15.0),
|
||||
child: Icon(Icons.lock),
|
||||
),
|
||||
suffixIcon: IconButton(
|
||||
@@ -146,51 +176,45 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Checkbox(
|
||||
value: checked,
|
||||
value: savePassword,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
checked = value!;
|
||||
savePassword = value!;
|
||||
});
|
||||
}),
|
||||
const Text('Save Password'),
|
||||
const Text('save password'),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _signInField() {
|
||||
return ElevatedButton(
|
||||
onPressed: () {
|
||||
login(urlController.value.text, usernameController.value.text,
|
||||
passwordController.value.text)
|
||||
.then((
|
||||
status,
|
||||
) {
|
||||
if (status.startsWith('successful')) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return const WelcomeScreen();
|
||||
},
|
||||
Widget _signInButton(BuildContext _context) {
|
||||
return _provider.status == AuthStatus.authenticating
|
||||
? Align(
|
||||
alignment: Alignment.center,
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
: SizedBox(
|
||||
height: _deviceHeight * 0.06,
|
||||
width: _deviceWidth * 0.30,
|
||||
child: MaterialButton(
|
||||
onPressed: () async {
|
||||
enableInput = !enableInput;
|
||||
if (_formKey.currentState!.validate()) {
|
||||
//Login User
|
||||
await _provider.login(
|
||||
_url, _username, _password, savePassword);
|
||||
}
|
||||
enableInput = !enableInput;
|
||||
},
|
||||
color: Colors.grey,
|
||||
child: Text(
|
||||
'login',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
SnackBarService.instance.showSnackBarSuccess(status);
|
||||
} else {
|
||||
SnackBarService.instance.showSnackBarError(status);
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"sign in".toUpperCase(),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
const Icon(Icons.login)
|
||||
],
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class WelcomeScreen extends StatefulWidget {
|
||||
const WelcomeScreen({super.key});
|
||||
|
||||
@override
|
||||
State<WelcomeScreen> createState() => _WelcomeScreenState();
|
||||
}
|
||||
|
||||
class _WelcomeScreenState extends State<WelcomeScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Scaffold(
|
||||
body: Column(
|
||||
children: [
|
||||
|
||||
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ class NavigationService {
|
||||
navigatorKey = GlobalKey<NavigatorState>();
|
||||
}
|
||||
|
||||
Future<dynamic>? navigateToReplacement(String _routeName) {
|
||||
Future<dynamic> navigateToReplacement(String _routeName) {
|
||||
return navigatorKey!.currentState!.pushReplacementNamed(_routeName);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user