Skip to content

Commit

Permalink
Merge pull request #2682 from nextcloud/feat/neon_rich_text
Browse files Browse the repository at this point in the history
feat(neon_rich_text): Init
  • Loading branch information
provokateurin authored Dec 8, 2024
2 parents b5df122 + 0a3aa4c commit f47620f
Show file tree
Hide file tree
Showing 39 changed files with 1,119 additions and 950 deletions.
221 changes: 94 additions & 127 deletions .github/workflows/dependency_overrides.patch

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions commitlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ rules:
- neon_http_client
- neon_lints
- neon_lints_test
- neon_rich_text
- neon_storage
- news_app
- nextcloud
Expand Down
7 changes: 7 additions & 0 deletions packages/neon_framework/example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,13 @@ packages:
relative: true
source: path
version: "1.0.0"
neon_rich_text:
dependency: "direct overridden"
description:
path: "../packages/neon_rich_text"
relative: true
source: path
version: "0.1.0"
nested:
dependency: transitive
description:
Expand Down
4 changes: 3 additions & 1 deletion packages/neon_framework/example/pubspec_overrides.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# melos_managed_dependency_overrides: account_repository,cookie_store,dashboard_app,dynamite_runtime,files_app,interceptor_http_client,neon_framework,neon_http_client,neon_lints,news_app,nextcloud,notes_app,notifications_app,notifications_push_repository,sort_box,talk_app
# melos_managed_dependency_overrides: account_repository,cookie_store,dashboard_app,dynamite_runtime,files_app,interceptor_http_client,neon_framework,neon_http_client,neon_lints,neon_rich_text,news_app,nextcloud,notes_app,notifications_app,notifications_push_repository,sort_box,talk_app
dependency_overrides:
account_repository:
path: ../packages/account_repository
Expand All @@ -18,6 +18,8 @@ dependency_overrides:
path: ../packages/neon_http_client
neon_lints:
path: ../../neon_lints
neon_rich_text:
path: ../packages/neon_rich_text
news_app:
path: ../packages/news_app
nextcloud:
Expand Down
1 change: 0 additions & 1 deletion packages/neon_framework/lib/widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export 'package:neon_framework/src/widgets/image.dart' hide NeonImage;
export 'package:neon_framework/src/widgets/linear_progress_indicator.dart';
export 'package:neon_framework/src/widgets/list_view.dart';
export 'package:neon_framework/src/widgets/relative_time.dart';
export 'package:neon_framework/src/widgets/rich_text/rich_text.dart';
export 'package:neon_framework/src/widgets/server_icon.dart';
export 'package:neon_framework/src/widgets/user_avatar.dart' hide NeonUserStatusIndicator;
export 'package:neon_framework/src/widgets/user_status_icon.dart';
1 change: 1 addition & 0 deletions packages/neon_framework/packages/neon_rich_text/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include: package:neon_lints/flutter.yaml

custom_lint:
rules:
- avoid_exports: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export 'src/rich_objects/deck_card.dart';
export 'src/rich_objects/fallback.dart';
export 'src/rich_objects/file.dart';
export 'src/rich_objects/mention.dart';
export 'src/rich_text.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
import 'package:neon_rich_text/src/rich_objects/deck_card.dart';
import 'package:neon_rich_text/src/rich_objects/fallback.dart';
import 'package:neon_rich_text/src/rich_objects/file.dart';
import 'package:neon_rich_text/src/rich_objects/mention.dart';
import 'package:nextcloud/core.dart' as core;

