Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add post info #20

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ jobs:
- name: 🕵️ Analyze project source
run: melos analyze --fatal-infos
- name: Clean Flutter generated files
# Works around invertase/dart_custom_lint#268
# Works around invertase/dart_custom_lint#271
run: melos run clean
- name: 🕵️ Run Custom Lint Rules
run: melos run custom_lint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ base class FeedService extends _$FeedService {
}

/// Replace the current posts with newly generated posts.
Future<void> replacePosts(List<PostEntity> newPosts) async {
Future<void> addPosts(List<PostEntity> newPosts) async {
// You can only change a Notifier's `state` by adding methods that assign a new value.
// You can't mutate the state directly, nor can you change it outside of a method.
if (state case AsyncData<FeedModel>(:final value)) {
Expand Down
16 changes: 15 additions & 1 deletion packages/app/lib/src/features/home/domain/post_entity.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import 'package:freezed_annotation/freezed_annotation.dart';

part 'post_entity.freezed.dart';

extension type const UserId(String id) {}

/// {@template our_democracy.features.home.domain.post}
/// Represent a post, which is a single item in a feed.
/// {@endtemplate}
Expand All @@ -16,8 +18,20 @@ sealed class PostEntity with _$PostEntity {
///
/// Create a new, immutable instance of [PostEntity].
const factory PostEntity({
/// The title of the post.
required String headline,

/// The textual content of the post.
required String body,
required String? body,

/// The author of the post.
required UserId author,

///
required LatLong location,

///
required DateTime timestamp,

/// An optional media to display alongside the post.
required ImageProvider? image,
Expand Down
23 changes: 21 additions & 2 deletions packages/app/lib/src/features/home/presentation/home/feed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ class Feed extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return ListView.builder(
prototypeItem: const _Post(post: PostEntity(body: '', image: null)),
prototypeItem: _Post(
post: PostEntity(
headline: '',
body: null,
image: null,
location: LatLong(0, 0),
timestamp: DateTime.fromMicrosecondsSinceEpoch(0, isUtc: true),
author: const UserId(''),
),
),
itemBuilder: (context, index) {
// Calculate the page and index in the page.
final page = index ~/ pageSize + 1;
Expand All @@ -41,8 +50,12 @@ class Feed extends ConsumerWidget {
// If there's an error, display it as another post.
AsyncError(:final error) => _Post(
post: PostEntity(
headline: 'Error',
body: error.toString(),
image: null,
author: const UserId(''),
location: LatLong(0, 0),
timestamp: DateTime.now(),
),
),
// If we're loading, display a loading indicator.
Expand Down Expand Up @@ -78,7 +91,13 @@ class _Post extends StatelessWidget {
// Else, return null.
null => null,
},
title: Text(post.body),
title: Text(post.headline),
subtitle: switch (post.body) {
// If the body is not null, use it as the content.
final String body => Text(body),
// Else, return null.
null => null,
},
),
);
}
Expand Down
18 changes: 9 additions & 9 deletions packages/app/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -454,10 +454,10 @@ packages:
dependency: "direct dev"
description:
name: flutter_checks
sha256: d31fea8435871fb13fe038eae0e2484530d76d6eb1898def793b71651a6c42a0
sha256: b0a40b15d38436b7c08b83353e74b0569c1ec687bd81c9556091fbb8807c1b99
url: "https://pub.dev"
source: hosted
version: "0.1.0"
version: "0.1.1"
flutter_gen_core:
dependency: transitive
description:
Expand Down Expand Up @@ -520,18 +520,18 @@ packages:
dependency: "direct overridden"
description:
name: flutter_web_auth_2
sha256: e12eb041997703ad4fa23490a9a93fd1390a9d8b2329a6f69a1f98f8ec61430d
sha256: "366b7cb97ee3e6c64d59ba6d59f28d88ecff22cd4e21abd7f989d304869fb3aa"
url: "https://pub.dev"
source: hosted
version: "4.0.0-alpha.8"
version: "4.0.0"
flutter_web_auth_2_platform_interface:
dependency: transitive
description:
name: flutter_web_auth_2_platform_interface
sha256: "49c6d660f632d181102d4eeca7b72ea88124d7f4526fd0117f98c4261e9e72b0"
sha256: f9c2d9ccf07327c8d2cc9db2b7625577f5e3efb78a2a91fb5d5b858b948c368b
url: "https://pub.dev"
source: hosted
version: "4.0.0-alpha.1"
version: "4.0.0"
flutter_web_plugins:
dependency: "direct main"
description: flutter
Expand Down Expand Up @@ -1487,7 +1487,7 @@ packages:
source: hosted
version: "0.1.6"
web_socket_channel:
dependency: "direct overridden"
dependency: transitive
description:
name: web_socket_channel
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
Expand All @@ -1514,10 +1514,10 @@ packages:
dependency: transitive
description:
name: win32_registry
sha256: "723b7f851e5724c55409bb3d5a32b203b3afe8587eaf5dafb93a5fed8ecda0d6"
sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852"
url: "https://pub.dev"
source: hosted
version: "1.1.4"
version: "1.1.5"
window_to_front:
dependency: transitive
description:
Expand Down
5 changes: 2 additions & 3 deletions packages/app/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ environment:
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependency_overrides:
flutter_web_auth_2: 4.0.0-alpha.8 # Supports WASM.
web_socket_channel: 3.0.1 # Supports latest `package:web`.
flutter_web_auth_2: 4.0.0 # Supports WASM.

dependencies:
appwrite: ^13.0.0
Expand Down Expand Up @@ -65,7 +64,7 @@ dev_dependencies:
build_web_compilers: ^4.0.11
custom_lint: ^0.6.4
dhttpd: ^4.1.0
flutter_checks: ^0.1.0
flutter_checks: ^0.1.1
flutter_gen_runner: ^5.7.0
flutter_launcher_icons: ^0.13.1
flutter_native_splash: ^2.4.1
Expand Down
10 changes: 9 additions & 1 deletion packages/app/test/helpers/riverpod.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/// This library contains utilities for testing with Riverpod.
library;

import 'package:flutter_test/flutter_test.dart';
import 'package:checks/checks.dart';
import 'package:flutter_test/flutter_test.dart' hide isA;
import 'package:hooks_riverpod/hooks_riverpod.dart';

typedef Overrides = List<Override>;
Expand All @@ -25,3 +26,10 @@ ProviderContainer createContainer({

return container;
}

extension AsyncValueChecks<T> on Subject<AsyncValue<T>> {
Subject<T> isData() => isA<AsyncData<T>>().has((i) => i.value, 'value');
Subject<Object> isError() =>
isA<AsyncError<T>>().has((i) => i.error, 'error');
void isLoading() => isA<AsyncLoading<T>>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'package:checks/checks.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:nexus/src/features/home/application/feed_service.dart';
import 'package:nexus/src/features/home/domain/feed_entity.dart';
import 'package:nexus/src/features/home/domain/post_entity.dart';

import '../../../../helpers/riverpod.dart';

void main() {
group('Feed service', () async {
test('allows mutating the posts locally', () async {
// Arrange
final container = createContainer();
final feedServiceSubscription = container.listen(
feedServiceProvider(const FeedEntity.world(), 1),
(_, __) {},
);

// Assert
check(feedServiceSubscription.read())
.isData()
.has((i) => i.posts, 'posts')
.length
.equals(20);

// Act
final postsToAdd = List.generate(
20,
(index) => const PostEntity(
image: null,
body: 'body',
),
);

await container
.read(feedServiceProvider(const FeedEntity.world(), 1).notifier)
.addPosts(postsToAdd);

// Assert
check(feedServiceSubscription.read())
.isData()
.has((i) => i.posts, 'posts')
.length
.equals(40);
});
});
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import 'package:checks/checks.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:nexus/src/features/sample/application/sample_items_service.dart';

import '../../../../helpers/riverpod.dart';

void main() {
group('Sample items service', () {
test('provides three items', () async {
final container = ProviderContainer();
final container = createContainer();

final model = container.read(sampleItemsServiceProvider);
check(model.items.length).equals(3);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import 'package:checks/checks.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:mocktail/mocktail.dart';
import 'package:nexus/src/features/settings/application/settings_service.dart';
import 'package:nexus/src/features/settings/data/preferences_repository.dart';
import 'package:nexus/src/features/settings/domain/settings_model.dart';

import '../../../../helpers/mocks.dart';
import '../../../../helpers/riverpod.dart';

void main() {
group('SettingsService', () {
Expand All @@ -16,7 +16,7 @@ void main() {
when(() => mockSharedPreferences.setString(any(), any()))
.thenAnswer((_) async => true);

final container = ProviderContainer(
final container = createContainer(
overrides: [
sharedPreferencesProvider.overrideWithValue(mockSharedPreferences),
initialSettingsProvider.overrideWithValue(defaultSettings),
Expand All @@ -38,7 +38,7 @@ void main() {
group('initialSettings', () {
test('should throw an error if initialSettings are not provided', () {
// Arrange
final container = ProviderContainer();
final container = createContainer();

// Act
SettingsModel call() => container.read(initialSettingsProvider);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import 'package:checks/checks.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:mocktail/mocktail.dart';
import 'package:nexus/src/features/settings/application/settings_service.dart';
import 'package:nexus/src/features/settings/data/preferences_repository.dart';

import '../../../../helpers/mocks.dart';
import '../../../../helpers/riverpod.dart';

void main() {
group('PreferencesRepository', () {
test('should update the theme mode', () async {
// Arrange
final mockSharedPreferences = MockSharedPreferences();

final container = ProviderContainer(
final container = createContainer(
overrides: [
sharedPreferencesProvider.overrideWithValue(mockSharedPreferences),
initialSettingsProvider.overrideWithValue(defaultSettings),
Expand All @@ -36,7 +36,7 @@ void main() {
when(() => mockSharedPreferences.getString('prefs'))
.thenReturn('{"themeMode":"dark"}');

final container = ProviderContainer(
final container = createContainer(
overrides: [
sharedPreferencesProvider.overrideWithValue(mockSharedPreferences),
initialSettingsProvider.overrideWithValue(defaultSettings),
Expand All @@ -55,7 +55,7 @@ void main() {
group('sharedPreferences', () {
test('should throw an error if SharedPreferences is not provided', () {
// Arrange
final container = ProviderContainer();
final container = createContainer();

// Act
PreferencesRepository call() =>
Expand Down
Loading