Skip to content

Commit

Permalink
feat(neon_dashboard): Init
Browse files Browse the repository at this point in the history
Signed-off-by: jld3103 <jld3103yt@gmail.com>
  • Loading branch information
provokateurin committed Oct 19, 2023
1 parent d76e515 commit 96dfed0
Show file tree
Hide file tree
Showing 38 changed files with 1,147 additions and 0 deletions.
1 change: 1 addition & 0 deletions .cspell/dart_flutter.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ autofocus
endtemplate
expando
gapless
goldens
lerp
pubspec
sublist
Expand Down
1 change: 1 addition & 0 deletions commitlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ rules:
- dynamite_runtime
- file_icons
- neon
- neon_dashboard
- neon_files
- neon_news
- neon_notes
Expand Down
10 changes: 10 additions & 0 deletions packages/neon/neon_dashboard/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "ead455963c12b453cdb2358cad34969c76daf180"
channel: "stable"

project_type: package
1 change: 1 addition & 0 deletions packages/neon/neon_dashboard/LICENSE
5 changes: 5 additions & 0 deletions packages/neon/neon_dashboard/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include: package:neon_lints/flutter.yaml

analyzer:
exclude:
- lib/l10n/**
Binary file added packages/neon/neon_dashboard/assets/app.svg.vec
Binary file not shown.
Empty file.
6 changes: 6 additions & 0 deletions packages/neon/neon_dashboard/l10n.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
arb-dir: lib/l10n
template-arb-file: en.arb
output-localization-file: localizations.dart
synthetic-package: false
output-dir: lib/l10n
nullable-getter: false
4 changes: 4 additions & 0 deletions packages/neon/neon_dashboard/lib/l10n/en.arb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"@@locale": "en",
"noEntries": "No entries"
}
125 changes: 125 additions & 0 deletions packages/neon/neon_dashboard/lib/l10n/localizations.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/intl.dart' as intl;

import 'localizations_en.dart';

/// Callers can lookup localized strings with an instance of AppLocalizations
/// returned by `AppLocalizations.of(context)`.
///
/// Applications need to include `AppLocalizations.delegate()` in their app's
/// `localizationDelegates` list, and the locales they support in the app's
/// `supportedLocales` list. For example:
///
/// ```dart
/// import 'l10n/localizations.dart';
///
/// return MaterialApp(
/// localizationsDelegates: AppLocalizations.localizationsDelegates,
/// supportedLocales: AppLocalizations.supportedLocales,
/// home: MyApplicationHome(),
/// );
/// ```
///
/// ## Update pubspec.yaml
///
/// Please make sure to update your pubspec.yaml to include the following
/// packages:
///
/// ```yaml
/// dependencies:
/// # Internationalization support.
/// flutter_localizations:
/// sdk: flutter
/// intl: any # Use the pinned version from flutter_localizations
///
/// # Rest of dependencies
/// ```
///
/// ## iOS Applications
///
/// iOS applications define key application metadata, including supported
/// locales, in an Info.plist file that is built into the application bundle.
/// To configure the locales supported by your app, you’ll need to edit this
/// file.
///
/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
/// Then, in the Project Navigator, open the Info.plist file under the Runner
/// project’s Runner folder.
///
/// Next, select the Information Property List item, select Add Item from the
/// Editor menu, then select Localizations from the pop-up menu.
///
/// Select and expand the newly-created Localizations item then, for each
/// locale your application supports, add a new item and select the locale
/// you wish to add from the pop-up menu in the Value field. This list should
/// be consistent with the languages listed in the AppLocalizations.supportedLocales
/// property.
abstract class AppLocalizations {
AppLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString());

final String localeName;

static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations)!;
}

static const LocalizationsDelegate<AppLocalizations> delegate = _AppLocalizationsDelegate();

/// A list of this localizations delegate along with the default localizations
/// delegates.
///
/// Returns a list of localizations delegates containing this delegate along with
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
/// and GlobalWidgetsLocalizations.delegate.
///
/// Additional delegates can be added by appending to this list in
/// MaterialApp. This list does not have to be used at all if a custom list
/// of delegates is preferred or required.
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates = <LocalizationsDelegate<dynamic>>[
delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
];

/// A list of this localizations delegate's supported locales.
static const List<Locale> supportedLocales = <Locale>[Locale('en')];

/// No description provided for @noEntries.
///
/// In en, this message translates to:
/// **'No entries'**
String get noEntries;
}

class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();

@override
Future<AppLocalizations> load(Locale locale) {
return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));
}

@override
bool isSupported(Locale locale) => <String>['en'].contains(locale.languageCode);

@override
bool shouldReload(_AppLocalizationsDelegate old) => false;
}

AppLocalizations lookupAppLocalizations(Locale locale) {
// Lookup logic when only language code is specified.
switch (locale.languageCode) {
case 'en':
return AppLocalizationsEn();
}

throw FlutterError('AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
'an issue with the localizations generation tool. Please file an issue '
'on GitHub with a reproducible sample app and the gen-l10n configuration '
'that was used.');
}
9 changes: 9 additions & 0 deletions packages/neon/neon_dashboard/lib/l10n/localizations_en.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'localizations.dart';

/// The translations for English (`en`).
class AppLocalizationsEn extends AppLocalizations {
AppLocalizationsEn([String locale = 'en']) : super(locale);

@override
String get noEntries => 'No entries';
}
1 change: 1 addition & 0 deletions packages/neon/neon_dashboard/lib/neon_dashboard.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'src/app.dart';
44 changes: 44 additions & 0 deletions packages/neon/neon_dashboard/lib/src/app.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:neon/models.dart';
import 'package:neon_dashboard/l10n/localizations.dart';
import 'package:neon_dashboard/src/blocs/dashboard.dart';
import 'package:neon_dashboard/src/options.dart';
import 'package:neon_dashboard/src/pages/main.dart';
import 'package:neon_dashboard/src/routes.dart';
import 'package:nextcloud/core.dart' as core;
import 'package:nextcloud/nextcloud.dart';

/// Implementation of the server `dashboard` app.
class DashboardApp extends AppImplementation<DashboardBloc, DashboardAppSpecificOptions> {
/// Creates a new Dashboard app implementation instance.
DashboardApp();

@override
final String id = AppIDs.dashboard;

@override
final LocalizationsDelegate<AppLocalizations> localizationsDelegate = AppLocalizations.delegate;

@override
final List<Locale> supportedLocales = AppLocalizations.supportedLocales;

@override
late final DashboardAppSpecificOptions options = DashboardAppSpecificOptions(storage);

@override
DashboardBloc buildBloc(final Account account) => DashboardBloc(account);

@override
final Widget page = const DashboardMainPage();

@override
final RouteBase route = $dashboardAppRoute;

@override
(bool?, String?) isSupported(
final Account account,
final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities,
) =>
const (null, null);
}
103 changes: 103 additions & 0 deletions packages/neon/neon_dashboard/lib/src/blocs/dashboard.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:neon/blocs.dart';
import 'package:neon/models.dart';
import 'package:neon_dashboard/src/utils/find.dart';
import 'package:nextcloud/dashboard.dart' as dashboard;
import 'package:rxdart/rxdart.dart';

/// Events for [DashboardBloc].
abstract class DashboardBlocEvents {}

/// States for [DashboardBloc].
abstract class DashboardBlocStates {
/// Dashboard widgets that are displayed.
BehaviorSubject<Result<Map<dashboard.Widget, dashboard.WidgetItems?>>> get widgets;
}

/// Implements the business logic for fetching dashboard widgets and their items.
class DashboardBloc extends InteractiveBloc implements DashboardBlocEvents, DashboardBlocStates {
/// Creates a new Dashboard Bloc instance.
///
/// Automatically starts fetching the widgets and their items and refreshes everything every 30 seconds.
DashboardBloc(this._account) {
unawaited(refresh());

_timer = TimerBloc().registerTimer(const Duration(seconds: 30), refresh);
}

final Account _account;
late final NeonTimer _timer;

@override
BehaviorSubject<Result<Map<dashboard.Widget, dashboard.WidgetItems?>>> widgets = BehaviorSubject();

@override
void dispose() {
_timer.cancel();
unawaited(widgets.close());
super.dispose();
}

@override
Future<void> refresh() async {
widgets.add(widgets.valueOrNull?.asLoading() ?? Result.loading());

try {
final widgets = <String, dashboard.WidgetItems?>{};
final v1WidgetIDs = <String>[];
final v2WidgetIDs = <String>[];

final response = await _account.client.dashboard.dashboardApi.getWidgets();

for (final widget in response.body.ocs.data.values) {
if (widget.itemApiVersions.contains(2)) {
v2WidgetIDs.add(widget.id);
} else if (widget.itemApiVersions.contains(1)) {
v1WidgetIDs.add(widget.id);
} else {
debugPrint('Widget supports none of the API versions: ${widget.id}');
}
}

if (v1WidgetIDs.isNotEmpty) {
debugPrint('Loading v1 widgets: ${v1WidgetIDs.join(', ')}');

final response = await _account.client.dashboard.dashboardApi.getWidgetItems(widgets: v1WidgetIDs);
for (final entry in response.body.ocs.data.entries) {
widgets[entry.key] = dashboard.WidgetItems(
(final b) => b
..items.replace(entry.value)
..emptyContentMessage = ''
..halfEmptyContentMessage = '',
);
}
}

if (v2WidgetIDs.isNotEmpty) {
debugPrint('Loading v2 widgets: ${v2WidgetIDs.join(', ')}');

final response = await _account.client.dashboard.dashboardApi.getWidgetItemsV2(widgets: v2WidgetIDs);
widgets.addEntries(response.body.ocs.data.entries);
}

this.widgets.add(
Result.success(
widgets.map(
(final id, final items) => MapEntry(
response.body.ocs.data.values.find(id),
items,
),
),
),
);
} catch (e, s) {
debugPrint(e.toString());
debugPrint(s.toString());

widgets.add(Result.error(e));
return;
}
}
}
10 changes: 10 additions & 0 deletions packages/neon/neon_dashboard/lib/src/options.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import 'package:neon/settings.dart';

/// Settings options specific to the dashboard app.
class DashboardAppSpecificOptions extends NextcloudAppOptions {
/// Creates a new dashboard options instance.
DashboardAppSpecificOptions(super.storage) {
super.categories = [];
super.options = [];
}
}
Loading

0 comments on commit 96dfed0

Please sign in to comment.