Skip to content

Commit

Permalink
Merge pull request #75 from lotusgate/dev
Browse files Browse the repository at this point in the history
v.1.2.3
  • Loading branch information
lotusprey committed Mar 14, 2023
2 parents 414c4d4 + 39bc669 commit 57acf8f
Show file tree
Hide file tree
Showing 102 changed files with 3,336 additions and 2,763 deletions.
4 changes: 1 addition & 3 deletions android/app/build.gradle
Expand Up @@ -81,7 +81,5 @@ flutter {
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// Workmanager
// implementation 'androidx.work:work-runtime-ktx:2.7.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
4 changes: 2 additions & 2 deletions android/build.gradle
@@ -1,6 +1,6 @@
buildscript {
ext {
kotlin_version = '1.6.21'
kotlin_version = '1.7.10'
}

repositories {
Expand All @@ -9,7 +9,7 @@ buildscript {
}

dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand Down
1 change: 0 additions & 1 deletion android/gradle.properties
@@ -1,5 +1,4 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
# Without this line there may be crashes on Android 6.0 devices apparently.
Expand Down
2 changes: 1 addition & 1 deletion android/gradle/wrapper/gradle-wrapper.properties
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
28 changes: 22 additions & 6 deletions ios/Podfile.lock
Expand Up @@ -6,8 +6,15 @@ PODS:
- Flutter
- flutter_secure_storage (6.0.0):
- Flutter
- path_provider_ios (0.0.1):
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- sqflite (0.0.2):
- Flutter
- FMDB (>= 2.7.5)
- url_launcher_ios (0.0.1):
- Flutter
- workmanager (0.0.1):
Expand All @@ -18,10 +25,15 @@ DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- workmanager (from `.symlinks/plugins/workmanager/ios`)

SPEC REPOS:
trunk:
- FMDB

EXTERNAL SOURCES:
app_links:
:path: ".symlinks/plugins/app_links/ios"
Expand All @@ -31,8 +43,10 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_local_notifications/ios"
flutter_secure_storage:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
path_provider_ios:
:path: ".symlinks/plugins/path_provider_ios/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/ios"
sqflite:
:path: ".symlinks/plugins/sqflite/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
workmanager:
Expand All @@ -43,8 +57,10 @@ SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6

PODFILE CHECKSUM: fe0e1ee7f3d1f7d00b11b474b62dd62134535aea
Expand Down
2 changes: 2 additions & 0 deletions ios/Runner.xcodeproj/project.pbxproj
Expand Up @@ -207,6 +207,7 @@
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
Expand Down Expand Up @@ -260,6 +261,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
Expand Down
2 changes: 2 additions & 0 deletions ios/Runner/Info.plist
Expand Up @@ -71,5 +71,7 @@
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
@@ -1,86 +1,20 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:otraku/activity/activity_models.dart';
import 'package:otraku/common/pagination.dart';
import 'package:otraku/home/home_provider.dart';
import 'package:otraku/utils/api.dart';
import 'package:otraku/utils/graphql.dart';
import 'package:otraku/common/pagination.dart';
import 'package:otraku/utils/options.dart';

/// Toggles an activity like and returns an error if unsuccessful.
Future<Object?> toggleActivityLike(Activity activity) async {
try {
await Api.get(GqlMutation.toggleLike, {
'id': activity.id,
'type': 'ACTIVITY',
});
return null;
} catch (e) {
return e;
}
}

/// Toggles an activity subscription and returns an error if unsuccessful.
Future<Object?> toggleActivitySubscription(Activity activity) async {
try {
await Api.get(GqlMutation.toggleActivitySubscription, {
'id': activity.id,
'subscribe': activity.isSubscribed,
});
return null;
} catch (e) {
return e;
}
}

/// Pins/Unpins an activity and returns an error if unsuccessful.
Future<Object?> toggleActivityPin(Activity activity) async {
try {
await Api.get(GqlMutation.toggleActivityPin, {
'id': activity.id,
'pinned': activity.isPinned,
});
return null;
} catch (e) {
return e;
}
}

/// Toggles a reply like and returns an error if unsuccessful.
Future<Object?> toggleReplyLike(ActivityReply reply) async {
try {
await Api.get(GqlMutation.toggleLike, {
'id': reply.id,
'type': 'ACTIVITY_REPLY',
});
return null;
} catch (e) {
return e;
}
}

/// Deletes an activity and returns an error if unsuccessful.
Future<Object?> deleteActivity(int activityId) async {
try {
await Api.get(GqlMutation.deleteActivity, {'id': activityId});
return null;
} catch (e) {
return e;
}
}

/// Deletes an activity reply and returns an error if unsuccessful.
Future<Object?> deleteActivityReply(int replyId) async {
try {
await Api.get(GqlMutation.deleteActivityReply, {'id': replyId});
return null;
} catch (e) {
return e;
}
}

final activityProvider = StateNotifierProvider.autoDispose
.family<ActivityNotifier, AsyncValue<ActivityState>, int>(
(ref, userId) => ActivityNotifier(userId, Options().id!),
final activitiesProvider = StateNotifierProvider.autoDispose
.family<ActivitiesNotifier, AsyncValue<Pagination<Activity>>, int?>(
(ref, userId) => ActivitiesNotifier(
userId: userId,
viewerId: Options().id!,
filter: ref.watch(activityFilterProvider(userId)),
shouldLoad:
userId != null || ref.watch(homeProvider.select((s) => s.didLoadFeed)),
),
);

final activityFilterProvider = StateNotifierProvider.autoDispose
Expand All @@ -101,134 +35,6 @@ final activityFilterProvider = StateNotifierProvider.autoDispose
},
);

final activitiesProvider = StateNotifierProvider.autoDispose
.family<ActivitiesNotifier, AsyncValue<Pagination<Activity>>, int?>(
(ref, userId) => ActivitiesNotifier(
userId: userId,
viewerId: Options().id!,
filter: ref.watch(activityFilterProvider(userId)),
shouldLoad:
userId != null || ref.watch(homeProvider.select((s) => s.didLoadFeed)),
),
);

class ActivityNotifier extends StateNotifier<AsyncValue<ActivityState>> {
ActivityNotifier(this.userId, this.viewerId)
: super(const AsyncValue.loading()) {
fetch();
}

final int userId;
final int viewerId;

Future<void> fetch() async {
state = await AsyncValue.guard(() async {
final replies = state.value?.replies ?? Pagination();

final data = await Api.get(GqlQuery.activity, {
'id': userId,
'page': replies.next,
if (replies.next == 1) 'withActivity': true,
});

final items = <ActivityReply>[];
for (final r in data['Page']['activityReplies']) {
final item = ActivityReply.maybe(r);
if (item != null) items.add(item);
}

final activity =
state.value?.activity ?? Activity.maybe(data['Activity'], viewerId);
if (activity == null) throw StateError('Could not parse activity');

return ActivityState(
activity,
replies.append(
items,
data['Page']['pageInfo']['hasNextPage'] ?? false,
),
);
});
}

/// Deserializes [map] and replaces the current activity.
void replaceActivity(Map<String, dynamic> map, int viewerId) {
if (!state.hasValue) return;
final value = state.value!;

final activity = Activity.maybe(map, viewerId);
if (activity == null) return;

state = AsyncData(ActivityState(activity, value.replies));
}

/// Deserializes [map] and appends it at the end.
void appendReply(Map<String, dynamic> map) {
if (!state.hasValue) return;
final value = state.value!;

final reply = ActivityReply.maybe(map);
if (reply == null) return;

value.activity.replyCount++;
state = AsyncData(ActivityState(
value.activity,
Pagination.from(
items: [...value.replies.items, reply],
hasNext: value.replies.hasNext,
next: value.replies.next,
),
));
}

/// Replaces an existing reply with another one.
void replaceReply(Map<String, dynamic> map) {
if (!state.hasValue) return;
final value = state.value!;

final reply = ActivityReply.maybe(map);
if (reply == null) return;

for (int i = 0; i < value.replies.items.length; i++) {
if (value.replies.items[i].id == reply.id) {
value.replies.items[i] = reply;
state = AsyncData(ActivityState(
value.activity,
Pagination.from(
items: value.replies.items,
hasNext: value.replies.hasNext,
next: value.replies.next,
),
));
return;
}
}
}

/// Removes an already deleted reply.
void removeReply(int replyId) {
if (!state.hasValue) return;
final value = state.value!;

for (int i = 0; i < value.replies.items.length; i++) {
if (value.replies.items[i].id == replyId) {
value.replies.items.removeAt(i);
value.activity.replyCount--;

state = AsyncData(ActivityState(
value.activity,
Pagination.from(
items: value.replies.items,
hasNext: value.replies.hasNext,
next: value.replies.next,
),
));
return;
}
}
}
}

class ActivitiesNotifier
extends StateNotifier<AsyncValue<Pagination<Activity>>> {
ActivitiesNotifier({
Expand All @@ -245,19 +51,27 @@ class ActivitiesNotifier
final int viewerId;
final ActivityFilter filter;

/// [_lastCreatedAt] is used to track pages, instead of the next page value
/// of the state. This prevents duplicates when more pages are loaded,
/// as new activities are created often.
int? _lastCreatedAt;

Future<void> fetch() async {
state = await AsyncValue.guard(() async {
final value = state.valueOrNull ?? Pagination();

final data = await Api.get(GqlQuery.activities, {
'page': value.next,
'typeIn': filter.typeIn.map((t) => t.name).toList(),
if (userId != null) ...{
'userId': userId,
} else ...{
'isFollowing': filter.onFollowing,
'hasRepliesOrTypeText': (filter.onFollowing ?? true) ? null : true,
if ((filter.onFollowing ?? false))
'userIdNot': viewerId
else
'hasRepliesOrText': true,
},
if (_lastCreatedAt != null) 'createdBefore': _lastCreatedAt!
});

final items = <Activity>[];
Expand All @@ -266,6 +80,10 @@ class ActivitiesNotifier
if (item != null) items.add(item);
}

if (data['Page']['activities']?.isNotEmpty ?? false) {
_lastCreatedAt = data['Page']['activities'].last['createdAt'];
}

return value.append(
items,
data['Page']['pageInfo']['hasNextPage'] ?? false,
Expand Down

0 comments on commit 57acf8f

Please sign in to comment.