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) ...[
CachedURLImage(
url: article.mediaThumbnail!,
requestManager: Provider.of<RequestManager>(context),
client: RxBlocProvider.of<AccountsBloc>(context).activeAccount.value!.client,
account: RxBlocProvider.of<AccountsBloc>(context).activeAccount.value!,
width: 100,
height: 50,
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 != ''
? CachedURLImage(
url: feed.faviconLink!,
requestManager: Provider.of<RequestManager>(context),
client: RxBlocProvider.of<AccountsBloc>(context).activeAccount.value!.client,
account: RxBlocProvider.of<AccountsBloc>(context).activeAccount.value!,
height: size,
width: size,
)

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

@ -1,10 +1,12 @@
library notifications;
import 'package:flutter/material.dart';
import 'package:flutter_rx_bloc/flutter_rx_bloc.dart';
import 'package:intersperse/intersperse.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:neon/l10n/localizations.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/neon.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 {
NotificationsBloc(
this.options,
this.requestManager,
this.client,
this._requestManager,
this._client,
) {
_$refreshEvent.listen((final _) => _loadNotifications());
_$deleteNotificationEvent.listen((final id) {
_wrapAction(() async => client.notifications.deleteNotification(id: id));
_wrapAction(() async => _client.notifications.deleteNotification(id: id));
});
_$deleteAllNotificationsEvent.listen((final notification) {
_wrapAction(() async => client.notifications.deleteAllNotifications());
_wrapAction(() async => _client.notifications.deleteAllNotifications());
});
_notificationsSubject.listen((final result) {
@ -52,7 +52,7 @@ class NotificationsBloc extends $NotificationsBloc {
}
void _wrapAction(final Future Function() call) {
final stream = requestManager.wrapWithoutCache(call).asBroadcastStream();
final stream = _requestManager.wrapWithoutCache(call).asBroadcastStream();
stream.whereError().listen(_errorsStreamController.add);
stream.whereSuccess().listen((final _) async {
_loadNotifications();
@ -60,11 +60,11 @@ class NotificationsBloc extends $NotificationsBloc {
}
void _loadNotifications() {
requestManager
_requestManager
.wrapNextcloud<List<NotificationsNotification>, NotificationsListNotifications>(
client.id,
_client.id,
'notifications-notifications',
() async => client.notifications.listNotifications(),
() async => _client.notifications.listNotifications(),
(final response) => response.ocs!.data!,
previousData: _notificationsSubject.valueOrNull?.data,
)
@ -72,8 +72,8 @@ class NotificationsBloc extends $NotificationsBloc {
}
final NotificationsAppSpecificOptions options;
final RequestManager requestManager;
final NextcloudClient client;
final RequestManager _requestManager;
final NextcloudClient _client;
final _notificationsSubject = BehaviorSubject<Result<List<NotificationsNotification>>>();
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,
child: CachedURLImage(
url: notification.icon!,
requestManager: widget.bloc.requestManager,
client: widget.bloc.client,
account: RxBlocProvider.of<AccountsBloc>(context).activeAccount.value!,
width: 40,
height: 40,
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:flutter/material.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_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:flutter_rx_bloc/flutter_rx_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:http/http.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl_standalone.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.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 GlobalOptions _globalOptions;
late RequestManager _requestManager;
late CapabilitiesBloc _capabilitiesBloc;
late AppsBloc _appsBloc;
@ -46,9 +45,8 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
windowManager.addListener(this);
}
_requestManager = Provider.of<RequestManager>(context, listen: false);
_capabilitiesBloc = CapabilitiesBloc(
_requestManager,
Provider.of<RequestManager>(context, listen: false),
widget.account.client,
);
_capabilitiesBloc.capabilities.listen((final result) async {
@ -446,8 +444,7 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
Flexible(
child: CachedURLImage(
url: capabilitiesData.capabilities!.theming!.logo!,
requestManager: _requestManager,
client: widget.account.client,
account: widget.account,
),
),
],
@ -633,8 +630,7 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
child: capabilitiesData?.capabilities?.theming?.logo != null
? CachedURLImage(
url: capabilitiesData!.capabilities!.theming!.logo!,
requestManager: _requestManager,
client: widget.account.client,
account: widget.account,
)
: null,
)

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

@ -1,10 +1,11 @@
part of '../neon.dart';
final _cacheManager = DefaultCacheManager();
class CachedURLImage extends StatelessWidget {
const CachedURLImage({
required this.url,
required this.requestManager,
required this.client,
required this.account,
this.height,
this.width,
this.fit,
@ -13,8 +14,7 @@ class CachedURLImage extends StatelessWidget {
});
final String url;
final RequestManager requestManager;
final NextcloudClient client;
final Account account;
final double? height;
final double? width;
@ -24,71 +24,59 @@ class CachedURLImage extends StatelessWidget {
final Color? color;
@override
Widget build(final BuildContext context) => SizedBox(
height: height,
width: width,
child: Center(
child: ResultStreamBuilder<Uint8List>(
// 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.
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(),
),
],
],
),
),
Widget build(final BuildContext context) => FutureBuilder<File>(
// Really weird false positive
// ignore: discarded_futures
future: _cacheManager.getSingleFile(
url,
headers: account.client.baseHeaders,
),
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
source: sdk
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:
dependency: "direct main"
description:
@ -703,6 +710,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.7"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.1"
permission_handler:
dependency: "direct main"
description:
@ -1219,6 +1233,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
uuid:
dependency: transitive
description:
name: uuid
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.6"
vector_math:
dependency: transitive
description:

1
packages/neon/pubspec.yaml

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

Loading…
Cancel
Save