Compare commits

...

11 Commits

Author SHA1 Message Date
LittleSheep
c061ef2132 🐛 Bug fixes 2025-08-11 01:44:18 +08:00
LittleSheep
c378309bdd 📝 Update localization 2025-08-11 01:44:12 +08:00
LittleSheep
b2c5d64fc5 Keyboard navigation basis 2025-08-10 16:57:11 +08:00
LittleSheep
5371637b16 🔀 Merge pull request #161 from Texas0295/v3
🐛 linux: guard FirebaseMessaging on unsupported platforms
2025-08-10 14:05:48 +08:00
LittleSheep
c5cbf0af37 ⬆️ Upgrade android native project 2025-08-10 13:51:19 +08:00
LittleSheep
1a31e22450 🐛 Fix stickers pack unable to create 2025-08-10 13:23:15 +08:00
Texas0295
49db54529d 🐛 linux: guard FirebaseMessaging on unsupported platforms 2025-08-10 13:17:48 +08:00
LittleSheep
8e0c0c6054 🚀 Launch 3.1.0+122 2025-08-10 04:16:58 +08:00
LittleSheep
f3d1183076 🐛 Fix android update 2025-08-10 04:16:53 +08:00
LittleSheep
a9f7f0cce0 🐛 Fix bugs, ah bugs ha ha, bugs 2025-08-10 04:04:31 +08:00
LittleSheep
f2943f8411 🐛 Fix iOS notify delegate wrong path 2025-08-10 03:30:57 +08:00
22 changed files with 834 additions and 593 deletions

View File

@@ -51,6 +51,12 @@ android {
buildTypes { buildTypes {
release { release {
signingConfig = signingConfigs.getByName("release") signingConfig = signingConfigs.getByName("release")
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
} }
} }
} }
@@ -58,7 +64,7 @@ android {
dependencies { dependencies {
implementation("com.google.android.material:material:1.12.0") implementation("com.google.android.material:material:1.12.0")
implementation("com.github.bumptech.glide:glide:4.16.0") implementation("com.github.bumptech.glide:glide:4.16.0")
implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation("com.squareup.okhttp3:okhttp:5.1.0")
} }
flutter { flutter {

5
android/app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,5 @@
# JNI Zero initialization (required for WebRTC native method registration)
-keep class livekit.org.jni_zero.JniInit {
# Keep the init method un-obfuscated for native code callback
private static java.lang.Object[] init();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip

View File

@@ -18,11 +18,11 @@ pluginManagement {
plugins { plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.10.1" apply false id("com.android.application") version "8.12.0" apply false
// START: FlutterFire Configuration // START: FlutterFire Configuration
id("com.google.gms.google-services") version("4.3.15") apply false id("com.google.gms.google-services") version("4.3.15") apply false
// END: FlutterFire Configuration // END: FlutterFire Configuration
id("org.jetbrains.kotlin.android") version "1.8.22" apply false id("org.jetbrains.kotlin.android") version("2.2.0") apply false
} }
include(":app") include(":app")

View File

@@ -789,5 +789,6 @@
"linkKey": "Link Name", "linkKey": "Link Name",
"linkValue": "URL", "linkValue": "URL",
"debugOptions": "Debug Options", "debugOptions": "Debug Options",
"joinedAt": "Joined at {}" "joinedAt": "Joined at {}",
"searchAccounts": "Search accounts..."
} }

File diff suppressed because it is too large Load Diff

View File

@@ -34,7 +34,7 @@ class NotifyDelegate: UIResponder, UNUserNotificationCenterDelegate {
} }
let serverUrl = UserDefaults.standard.getServerUrl() let serverUrl = UserDefaults.standard.getServerUrl()
let url = "\(serverUrl)/chat/\(metadata["room_id"] ?? "")/messages" let url = "\(serverUrl)/sphere/chat/\(metadata["room_id"] ?? "")/messages"
let parameters: [String: Any?] = [ let parameters: [String: Any?] = [
"content": textResponse.userText, "content": textResponse.userText,

View File

@@ -28,6 +28,7 @@ import 'package:relative_time/relative_time.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:island/widgets/keyboard_navigation.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
import 'package:flutter_langdetect/flutter_langdetect.dart' as langdetect; import 'package:flutter_langdetect/flutter_langdetect.dart' as langdetect;
@@ -244,30 +245,32 @@ class IslandApp extends HookConsumerWidget {
final router = ref.watch(routerProvider); final router = ref.watch(routerProvider);
return MaterialApp.router( return KeyboardNavigation(
theme: theme?.light, child: MaterialApp.router(
darkTheme: theme?.dark, theme: theme?.light,
themeMode: ThemeMode.system, darkTheme: theme?.dark,
routerConfig: router, themeMode: ThemeMode.system,
supportedLocales: context.supportedLocales, routerConfig: router,
localizationsDelegates: [ supportedLocales: context.supportedLocales,
...context.localizationDelegates, localizationsDelegates: [
CroppyLocalizations.delegate, ...context.localizationDelegates,
RelativeTimeLocalizations.delegate, CroppyLocalizations.delegate,
], RelativeTimeLocalizations.delegate,
locale: context.locale, ],
builder: (context, child) { locale: context.locale,
return Overlay( builder: (context, child) {
key: globalOverlay, return Overlay(
initialEntries: [ key: globalOverlay,
OverlayEntry( initialEntries: [
builder: OverlayEntry(
(_) => builder:
WindowScaffold(child: child ?? const SizedBox.shrink()), (_) =>
), WindowScaffold(child: child ?? const SizedBox.shrink()),
], ),
); ],
}, );
},
),
); );
} }
} }