/// Renders a rich object [parameter] to be interactive.
@internal
InlineSpan buildRichObjectParameter({
required core.RichObjectParameter parameter,
required TextStyle? textStyle,
required bool isPreview,
}) {
if (isPreview) {
return TextSpan(
text: parameter.name,
style: textStyle,
);
}

return WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: switch (parameter.type) {
core.RichObjectParameter_Type.user ||
core.RichObjectParameter_Type.call ||
core.RichObjectParameter_Type.guest ||
core.RichObjectParameter_Type.userGroup =>
NeonRichObjectMention(
parameter: parameter,
textStyle: textStyle,
),
core.RichObjectParameter_Type.file => NeonRichObjectFile(
parameter: parameter,
textStyle: textStyle,
),
core.RichObjectParameter_Type.deckCard => NeonRichObjectDeckCard(
parameter: parameter,
),
_ => NeonRichObjectFallback(
parameter: parameter,
textStyle: textStyle,
),
},
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,9 @@ import 'package:built_value/json_object.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
import 'package:intersperse/intersperse.dart';
import 'package:neon_framework/src/widgets/rich_text/deck_card.dart';
import 'package:neon_framework/src/widgets/rich_text/fallback.dart';
import 'package:neon_framework/src/widgets/rich_text/file.dart';
import 'package:neon_framework/src/widgets/rich_text/mention.dart';
import 'package:neon_rich_text/src/rich_objects/rich_objects.dart';
import 'package:nextcloud/core.dart' as core;

export 'package:neon_framework/src/widgets/rich_text/deck_card.dart';
export 'package:neon_framework/src/widgets/rich_text/fallback.dart';
export 'package:neon_framework/src/widgets/rich_text/file.dart';
export 'package:neon_framework/src/widgets/rich_text/mention.dart';

/// Renders the [text] as a rich [TextSpan].
TextSpan buildRichTextSpan({
required String text,
Expand Down Expand Up @@ -132,42 +124,3 @@ TextSpan buildRichTextSpan({
children: children,
);
}

/// Renders a rich object [parameter] to be interactive.
InlineSpan buildRichObjectParameter({
required core.RichObjectParameter parameter,
required TextStyle? textStyle,
required bool isPreview,
}) {
if (isPreview) {
return TextSpan(
text: parameter.name,
style: textStyle,
);
}

return WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: switch (parameter.type) {
core.RichObjectParameter_Type.user ||
core.RichObjectParameter_Type.call ||
core.RichObjectParameter_Type.guest ||
core.RichObjectParameter_Type.userGroup =>
NeonRichObjectMention(
parameter: parameter,
textStyle: textStyle,
),
core.RichObjectParameter_Type.file => NeonRichObjectFile(
parameter: parameter,
textStyle: textStyle,
),
core.RichObjectParameter_Type.deckCard => NeonRichObjectDeckCard(
parameter: parameter,
),
_ => NeonRichObjectFallback(
parameter: parameter,
textStyle: textStyle,
),
},
);
}
38 changes: 38 additions & 0 deletions packages/neon_framework/packages/neon_rich_text/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: neon_rich_text
description: A package for rendering rich text from Nextcloud
version: 0.1.0
publish_to: none

environment:
sdk: ^3.0.0
flutter: ^3.22.0

dependencies:
built_collection: ^5.0.0
built_value: ^8.9.0
flutter:
sdk: flutter
flutter_material_design_icons: ^1.1.7296
intersperse: ^2.0.0
meta: ^1.0.0
neon_framework:
git:
url: https://github.com/nextcloud/neon
path: packages/neon_framework
nextcloud: ^8.1.0

dev_dependencies:
custom_lint: ^0.7.0
flutter_test:
sdk: flutter
mocktail: ^1.0.4
neon_lints:
git:
url: https://github.com/nextcloud/neon
path: packages/neon_lints
provider: ^6.1.2
rxdart: ^0.28.0
url_launcher_platform_interface: ^2.3.2

flutter:
uses-material-design: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box
dependency_overrides:
account_repository:
path: ../account_repository
cookie_store:
path: ../../../cookie_store
dynamite_runtime:
path: ../../../dynamite/packages/dynamite_runtime
interceptor_http_client:
path: ../../../interceptor_http_client
neon_framework:
path: ../..
neon_http_client:
path: ../neon_http_client
neon_lints:
path: ../../../neon_lints
nextcloud:
path: ../../../nextcloud
notifications_push_repository:
path: ../notifications_push_repository
sort_box:
path: ../sort_box
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:neon_framework/models.dart';
import 'package:neon_framework/testing.dart';
import 'package:neon_rich_text/neon_rich_text.dart';
import 'package:nextcloud/core.dart' as core;
import 'package:provider/provider.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';

void main() {
late MockUrlLauncher urlLauncher;
late Account account;

setUpAll(() {
FakeNeonStorage.setup();

registerFallbackValue(const LaunchOptions());

urlLauncher = MockUrlLauncher();
// ignore: discarded_futures
when(() => urlLauncher.launchUrl(any(), any())).thenAnswer((_) async => true);

UrlLauncherPlatform.instance = urlLauncher;
});

setUp(() {
account = MockAccount();
});

testWidgets('Deck card', (tester) async {
await tester.pumpWidgetWithAccessibility(
TestApp(
providers: [
Provider<Account>.value(value: account),
],
child: NeonRichObjectDeckCard(
parameter: core.RichObjectParameter(
(b) => b
..type = core.RichObjectParameter_Type.deckCard
..id = ''
..name = 'name'
..boardname = 'boardname'
..stackname = 'stackname'
..link = '/link',
),
),
),
);
expect(find.text('name'), findsOne);
expect(find.text('boardname: stackname'), findsOne);
await expectLater(
find.byType(NeonRichObjectDeckCard),
matchesGoldenFile('goldens/rich_text_object_deck_card.png'),
);

await tester.tap(find.byType(NeonRichObjectDeckCard));
verify(() => urlLauncher.launchUrl('https://cloud.example.com:8443/link', any())).called(1);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:neon_framework/models.dart';
import 'package:neon_framework/testing.dart';
import 'package:neon_framework/widgets.dart';
import 'package:neon_rich_text/neon_rich_text.dart';
import 'package:nextcloud/core.dart' as core;
import 'package:provider/provider.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';

void main() {
late MockUrlLauncher urlLauncher;
late Account account;

setUpAll(() {
FakeNeonStorage.setup();

registerFallbackValue(const LaunchOptions());

urlLauncher = MockUrlLauncher();
// ignore: discarded_futures
when(() => urlLauncher.launchUrl(any(), any())).thenAnswer((_) async => true);

UrlLauncherPlatform.instance = urlLauncher;
});

setUp(() {
account = MockAccount();
});

group('Fallback', () {
testWidgets('Opens link', (tester) async {
await tester.pumpWidgetWithAccessibility(
TestApp(
providers: [
Provider<Account>.value(value: account),
],
child: NeonRichObjectFallback(
parameter: core.RichObjectParameter(
(b) => b
..type = core.RichObjectParameter_Type.calendarEvent
..id = ''
..name = 'name'
..link = '/link',
),
textStyle: null,
),
),
);

await tester.tap(find.byType(NeonRichObjectFallback));
verify(() => urlLauncher.launchUrl('https://cloud.example.com:8443/link', any())).called(1);
});

testWidgets('Without icon', (tester) async {
await tester.pumpWidgetWithAccessibility(
TestApp(
child: NeonRichObjectFallback(
parameter: core.RichObjectParameter(
(b) => b
..type = core.RichObjectParameter_Type.addressbook
..id = ''
..name = 'name',
),
textStyle: null,
),
),
);
expect(find.byType(NeonUriImage), findsNothing);
expect(find.text('name'), findsOne);
await expectLater(
find.byType(NeonRichObjectFallback),
matchesGoldenFile('goldens/rich_text_object_fallback_without_icon.png'),
);
});

testWidgets('With icon', (tester) async {
await tester.pumpWidgetWithAccessibility(
TestApp(
providers: [
Provider<Account>.value(value: account),
],
child: NeonRichObjectFallback(
parameter: core.RichObjectParameter(
(b) => b
..type = core.RichObjectParameter_Type.addressbook
..id = ''
..name = 'name'
..iconUrl = '',
),
textStyle: null,
),
),
);
expect(find.byType(NeonUriImage), findsOne);
expect(find.text('name'), findsOne);
await expectLater(
find.byType(NeonRichObjectFallback),
matchesGoldenFile('goldens/rich_text_object_fallback_with_icon.png'),
);
});
});
}
Loading

0 comments on commit f47620f

Please sign in to comment.