270 lines
7.5 KiB
Dart
270 lines
7.5 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import '../core/api/client.dart';
|
|
import '../core/api/endpoints.dart';
|
|
import '../core/api/exceptions.dart';
|
|
import '../models/idea.dart';
|
|
import '../models/collaboration.dart';
|
|
|
|
class IdeasState {
|
|
final List<Idea> ideas;
|
|
final Idea? selectedIdea;
|
|
final bool isLoading;
|
|
final String? error;
|
|
final String viewMode;
|
|
|
|
IdeasState({
|
|
this.ideas = const [],
|
|
this.selectedIdea,
|
|
this.isLoading = false,
|
|
this.error,
|
|
this.viewMode = 'list',
|
|
});
|
|
|
|
IdeasState copyWith({
|
|
List<Idea>? ideas,
|
|
Idea? selectedIdea,
|
|
bool? isLoading,
|
|
String? error,
|
|
String? viewMode,
|
|
}) =>
|
|
IdeasState(
|
|
ideas: ideas ?? this.ideas,
|
|
selectedIdea: selectedIdea ?? this.selectedIdea,
|
|
isLoading: isLoading ?? this.isLoading,
|
|
error: error,
|
|
viewMode: viewMode ?? this.viewMode,
|
|
);
|
|
}
|
|
|
|
class IdeasNotifier extends StateNotifier<IdeasState> {
|
|
final ApiClient _api;
|
|
|
|
IdeasNotifier(this._api) : super(IdeasState());
|
|
|
|
Future<void> loadIdeas({String? workspaceId}) async {
|
|
state = state.copyWith(isLoading: true, error: null);
|
|
try {
|
|
final params = <String, dynamic>{};
|
|
if (workspaceId != null) params['workspace_id'] = workspaceId;
|
|
final response = await _api.get(Endpoints.ideas, params: params);
|
|
final list = (response.data as List<dynamic>)
|
|
.map((e) => Idea.fromJson(e as Map<String, dynamic>))
|
|
.toList();
|
|
state = state.copyWith(ideas: list, isLoading: false);
|
|
} on ApiException catch (e) {
|
|
state = state.copyWith(isLoading: false, error: e.message);
|
|
}
|
|
}
|
|
|
|
Future<void> loadIdea(String id) async {
|
|
state = state.copyWith(isLoading: true, error: null);
|
|
try {
|
|
final response = await _api.get(Endpoints.idea(id));
|
|
final idea =
|
|
Idea.fromJson(response.data as Map<String, dynamic>);
|
|
state = state.copyWith(selectedIdea: idea, isLoading: false);
|
|
} on ApiException catch (e) {
|
|
state = state.copyWith(isLoading: false, error: e.message);
|
|
}
|
|
}
|
|
|
|
Future<bool> createIdea(CreateIdeaRequest request) async {
|
|
try {
|
|
final response = await _api.post(Endpoints.ideas, data: request.toJson());
|
|
final idea =
|
|
Idea.fromJson(response.data as Map<String, dynamic>);
|
|
state = state.copyWith(
|
|
ideas: [idea, ...state.ideas],
|
|
selectedIdea: idea,
|
|
);
|
|
return true;
|
|
} on ApiException catch (e) {
|
|
state = state.copyWith(error: e.message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<bool> updateIdea(String id, UpdateIdeaRequest request) async {
|
|
try {
|
|
final response =
|
|
await _api.patch(Endpoints.idea(id), data: request.toJson());
|
|
final updated =
|
|
Idea.fromJson(response.data as Map<String, dynamic>);
|
|
state = state.copyWith(
|
|
ideas: state.ideas.map((i) => i.id == id ? updated : i).toList(),
|
|
selectedIdea: updated,
|
|
);
|
|
return true;
|
|
} on ApiException catch (e) {
|
|
state = state.copyWith(error: e.message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<bool> deleteIdea(String id) async {
|
|
try {
|
|
await _api.delete(Endpoints.idea(id));
|
|
state = state.copyWith(
|
|
ideas: state.ideas.where((i) => i.id != id).toList(),
|
|
selectedIdea: null,
|
|
);
|
|
return true;
|
|
} on ApiException catch (e) {
|
|
state = state.copyWith(error: e.message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<bool> updateFunnel(String ideaId, String status) async {
|
|
try {
|
|
final response = await _api.post(Endpoints.ideaFunnel(ideaId), data: {
|
|
'status': status,
|
|
});
|
|
final updated =
|
|
Idea.fromJson(response.data as Map<String, dynamic>);
|
|
state = state.copyWith(
|
|
ideas: state.ideas.map((i) => i.id == ideaId ? updated : i).toList(),
|
|
selectedIdea: updated,
|
|
);
|
|
return true;
|
|
} on ApiException catch (e) {
|
|
state = state.copyWith(error: e.message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void setViewMode(String mode) {
|
|
state = state.copyWith(viewMode: mode);
|
|
}
|
|
|
|
void clearError() {
|
|
state = state.copyWith(error: null);
|
|
}
|
|
}
|
|
|
|
final ideasProvider =
|
|
StateNotifierProvider<IdeasNotifier, IdeasState>((ref) {
|
|
final api = ref.read(apiClientProvider);
|
|
return IdeasNotifier(api);
|
|
});
|
|
|
|
class IdeaCommentsState {
|
|
final List<Comment> comments;
|
|
final bool isLoading;
|
|
final String? error;
|
|
|
|
IdeaCommentsState({
|
|
this.comments = const [],
|
|
this.isLoading = false,
|
|
this.error,
|
|
});
|
|
|
|
IdeaCommentsState copyWith({
|
|
List<Comment>? comments,
|
|
bool? isLoading,
|
|
String? error,
|
|
}) =>
|
|
IdeaCommentsState(
|
|
comments: comments ?? this.comments,
|
|
isLoading: isLoading ?? this.isLoading,
|
|
error: error,
|
|
);
|
|
}
|
|
|
|
class IdeaCommentsNotifier extends StateNotifier<IdeaCommentsState> {
|
|
final ApiClient _api;
|
|
final String ideaId;
|
|
|
|
IdeaCommentsNotifier(this._api, this.ideaId) : super(IdeaCommentsState());
|
|
|
|
Future<void> loadComments() async {
|
|
state = state.copyWith(isLoading: true);
|
|
try {
|
|
final response = await _api.get(Endpoints.ideaComments(ideaId));
|
|
final list = (response.data as List<dynamic>)
|
|
.map((e) => Comment.fromJson(e as Map<String, dynamic>))
|
|
.toList();
|
|
state = state.copyWith(comments: list, isLoading: false);
|
|
} on ApiException catch (e) {
|
|
state = state.copyWith(isLoading: false, error: e.message);
|
|
}
|
|
}
|
|
|
|
Future<bool> addComment(String body, {String? parentId}) async {
|
|
try {
|
|
final response = await _api.post(Endpoints.ideaComments(ideaId), data: {
|
|
'body': body,
|
|
if (parentId != null) 'parent_id': parentId,
|
|
});
|
|
final comment =
|
|
Comment.fromJson(response.data as Map<String, dynamic>);
|
|
state = state.copyWith(comments: [...state.comments, comment]);
|
|
return true;
|
|
} on ApiException catch (e) {
|
|
state = state.copyWith(error: e.message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<void> toggleVote(String ideaId) async {
|
|
try {
|
|
await _api.post(Endpoints.ideaVote(ideaId));
|
|
} on ApiException catch (e) {
|
|
state = state.copyWith(error: e.message);
|
|
}
|
|
}
|
|
}
|
|
|
|
final ideaCommentsProvider =
|
|
StateNotifierProvider.family<IdeaCommentsNotifier, IdeaCommentsState, String>(
|
|
(ref, ideaId) {
|
|
final api = ref.read(apiClientProvider);
|
|
return IdeaCommentsNotifier(api, ideaId);
|
|
});
|
|
|
|
class IdeaActivityState {
|
|
final List<Activity> activities;
|
|
final bool isLoading;
|
|
|
|
IdeaActivityState({
|
|
this.activities = const [],
|
|
this.isLoading = false,
|
|
});
|
|
|
|
IdeaActivityState copyWith({
|
|
List<Activity>? activities,
|
|
bool? isLoading,
|
|
}) =>
|
|
IdeaActivityState(
|
|
activities: activities ?? this.activities,
|
|
isLoading: isLoading ?? this.isLoading,
|
|
);
|
|
}
|
|
|
|
class IdeaActivityNotifier extends StateNotifier<IdeaActivityState> {
|
|
final ApiClient _api;
|
|
final String ideaId;
|
|
|
|
IdeaActivityNotifier(this._api, this.ideaId) : super(IdeaActivityState());
|
|
|
|
Future<void> loadActivities() async {
|
|
state = state.copyWith(isLoading: true);
|
|
try {
|
|
final response = await _api.get(Endpoints.ideaActivity(ideaId));
|
|
final list = (response.data as List<dynamic>)
|
|
.map((e) => Activity.fromJson(e as Map<String, dynamic>))
|
|
.toList();
|
|
state = state.copyWith(activities: list, isLoading: false);
|
|
} catch (_) {
|
|
state = state.copyWith(isLoading: false);
|
|
}
|
|
}
|
|
}
|
|
|
|
final ideaActivityProvider =
|
|
StateNotifierProvider.family<IdeaActivityNotifier, IdeaActivityState, String>(
|
|
(ref, ideaId) {
|
|
final api = ref.read(apiClientProvider);
|
|
return IdeaActivityNotifier(api, ideaId);
|
|
});
|