View File

@@ -216,6 +216,7 @@ class RelationshipScreen extends HookConsumerWidget {
final result = await showModalBottomSheet( final result = await showModalBottomSheet(
context: context, context: context,
useRootNavigator: true, useRootNavigator: true,
isScrollControlled: true,
builder: (context) => AccountPickerSheet(), builder: (context) => AccountPickerSheet(),
); );
if (result == null) return; if (result == null) return;

View File

@@ -227,6 +227,7 @@ class ChatListScreen extends HookConsumerWidget {
final result = await showModalBottomSheet( final result = await showModalBottomSheet(
context: context, context: context,
useRootNavigator: true, useRootNavigator: true,
isScrollControlled: true,
builder: (context) => const AccountPickerSheet(), builder: (context) => const AccountPickerSheet(),
); );
if (result == null) return; if (result == null) return;

View File

@@ -339,7 +339,7 @@ class ChatRoomScreen extends HookConsumerWidget {
} }
await apiClient.post( await apiClient.post(
'/chat/${chatRoom.value!.id}/members/me', '/sphere/chat/${chatRoom.value!.id}/members/me',
); );
ref.invalidate(chatroomIdentityProvider(id)); ref.invalidate(chatroomIdentityProvider(id));
} catch (err) { } catch (err) {
@@ -929,7 +929,7 @@ class ChatRoomScreen extends HookConsumerWidget {
if (attachment.isOnCloud) { if (attachment.isOnCloud) {
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
await client.delete( await client.delete(
'/files/${attachment.data.id}', '/drive/files/${attachment.data.id}',
); );
} }
final clone = List.of(attachments.value); final clone = List.of(attachments.value);

View File

@@ -589,6 +589,7 @@ class _ChatMemberListSheet extends HookConsumerWidget {
final result = await showModalBottomSheet( final result = await showModalBottomSheet(
context: context, context: context,
useRootNavigator: true, useRootNavigator: true,
isScrollControlled: true,
builder: (context) => const AccountPickerSheet(), builder: (context) => const AccountPickerSheet(),
); );
if (result == null) return; if (result == null) return;
@@ -727,7 +728,7 @@ class _ChatMemberListSheet extends HookConsumerWidget {
apiClientProvider, apiClientProvider,
); );
await apiClient.delete( await apiClient.delete(
'/chat/$roomId/members/${member.accountId}', '/sphere/chat/$roomId/members/${member.accountId}',
); );
// Refresh both providers // Refresh both providers
memberNotifier.reset(); memberNotifier.reset();

View File

@@ -708,6 +708,7 @@ class _PublisherMemberListSheet extends HookConsumerWidget {
Future<void> invitePerson() async { Future<void> invitePerson() async {
final result = await showModalBottomSheet( final result = await showModalBottomSheet(
useRootNavigator: true,
isScrollControlled: true, isScrollControlled: true,
context: context, context: context,
builder: (context) => const AccountPickerSheet(), builder: (context) => const AccountPickerSheet(),

View File

@@ -180,6 +180,7 @@ class StickerPackDetailScreen extends HookConsumerWidget {
.pushNamed( .pushNamed(
'creatorStickerEdit', 'creatorStickerEdit',
pathParameters: { pathParameters: {
'name': pubName,
'packId': id, 'packId': id,
'id': sticker.id, 'id': sticker.id,
}, },

View File

@@ -31,7 +31,7 @@ class StickersScreen extends HookConsumerWidget {
context context
.pushNamed( .pushNamed(
'creatorStickerPackNew', 'creatorStickerPackNew',
queryParameters: {'name': pubName}, pathParameters: {'name': pubName},
) )
.then((value) { .then((value) {
if (value != null) { if (value != null) {
@@ -187,10 +187,8 @@ class EditStickerPacksScreen extends HookConsumerWidget {
'description': descriptionController.text, 'description': descriptionController.text,
'prefix': prefixController.text, 'prefix': prefixController.text,
}, },
options: Options( queryParameters: {'pub': pubName},
method: packId == null ? 'POST' : 'PATCH', options: Options(method: packId == null ? 'POST' : 'PATCH'),
headers: {'X-Pub': pubName},
),
); );
if (!context.mounted) return; if (!context.mounted) return;
context.pop(SnStickerPack.fromJson(resp.data)); context.pop(SnStickerPack.fromJson(resp.data));

View File

@@ -488,6 +488,7 @@ class _RealmMemberListSheet extends HookConsumerWidget {
Future<void> invitePerson() async { Future<void> invitePerson() async {
final result = await showModalBottomSheet( final result = await showModalBottomSheet(
isScrollControlled: true, isScrollControlled: true,
useRootNavigator: true,
context: context, context: context,
builder: (context) => const AccountPickerSheet(), builder: (context) => const AccountPickerSheet(),
); );

View File

@@ -67,6 +67,9 @@ Future<void> subscribePushNotification(
Dio apiClient, { Dio apiClient, {
bool detailedErrors = false, bool detailedErrors = false,
}) async { }) async {
if (Platform.isLinux){
return;
}
await FirebaseMessaging.instance.requestPermission( await FirebaseMessaging.instance.requestPermission(
alert: true, alert: true,
badge: true, badge: true,

View File

@@ -305,7 +305,7 @@ class _UpdateSheetState extends State<_UpdateSheet> {
UpdateModel model = UpdateModel( UpdateModel model = UpdateModel(
downloadUrl, downloadUrl,
"solian-update-${widget.release.tagName}.apk", "solian-update-${widget.release.tagName}.apk",
"ic_launcher", "launcher_icon",
'https://apps.apple.com/us/app/solian/id6499032345', 'https://apps.apple.com/us/app/solian/id6499032345',
); );
AzhonAppUpdate.update(model); AzhonAppUpdate.update(model);

View File

@@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -44,9 +45,8 @@ class AccountPickerSheet extends HookConsumerWidget {
} }
return Container( return Container(
constraints: BoxConstraints( padding: MediaQuery.of(context).viewInsets,
maxHeight: MediaQuery.of(context).size.height * 0.4, height: MediaQuery.of(context).size.height * 0.6,
),
child: Column( child: Column(
children: [ children: [
Padding( Padding(
@@ -54,8 +54,8 @@ class AccountPickerSheet extends HookConsumerWidget {
child: TextField( child: TextField(
controller: searchController, controller: searchController,
onChanged: onSearchChanged, onChanged: onSearchChanged,
decoration: const InputDecoration( decoration: InputDecoration(
hintText: 'Search accounts...', hintText: 'searchAccounts'.tr(),
contentPadding: EdgeInsets.symmetric( contentPadding: EdgeInsets.symmetric(
horizontal: 18, horizontal: 18,
vertical: 16, vertical: 16,

View File

@@ -0,0 +1,86 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
enum VimMode { normal, insert }
class KeyboardNavigation extends StatefulWidget {
const KeyboardNavigation({super.key, required this.child});
final Widget child;
@override
State<KeyboardNavigation> createState() => _KeyboardNavigationState();
}
class _KeyboardNavigationState extends State<KeyboardNavigation> {
VimMode _mode = VimMode.normal;
final FocusScopeNode _focusScopeNode = FocusScopeNode();
@override
void dispose() {
_focusScopeNode.dispose();
super.dispose();
}
KeyEventResult _handleKeyEvent(FocusNode node, KeyEvent event) {
if (event is! KeyDownEvent && event is! KeyRepeatEvent) {
return KeyEventResult.ignored;
}
if (_mode == VimMode.normal) {
if (event.logicalKey == LogicalKeyboardKey.keyJ) {
node.focusInDirection(TraversalDirection.down);
return KeyEventResult.handled;
} else if (event.logicalKey == LogicalKeyboardKey.keyK) {
node.focusInDirection(TraversalDirection.up);
return KeyEventResult.handled;
} else if (event.logicalKey == LogicalKeyboardKey.keyH) {
final focusNode = FocusManager.instance.primaryFocus;
if (focusNode != null) {
final scrollable = Scrollable.of(focusNode.context!);
if (scrollable.position.axis == Axis.horizontal) {
scrollable.position.moveTo(scrollable.position.pixels - 50);
return KeyEventResult.handled;
}
}
node.focusInDirection(TraversalDirection.left);
return KeyEventResult.handled;
} else if (event.logicalKey == LogicalKeyboardKey.keyL) {
final focusNode = FocusManager.instance.primaryFocus;
if (focusNode != null) {
final scrollable = Scrollable.of(focusNode.context!);
if (scrollable.position.axis == Axis.horizontal) {
scrollable.position.moveTo(scrollable.position.pixels + 50);
return KeyEventResult.handled;
}
}
node.focusInDirection(TraversalDirection.right);
return KeyEventResult.handled;
} else if (event.logicalKey == LogicalKeyboardKey.keyI) {
setState(() {
_mode = VimMode.insert;
});
return KeyEventResult.handled;
}
} else if (_mode == VimMode.insert) {
if (event.logicalKey == LogicalKeyboardKey.escape) {
setState(() {
_mode = VimMode.normal;
});
// Unfocus the current widget to prevent typing
node.unfocus();
return KeyEventResult.handled;
}
}
return KeyEventResult.ignored;
}
@override
Widget build(BuildContext context) {
return Focus(
focusNode: _focusScopeNode,
onKeyEvent: _handleKeyEvent,
child: widget.child,
);
}
}

View File

@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 3.1.0+121 version: 3.1.0+122
environment: environment:
sdk: ^3.7.2 sdk: ^3.7.2