Browse Source

Merge pull request #73 from jld3103/fix/cached-url-images

neon: Fix cached url images
pull/74/head
jld3103 2 years ago committed by GitHub
parent
commit
241bacfe19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      packages/neon/lib/src/apps/news/widgets/articles_view.dart
  2. 3
      packages/neon/lib/src/apps/news/widgets/feed_icon.dart
  3. 2
      packages/neon/lib/src/apps/notifications/app.dart
  4. 20
      packages/neon/lib/src/apps/notifications/blocs/notifications.dart
  5. 3
      packages/neon/lib/src/apps/notifications/pages/main.dart
  6. 2
      packages/neon/lib/src/neon.dart
  7. 10
      packages/neon/lib/src/pages/home/home.dart
  8. 126
      packages/neon/lib/src/widgets/cached_url_image.dart
  9. 21
      packages/neon/pubspec.lock
  10. 1
      packages/neon/pubspec.yaml

3
packages/neon/lib/src/apps/news/widgets/articles_view.dart

@ -181,8 +181,7 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
if (article.mediaThumbnail != null) ...[ if (article.mediaThumbnail != null) ...[
CachedURLImage( CachedURLImage(
url: article.mediaThumbnail!, url: article.mediaThumbnail!,
requestManager: Provider.of<RequestManager>(context), account: RxBlocProvider.of<AccountsBloc>(context).activeAccount.value!,
client: RxBlocProvider.of<AccountsBloc>(context).activeAccount.value!.client,
width: 100, width: 100,
height: 50, height: 50,
fit: BoxFit.cover, fit: BoxFit.cover,

3
packages/neon/lib/src/apps/news/widgets/feed_icon.dart

@ -21,8 +21,7 @@ class NewsFeedIcon extends StatelessWidget {
child: feed.faviconLink != null && feed.faviconLink != '' child: feed.faviconLink != null && feed.faviconLink != ''
? CachedURLImage( ? CachedURLImage(
url: feed.faviconLink!, url: feed.faviconLink!,
requestManager: Provider.of<RequestManager>(context), account: RxBlocProvider.of<AccountsBloc>(context).activeAccount.value!,
client: RxBlocProvider.of<AccountsBloc>(context).activeAccount.value!.client,
height: size, height: size,
width: size, width: size,
) )

2
packages/neon/lib/src/apps/notifications/app.dart

@ -1,10 +1,12 @@
library notifications; library notifications;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_rx_bloc/flutter_rx_bloc.dart';
import 'package:intersperse/intersperse.dart'; import 'package:intersperse/intersperse.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:neon/l10n/localizations.dart'; import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/apps/notifications/blocs/notifications.dart'; import 'package:neon/src/apps/notifications/blocs/notifications.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/blocs/apps.dart'; import 'package:neon/src/blocs/apps.dart';
import 'package:neon/src/neon.dart'; import 'package:neon/src/neon.dart';
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';

20
packages/neon/lib/src/apps/notifications/blocs/notifications.dart

@ -29,17 +29,17 @@ abstract class NotificationsBlocStates {
class NotificationsBloc extends $NotificationsBloc { class NotificationsBloc extends $NotificationsBloc {
NotificationsBloc( NotificationsBloc(
this.options, this.options,
this.requestManager, this._requestManager,
this.client, this._client,
) { ) {
_$refreshEvent.listen((final _) => _loadNotifications()); _$refreshEvent.listen((final _) => _loadNotifications());
_$deleteNotificationEvent.listen((final id) { _$deleteNotificationEvent.listen((final id) {
_wrapAction(() async => client.notifications.deleteNotification(id: id)); _wrapAction(() async => _client.notifications.deleteNotification(id: id));
}); });
_$deleteAllNotificationsEvent.listen((final notification) { _$deleteAllNotificationsEvent.listen((final notification) {
_wrapAction(() async => client.notifications.deleteAllNotifications()); _wrapAction(() async => _client.notifications.deleteAllNotifications());
}); });
_notificationsSubject.listen((final result) { _notificationsSubject.listen((final result) {
@ -52,7 +52,7 @@ class NotificationsBloc extends $NotificationsBloc {
} }
void _wrapAction(final Future Function() call) { void _wrapAction(final Future Function() call) {
final stream = requestManager.wrapWithoutCache(call).asBroadcastStream(); final stream = _requestManager.wrapWithoutCache(call).asBroadcastStream();
stream.whereError().listen(_errorsStreamController.add); stream.whereError().listen(_errorsStreamController.add);
stream.whereSuccess().listen((final _) async { stream.whereSuccess().listen((final _) async {
_loadNotifications(); _loadNotifications();
@ -60,11 +60,11 @@ class NotificationsBloc extends $NotificationsBloc {
} }
void _loadNotifications() { void _loadNotifications() {
requestManager _requestManager
.wrapNextcloud<List<NotificationsNotification>, NotificationsListNotifications>( .wrapNextcloud<List<NotificationsNotification>, NotificationsListNotifications>(
client.id, _client.id,
'notifications-notifications', 'notifications-notifications',
() async => client.notifications.listNotifications(), () async => _client.notifications.listNotifications(),
(final response) => response.ocs!.data!, (final response) => response.ocs!.data!,
previousData: _notificationsSubject.valueOrNull?.data, previousData: _notificationsSubject.valueOrNull?.data,
) )
@ -72,8 +72,8 @@ class NotificationsBloc extends $NotificationsBloc {
} }
final NotificationsAppSpecificOptions options; final NotificationsAppSpecificOptions options;
final RequestManager requestManager; final RequestManager _requestManager;
final NextcloudClient client; final NextcloudClient _client;
final _notificationsSubject = BehaviorSubject<Result<List<NotificationsNotification>>>(); final _notificationsSubject = BehaviorSubject<Result<List<NotificationsNotification>>>();
final _errorsStreamController = StreamController<Exception>(); final _errorsStreamController = StreamController<Exception>();

3
packages/neon/lib/src/apps/notifications/pages/main.dart

@ -115,8 +115,7 @@ class _NotificationsMainPageState extends State<NotificationsMainPage> {
height: 40, height: 40,
child: CachedURLImage( child: CachedURLImage(
url: notification.icon!, url: notification.icon!,
requestManager: widget.bloc.requestManager, account: RxBlocProvider.of<AccountsBloc>(context).activeAccount.value!,
client: widget.bloc.client,
width: 40, width: 40,
height: 40, height: 40,
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,

2
packages/neon/lib/src/neon.dart

@ -11,13 +11,13 @@ import 'package:file_picker/file_picker.dart';
import 'package:filesize/filesize.dart'; import 'package:filesize/filesize.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_file_dialog/flutter_file_dialog.dart'; import 'package:flutter_file_dialog/flutter_file_dialog.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:flutter_rx_bloc/flutter_rx_bloc.dart'; import 'package:flutter_rx_bloc/flutter_rx_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl_standalone.dart'; import 'package:intl/intl_standalone.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:neon/l10n/localizations.dart'; import 'package:neon/l10n/localizations.dart';

10
packages/neon/lib/src/pages/home/home.dart

@ -24,7 +24,6 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
late NeonPlatform _platform; late NeonPlatform _platform;
late GlobalOptions _globalOptions; late GlobalOptions _globalOptions;
late RequestManager _requestManager;
late CapabilitiesBloc _capabilitiesBloc; late CapabilitiesBloc _capabilitiesBloc;
late AppsBloc _appsBloc; late AppsBloc _appsBloc;
@ -46,9 +45,8 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
windowManager.addListener(this); windowManager.addListener(this);
} }
_requestManager = Provider.of<RequestManager>(context, listen: false);
_capabilitiesBloc = CapabilitiesBloc( _capabilitiesBloc = CapabilitiesBloc(
_requestManager, Provider.of<RequestManager>(context, listen: false),
widget.account.client, widget.account.client,
); );
_capabilitiesBloc.capabilities.listen((final result) async { _capabilitiesBloc.capabilities.listen((final result) async {
@ -446,8 +444,7 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
Flexible( Flexible(
child: CachedURLImage( child: CachedURLImage(
url: capabilitiesData.capabilities!.theming!.logo!, url: capabilitiesData.capabilities!.theming!.logo!,
requestManager: _requestManager, account: widget.account,
client: widget.account.client,
), ),
), ),
], ],
@ -633,8 +630,7 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
child: capabilitiesData?.capabilities?.theming?.logo != null child: capabilitiesData?.capabilities?.theming?.logo != null
? CachedURLImage( ? CachedURLImage(
url: capabilitiesData!.capabilities!.theming!.logo!, url: capabilitiesData!.capabilities!.theming!.logo!,
requestManager: _requestManager, account: widget.account,
client: widget.account.client,
) )
: null, : null,
) )

126
packages/neon/lib/src/widgets/cached_url_image.dart

@ -1,10 +1,11 @@
part of '../neon.dart'; part of '../neon.dart';
final _cacheManager = DefaultCacheManager();
class CachedURLImage extends StatelessWidget { class CachedURLImage extends StatelessWidget {
const CachedURLImage({ const CachedURLImage({
required this.url, required this.url,
required this.requestManager, required this.account,
required this.client,
this.height, this.height,
this.width, this.width,
this.fit, this.fit,
@ -13,8 +14,7 @@ class CachedURLImage extends StatelessWidget {
}); });
final String url; final String url;
final RequestManager requestManager; final Account account;
final NextcloudClient client;
final double? height; final double? height;
final double? width; final double? width;
@ -24,71 +24,59 @@ class CachedURLImage extends StatelessWidget {
final Color? color; final Color? color;
@override @override
Widget build(final BuildContext context) => SizedBox( Widget build(final BuildContext context) => FutureBuilder<File>(
height: height, // Really weird false positive
width: width, // ignore: discarded_futures
child: Center( future: _cacheManager.getSingleFile(
child: ResultStreamBuilder<Uint8List>( url,
// TODO: Cache this properly. It was not working in listviews where the old image was still rendered in the same index, until it was scrolled out of view. headers: account.client.baseHeaders,
stream: requestManager.wrapBytes(
client.id,
'image-${base64.encode(url.codeUnits)}',
() async => (await http.get(
Uri.parse(url),
headers: client.baseHeaders,
))
.bodyBytes,
preferCache: true,
),
builder: (
final context,
final data,
final error,
final loading,
) =>
Stack(
children: [
if (data != null) ...[
SizedBox(
height: height,
width: width,
child: Builder(
builder: (final context) {
try {
// TODO: Is this safe enough? Research in XML spec if a space is allowed between the < and the tag name
if (utf8.decode(data).contains('<svg')) {
return SvgPicture.memory(
data,
color: color,
);
}
} catch (_) {
// If the data is not UTF-8
}
return Image.memory(
data,
fit: fit,
);
},
),
),
],
if (error != null) ...[
Icon(
Icons.error_outline,
size: height != null && width != null ? min(height!, width!) : height ?? width,
),
],
if (loading) ...[
SizedBox(
width: width,
child: const CustomLinearProgressIndicator(),
),
],
],
),
),
), ),
builder: (final context, final fileSnapshot) {
if (fileSnapshot.hasData) {
final content = fileSnapshot.data!.readAsBytesSync();
var isSvg = false;
if (Uri.parse(url).path.endsWith('.svg')) {
isSvg = true;
}
if (!isSvg) {
try {
// TODO: Is this safe enough?
if (utf8.decode(content).contains('<svg')) {
isSvg = true;
}
} catch (_) {
// If the data is not UTF-8
}
}
if (isSvg) {
return SvgPicture.memory(
content,
height: height,
width: width,
fit: fit ?? BoxFit.contain,
color: color,
);
}
return Image.memory(
content,
height: height,
width: width,
fit: fit,
);
}
if (fileSnapshot.hasError) {
return Icon(
Icons.error_outline,
size: height != null && width != null ? min(height!, width!) : height ?? width,
);
}
return SizedBox(
width: width,
child: const CustomLinearProgressIndicator(),
);
},
); );
} }

21
packages/neon/pubspec.lock

@ -244,6 +244,13 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_cache_manager:
dependency: "direct main"
description:
name: flutter_cache_manager
url: "https://pub.dartlang.org"
source: hosted
version: "3.3.0"
flutter_dotenv: flutter_dotenv:
dependency: "direct main" dependency: "direct main"
description: description:
@ -703,6 +710,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.7" version: "2.0.7"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.1"
permission_handler: permission_handler:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1219,6 +1233,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.1" version: "3.0.1"
uuid:
dependency: transitive
description:
name: uuid
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.6"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:

1
packages/neon/pubspec.yaml

@ -15,6 +15,7 @@ dependencies:
filesize: ^2.0.1 filesize: ^2.0.1
flutter: flutter:
sdk: flutter sdk: flutter
flutter_cache_manager: ^3.3.0
flutter_dotenv: ^5.0.2 flutter_dotenv: ^5.0.2
flutter_file_dialog: ^2.3.0 flutter_file_dialog: ^2.3.0
flutter_html: ^3.0.0-alpha.3 flutter_html: ^3.0.0-alpha.3

Loading…
Cancel
Save