Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a19af3388d | |||
| bae6597ec2 | |||
| bc48a49de8 | |||
| a687e3637f | |||
| c8becd6afd | |||
| e63370bf0a | |||
| f39b8ddb30 | |||
| def7519feb | |||
| e86d1a59af | |||
| c8f3861eb5 | |||
| a2e734c498 | |||
| 3a336ddf88 |
+29
-44
@@ -1,12 +1,14 @@
|
|||||||
# CI runs first; build only proceeds if analyze and test pass.
|
# CI runs only on release tags.
|
||||||
#
|
#
|
||||||
# Push to dev: analyze + test → build APK → upload artifact (fabledapp-dev-<sha>)
|
# Tag v*: analyze + test → build APK → attach to Forgejo Release
|
||||||
# Push to main: analyze + test only (no build — wait for release tag)
|
|
||||||
# Tag v26.03.11: analyze + test → build APK → upload artifact + create Forgejo Release
|
|
||||||
# Pull request: analyze + test only
|
|
||||||
#
|
#
|
||||||
# To cut a release:
|
# To cut a release:
|
||||||
# git tag v26.03.11 && git push origin v26.03.11
|
# Create a release via the Forgejo UI on main with a v* tag name.
|
||||||
|
# The tag push triggers this workflow; the build job attaches the APK.
|
||||||
|
#
|
||||||
|
# Note: JavaScript actions (actions/checkout, actions/upload-artifact) cannot run
|
||||||
|
# inside the Flutter container because it has no Node.js. All steps use shell
|
||||||
|
# commands directly instead.
|
||||||
#
|
#
|
||||||
# Required secrets (repo → Settings → Secrets → Actions):
|
# Required secrets (repo → Settings → Secrets → Actions):
|
||||||
# RELEASE_TOKEN — Forgejo PAT with write:repository scope
|
# RELEASE_TOKEN — Forgejo PAT with write:repository scope
|
||||||
@@ -14,25 +16,7 @@ name: CI & Build
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main, dev]
|
|
||||||
tags: ["v*"]
|
tags: ["v*"]
|
||||||
paths:
|
|
||||||
- "lib/**"
|
|
||||||
- "test/**"
|
|
||||||
- "pubspec.yaml"
|
|
||||||
- "pubspec.lock"
|
|
||||||
- "analysis_options.yaml"
|
|
||||||
- "android/**"
|
|
||||||
- "assets/**"
|
|
||||||
- ".forgejo/workflows/ci.yml"
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- "lib/**"
|
|
||||||
- "test/**"
|
|
||||||
- "pubspec.yaml"
|
|
||||||
- "pubspec.lock"
|
|
||||||
- "analysis_options.yaml"
|
|
||||||
- ".forgejo/workflows/ci.yml"
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
@@ -41,7 +25,10 @@ jobs:
|
|||||||
container:
|
container:
|
||||||
image: ghcr.io/cirruslabs/flutter:stable
|
image: ghcr.io/cirruslabs/flutter:stable
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- name: Checkout
|
||||||
|
run: |
|
||||||
|
git clone "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" .
|
||||||
|
git checkout "$GITHUB_SHA"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: flutter pub get
|
run: flutter pub get
|
||||||
@@ -55,17 +42,14 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
name: Build release APK
|
name: Build release APK
|
||||||
needs: [analyze]
|
needs: [analyze]
|
||||||
# Build on dev branch pushes and version tag pushes only.
|
|
||||||
# main branch pushes run CI for safety but do not build —
|
|
||||||
# the release tag (v*) is the sole trigger for a production APK.
|
|
||||||
if: |
|
|
||||||
github.event_name == 'push' &&
|
|
||||||
(github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/'))
|
|
||||||
runs-on: py3.12-node22
|
runs-on: py3.12-node22
|
||||||
container:
|
container:
|
||||||
image: ghcr.io/cirruslabs/flutter:stable
|
image: ghcr.io/cirruslabs/flutter:stable
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- name: Checkout
|
||||||
|
run: |
|
||||||
|
git clone "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" .
|
||||||
|
git checkout "$GITHUB_SHA"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: flutter pub get
|
run: flutter pub get
|
||||||
@@ -76,21 +60,22 @@ jobs:
|
|||||||
- name: Set artifact name
|
- name: Set artifact name
|
||||||
id: artifact
|
id: artifact
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then
|
echo "name=fabledapp-${{ github.ref_name }}-${{ github.sha }}" >> $GITHUB_OUTPUT
|
||||||
echo "name=fabledapp-dev-${{ github.sha }}" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "name=fabledapp-${{ github.sha }}" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact to Forgejo
|
||||||
uses: actions/upload-artifact@v6
|
env:
|
||||||
with:
|
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
name: ${{ steps.artifact.outputs.name }}
|
APK: build/app/outputs/flutter-apk/app-release.apk
|
||||||
path: build/app/outputs/flutter-apk/app-release.apk
|
API: https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledApp
|
||||||
retention-days: 30
|
ARTIFACT_NAME: ${{ steps.artifact.outputs.name }}
|
||||||
|
run: |
|
||||||
|
# Upload the APK as a workflow artifact via the Forgejo API.
|
||||||
|
curl -s -X POST "$API/actions/artifacts" \
|
||||||
|
-H "Authorization: token $RELEASE_TOKEN" \
|
||||||
|
-F "name=$ARTIFACT_NAME" \
|
||||||
|
-F "file=@$APK" || echo "Artifact upload skipped (API may not support this endpoint)."
|
||||||
|
|
||||||
- name: Publish Forgejo release
|
- name: Publish Forgejo release
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
env:
|
env:
|
||||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
TAG: ${{ github.ref_name }}
|
TAG: ${{ github.ref_name }}
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ linter:
|
|||||||
rules:
|
rules:
|
||||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
# ?'key': value applies ? to the key (never null for string literals);
|
||||||
|
# the if (x != null) form is correct for nullable-value map entries.
|
||||||
|
use_null_aware_elements: false
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Additional information about this file can be found at
|
||||||
# https://dart.dev/guides/language/analysis-options
|
# https://dart.dev/guides/language/analysis-options
|
||||||
|
|||||||
+8
-2
@@ -283,7 +283,13 @@ class _ShellState extends ConsumerState<_Shell> {
|
|||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
const _QuickCaptureBar(),
|
const _QuickCaptureBar(),
|
||||||
Expanded(child: child),
|
Expanded(
|
||||||
|
child: MediaQuery.removePadding(
|
||||||
|
context: context,
|
||||||
|
removeTop: true,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
bottomNavigationBar: NavigationBar(
|
bottomNavigationBar: NavigationBar(
|
||||||
@@ -371,7 +377,7 @@ class _QuickCaptureBarState extends ConsumerState<_QuickCaptureBar> {
|
|||||||
|
|
||||||
String _hintForLocation(String location) {
|
String _hintForLocation(String location) {
|
||||||
if (location.startsWith(Routes.library) &&
|
if (location.startsWith(Routes.library) &&
|
||||||
location.contains('tasks')) return 'Add a task…';
|
location.contains('tasks')) { return 'Add a task…'; }
|
||||||
if (location.startsWith(Routes.conversations)) return 'Ask Fabled…';
|
if (location.startsWith(Routes.conversations)) return 'Ask Fabled…';
|
||||||
return 'Capture a note…';
|
return 'Capture a note…';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class BriefingApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET /api/briefing/conversations/<id>/messages
|
/// GET /api/briefing/conversations/`<id>`/messages
|
||||||
Future<List<Message>> getMessages(int convId) async {
|
Future<List<Message>> getMessages(int convId) async {
|
||||||
try {
|
try {
|
||||||
final response =
|
final response =
|
||||||
|
|||||||
@@ -4,18 +4,15 @@ import 'api_client_provider.dart';
|
|||||||
|
|
||||||
enum AuthStatus { unknown, authenticated, unauthenticated }
|
enum AuthStatus { unknown, authenticated, unauthenticated }
|
||||||
|
|
||||||
final authProvider = StateNotifierProvider<AuthNotifier, AuthStatus>((ref) {
|
final authProvider = NotifierProvider<AuthNotifier, AuthStatus>(AuthNotifier.new);
|
||||||
return AuthNotifier(ref);
|
|
||||||
});
|
|
||||||
|
|
||||||
class AuthNotifier extends StateNotifier<AuthStatus> {
|
class AuthNotifier extends Notifier<AuthStatus> {
|
||||||
final Ref _ref;
|
@override
|
||||||
|
AuthStatus build() => AuthStatus.unknown;
|
||||||
AuthNotifier(this._ref) : super(AuthStatus.unknown);
|
|
||||||
|
|
||||||
Future<void> verify() async {
|
Future<void> verify() async {
|
||||||
try {
|
try {
|
||||||
final repo = _ref.read(authRepositoryProvider);
|
final repo = ref.read(authRepositoryProvider);
|
||||||
final ok = await repo.verify();
|
final ok = await repo.verify();
|
||||||
state = ok ? AuthStatus.authenticated : AuthStatus.unauthenticated;
|
state = ok ? AuthStatus.authenticated : AuthStatus.unauthenticated;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
@@ -24,14 +21,14 @@ class AuthNotifier extends StateNotifier<AuthStatus> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> login(String username, String password) async {
|
Future<void> login(String username, String password) async {
|
||||||
final repo = _ref.read(authRepositoryProvider);
|
final repo = ref.read(authRepositoryProvider);
|
||||||
await repo.login(username, password);
|
await repo.login(username, password);
|
||||||
state = AuthStatus.authenticated;
|
state = AuthStatus.authenticated;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> logout() async {
|
Future<void> logout() async {
|
||||||
try {
|
try {
|
||||||
final repo = _ref.read(authRepositoryProvider);
|
final repo = ref.read(authRepositoryProvider);
|
||||||
await repo.logout();
|
await repo.logout();
|
||||||
} finally {
|
} finally {
|
||||||
state = AuthStatus.unauthenticated;
|
state = AuthStatus.unauthenticated;
|
||||||
|
|||||||
@@ -5,7 +5,13 @@ import '../data/models/message.dart';
|
|||||||
import 'api_client_provider.dart';
|
import 'api_client_provider.dart';
|
||||||
|
|
||||||
/// Drives the loading indicator in BriefingScreen's reply area.
|
/// Drives the loading indicator in BriefingScreen's reply area.
|
||||||
final isBriefingStreamingProvider = StateProvider<bool>((ref) => false);
|
final isBriefingStreamingProvider =
|
||||||
|
NotifierProvider<_BoolNotifier, bool>(_BoolNotifier.new);
|
||||||
|
|
||||||
|
class _BoolNotifier extends Notifier<bool> {
|
||||||
|
@override
|
||||||
|
bool build() => false;
|
||||||
|
}
|
||||||
|
|
||||||
final briefingProvider =
|
final briefingProvider =
|
||||||
AsyncNotifierProvider<BriefingNotifier, BriefingConversation>(
|
AsyncNotifierProvider<BriefingNotifier, BriefingConversation>(
|
||||||
@@ -32,7 +38,7 @@ class BriefingNotifier extends AsyncNotifier<BriefingConversation> {
|
|||||||
/// 3. SSE stream (best-effort)
|
/// 3. SSE stream (best-effort)
|
||||||
/// 4. Poll until complete
|
/// 4. Poll until complete
|
||||||
Future<void> sendReply(String content) async {
|
Future<void> sendReply(String content) async {
|
||||||
final conv = state.valueOrNull;
|
final conv = state.value;
|
||||||
if (conv == null) return;
|
if (conv == null) return;
|
||||||
final convId = conv.id;
|
final convId = conv.id;
|
||||||
final chatApi = ref.read(chatApiProvider);
|
final chatApi = ref.read(chatApiProvider);
|
||||||
@@ -65,7 +71,7 @@ class BriefingNotifier extends AsyncNotifier<BriefingConversation> {
|
|||||||
try {
|
try {
|
||||||
await for (final chunk in chatApi.streamGeneration(convId)) {
|
await for (final chunk in chatApi.streamGeneration(convId)) {
|
||||||
streamedContent = true;
|
streamedContent = true;
|
||||||
final current = state.valueOrNull;
|
final current = state.value;
|
||||||
if (current == null) break;
|
if (current == null) break;
|
||||||
final msgs = current.messages;
|
final msgs = current.messages;
|
||||||
if (msgs.isEmpty) continue;
|
if (msgs.isEmpty) continue;
|
||||||
@@ -88,7 +94,7 @@ class BriefingNotifier extends AsyncNotifier<BriefingConversation> {
|
|||||||
final hasContent = fresh.any(
|
final hasContent = fresh.any(
|
||||||
(m) => m.role == MessageRole.assistant && m.content.isNotEmpty,
|
(m) => m.role == MessageRole.assistant && m.content.isNotEmpty,
|
||||||
);
|
);
|
||||||
final current = state.valueOrNull;
|
final current = state.value;
|
||||||
if (current != null && (!streamedContent || done || hasContent)) {
|
if (current != null && (!streamedContent || done || hasContent)) {
|
||||||
state = AsyncData(current.copyWith(messages: fresh));
|
state = AsyncData(current.copyWith(messages: fresh));
|
||||||
}
|
}
|
||||||
@@ -96,7 +102,7 @@ class BriefingNotifier extends AsyncNotifier<BriefingConversation> {
|
|||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// Clear the generating placeholder so UI doesn't spin forever.
|
// Clear the generating placeholder so UI doesn't spin forever.
|
||||||
final current = state.valueOrNull;
|
final current = state.value;
|
||||||
if (current != null) {
|
if (current != null) {
|
||||||
final msgs = current.messages;
|
final msgs = current.messages;
|
||||||
if (msgs.isNotEmpty && msgs.last.status == 'generating') {
|
if (msgs.isNotEmpty && msgs.last.status == 'generating') {
|
||||||
|
|||||||
@@ -4,16 +4,20 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
import 'settings_provider.dart';
|
import 'settings_provider.dart';
|
||||||
|
|
||||||
final captureQueueProvider =
|
final captureQueueProvider =
|
||||||
StateNotifierProvider<CaptureQueueNotifier, List<String>>(
|
NotifierProvider<CaptureQueueNotifier, List<String>>(
|
||||||
(ref) => CaptureQueueNotifier(ref.watch(sharedPreferencesProvider)),
|
CaptureQueueNotifier.new,
|
||||||
);
|
);
|
||||||
|
|
||||||
class CaptureQueueNotifier extends StateNotifier<List<String>> {
|
class CaptureQueueNotifier extends Notifier<List<String>> {
|
||||||
static const _key = 'capture_queue';
|
static const _key = 'capture_queue';
|
||||||
final SharedPreferences _prefs;
|
|
||||||
|
|
||||||
CaptureQueueNotifier(this._prefs)
|
SharedPreferences get _prefs => ref.read(sharedPreferencesProvider);
|
||||||
: super(_prefs.getStringList(_key) ?? []);
|
|
||||||
|
@override
|
||||||
|
List<String> build() {
|
||||||
|
final prefs = ref.watch(sharedPreferencesProvider);
|
||||||
|
return prefs.getStringList(_key) ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> enqueue(String text) async {
|
Future<void> enqueue(String text) async {
|
||||||
final updated = [...state, text];
|
final updated = [...state, text];
|
||||||
|
|||||||
@@ -15,20 +15,27 @@ class CaptureResult {
|
|||||||
|
|
||||||
/// The most recent capture result. UI watches this to show snackbars.
|
/// The most recent capture result. UI watches this to show snackbars.
|
||||||
/// Reset to null by the notifier before each new item so listeners always fire.
|
/// Reset to null by the notifier before each new item so listeners always fire.
|
||||||
final captureResultProvider = StateProvider<CaptureResult?>((_) => null);
|
final captureResultProvider =
|
||||||
|
NotifierProvider<_CaptureResultNotifier, CaptureResult?>(
|
||||||
|
_CaptureResultNotifier.new);
|
||||||
|
|
||||||
|
class _CaptureResultNotifier extends Notifier<CaptureResult?> {
|
||||||
|
@override
|
||||||
|
CaptureResult? build() => null;
|
||||||
|
}
|
||||||
|
|
||||||
/// In-memory sequential work queue for quick captures.
|
/// In-memory sequential work queue for quick captures.
|
||||||
/// Separate from [captureQueueProvider] (which is the offline persistence queue).
|
/// Separate from [captureQueueProvider] (which is the offline persistence queue).
|
||||||
final captureWorkQueueProvider =
|
final captureWorkQueueProvider =
|
||||||
StateNotifierProvider<CaptureWorkQueueNotifier, List<String>>(
|
NotifierProvider<CaptureWorkQueueNotifier, List<String>>(
|
||||||
(ref) => CaptureWorkQueueNotifier(ref),
|
CaptureWorkQueueNotifier.new,
|
||||||
);
|
);
|
||||||
|
|
||||||
class CaptureWorkQueueNotifier extends StateNotifier<List<String>> {
|
class CaptureWorkQueueNotifier extends Notifier<List<String>> {
|
||||||
final Ref _ref;
|
|
||||||
bool _running = false;
|
bool _running = false;
|
||||||
|
|
||||||
CaptureWorkQueueNotifier(this._ref) : super([]);
|
@override
|
||||||
|
List<String> build() => [];
|
||||||
|
|
||||||
/// Add text to the queue and start the drain loop if not already running.
|
/// Add text to the queue and start the drain loop if not already running.
|
||||||
void enqueue(String text) {
|
void enqueue(String text) {
|
||||||
@@ -43,9 +50,9 @@ class CaptureWorkQueueNotifier extends StateNotifier<List<String>> {
|
|||||||
while (state.isNotEmpty) {
|
while (state.isNotEmpty) {
|
||||||
final text = state.first;
|
final text = state.first;
|
||||||
// Signal "no result yet" so the same result value can re-trigger watch.
|
// Signal "no result yet" so the same result value can re-trigger watch.
|
||||||
_ref.read(captureResultProvider.notifier).state = null;
|
ref.read(captureResultProvider.notifier).state = null;
|
||||||
try {
|
try {
|
||||||
final api = _ref.read(quickCaptureApiProvider);
|
final api = ref.read(quickCaptureApiProvider);
|
||||||
final result = await api.capture(text);
|
final result = await api.capture(text);
|
||||||
|
|
||||||
// Dequeue on success.
|
// Dequeue on success.
|
||||||
@@ -54,32 +61,32 @@ class CaptureWorkQueueNotifier extends StateNotifier<List<String>> {
|
|||||||
// Invalidate content providers so lists refresh.
|
// Invalidate content providers so lists refresh.
|
||||||
switch (result.type) {
|
switch (result.type) {
|
||||||
case 'note':
|
case 'note':
|
||||||
_ref.invalidate(notesProvider);
|
ref.invalidate(notesProvider);
|
||||||
case 'task':
|
case 'task':
|
||||||
case 'todo':
|
case 'todo':
|
||||||
_ref.invalidate(tasksProvider);
|
ref.invalidate(tasksProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publish result for snackbar.
|
// Publish result for snackbar.
|
||||||
final msg = result.message.isNotEmpty
|
final msg = result.message.isNotEmpty
|
||||||
? result.message
|
? result.message
|
||||||
: '${_typeLabel(result.type)} created: ${result.title}';
|
: '${_typeLabel(result.type)} created: ${result.title}';
|
||||||
_ref.read(captureResultProvider.notifier).state = CaptureResult(msg);
|
ref.read(captureResultProvider.notifier).state = CaptureResult(msg);
|
||||||
} on NetworkException catch (_) {
|
} on NetworkException catch (_) {
|
||||||
// Persist to offline queue and stop draining — still offline.
|
// Persist to offline queue and stop draining — still offline.
|
||||||
await _ref.read(captureQueueProvider.notifier).enqueue(text);
|
await ref.read(captureQueueProvider.notifier).enqueue(text);
|
||||||
state = state.length > 1 ? state.sublist(1) : [];
|
state = state.length > 1 ? state.sublist(1) : [];
|
||||||
_ref.read(captureResultProvider.notifier).state = CaptureResult(
|
ref.read(captureResultProvider.notifier).state = CaptureResult(
|
||||||
"You're offline — capture saved and will retry automatically.",
|
"You're offline — capture saved and will retry automatically.",
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
} on AppException catch (e) {
|
} on AppException catch (e) {
|
||||||
state = state.length > 1 ? state.sublist(1) : [];
|
state = state.length > 1 ? state.sublist(1) : [];
|
||||||
_ref.read(captureResultProvider.notifier).state =
|
ref.read(captureResultProvider.notifier).state =
|
||||||
CaptureResult(e.message, isError: true);
|
CaptureResult(e.message, isError: true);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
state = state.length > 1 ? state.sublist(1) : [];
|
state = state.length > 1 ? state.sublist(1) : [];
|
||||||
_ref.read(captureResultProvider.notifier).state =
|
ref.read(captureResultProvider.notifier).state =
|
||||||
CaptureResult('Capture failed. Please try again.', isError: true);
|
CaptureResult('Capture failed. Please try again.', isError: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,19 @@ import '../data/models/conversation.dart';
|
|||||||
import '../data/models/message.dart';
|
import '../data/models/message.dart';
|
||||||
import 'api_client_provider.dart';
|
import 'api_client_provider.dart';
|
||||||
|
|
||||||
// Separate StateProvider so UI re-builds immediately when streaming starts/stops.
|
// Separate NotifierProvider.family so UI re-builds immediately when streaming starts/stops.
|
||||||
final isStreamingProvider = StateProvider.family<bool, int>((ref, _) => false);
|
final isStreamingProvider =
|
||||||
|
NotifierProvider.family<_IsStreamingNotifier, bool, int>(
|
||||||
|
(convId) => _IsStreamingNotifier(convId),
|
||||||
|
);
|
||||||
|
|
||||||
|
class _IsStreamingNotifier extends Notifier<bool> {
|
||||||
|
// ignore: avoid_unused_constructor_parameters
|
||||||
|
_IsStreamingNotifier(int convId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool build() => false;
|
||||||
|
}
|
||||||
|
|
||||||
final conversationsProvider =
|
final conversationsProvider =
|
||||||
AsyncNotifierProvider<ConversationsNotifier, List<Conversation>>(
|
AsyncNotifierProvider<ConversationsNotifier, List<Conversation>>(
|
||||||
@@ -20,14 +31,14 @@ class ConversationsNotifier extends AsyncNotifier<List<Conversation>> {
|
|||||||
Future<Conversation> create(String title) async {
|
Future<Conversation> create(String title) async {
|
||||||
final conv =
|
final conv =
|
||||||
await ref.read(chatRepositoryProvider).createConversation(title);
|
await ref.read(chatRepositoryProvider).createConversation(title);
|
||||||
state = AsyncData([conv, ...state.valueOrNull ?? []]);
|
state = AsyncData([conv, ...state.value ?? []]);
|
||||||
return conv;
|
return conv;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> delete(int id) async {
|
Future<void> delete(int id) async {
|
||||||
await ref.read(chatRepositoryProvider).deleteConversation(id);
|
await ref.read(chatRepositoryProvider).deleteConversation(id);
|
||||||
state = AsyncData([
|
state = AsyncData([
|
||||||
for (final c in state.valueOrNull ?? [])
|
for (final c in state.value ?? [])
|
||||||
if (c.id != id) c,
|
if (c.id != id) c,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -35,7 +46,7 @@ class ConversationsNotifier extends AsyncNotifier<List<Conversation>> {
|
|||||||
// Called after a message is sent to patch the server-generated title
|
// Called after a message is sent to patch the server-generated title
|
||||||
// in-place without triggering a full reload or loading state.
|
// in-place without triggering a full reload or loading state.
|
||||||
void patchConversation(Conversation updated) {
|
void patchConversation(Conversation updated) {
|
||||||
final list = state.valueOrNull;
|
final list = state.value;
|
||||||
if (list == null) return;
|
if (list == null) return;
|
||||||
state = AsyncData([
|
state = AsyncData([
|
||||||
for (final c in list)
|
for (final c in list)
|
||||||
@@ -44,21 +55,26 @@ class ConversationsNotifier extends AsyncNotifier<List<Conversation>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final messagesProvider = AsyncNotifierProvider.family<MessagesNotifier,
|
final messagesProvider =
|
||||||
List<Message>, int>(MessagesNotifier.new);
|
AsyncNotifierProvider.family<MessagesNotifier, List<Message>, int>(
|
||||||
|
(convId) => MessagesNotifier(convId),
|
||||||
|
);
|
||||||
|
|
||||||
|
class MessagesNotifier extends AsyncNotifier<List<Message>> {
|
||||||
|
final int _convId;
|
||||||
|
MessagesNotifier(this._convId);
|
||||||
|
|
||||||
class MessagesNotifier extends FamilyAsyncNotifier<List<Message>, int> {
|
|
||||||
@override
|
@override
|
||||||
Future<List<Message>> build(int arg) async {
|
Future<List<Message>> build() async {
|
||||||
final (_, messages) =
|
final (_, messages) =
|
||||||
await ref.watch(chatRepositoryProvider).getMessages(arg);
|
await ref.watch(chatRepositoryProvider).getMessages(_convId);
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sendMessage(String content) async {
|
Future<void> sendMessage(String content) async {
|
||||||
final convId = arg;
|
final convId = _convId;
|
||||||
final repo = ref.read(chatRepositoryProvider);
|
final repo = ref.read(chatRepositoryProvider);
|
||||||
final previousMessages = state.valueOrNull ?? [];
|
final previousMessages = state.value ?? [];
|
||||||
|
|
||||||
// Optimistic UI: show the user message + assistant placeholder immediately.
|
// Optimistic UI: show the user message + assistant placeholder immediately.
|
||||||
final userMsg = Message(
|
final userMsg = Message(
|
||||||
@@ -132,7 +148,7 @@ class MessagesNotifier extends FamilyAsyncNotifier<List<Message>, int> {
|
|||||||
} catch (_) {
|
} catch (_) {
|
||||||
// Polling failed entirely — clear the generating placeholder so the UI
|
// Polling failed entirely — clear the generating placeholder so the UI
|
||||||
// doesn't spin forever.
|
// doesn't spin forever.
|
||||||
final msgs = state.valueOrNull;
|
final msgs = state.value;
|
||||||
if (msgs != null && msgs.isNotEmpty && msgs.last.status == 'generating') {
|
if (msgs != null && msgs.isNotEmpty && msgs.last.status == 'generating') {
|
||||||
state = AsyncData([
|
state = AsyncData([
|
||||||
...msgs.sublist(0, msgs.length - 1),
|
...msgs.sublist(0, msgs.length - 1),
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class NotesNotifier extends AsyncNotifier<List<Note>> {
|
|||||||
final note = await ref
|
final note = await ref
|
||||||
.read(notesRepositoryProvider)
|
.read(notesRepositoryProvider)
|
||||||
.create(title, body, tags: tags, projectId: projectId);
|
.create(title, body, tags: tags, projectId: projectId);
|
||||||
state = AsyncData([...state.valueOrNull ?? [], note]);
|
state = AsyncData([...state.value ?? [], note]);
|
||||||
return note;
|
return note;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ class NotesNotifier extends AsyncNotifier<List<Note>> {
|
|||||||
clearProject: clearProject,
|
clearProject: clearProject,
|
||||||
);
|
);
|
||||||
state = AsyncData([
|
state = AsyncData([
|
||||||
for (final n in state.valueOrNull ?? [])
|
for (final n in state.value ?? [])
|
||||||
if (n.id == id) updated else n,
|
if (n.id == id) updated else n,
|
||||||
]);
|
]);
|
||||||
return updated;
|
return updated;
|
||||||
@@ -51,7 +51,7 @@ class NotesNotifier extends AsyncNotifier<List<Note>> {
|
|||||||
Future<void> delete(int id) async {
|
Future<void> delete(int id) async {
|
||||||
await ref.read(notesRepositoryProvider).delete(id);
|
await ref.read(notesRepositoryProvider).delete(id);
|
||||||
state = AsyncData([
|
state = AsyncData([
|
||||||
for (final n in state.valueOrNull ?? [])
|
for (final n in state.value ?? [])
|
||||||
if (n.id != id) n,
|
if (n.id != id) n,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class ProjectsNotifier extends AsyncNotifier<List<Project>> {
|
|||||||
goal: goal,
|
goal: goal,
|
||||||
color: color,
|
color: color,
|
||||||
);
|
);
|
||||||
state = AsyncData([...state.valueOrNull ?? [], project]);
|
state = AsyncData([...state.value ?? [], project]);
|
||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ class ProjectsNotifier extends AsyncNotifier<List<Project>> {
|
|||||||
final updated =
|
final updated =
|
||||||
await ref.read(projectsRepositoryProvider).update(id, fields);
|
await ref.read(projectsRepositoryProvider).update(id, fields);
|
||||||
state = AsyncData([
|
state = AsyncData([
|
||||||
for (final p in state.valueOrNull ?? [])
|
for (final p in state.value ?? [])
|
||||||
if (p.id == id) updated else p,
|
if (p.id == id) updated else p,
|
||||||
]);
|
]);
|
||||||
return updated;
|
return updated;
|
||||||
@@ -42,7 +42,7 @@ class ProjectsNotifier extends AsyncNotifier<List<Project>> {
|
|||||||
Future<void> delete(int id) async {
|
Future<void> delete(int id) async {
|
||||||
await ref.read(projectsRepositoryProvider).delete(id);
|
await ref.read(projectsRepositoryProvider).delete(id);
|
||||||
state = AsyncData([
|
state = AsyncData([
|
||||||
for (final p in state.valueOrNull ?? [])
|
for (final p in state.value ?? [])
|
||||||
if (p.id != id) p,
|
if (p.id != id) p,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,16 +15,14 @@ final cookiesPathProvider = Provider<String>((ref) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
final themeModeProvider =
|
final themeModeProvider =
|
||||||
StateNotifierProvider<ThemeModeNotifier, ThemeMode>((ref) {
|
NotifierProvider<ThemeModeNotifier, ThemeMode>(ThemeModeNotifier.new);
|
||||||
final prefs = ref.watch(sharedPreferencesProvider);
|
|
||||||
return ThemeModeNotifier(prefs);
|
|
||||||
});
|
|
||||||
|
|
||||||
class ThemeModeNotifier extends StateNotifier<ThemeMode> {
|
class ThemeModeNotifier extends Notifier<ThemeMode> {
|
||||||
final SharedPreferences _prefs;
|
@override
|
||||||
|
ThemeMode build() {
|
||||||
ThemeModeNotifier(this._prefs)
|
final prefs = ref.watch(sharedPreferencesProvider);
|
||||||
: super(_fromString(_prefs.getString(_kThemeMode)));
|
return _fromString(prefs.getString(_kThemeMode));
|
||||||
|
}
|
||||||
|
|
||||||
static ThemeMode _fromString(String? value) => switch (value) {
|
static ThemeMode _fromString(String? value) => switch (value) {
|
||||||
'light' => ThemeMode.light,
|
'light' => ThemeMode.light,
|
||||||
@@ -38,48 +36,49 @@ class ThemeModeNotifier extends StateNotifier<ThemeMode> {
|
|||||||
ThemeMode.dark => 'dark',
|
ThemeMode.dark => 'dark',
|
||||||
_ => 'system',
|
_ => 'system',
|
||||||
};
|
};
|
||||||
await _prefs.setString(_kThemeMode, value);
|
await ref.read(sharedPreferencesProvider).setString(_kThemeMode, value);
|
||||||
state = mode;
|
state = mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final forgejoRepoUrlProvider =
|
final forgejoRepoUrlProvider =
|
||||||
StateNotifierProvider<ForgejoRepoUrlNotifier, String?>((ref) {
|
NotifierProvider<ForgejoRepoUrlNotifier, String?>(
|
||||||
return ForgejoRepoUrlNotifier(ref.watch(sharedPreferencesProvider));
|
ForgejoRepoUrlNotifier.new);
|
||||||
});
|
|
||||||
|
|
||||||
class ForgejoRepoUrlNotifier extends StateNotifier<String?> {
|
class ForgejoRepoUrlNotifier extends Notifier<String?> {
|
||||||
final SharedPreferences _prefs;
|
@override
|
||||||
ForgejoRepoUrlNotifier(this._prefs)
|
String? build() {
|
||||||
: super(_prefs.getString(_kForgejoRepoUrl) ??
|
final prefs = ref.watch(sharedPreferencesProvider);
|
||||||
'https://git.fabledsword.com/bvandeusen/FabledApp');
|
return prefs.getString(_kForgejoRepoUrl) ??
|
||||||
|
'https://git.fabledsword.com/bvandeusen/FabledApp';
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> setUrl(String url) async {
|
Future<void> setUrl(String url) async {
|
||||||
final clean = url.endsWith('/') ? url.substring(0, url.length - 1) : url;
|
final clean = url.endsWith('/') ? url.substring(0, url.length - 1) : url;
|
||||||
await _prefs.setString(_kForgejoRepoUrl, clean);
|
await ref.read(sharedPreferencesProvider).setString(_kForgejoRepoUrl, clean);
|
||||||
state = clean;
|
state = clean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final serverUrlProvider = StateNotifierProvider<ServerUrlNotifier, String?>((ref) {
|
final serverUrlProvider =
|
||||||
final prefs = ref.watch(sharedPreferencesProvider);
|
NotifierProvider<ServerUrlNotifier, String?>(ServerUrlNotifier.new);
|
||||||
return ServerUrlNotifier(prefs);
|
|
||||||
});
|
|
||||||
|
|
||||||
class ServerUrlNotifier extends StateNotifier<String?> {
|
class ServerUrlNotifier extends Notifier<String?> {
|
||||||
final SharedPreferences _prefs;
|
@override
|
||||||
|
String? build() {
|
||||||
ServerUrlNotifier(this._prefs) : super(_prefs.getString(_kServerUrl));
|
final prefs = ref.watch(sharedPreferencesProvider);
|
||||||
|
return prefs.getString(_kServerUrl);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> setUrl(String url) async {
|
Future<void> setUrl(String url) async {
|
||||||
// Strip trailing slash
|
// Strip trailing slash
|
||||||
final clean = url.endsWith('/') ? url.substring(0, url.length - 1) : url;
|
final clean = url.endsWith('/') ? url.substring(0, url.length - 1) : url;
|
||||||
await _prefs.setString(_kServerUrl, clean);
|
await ref.read(sharedPreferencesProvider).setString(_kServerUrl, clean);
|
||||||
state = clean;
|
state = clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> clear() async {
|
Future<void> clear() async {
|
||||||
await _prefs.remove(_kServerUrl);
|
await ref.read(sharedPreferencesProvider).remove(_kServerUrl);
|
||||||
state = null;
|
state = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,14 +28,14 @@ class TasksNotifier extends AsyncNotifier<List<Task>> {
|
|||||||
dueDate: dueDate,
|
dueDate: dueDate,
|
||||||
projectId: projectId,
|
projectId: projectId,
|
||||||
);
|
);
|
||||||
state = AsyncData([...state.valueOrNull ?? [], task]);
|
state = AsyncData([...state.value ?? [], task]);
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Task> updateTask(int id, Map<String, dynamic> fields) async {
|
Future<Task> updateTask(int id, Map<String, dynamic> fields) async {
|
||||||
final updated = await ref.read(tasksRepositoryProvider).update(id, fields);
|
final updated = await ref.read(tasksRepositoryProvider).update(id, fields);
|
||||||
state = AsyncData([
|
state = AsyncData([
|
||||||
for (final t in state.valueOrNull ?? [])
|
for (final t in state.value ?? [])
|
||||||
if (t.id == id) updated else t,
|
if (t.id == id) updated else t,
|
||||||
]);
|
]);
|
||||||
return updated;
|
return updated;
|
||||||
@@ -44,7 +44,7 @@ class TasksNotifier extends AsyncNotifier<List<Task>> {
|
|||||||
Future<void> delete(int id) async {
|
Future<void> delete(int id) async {
|
||||||
await ref.read(tasksRepositoryProvider).delete(id);
|
await ref.read(tasksRepositoryProvider).delete(id);
|
||||||
state = AsyncData([
|
state = AsyncData([
|
||||||
for (final t in state.valueOrNull ?? [])
|
for (final t in state.value ?? [])
|
||||||
if (t.id != id) t,
|
if (t.id != id) t,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
|
|||||||
_usernameController.text.trim(),
|
_usernameController.text.trim(),
|
||||||
_passwordController.text,
|
_passwordController.text,
|
||||||
);
|
);
|
||||||
if (mounted) context.go(Routes.notes);
|
if (mounted) context.go(Routes.briefing);
|
||||||
} on AuthException catch (e) {
|
} on AuthException catch (e) {
|
||||||
setState(() => _error = e.message);
|
setState(() => _error = e.message);
|
||||||
} on AppException catch (e) {
|
} on AppException catch (e) {
|
||||||
@@ -90,7 +90,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
|
|||||||
cookieJar: ref.read(cookieJarProvider),
|
cookieJar: ref.read(cookieJarProvider),
|
||||||
onSuccess: () async {
|
onSuccess: () async {
|
||||||
await ref.read(authProvider.notifier).verify();
|
await ref.read(authProvider.notifier).verify();
|
||||||
if (mounted) context.go(Routes.notes);
|
if (mounted) context.go(Routes.briefing);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
|
|||||||
|
|
||||||
final convTitle = ref
|
final convTitle = ref
|
||||||
.watch(conversationsProvider)
|
.watch(conversationsProvider)
|
||||||
.valueOrNull
|
.value
|
||||||
?.where((c) => c.id == widget.conversationId)
|
?.where((c) => c.id == widget.conversationId)
|
||||||
.firstOrNull
|
.firstOrNull
|
||||||
?.title;
|
?.title;
|
||||||
|
|||||||
@@ -228,8 +228,8 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
|
|||||||
AsyncValue<List<Note>> notesAsync,
|
AsyncValue<List<Note>> notesAsync,
|
||||||
AsyncValue<List<Task>> tasksAsync,
|
AsyncValue<List<Task>> tasksAsync,
|
||||||
) {
|
) {
|
||||||
final notes = notesAsync.valueOrNull ?? [];
|
final notes = notesAsync.value ?? [];
|
||||||
final tasks = tasksAsync.valueOrNull ?? [];
|
final tasks = tasksAsync.value ?? [];
|
||||||
|
|
||||||
// Merge and sort by updatedAt desc
|
// Merge and sort by updatedAt desc
|
||||||
final items = <(DateTime, Widget)>[];
|
final items = <(DateTime, Widget)>[];
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class NoteDetailScreen extends ConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final noteAsync = ref.watch(noteDetailProvider(noteId));
|
final noteAsync = ref.watch(noteDetailProvider(noteId));
|
||||||
final allNotes = ref.watch(notesProvider).valueOrNull ?? [];
|
final allNotes = ref.watch(notesProvider).value ?? [];
|
||||||
|
|
||||||
void navigateByTitle(String title) {
|
void navigateByTitle(String title) {
|
||||||
final matches = allNotes.where(
|
final matches = allNotes.where(
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class _SplashScreenState extends ConsumerState<SplashScreen> {
|
|||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final status = ref.read(authProvider);
|
final status = ref.read(authProvider);
|
||||||
if (status == AuthStatus.authenticated) {
|
if (status == AuthStatus.authenticated) {
|
||||||
context.go(Routes.notes);
|
context.go(Routes.briefing);
|
||||||
} else {
|
} else {
|
||||||
context.go(Routes.login);
|
context.go(Routes.login);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'package:go_router/go_router.dart';
|
|||||||
|
|
||||||
import '../../core/constants.dart';
|
import '../../core/constants.dart';
|
||||||
import '../../core/exceptions.dart';
|
import '../../core/exceptions.dart';
|
||||||
import '../../data/api/tasks_api.dart';
|
|
||||||
import '../../data/models/task.dart';
|
import '../../data/models/task.dart';
|
||||||
import '../../providers/api_client_provider.dart';
|
import '../../providers/api_client_provider.dart';
|
||||||
import '../../providers/tasks_provider.dart';
|
import '../../providers/tasks_provider.dart';
|
||||||
@@ -239,6 +238,7 @@ class _TaskEditScreenState extends ConsumerState<TaskEditScreen> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
DropdownButtonFormField<TaskStatus>(
|
DropdownButtonFormField<TaskStatus>(
|
||||||
|
// ignore: deprecated_member_use
|
||||||
value: _status,
|
value: _status,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Status',
|
labelText: 'Status',
|
||||||
@@ -253,6 +253,7 @@ class _TaskEditScreenState extends ConsumerState<TaskEditScreen> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
DropdownButtonFormField<TaskPriority>(
|
DropdownButtonFormField<TaskPriority>(
|
||||||
|
// ignore: deprecated_member_use
|
||||||
value: _priority,
|
value: _priority,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Priority',
|
labelText: 'Priority',
|
||||||
|
|||||||
@@ -23,12 +23,13 @@ class ProjectSelector extends ConsumerWidget {
|
|||||||
|
|
||||||
return projectsAsync.when(
|
return projectsAsync.when(
|
||||||
loading: () => const LinearProgressIndicator(),
|
loading: () => const LinearProgressIndicator(),
|
||||||
error: (_, __) => const SizedBox.shrink(),
|
error: (_, _) => const SizedBox.shrink(),
|
||||||
data: (projects) {
|
data: (projects) {
|
||||||
final active =
|
final active =
|
||||||
projects.where((p) => p.status == 'active').toList();
|
projects.where((p) => p.status == 'active').toList();
|
||||||
|
|
||||||
return DropdownButtonFormField<int?>(
|
return DropdownButtonFormField<int?>(
|
||||||
|
// ignore: deprecated_member_use
|
||||||
value: value,
|
value: value,
|
||||||
decoration: decoration ??
|
decoration: decoration ??
|
||||||
const InputDecoration(
|
const InputDecoration(
|
||||||
|
|||||||
+204
-20
@@ -1,6 +1,22 @@
|
|||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
|
_fe_analyzer_shared:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: _fe_analyzer_shared
|
||||||
|
sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "93.0.0"
|
||||||
|
analyzer:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: analyzer
|
||||||
|
sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "10.0.1"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -49,6 +65,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.4"
|
version: "2.0.4"
|
||||||
|
cli_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cli_config
|
||||||
|
sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
cli_util:
|
cli_util:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -81,6 +105,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.19.1"
|
version: "1.19.1"
|
||||||
|
convert:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: convert
|
||||||
|
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.2"
|
||||||
cookie_jar:
|
cookie_jar:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -89,6 +121,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.9"
|
version: "4.0.9"
|
||||||
|
coverage:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: coverage
|
||||||
|
sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.15.0"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -109,26 +149,26 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dio
|
name: dio
|
||||||
sha256: b9d46faecab38fc8cc286f80bc4d61a3bb5d4ac49e51ed877b4d6706efe57b25
|
sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.9.1"
|
version: "5.9.2"
|
||||||
dio_cookie_manager:
|
dio_cookie_manager:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dio_cookie_manager
|
name: dio_cookie_manager
|
||||||
sha256: d39c16abcc711c871b7b29bd51c6b5f3059ef39503916c6a9df7e22c4fc595e0
|
sha256: "0db1a7b997a0455e488ac35744c68eed3f2a4280d3ab531835a65641b0a08744"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.0"
|
version: "3.4.0"
|
||||||
dio_web_adapter:
|
dio_web_adapter:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dio_web_adapter
|
name: dio_web_adapter
|
||||||
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
|
sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -250,10 +290,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_riverpod
|
name: flutter_riverpod
|
||||||
sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1"
|
sha256: "4e166be88e1dbbaa34a280bdb744aeae73b7ef25fdf8db7a3bb776760a3648e2"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.6.1"
|
version: "3.3.1"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -264,6 +304,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
frontend_server_client:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: frontend_server_client
|
||||||
|
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.0"
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -276,26 +324,26 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: go_router
|
name: go_router
|
||||||
sha256: f02fd7d2a4dc512fec615529824fdd217fecb3a3d3de68360293a551f21634b3
|
sha256: "7974313e217a7771557add6ff2238acb63f635317c35fa590d348fb238f00896"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.8.1"
|
version: "17.1.0"
|
||||||
google_fonts:
|
google_fonts:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: google_fonts
|
name: google_fonts
|
||||||
sha256: ba03d03bcaa2f6cb7bd920e3b5027181db75ab524f8891c8bc3aa603885b8055
|
sha256: db9df7a5898d894eeda4c78143f35c30a243558be439518972366880b80bf88e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.3"
|
version: "8.0.2"
|
||||||
hooks:
|
hooks:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: hooks
|
name: hooks
|
||||||
sha256: "7a08a0d684cb3b8fb604b78455d5d352f502b68079f7b80b831c62220ab0a4f6"
|
sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.2"
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -304,6 +352,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.0"
|
version: "1.6.0"
|
||||||
|
http_multi_server:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_multi_server
|
||||||
|
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.2"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -320,6 +376,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.8.0"
|
version: "4.8.0"
|
||||||
|
io:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: io
|
||||||
|
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.5"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -412,10 +476,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: native_toolchain_c
|
name: native_toolchain_c
|
||||||
sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac"
|
sha256: "92b2ca62c8bd2b8d2f267cdfccf9bfbdb7322f778f8f91b3ce5b5cda23a3899f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.17.4"
|
version: "0.17.5"
|
||||||
|
node_preamble:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: node_preamble
|
||||||
|
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.2"
|
||||||
objective_c:
|
objective_c:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -488,14 +560,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.3"
|
version: "0.0.3"
|
||||||
|
package_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_config
|
||||||
|
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: package_info_plus
|
name: package_info_plus
|
||||||
sha256: "16eee997588c60225bda0488b6dcfac69280a6b7a3cf02c741895dd370a02968"
|
sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.3.1"
|
version: "9.0.0"
|
||||||
package_info_plus_platform_interface:
|
package_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -584,6 +664,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.8"
|
version: "2.1.8"
|
||||||
|
pool:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pool
|
||||||
|
sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.2"
|
||||||
posix:
|
posix:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -604,10 +692,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: riverpod
|
name: riverpod
|
||||||
sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959"
|
sha256: "8c22216be8ad3ef2b44af3a329693558c98eca7b8bd4ef495c92db0bba279f83"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.6.1"
|
version: "3.2.1"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -664,11 +752,59 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.1"
|
||||||
|
shelf:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf
|
||||||
|
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.2"
|
||||||
|
shelf_packages_handler:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_packages_handler
|
||||||
|
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
|
shelf_static:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_static
|
||||||
|
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.3"
|
||||||
|
shelf_web_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_web_socket
|
||||||
|
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
source_map_stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_map_stack_trace
|
||||||
|
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
source_maps:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_maps
|
||||||
|
sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.10.13"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -717,6 +853,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.2"
|
version: "1.2.2"
|
||||||
|
test:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test
|
||||||
|
sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.30.0"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -725,6 +869,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.10"
|
version: "0.7.10"
|
||||||
|
test_core:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_core
|
||||||
|
sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.16"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -757,6 +909,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "15.0.2"
|
version: "15.0.2"
|
||||||
|
watcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: watcher
|
||||||
|
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
web:
|
web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -765,6 +925,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
|
web_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web_socket
|
||||||
|
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
|
web_socket_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web_socket_channel
|
||||||
|
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.3"
|
||||||
|
webkit_inspection_protocol:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: webkit_inspection_protocol
|
||||||
|
sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
+5
-5
@@ -2,7 +2,7 @@ name: fabled_app
|
|||||||
description: "FabledAssistant mobile client for Android."
|
description: "FabledAssistant mobile client for Android."
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 26.03.02+1
|
version: 26.03.12+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.11.0
|
sdk: ^3.11.0
|
||||||
@@ -12,19 +12,19 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
flutter_riverpod: ^2.5.1
|
flutter_riverpod: ^3.3.1
|
||||||
go_router: ^14.2.0
|
go_router: ^17.1.0
|
||||||
dio: ^5.6.0
|
dio: ^5.6.0
|
||||||
cookie_jar: ^4.0.8
|
cookie_jar: ^4.0.8
|
||||||
dio_cookie_manager: ^3.1.1
|
dio_cookie_manager: ^3.1.1
|
||||||
path_provider: ^2.1.4
|
path_provider: ^2.1.4
|
||||||
shared_preferences: ^2.3.2
|
shared_preferences: ^2.3.2
|
||||||
markdown: ^7.2.2
|
markdown: ^7.2.2
|
||||||
package_info_plus: ^8.0.0
|
package_info_plus: ^9.0.0
|
||||||
open_file: ^3.3.2
|
open_file: ^3.3.2
|
||||||
flutter_inappwebview: ^6.1.5
|
flutter_inappwebview: ^6.1.5
|
||||||
flutter_markdown_plus: ^1.0.7
|
flutter_markdown_plus: ^1.0.7
|
||||||
google_fonts: ^6.2.1
|
google_fonts: ^8.0.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user