🐛 Bug fixes android sharing, ios notifications and more

This commit is contained in:
LittleSheep 2025-06-28 01:10:44 +08:00
parent 243ecb3f71
commit a23dcfe702
12 changed files with 123 additions and 91 deletions

View File

@ -46,12 +46,37 @@
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="video/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="video/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/*" />
</intent-filter>
</activity>

View File

@ -987,7 +987,7 @@
INFOPLIST_FILE = SolianNotificationService/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianNotificationService;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -1029,7 +1029,7 @@
INFOPLIST_FILE = SolianNotificationService/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianNotificationService;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -1068,7 +1068,7 @@
INFOPLIST_FILE = SolianNotificationService/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianNotificationService;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",

View File

@ -11,6 +11,21 @@ import UIKit
) -> Bool {
UNUserNotificationCenter.current().delegate = notifyDelegate
let replyableMessageCategory = UNNotificationCategory(
identifier: "REPLYABLE_MESSAGE",
actions: [
UNTextInputNotificationAction(
identifier: "reply_action",
title: "Reply",
options: []
),
],
intentIdentifiers: [],
options: []
)
UNUserNotificationCenter.current().setNotificationCategories([replyableMessageCategory])
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

View File

@ -10,40 +10,51 @@ import Alamofire
class NotifyDelegate: UIResponder, UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
if let textResponse = response as? UNTextInputNotificationResponse {
let content = response.notification.request.content
guard let metadata = content.userInfo["meta"] as? [AnyHashable: Any] else {
return
}
var token: String? = UserDefaults.standard.getFlutterToken()
if token == nil {
return
}
let serverUrl = UserDefaults.standard.getServerUrl()
let url = "\(serverUrl)/chat/\(metadata["room_id"] ?? "")/messages"
let parameters: [String: Any?] = [
"content": textResponse.userText,
"replied_message_id": metadata["message_id"]
]
AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: HTTPHeaders(
[HTTPHeader(name: "Authorization", value: "AtField \(token!)")]
))
.validate()
.responseString { response in
switch response.result {
case .success(_):
break
case .failure(let error):
print("Failed to send chat reply message: \(error)")
break
}
}
guard let textResponse = response as? UNTextInputNotificationResponse else {
completionHandler()
return
}
completionHandler()
let content = response.notification.request.content
// Only handle replies for new messages
guard let notificationType = content.userInfo["type"] as? String, notificationType == "messages.new" else {
completionHandler()
return
}
guard let metadata = content.userInfo["meta"] as? [AnyHashable: Any] else {
completionHandler()
return
}
guard let token = UserDefaults.standard.getFlutterToken() else {
completionHandler()
return
}
let serverUrl = UserDefaults.standard.getServerUrl()
let url = "\(serverUrl)/chat/\(metadata["room_id"] ?? "")/messages"
let parameters: [String: Any?] = [
"content": textResponse.userText,
"replied_message_id": metadata["message_id"]
]
AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: HTTPHeaders(
[HTTPHeader(name: "Authorization", value: "AtField \(token)")]
))
.validate()
.responseString { response in
switch response.result {
case .success(_):
break
case .failure(let error):
print("Failed to send chat reply message: \(error)")
break
}
// Call completion handler after network request is finished
completionHandler()
}
}
}

View File

@ -60,21 +60,7 @@ class NotificationService: UNNotificationServiceExtension {
let pfpIdentifier = meta["pfp"] as? String
let replyableMessageCategory = UNNotificationCategory(
identifier: content.categoryIdentifier,
actions: [
UNTextInputNotificationAction(
identifier: "reply_action",
title: "Reply",
options: []
),
],
intentIdentifiers: [],
options: []
)
UNUserNotificationCenter.current().setNotificationCategories([replyableMessageCategory])
content.categoryIdentifier = replyableMessageCategory.identifier
content.categoryIdentifier = "REPLYABLE_MESSAGE"
let metaCopy = meta as? [String: Any] ?? [:]
let pfpUrl = pfpIdentifier != nil ? getAttachmentUrl(for: pfpIdentifier!) : nil

View File

@ -53,7 +53,10 @@ final routerProvider = Provider<GoRouter>((ref) {
// Standalone routes without bottom navigation
GoRoute(
path: '/posts/compose',
builder: (context, state) => const PostComposeScreen(),
builder:
(context, state) => PostComposeScreen(
initialState: state.extra as PostComposeInitialState?,
),
),
GoRoute(
path: '/posts/:id/edit',

View File

@ -54,25 +54,26 @@ Future<SnSubscriptionStatus> publisherSubscriptionStatus(
@riverpod
Future<Color?> publisherAppbarForcegroundColor(Ref ref, String pubName) async {
final publisher = await ref.watch(publisherProvider(pubName).future);
if (publisher.background == null) return null;
final palette = await PaletteGenerator.fromImageProvider(
CloudImageWidget.provider(
fileId: publisher.background!.id,
serverUrl: ref.watch(serverUrlProvider),
),
);
final dominantColor = palette.dominantColor?.color;
if (dominantColor == null) return null;
return dominantColor.computeLuminance() > 0.5 ? Colors.black : Colors.white;
try {
final publisher = await ref.watch(publisherProvider(pubName).future);
if (publisher.background == null) return null;
final palette = await PaletteGenerator.fromImageProvider(
CloudImageWidget.provider(
fileId: publisher.background!.id,
serverUrl: ref.watch(serverUrlProvider),
),
);
final dominantColor = palette.dominantColor?.color;
if (dominantColor == null) return null;
return dominantColor.computeLuminance() > 0.5 ? Colors.black : Colors.white;
} catch (_) {
return null;
}
}
class PublisherProfileScreen extends HookConsumerWidget {
final String name;
const PublisherProfileScreen({
super.key,
required this.name,
});
const PublisherProfileScreen({super.key, required this.name});
@override
Widget build(BuildContext context, WidgetRef ref) {

View File

@ -186,13 +186,7 @@ class CloudFileZoomIn extends HookConsumerWidget {
Future<void> saveToGallery() async {
try {
// Show loading indicator
final scaffold = ScaffoldMessenger.of(context);
scaffold.showSnackBar(
const SnackBar(
content: Text('Saving image to gallery...'),
duration: Duration(seconds: 1),
),
);
showSnackBar('Saving image to gallery...');
// Get the image URL
final client = ref.watch(apiClientProvider);
@ -209,12 +203,7 @@ class CloudFileZoomIn extends HookConsumerWidget {
await Gal.putImage(filePath, album: 'Solar Network');
// Show success message
scaffold.showSnackBar(
const SnackBar(
content: Text('Image saved to gallery'),
duration: Duration(seconds: 2),
),
);
showSnackBar('Image saved to gallery');
} catch (e) {
showErrorAlert(e);
}

View File

@ -29,7 +29,7 @@ class PublisherCard extends ConsumerWidget {
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: () {
context.push('/publishers/${publisher.id}');
context.push('/publishers/${publisher.name}');
},
child: AspectRatio(
aspectRatio: 16 / 7,

View File

@ -13,6 +13,7 @@ import 'package:island/pods/network.dart';
import 'package:island/pods/config.dart';
import 'package:island/pods/userinfo.dart';
import 'package:island/services/file.dart';
import 'package:mime/mime.dart';
import 'dart:io';
import 'package:path/path.dart' as path;
@ -149,9 +150,9 @@ class _ShareSheetState extends ConsumerState<ShareSheet> {
case ShareContentType.file:
if (widget.content.files != null) {
// Convert XFiles to UniversalFiles
for (final xFile in widget.content.files!) {
final file = File(xFile.path);
final mimeType = xFile.mimeType;
for (final file in widget.content.files!) {
var mimeType = file.mimeType;
mimeType ??= lookupMimeType(file.path);
UniversalFileType fileType;
if (mimeType?.startsWith('image/') == true) {

View File

@ -1470,7 +1470,7 @@ packages:
source: hosted
version: "1.16.0"
mime:
dependency: transitive
dependency: "direct main"
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
@ -1785,10 +1785,10 @@ packages:
dependency: transitive
description:
name: record_linux
sha256: "29e7735b05c1944bb6c9b72a36c08d4a1b24117e712d6a9523c003bde12bf484"
sha256: "0626678a092c75ce6af1e32fe7fd1dea709b92d308bc8e3b6d6348e2430beb95"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.1.1"
record_macos:
dependency: transitive
description:
@ -2254,10 +2254,10 @@ packages:
dependency: transitive
description:
name: synchronized
sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6"
sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
url: "https://pub.dev"
source: hosted
version: "3.3.1"
version: "3.4.0"
table_calendar:
dependency: "direct main"
description:

View File

@ -126,6 +126,7 @@ dependencies:
git:
url: https://github.com/lionelmennig/textfield_tags.git
ref: fixes/allow-controller-re-registration
mime: ^2.0.0
dev_dependencies:
flutter_test: