Skip to content

Commit

Permalink
fix(favorites): updated starred state is not reflecting (#199)
Browse files Browse the repository at this point in the history
* bfix(favorites): changed state of starred is not reflected when moved to previous screen

* rfac: refactor tests

* bfix: update project state when personal project is updated

* bfix: fix user profile test
  • Loading branch information
aman-singh7 authored Mar 3, 2022
1 parent daa6649 commit 39c6d40
Show file tree
Hide file tree
Showing 14 changed files with 274 additions and 87 deletions.
48 changes: 46 additions & 2 deletions lib/models/projects.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ class Project {
ProjectRelationships relationships;
List<Collaborator>? collaborators;

Project copyWith({
String? id,
String? type,
ProjectAttributes? attributes,
ProjectRelationships? relationships,
List<Collaborator>? collaborators,
}) {
return Project(
id: id ?? this.id,
type: type ?? this.type,
attributes: attributes ?? this.attributes,
relationships: relationships ?? this.relationships,
collaborators: collaborators ?? this.collaborators,
);
}

bool get hasAuthorAccess {
var currentUser = locator<LocalStorageService>().currentUser;

Expand Down Expand Up @@ -81,7 +97,7 @@ class ProjectAttributes {
this.description,
required this.view,
required this.tags,
this.isStarred,
required this.isStarred,
required this.authorName,
required this.starsCount,
});
Expand All @@ -94,9 +110,37 @@ class ProjectAttributes {
String? description;
int view;
List<Tag> tags;
bool? isStarred;
bool isStarred;
String authorName;
int starsCount;

ProjectAttributes copyWith({
String? name,
String? projectAccessType,
DateTime? createdAt,
DateTime? updatedAt,
ImagePreview? imagePreview,
String? description,
int? view,
List<Tag>? tags,
bool? isStarred,
String? authorName,
int? starsCount,
}) {
return ProjectAttributes(
name: name ?? this.name,
projectAccessType: projectAccessType ?? this.projectAccessType,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
imagePreview: imagePreview ?? this.imagePreview,
view: view ?? this.view,
tags: tags ?? this.tags,
authorName: authorName ?? this.authorName,
starsCount: starsCount ?? this.starsCount,
description: description ?? this.description,
isStarred: isStarred ?? this.isStarred,
);
}
}

class ImagePreview {
Expand Down
15 changes: 14 additions & 1 deletion lib/ui/views/profile/user_favourites_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import 'package:mobile_app/ui/components/cv_add_icon_button.dart';
import 'package:mobile_app/ui/views/base_view.dart';
import 'package:mobile_app/ui/views/projects/components/project_card.dart';
import 'package:mobile_app/ui/views/projects/project_details_view.dart';
import 'package:mobile_app/viewmodels/profile/profile_viewmodel.dart';
import 'package:mobile_app/viewmodels/profile/user_favourites_viewmodel.dart';
import 'package:provider/provider.dart';

class UserFavouritesView extends StatefulWidget {
const UserFavouritesView({
Expand All @@ -30,7 +32,14 @@ class _UserFavouritesViewState extends State<UserFavouritesView>
Widget build(BuildContext context) {
super.build(context);
return BaseView<UserFavouritesViewModel>(
onModelReady: (model) => model.fetchUserFavourites(userId: widget.userId),
onModelReady: (model) {
model.fetchUserFavourites(userId: widget.userId);
context.watch<ProfileViewModel>().addListener(() {
final project = context.read<ProfileViewModel>().updatedProject;
if (project == null) return;
model.onProjectChanged(project);
});
},
builder: (context, model, child) {
final _items = <Widget>[];

Expand All @@ -44,6 +53,10 @@ class _UserFavouritesViewState extends State<UserFavouritesView>
var _result = await Get.toNamed(ProjectDetailsView.id,
arguments: project);
if (_result is bool) model.onProjectDeleted(project.id);
if (_result is Project) {
model.onProjectChanged(_result);
context.read<ProfileViewModel>().updatedProject = _result;
}
},
),
);
Expand Down
16 changes: 15 additions & 1 deletion lib/ui/views/profile/user_projects_view.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:mobile_app/models/projects.dart';
import 'package:mobile_app/ui/components/cv_add_icon_button.dart';
import 'package:mobile_app/ui/views/base_view.dart';
import 'package:mobile_app/ui/views/projects/components/project_card.dart';
import 'package:mobile_app/ui/views/projects/project_details_view.dart';
import 'package:mobile_app/viewmodels/profile/profile_viewmodel.dart';
import 'package:mobile_app/viewmodels/profile/user_projects_viewmodel.dart';
import 'package:provider/provider.dart';

class UserProjectsView extends StatefulWidget {
const UserProjectsView({
Expand All @@ -27,7 +30,14 @@ class _UserProjectsViewState extends State<UserProjectsView>
Widget build(BuildContext context) {
super.build(context);
return BaseView<UserProjectsViewModel>(
onModelReady: (model) => model.fetchUserProjects(userId: widget.userId),
onModelReady: (model) {
model.fetchUserProjects(userId: widget.userId);
context.watch<ProfileViewModel>().addListener(() {
var updatedProject = context.read<ProfileViewModel>().updatedProject;
if (updatedProject == null) return;
model.onProjectChanged(updatedProject);
});
},
builder: (context, model, child) {
final _items = <Widget>[];

Expand All @@ -41,6 +51,10 @@ class _UserProjectsViewState extends State<UserProjectsView>
var _result = await Get.toNamed(ProjectDetailsView.id,
arguments: project);
if (_result is bool) model.onProjectDeleted(project.id);
if (_result is Project) {
model.onProjectChanged(_result);
context.read<ProfileViewModel>().updatedProject = _result;
}
},
),
);
Expand Down
10 changes: 9 additions & 1 deletion lib/ui/views/projects/featured_projects_view.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:mobile_app/models/projects.dart';
import 'package:mobile_app/ui/components/cv_header.dart';
import 'package:mobile_app/ui/components/cv_primary_button.dart';
import 'package:mobile_app/ui/views/base_view.dart';
Expand Down Expand Up @@ -38,7 +39,14 @@ class _FeaturedProjectsViewState extends State<FeaturedProjectsView> {
FeaturedProjectCard(
project: project,
onViewPressed: () async {
await Get.toNamed(ProjectDetailsView.id, arguments: project);
final _result = await Get.toNamed(
ProjectDetailsView.id,
arguments: project,
);

if (_result is Project) {
model.updateFeaturedProject(_result);
}
},
),
);
Expand Down
160 changes: 85 additions & 75 deletions lib/ui/views/projects/project_details_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import 'package:mobile_app/locator.dart';
import 'package:mobile_app/models/collaborators.dart';
import 'package:mobile_app/models/projects.dart';
import 'package:mobile_app/services/dialog_service.dart';
import 'package:mobile_app/services/local_storage_service.dart';
import 'package:mobile_app/ui/components/cv_flat_button.dart';
import 'package:mobile_app/ui/views/base_view.dart';
import 'package:mobile_app/ui/views/profile/profile_view.dart';
Expand All @@ -32,8 +31,6 @@ class ProjectDetailsView extends StatefulWidget {
}

class _ProjectDetailsViewState extends State<ProjectDetailsView> {
final LocalStorageService _localStorageService =
locator<LocalStorageService>();
final DialogService _dialogService = locator<DialogService>();
late ProjectDetailsViewModel _model;
final _formKey = GlobalKey<FormState>();
Expand Down Expand Up @@ -566,92 +563,105 @@ class _ProjectDetailsViewState extends State<ProjectDetailsView> {
return BaseView<ProjectDetailsViewModel>(
onModelReady: (model) {
_model = model;
_model.receivedProject = _recievedProject;
// initialize collaborators & isStarred for the project..
_model.collaborators = _recievedProject.collaborators;
_model.isProjectStarred =
_recievedProject.attributes.isStarred ?? false;
_model.isProjectStarred = _recievedProject.attributes.isStarred;
_model.starCount = _recievedProject.attributes.starsCount;

_model.fetchProjectDetails(_recievedProject.id);
},
builder: (context, model, child) => Scaffold(
appBar: AppBar(
title: const Text('Project Details'),
actions: [
_buildShareActionButton(),
],
),
body: Builder(builder: (context) {
var _projectAttrs = _recievedProject.attributes;
var _items = <Widget>[];

// Adds project preview..
_items.add(_buildProjectPreview());

_items.add(const SizedBox(height: 16));

// Adds Project Name, Star & View count..
_items.add(_buildProjectNameHeader());

// Adds Project attributes..
_items.add(
Container(
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_buildProjectAuthor(),
_buildProjectDetailComponent(
'Project Access Type',
_projectAttrs.projectAccessType,
),
_buildProjectDescription(),
const Divider(height: 32),
Wrap(
spacing: 8,
crossAxisAlignment: WrapCrossAlignment.center,
builder: (context, model, child) => WillPopScope(
onWillPop: () async {
// Check whether the state (i.e starred or not) is changed
final bool isChanged = model.receivedProject!.attributes.isStarred ^
_recievedProject.attributes.isStarred;
Get.back(
result: isChanged ? model.receivedProject : null,
);
return false;
},
child: Scaffold(
appBar: AppBar(
title: const Text('Project Details'),
actions: [
_buildShareActionButton(),
],
),
body: Builder(
builder: (context) {
var _projectAttrs = _recievedProject.attributes;
var _items = <Widget>[];

// Adds project preview..
_items.add(_buildProjectPreview());

_items.add(const SizedBox(height: 16));

// Adds Project Name, Star & View count..
_items.add(_buildProjectNameHeader());

// Adds Project attributes..
_items.add(
Container(
padding:
const EdgeInsets.symmetric(vertical: 16, horizontal: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
if (!_recievedProject.hasAuthorAccess &&
_localStorageService.isLoggedIn)
_buildForkProjectButton(),
if (_localStorageService.isLoggedIn)
_buildStarProjectButton(),
_buildProjectAuthor(),
_buildProjectDetailComponent(
'Project Access Type',
_projectAttrs.projectAccessType,
),
_buildProjectDescription(),
const Divider(height: 32),
Wrap(
spacing: 8,
crossAxisAlignment: WrapCrossAlignment.center,
children: <Widget>[
if (!_recievedProject.hasAuthorAccess &&
model.isLoggedIn)
_buildForkProjectButton(),
if (model.isLoggedIn) _buildStarProjectButton(),
if (_recievedProject.hasAuthorAccess)
_buildAddCollaboratorsButton(),
],
),
const Divider(height: 32),
if (_recievedProject.hasAuthorAccess)
_buildAddCollaboratorsButton(),
Wrap(
spacing: 8,
children: <Widget>[
_buildEditButton(),
_buildDeleteButton(),
],
),
],
),
const Divider(height: 32),
if (_recievedProject.hasAuthorAccess)
Wrap(
spacing: 8,
children: <Widget>[
_buildEditButton(),
_buildDeleteButton(),
],
),
],
),
),
);
),
);

if (_model.isSuccess(_model.FETCH_PROJECT_DETAILS) &&
_model.collaborators.isNotEmpty) {
_items.add(const Divider());
if (_model.isSuccess(_model.FETCH_PROJECT_DETAILS) &&
_model.collaborators.isNotEmpty) {
_items.add(const Divider());

_items.add(_buildProjectHeader('Collaborators'));
_items.add(_buildProjectHeader('Collaborators'));

for (var collaborator in _model.collaborators) {
_items.add(
_buildCollaborator(collaborator),
for (var collaborator in _model.collaborators) {
_items.add(
_buildCollaborator(collaborator),
);
}
}
return ListView(
shrinkWrap: true,
padding: const EdgeInsets.all(16),
children: _items,
);
}
}
return ListView(
shrinkWrap: true,
padding: const EdgeInsets.all(16),
children: _items,
);
}),
},
),
),
),
);
}
Expand Down
10 changes: 10 additions & 0 deletions lib/viewmodels/profile/profile_viewmodel.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:mobile_app/enums/view_state.dart';
import 'package:mobile_app/locator.dart';
import 'package:mobile_app/models/failure_model.dart';
import 'package:mobile_app/models/projects.dart';
import 'package:mobile_app/models/user.dart';
import 'package:mobile_app/services/API/users_api.dart';
import 'package:mobile_app/viewmodels/base_viewmodel.dart';
Expand All @@ -20,6 +21,15 @@ class ProfileViewModel extends BaseModel {
notifyListeners();
}

Project? _updatedProject;

Project? get updatedProject => _updatedProject;

set updatedProject(Project? project) {
_updatedProject = project;
notifyListeners();
}

Future? fetchUserProfile(String? userId) async {
if (userId == null) return;
setStateFor(FETCH_USER_PROFILE, ViewState.Busy);
Expand Down
Loading

0 comments on commit 39c6d40

Please sign in to comment.