🐛 Dozen of hot fixes
This commit is contained in:
parent
9e8f6d57df
commit
ff475d43dd
5
.github/workflows/build.yml
vendored
5
.github/workflows/build.yml
vendored
@ -3,7 +3,7 @@ name: Build Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
- "*"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@ -59,6 +59,7 @@ jobs:
|
||||
sudo apt-get install -y libnotify-dev
|
||||
sudo apt-get install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
|
||||
sudo apt-get install -y gstreamer-1.0
|
||||
sudo apt-get install -y libsecret-1
|
||||
- run: flutter pub get
|
||||
- run: flutter build linux
|
||||
- name: Archive production artifacts
|
||||
@ -80,4 +81,4 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-output-linux-appimage
|
||||
path: './*.AppImage*'
|
||||
path: "./*.AppImage*"
|
||||
|
@ -98,7 +98,7 @@
|
||||
<receiver
|
||||
android:name=".receiver.ReplyReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
android:exported="true" />
|
||||
|
||||
<service
|
||||
android:name=".service.MessagingService"
|
||||
|
@ -1,14 +1,39 @@
|
||||
package dev.solsynth.solian
|
||||
|
||||
import android.content.Intent
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugins.sharedpreferences.LegacySharedPreferencesPlugin
|
||||
|
||||
class MainActivity : FlutterActivity()
|
||||
{
|
||||
private val CHANNEL = "dev.solsynth.solian/notifications"
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
// https://github.com/flutter/flutter/issues/153075#issuecomment-2693189362
|
||||
flutterEngine.plugins.add(LegacySharedPreferencesPlugin())
|
||||
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
|
||||
if (call.method == "initialLink") {
|
||||
val roomId = intent.getStringExtra("room_id")
|
||||
if (roomId != null) {
|
||||
result.success("/rooms/$roomId")
|
||||
} else {
|
||||
result.success(null)
|
||||
}
|
||||
} else {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
val roomId = intent.getStringExtra("room_id")
|
||||
if (roomId != null) {
|
||||
MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, CHANNEL).invokeMethod("newLink", "/rooms/$roomId")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,48 +1,47 @@
|
||||
package dev.solsynth.solian.network
|
||||
|
||||
import android.content.Context
|
||||
import okhttp3.*
|
||||
import android.content.SharedPreferences
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.Response
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
|
||||
class ApiClient(private val context: Context) {
|
||||
private val client = OkHttpClient()
|
||||
private val sharedPreferences: SharedPreferences = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
|
||||
|
||||
fun sendMessage(roomId: String, content: String, repliedMessageId: String, callback: () -> Unit) {
|
||||
val prefs = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
|
||||
val token = prefs.getString("flutter.token", null)
|
||||
val serverUrl = prefs.getString("flutter.serverUrl", null)
|
||||
|
||||
if (token == null || serverUrl == null) {
|
||||
fun sendMessage(roomId: String, message: String, replyTo: String, callback: (Boolean) -> Unit) {
|
||||
val token = sharedPreferences.getString("flutter.token", null)
|
||||
if (token == null) {
|
||||
callback(false)
|
||||
return
|
||||
}
|
||||
|
||||
val url = "$serverUrl/chat/$roomId/messages"
|
||||
|
||||
val json = JSONObject()
|
||||
json.put("content", content)
|
||||
json.put("replied_message_id", repliedMessageId)
|
||||
|
||||
val requestBody = json.toString().toRequestBody("application/json; charset=utf-8".toMediaType())
|
||||
|
||||
val json = JSONObject().apply {
|
||||
put("content", message)
|
||||
put("reply_to", replyTo)
|
||||
}
|
||||
val body = json.toString().toRequestBody("application/json; charset=utf-8".toMediaType())
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.post(requestBody)
|
||||
.addHeader("Authorization", "AtField $token")
|
||||
.url("https://solian.dev/api/rooms/$roomId/messages")
|
||||
.header("Authorization", "Bearer $token")
|
||||
.post(body)
|
||||
.build()
|
||||
|
||||
client.newCall(request).enqueue(object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
// Handle failure
|
||||
callback()
|
||||
callback(false)
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
// Handle success
|
||||
callback()
|
||||
callback(response.isSuccessful)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -72,6 +72,7 @@ class MessagingService: FirebaseMessagingService() {
|
||||
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
intent.putExtra("room_id", roomId)
|
||||
val pendingIntent = PendingIntent.getActivity(this, 0, intent, pendingIntentFlags)
|
||||
|
||||
val notificationBuilder = NotificationCompat.Builder(this, "messages")
|
||||
|
@ -89,14 +89,32 @@
|
||||
"authFactorInAppNotifyDescription": "A one-time code sent via in-app notification.",
|
||||
"authFactorPin": "Pin Code",
|
||||
"authFactorPinDescription": "It consists of 6 digits. It cannot be used to log in. When performing some dangerous operations, the system will ask you to enter this PIN for confirmation.",
|
||||
"realms": "Realms",
|
||||
"createRealm": "Create a Realm",
|
||||
"createRealmHint": "Meet friends with same interests, build communities, and more.",
|
||||
"editRealm": "Edit Realm",
|
||||
"deleteRealm": "Delete Realm",
|
||||
"deleteRealmHint": "Are you sure to delete this realm? This will also deleted all the channels, publishers, and posts under this realm.",
|
||||
"explore": "Explore",
|
||||
"exploreFilterSubscriptions": "Subscriptions",
|
||||
"exploreFilterFriends": "Friends",
|
||||
"discover": "Discover",
|
||||
"joinRealm": "Join Realm",
|
||||
"account": "Account",
|
||||
"name": "Name",
|
||||
"slug": "Slug",
|
||||
"slugHint": "The slug will be used in the URL to access this resource, it should be unique and URL safe.",
|
||||
"createChatRoom": "Create a Room",
|
||||
"editChatRoom": "Edit Room",
|
||||
"deleteChatRoom": "Delete Room",
|
||||
"deleteChatRoomHint": "Are you sure to delete this room? This action cannot be undone.",
|
||||
"chat": "Chat",
|
||||
"chatTabAll": "All",
|
||||
"chatTabDirect": "Direct Messages",
|
||||
"chatTabGroup": "Group Chats",
|
||||
"chatMessageHint": "Message in {}",
|
||||
"chatDirectMessageHint": "Message to {}",
|
||||
"directMessage": "Direct Message",
|
||||
"loading": "Loading...",
|
||||
"descriptionNone": "No description yet.",
|
||||
"invites": "Invites",
|
||||
@ -231,6 +249,7 @@
|
||||
"uploadingProgress": "Uploading {} of {}",
|
||||
"uploadAll": "Upload All",
|
||||
"stickerCopyPlaceholder": "Copy Placeholder",
|
||||
"realmSelection": "Select a Realm",
|
||||
"individual": "Individual",
|
||||
"firstPostBadgeName": "First Post",
|
||||
"firstPostBadgeDescription": "Created your first post on Solar Network",
|
||||
@ -286,6 +305,10 @@
|
||||
"levelingProgressExperience": "{} EXP",
|
||||
"levelingProgressLevel": "Level {}",
|
||||
"fileUploadingProgress": "Uploading file #{}: {}%",
|
||||
"removeChatMember": "Remove Chat Room Member",
|
||||
"removeChatMemberHint": "Are you sure to remove this member from the room?",
|
||||
"removeRealmMember": "Remove Realm Member",
|
||||
"removeRealmMemberHint": "Are you sure to remove this member from the realm?",
|
||||
"memberRole": "Member Role",
|
||||
"memberRoleHint": "Greater number has higher permission.",
|
||||
"memberRoleEdit": "Edit role for @{}",
|
||||
@ -293,6 +316,10 @@
|
||||
"openLinkConfirmDescription": "You're going to leave the Solar Network and open the link ({}) in your browser. It is not related to Solar Network. Beware of phishing and scams.",
|
||||
"brokenLink": "Unable open link {}... It might be broken or missing uri parts...",
|
||||
"copyToClipboard": "Copy to clipboard",
|
||||
"leaveChatRoom": "Leave Chat Room",
|
||||
"leaveChatRoomHint": "Are you sure to leave this chat room?",
|
||||
"leaveRealm": "Leave Realm",
|
||||
"leaveRealmHint": "Are you sure to leave this realm?",
|
||||
"walletNotFound": "Wallet not found",
|
||||
"walletCreateHint": "You don't have a wallet yet. Create one to start using the Solar Network eWallet.",
|
||||
"walletCreate": "Create a Wallet",
|
||||
@ -304,6 +331,12 @@
|
||||
"settingsBackgroundImageClear": "Clear Background Image",
|
||||
"settingsBackgroundGenerateColor": "Generate color scheme from Bacground Image",
|
||||
"messageNone": "No content to display",
|
||||
"unreadMessages": {
|
||||
"one": "{} unread message",
|
||||
"other": "{} unread messages"
|
||||
},
|
||||
"chatBreakNone": "None",
|
||||
"settingsRealmCompactView": "Compact Realm View",
|
||||
"settingsMixedFeed": "Mixed Feed",
|
||||
"settingsAutoTranslate": "Auto Translate",
|
||||
"settingsHideBottomNav": "Hide Bottom Navigation",
|
||||
@ -346,6 +379,7 @@
|
||||
"postVisibilityUnlisted": "Unlisted",
|
||||
"postVisibilityPrivate": "Private",
|
||||
"postTruncated": "Content truncated, tap to view full post",
|
||||
"copyMessage": "Copy Message",
|
||||
"authFactor": "Authentication Factor",
|
||||
"authFactorDelete": "Delete the Factor",
|
||||
"authFactorDeleteHint": "Are you sure you want to delete this authentication factor? This action cannot be undone.",
|
||||
@ -373,6 +407,10 @@
|
||||
"lastActiveAt": "Last active at {}",
|
||||
"authDeviceLogout": "Logout",
|
||||
"authDeviceLogoutHint": "Are you sure you want to logout this device? This will also disable the push notification to this device.",
|
||||
"typingHint": {
|
||||
"one": "{} is typing...",
|
||||
"other": "{} are typing..."
|
||||
},
|
||||
"authDeviceEditLabel": "Edit Label",
|
||||
"authDeviceLabelTitle": "Edit Device Label",
|
||||
"authDeviceLabelHint": "Enter a name for this device",
|
||||
@ -439,6 +477,21 @@
|
||||
"contactMethodSetPrimary": "Set as Primary",
|
||||
"contactMethodSetPrimaryHint": "Set this contact method as your primary contact method for account recovery and notifications",
|
||||
"contactMethodDeleteHint": "Are you sure to delete this contact method? This action cannot be undone.",
|
||||
"chatNotifyLevel": "Notify Level",
|
||||
"chatNotifyLevelDescription": "Decide how many notifications you will receive.",
|
||||
"chatNotifyLevelAll": "All",
|
||||
"chatNotifyLevelMention": "Mentions",
|
||||
"chatNotifyLevelNone": "None",
|
||||
"chatNotifyLevelUpdated": "The notify level has been updated to {}.",
|
||||
"chatBreak": "Take a Break",
|
||||
"chatBreakDescription": "Set a time, before that time, your notification level will be metions only, to take a break of the current topic they're talking about.",
|
||||
"chatBreakClear": "Clear the break time",
|
||||
"chatBreakHour": "{} break",
|
||||
"chatBreakDay": "{} day break",
|
||||
"chatBreakSet": "Break set for {}",
|
||||
"chatBreakCleared": "Chat break has been cleared.",
|
||||
"chatBreakCustom": "Custom duration",
|
||||
"chatBreakEnterMinutes": "Enter minutes",
|
||||
"firstName": "First Name",
|
||||
"middleName": "Middle Name",
|
||||
"lastName": "Last Name",
|
||||
@ -520,17 +573,29 @@
|
||||
"quickActions": "Quick Actions",
|
||||
"post": "Post",
|
||||
"copy": "Copy",
|
||||
"sendToChat": "Send to Chat",
|
||||
"failedToShareToPost": "Failed to share to post: {}",
|
||||
"shareToChatComingSoon": "Share to chat functionality coming soon",
|
||||
"failedToShareToChat": "Failed to share to chat: {}",
|
||||
"shareToSpecificChatComingSoon": "Share to {} coming soon",
|
||||
"directChat": "Direct Chat",
|
||||
"systemShareComingSoon": "System share functionality coming soon",
|
||||
"failedToShareToSystem": "Failed to share to system: {}",
|
||||
"failedToCopy": "Failed to copy: {}",
|
||||
"noChatRoomsAvailable": "No chat rooms available",
|
||||
"failedToLoadChats": "Failed to load chats",
|
||||
"contentToShare": "Content to share:",
|
||||
"unknownChat": "Unknown Chat",
|
||||
"addAdditionalMessage": "Add additional message...",
|
||||
"uploadingFiles": "Uploading files...",
|
||||
"sharedSuccessfully": "Shared successfully!",
|
||||
"shareSuccess": "Shared successfully!",
|
||||
"shareToSpecificChatSuccess": "Shared to {} successfully!",
|
||||
"wouldYouLikeToGoToChat": "Would you like to go to the chat?",
|
||||
"no": "No",
|
||||
"yes": "Yes",
|
||||
"navigateToChat": "Navigate to Chat",
|
||||
"wouldYouLikeToNavigateToChat": "Would you like to navigate to the chat?",
|
||||
"abuseReport": "Report",
|
||||
"abuseReportTitle": "Report Content",
|
||||
"abuseReportDescription": "Help us keep the community safe by reporting inappropriate content or behavior.",
|
||||
|
@ -7,6 +7,7 @@ import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:image_picker_android/image_picker_android.dart';
|
||||
@ -158,6 +159,28 @@ class IslandApp extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
useEffect(() {
|
||||
const channel = MethodChannel('dev.solsynth.solian/notifications');
|
||||
|
||||
Future<void> handleInitialLink() async {
|
||||
final String? link = await channel.invokeMethod('initialLink');
|
||||
if (link != null) {
|
||||
final router = ref.read(routerProvider);
|
||||
router.go(link);
|
||||
}
|
||||
}
|
||||
|
||||
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
||||
handleInitialLink();
|
||||
}
|
||||
|
||||
channel.setMethodCallHandler((call) async {
|
||||
if (call.method == 'newLink') {
|
||||
final String link = call.arguments;
|
||||
final router = ref.read(routerProvider);
|
||||
router.go(link);
|
||||
}
|
||||
});
|
||||
|
||||
// When the app is opened from a terminated state.
|
||||
FirebaseMessaging.instance.getInitialMessage().then((message) {
|
||||
if (message != null) {
|
||||
|
@ -79,33 +79,37 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
return EventCalanderScreen(name: name);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/creators',
|
||||
builder: (context, state) => const CreatorHubScreen(),
|
||||
ShellRoute(
|
||||
builder:
|
||||
(context, state, child) => CreatorHubShellScreen(child: child),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: ':name/posts',
|
||||
path: '/creators',
|
||||
builder: (context, state) => const CreatorHubScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/creators/:name/posts',
|
||||
builder: (context, state) {
|
||||
final name = state.pathParameters['name']!;
|
||||
return CreatorPostListScreen(pubName: name);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: ':name/stickers',
|
||||
path: '/creators/:name/stickers',
|
||||
builder: (context, state) {
|
||||
final name = state.pathParameters['name']!;
|
||||
return StickersScreen(pubName: name);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: ':name/stickers/new',
|
||||
path: '/creators/:name/stickers/new',
|
||||
builder: (context, state) {
|
||||
final name = state.pathParameters['name']!;
|
||||
return NewStickerPacksScreen(pubName: name);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: ':name/stickers/:packId/edit',
|
||||
path: '/creators/:name/stickers/:packId/edit',
|
||||
builder: (context, state) {
|
||||
final name = state.pathParameters['name']!;
|
||||
final packId = state.pathParameters['packId']!;
|
||||
@ -113,7 +117,7 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: ':name/stickers/:packId',
|
||||
path: '/creators/:name/stickers/:packId',
|
||||
builder: (context, state) {
|
||||
final name = state.pathParameters['name']!;
|
||||
final packId = state.pathParameters['packId']!;
|
||||
@ -121,14 +125,14 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: ':name/stickers/:packId/new',
|
||||
path: '/creators/:name/stickers/:packId/new',
|
||||
builder: (context, state) {
|
||||
final packId = state.pathParameters['packId']!;
|
||||
return NewStickersScreen(packId: packId);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: ':name/stickers/:packId/:id/edit',
|
||||
path: '/creators/:name/stickers/:packId/:id/edit',
|
||||
builder: (context, state) {
|
||||
final packId = state.pathParameters['packId']!;
|
||||
final id = state.pathParameters['id']!;
|
||||
@ -136,11 +140,11 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'new',
|
||||
path: '/creators/new',
|
||||
builder: (context, state) => const NewPublisherScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: ':name/edit',
|
||||
path: '/creators/:name/edit',
|
||||
builder: (context, state) {
|
||||
final name = state.pathParameters['name']!;
|
||||
return EditPublisherScreen(name: name);
|
||||
@ -173,56 +177,64 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
},
|
||||
routes: [
|
||||
// Explore tab
|
||||
GoRoute(
|
||||
path: '/',
|
||||
builder: (context, state) => const ExploreScreen(),
|
||||
ShellRoute(
|
||||
builder:
|
||||
(context, state, child) => ExploreShellScreen(child: child),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'posts/:id',
|
||||
path: '/',
|
||||
builder: (context, state) => const ExploreScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/posts/:id',
|
||||
builder: (context, state) {
|
||||
final id = state.pathParameters['id']!;
|
||||
return PostDetailScreen(id: id);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'publishers/:name',
|
||||
path: '/publishers/:name',
|
||||
builder: (context, state) {
|
||||
final name = state.pathParameters['name']!;
|
||||
return PublisherProfileScreen(name: name);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'discovery/realms',
|
||||
path: '/discovery/realms',
|
||||
builder: (context, state) => const DiscoveryRealmsScreen(),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Chat tab
|
||||
GoRoute(
|
||||
path: '/chat',
|
||||
builder: (context, state) => const ChatListScreen(),
|
||||
ShellRoute(
|
||||
builder:
|
||||
(context, state, child) => ChatShellScreen(child: child),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'new',
|
||||
path: '/chat',
|
||||
builder: (context, state) => const ChatListScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/chat/new',
|
||||
builder: (context, state) => const NewChatScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: ':id',
|
||||
path: '/chat/:id',
|
||||
builder: (context, state) {
|
||||
final id = state.pathParameters['id']!;
|
||||
return ChatRoomScreen(id: id);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: ':id/edit',
|
||||
path: '/chat/:id/edit',
|
||||
builder: (context, state) {
|
||||
final id = state.pathParameters['id']!;
|
||||
return EditChatScreen(id: id);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: ':id/detail',
|
||||
path: '/chat/:id/detail',
|
||||
builder: (context, state) {
|
||||
final id = state.pathParameters['id']!;
|
||||
return ChatDetailScreen(id: id);
|
||||
@ -258,39 +270,43 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
),
|
||||
|
||||
// Account tab
|
||||
GoRoute(
|
||||
path: '/account',
|
||||
builder: (context, state) => const AccountScreen(),
|
||||
ShellRoute(
|
||||
builder:
|
||||
(context, state, child) => AccountShellScreen(child: child),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'notifications',
|
||||
path: '/account',
|
||||
builder: (context, state) => const AccountScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/account/notifications',
|
||||
builder: (context, state) => const NotificationScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'wallet',
|
||||
path: '/account/wallet',
|
||||
builder: (context, state) => const WalletScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'relationships',
|
||||
path: '/account/relationships',
|
||||
builder: (context, state) => const RelationshipScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: ':name',
|
||||
path: '/account/:name',
|
||||
builder: (context, state) {
|
||||
final name = state.pathParameters['name']!;
|
||||
return AccountProfileScreen(name: name);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'me/update',
|
||||
path: '/account/me/update',
|
||||
builder: (context, state) => const UpdateProfileScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'me/leveling',
|
||||
path: '/account/me/leveling',
|
||||
builder: (context, state) => const LevelingScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'settings',
|
||||
path: '/account/settings',
|
||||
builder: (context, state) => const AccountSettingsScreen(),
|
||||
),
|
||||
],
|
||||
|
@ -143,7 +143,7 @@ class AccountScreen extends HookConsumerWidget {
|
||||
progress: user.value!.profile.levelingProgress,
|
||||
),
|
||||
onTap: () {
|
||||
context.push('/account/leveling');
|
||||
context.push('/account/me/leveling');
|
||||
},
|
||||
).padding(horizontal: 12),
|
||||
Row(
|
||||
@ -210,7 +210,7 @@ class AccountScreen extends HookConsumerWidget {
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||
title: Text('wallet').tr(),
|
||||
onTap: () {
|
||||
context.push('/wallet');
|
||||
context.push('/account/wallet');
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
|
@ -53,17 +53,21 @@ Future<List<SnAccountBadge>> accountBadges(Ref ref, String uname) async {
|
||||
|
||||
@riverpod
|
||||
Future<Color?> accountAppbarForcegroundColor(Ref ref, String uname) async {
|
||||
final account = await ref.watch(accountProvider(uname).future);
|
||||
if (account.profile.background == null) return null;
|
||||
final palette = await PaletteGenerator.fromImageProvider(
|
||||
CloudImageWidget.provider(
|
||||
fileId: account.profile.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 account = await ref.watch(accountProvider(uname).future);
|
||||
if (account.profile.background == null) return null;
|
||||
final palette = await PaletteGenerator.fromImageProvider(
|
||||
CloudImageWidget.provider(
|
||||
fileId: account.profile.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;
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
|
@ -4,19 +4,18 @@ import 'dart:math' as math;
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/user.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/pods/websocket.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/route.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/widgets/content/markdown.dart';
|
||||
import 'package:relative_time/relative_time.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
part 'notification.g.dart';
|
||||
|
||||
@ -180,36 +179,17 @@ class NotificationScreen extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
if (notification.meta['link'] is String) {
|
||||
final href = notification.meta['link'];
|
||||
final uri = Uri.tryParse(href);
|
||||
if (uri == null) {
|
||||
showSnackBar(
|
||||
'brokenLink'.tr(args: []),
|
||||
action: SnackBarAction(
|
||||
label: 'copyToClipboard'.tr(),
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: href));
|
||||
clearSnackBar(context);
|
||||
},
|
||||
),
|
||||
if (notification.meta['action_uri'] != null) {
|
||||
var uri = notification.meta['action_uri'] as String;
|
||||
if (uri.startsWith('/')) {
|
||||
// In-app routes
|
||||
rootNavigatorKey.currentContext?.push(
|
||||
notification.meta['action_uri'],
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
// External URLs
|
||||
launchUrlString(uri);
|
||||
}
|
||||
if (uri.scheme == 'solian') {
|
||||
context.push(
|
||||
['', uri.host, ...uri.pathSegments].join('/'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
showConfirmAlert(
|
||||
'openLinkConfirmDescription'.tr(args: [href]),
|
||||
'openLinkConfirm'.tr(),
|
||||
).then((value) {
|
||||
if (value) {
|
||||
launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user