to laptptop
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -41,4 +41,7 @@ app.*.map.json
|
|||||||
/android/app/debug
|
/android/app/debug
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
/assets/ibus_recordings.zip
|
|
||||||
|
# Ignore any file with the name "ibus_recordings.zip"
|
||||||
|
ibus_recordings.zip
|
||||||
|
bus_infotainment.rar
|
||||||
|
|||||||
36
Dockerfile
Normal file
36
Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Install Operating system and dependencies
|
||||||
|
FROM ubuntu:20.04
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND noninteractive
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install -y curl git wget unzip libgconf-2-4 gdb libstdc++6 libglu1-mesa fonts-droid-fallback lib32stdc++6 python3
|
||||||
|
RUN apt-get clean
|
||||||
|
|
||||||
|
# download Flutter SDK from Flutter Github repo
|
||||||
|
RUN git clone https://github.com/flutter/flutter.git /usr/local/flutter
|
||||||
|
|
||||||
|
# Set flutter environment path
|
||||||
|
ENV PATH="/usr/local/flutter/bin:/usr/local/flutter/bin/cache/dart-sdk/bin:${PATH}"
|
||||||
|
|
||||||
|
# Run flutter doctor
|
||||||
|
RUN flutter doctor
|
||||||
|
|
||||||
|
# Enable flutter web
|
||||||
|
RUN flutter channel master
|
||||||
|
RUN flutter upgrade
|
||||||
|
RUN flutter config --enable-web
|
||||||
|
|
||||||
|
# Copy files to container and build
|
||||||
|
RUN mkdir /app/
|
||||||
|
COPY . /app/
|
||||||
|
WORKDIR /app/
|
||||||
|
RUN flutter build web
|
||||||
|
|
||||||
|
# Record the exposed port
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
# make server startup script executable and start the web server
|
||||||
|
RUN ["chmod", "+x", "/app/server/server.sh"]
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/app/server/server.sh"]
|
||||||
@@ -24,7 +24,7 @@ if (flutterVersionName == null) {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
namespace "com.imbenji.bus_infotainment"
|
namespace "com.imbenji.bus_infotainment"
|
||||||
compileSdkVersion flutter.compileSdkVersion
|
compileSdkVersion 34
|
||||||
ndkVersion flutter.ndkVersion
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
|||||||
31
lib/auth/api_constants.dart
Normal file
31
lib/auth/api_constants.dart
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
class ApiConstants {
|
||||||
|
|
||||||
|
static const String APPWRITE_ENDPOINT = "https://cloud.imbenji.net/v1";
|
||||||
|
static const String APPWRITE_PROJECT_ID = "65de530c1c0a7ffc0c3f";
|
||||||
|
|
||||||
|
static const String INFO_Q_DATABASE_ID = "65de5cab16717444527b";
|
||||||
|
static const String DEST_Q_COLLECTION_ID = "65de9f2f925562a2eda8";
|
||||||
|
static const String MANUAL_Q_COLLECTION_ID = "65de9f1b6282fd209bdb";
|
||||||
|
static const String BUSSTOP_Q_COLLECTION_ID = "65de9ef464bfa5a0693d";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// function to convert date time to something that looks like: Today at 12:00 PM or Yesterday at 12:00 PM or 12/31/2021 at 12:00 PM, make sure to always use double digits for the time so 01 not 1
|
||||||
|
static String formatDateTime(DateTime dateTime) {
|
||||||
|
DateTime now = DateTime.now();
|
||||||
|
DateTime today = DateTime(now.year, now.month, now.day);
|
||||||
|
DateTime yesterday = DateTime(now.year, now.month, now.day - 1);
|
||||||
|
|
||||||
|
if (dateTime.isAfter(today)) {
|
||||||
|
return "Today at ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')} ${dateTime.hour > 12 ? "PM" : "AM"}";
|
||||||
|
} else if (dateTime.isAfter(yesterday)) {
|
||||||
|
return "Yesterday at ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')} ${dateTime.hour > 12 ? "PM" : "AM"}";
|
||||||
|
} else {
|
||||||
|
return "${dateTime.month.toString().padLeft(2, '0')}/${dateTime.day.toString().padLeft(2, '0')}/${dateTime.year.toString().padLeft(2, '0')} at ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')} ${dateTime.hour > 12 ? "PM" : "AM"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
171
lib/auth/auth_api.dart
Normal file
171
lib/auth/auth_api.dart
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import 'package:appwrite/appwrite.dart' as appwrite;
|
||||||
|
import 'package:appwrite/models.dart' as models;
|
||||||
|
import 'package:bus_infotainment/auth/api_constants.dart';
|
||||||
|
import 'package:bus_infotainment/utils/delegates.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
enum AuthStatus {
|
||||||
|
UNINITIALIZED,
|
||||||
|
AUTHENTICATED,
|
||||||
|
UNAUTHENTICATED,
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuthAPI extends ChangeNotifier {
|
||||||
|
|
||||||
|
appwrite.Client client = appwrite.Client();
|
||||||
|
late final appwrite.Account account;
|
||||||
|
|
||||||
|
late models.User _currentUser;
|
||||||
|
|
||||||
|
AuthStatus _status = AuthStatus.UNINITIALIZED;
|
||||||
|
|
||||||
|
// late UserInfo _userInfo;
|
||||||
|
|
||||||
|
// Getter methods
|
||||||
|
models.User get currentUser => _currentUser;
|
||||||
|
// UserInfo get userInfo => _userInfo;
|
||||||
|
AuthStatus get status => _status;
|
||||||
|
String? get username => _currentUser.name;
|
||||||
|
String? get email => _currentUser.email;
|
||||||
|
String? get userID => _currentUser.$id;
|
||||||
|
|
||||||
|
AuthAPI() {
|
||||||
|
init();
|
||||||
|
loadUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
client
|
||||||
|
.setEndpoint(ApiConstants.APPWRITE_ENDPOINT)
|
||||||
|
.setProject(ApiConstants.APPWRITE_PROJECT_ID)
|
||||||
|
.setSelfSigned();
|
||||||
|
account = appwrite.Account(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadUser() async {
|
||||||
|
try {
|
||||||
|
|
||||||
|
{
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
String? username = prefs.getString("APPWRITE_SESSIONID_USERNAME");
|
||||||
|
String? password = prefs.getString("APPWRITE_SESSIONID_PASSWORD");
|
||||||
|
if (username != null && password != null) {
|
||||||
|
await createEmailSession(email: username, password: password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
final user = await account.get();
|
||||||
|
_status = AuthStatus.AUTHENTICATED;
|
||||||
|
// _userInfo = await UserInfo.getFromId(this, userID!);
|
||||||
|
_currentUser = user;
|
||||||
|
} catch (e) {
|
||||||
|
_status = AuthStatus.UNAUTHENTICATED;
|
||||||
|
} finally {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<models.User> createUser({
|
||||||
|
required String displayName,
|
||||||
|
required String username,
|
||||||
|
required String email,
|
||||||
|
required String password,
|
||||||
|
}) async {
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final user = await account.create(
|
||||||
|
userId: appwrite.ID.unique(),
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
name: username,
|
||||||
|
);
|
||||||
|
|
||||||
|
final appwrite.Databases databases = appwrite.Databases(client);
|
||||||
|
|
||||||
|
// await databases.createDocument(
|
||||||
|
// databaseId: ApiConstants.SOCIALS_DATABASE_ID,
|
||||||
|
// collectionId: ApiConstants.USERS_COLLECTION_ID,
|
||||||
|
// documentId: user!.$id,
|
||||||
|
// data: {
|
||||||
|
// "username": username,
|
||||||
|
// "display_name": displayName,
|
||||||
|
// "bio": "",
|
||||||
|
// "status": "",
|
||||||
|
// "profilepicture_id": "default",
|
||||||
|
// "profilebanner_id": "default",
|
||||||
|
// "last_activity": DateTime.now().toIso8601String(),
|
||||||
|
// "guild_ids": [],
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
return user;
|
||||||
|
} finally {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Login */
|
||||||
|
Future<models.Session> createEmailSession({
|
||||||
|
required String email,
|
||||||
|
required String password,
|
||||||
|
}) async {
|
||||||
|
|
||||||
|
try {
|
||||||
|
final session = await account.createEmailSession(
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
);
|
||||||
|
_currentUser = await account.get();
|
||||||
|
_status = AuthStatus.AUTHENTICATED;
|
||||||
|
// _userInfo = await UserInfo.getFromId(this, userID!);
|
||||||
|
// _userInfo.username = _currentUser.name!;
|
||||||
|
// _userInfo.pushChanges(this);
|
||||||
|
|
||||||
|
// store session in shared preferences
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
prefs.setString("APPWRITE_SESSIONID_USERNAME", email);
|
||||||
|
prefs.setString("APPWRITE_SESSIONID_PASSWORD", password);
|
||||||
|
|
||||||
|
return session;
|
||||||
|
} finally {
|
||||||
|
onLogin.trigger(1);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Logout */
|
||||||
|
Future<void> deleteSession() async {
|
||||||
|
try {
|
||||||
|
await account.deleteSession(sessionId: "current");
|
||||||
|
|
||||||
|
{
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
prefs.remove("APPWRITE_SESSIONID_USERNAME");
|
||||||
|
prefs.remove("APPWRITE_SESSIONID_PASSWORD");
|
||||||
|
}
|
||||||
|
|
||||||
|
_status = AuthStatus.UNAUTHENTICATED;
|
||||||
|
} finally {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAuthenticated() {
|
||||||
|
return _status == AuthStatus.AUTHENTICATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Events
|
||||||
|
|
||||||
|
EventDelegate<int> _onLogin = EventDelegate();
|
||||||
|
EventDelegate get onLogin => _onLogin;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ void main() async {
|
|||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
LiveInformation liveInformation = LiveInformation();
|
LiveInformation liveInformation = LiveInformation();
|
||||||
await liveInformation.LoadDatasets();
|
await liveInformation.Initialize();
|
||||||
|
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,12 @@ class MyApp extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
darkTheme: ThemeData(
|
darkTheme: ThemeData(
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
/* dark theme settings */
|
// colorScheme: ColorScheme.dark(),
|
||||||
|
colorScheme: ColorScheme.fromSeed(
|
||||||
|
seedColor: Colors.blue,
|
||||||
|
brightness: Brightness.dark
|
||||||
|
|
||||||
|
)
|
||||||
),
|
),
|
||||||
themeMode: ThemeMode.dark,
|
themeMode: ThemeMode.dark,
|
||||||
|
|
||||||
|
|||||||
@@ -28,21 +28,20 @@ class _ibus_displayState extends State<ibus_display> {
|
|||||||
|
|
||||||
LiveInformation liveInformation = LiveInformation();
|
LiveInformation liveInformation = LiveInformation();
|
||||||
_receipt = liveInformation.announcementDelegate.addListener((value) {
|
_receipt = liveInformation.announcementDelegate.addListener((value) {
|
||||||
|
|
||||||
|
if (topLine == value.displayText){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
topLine = value.displayText;
|
topLine = value.displayText;
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
topLine = liveInformation.CurrentAnnouncement;
|
topLine = liveInformation.currentAnnouncement;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer _timer() => Timer.periodic(Duration(seconds: 1), (timer) {
|
|
||||||
setState(() {
|
|
||||||
topLine = LiveInformation().CurrentAnnouncement;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
String _padString(String input){
|
String _padString(String input){
|
||||||
|
|
||||||
if (input.length < 40){
|
if (input.length < 40){
|
||||||
@@ -115,7 +114,13 @@ class _ibus_displayState extends State<ibus_display> {
|
|||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
color: Colors.orange,
|
color: Colors.orange,
|
||||||
fontFamily: "ibus"
|
fontFamily: "ibus",
|
||||||
|
shadows: [
|
||||||
|
Shadow(
|
||||||
|
color: Colors.orange,
|
||||||
|
blurRadius: 5,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -13,31 +13,138 @@ class pages_Home extends StatelessWidget {
|
|||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
|
|
||||||
width: double.infinity,
|
|
||||||
|
|
||||||
margin: const EdgeInsets.all(10),
|
|
||||||
|
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
|
|
||||||
// Text("Home Page"),
|
Container(
|
||||||
|
height: 2,
|
||||||
ibus_display(),
|
color: Colors.white70,
|
||||||
|
|
||||||
SizedBox(
|
|
||||||
height: 10,
|
|
||||||
),
|
),
|
||||||
|
|
||||||
_QuickAnnouncements_IBUS(),
|
Container(
|
||||||
|
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade900,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.3),
|
||||||
|
blurRadius: 2,
|
||||||
|
spreadRadius: 4
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
|
||||||
|
margin: EdgeInsets.all(20),
|
||||||
|
|
||||||
|
child: ibus_display(),
|
||||||
|
|
||||||
SizedBox(
|
|
||||||
height: 10,
|
|
||||||
),
|
),
|
||||||
|
|
||||||
QuickAnnouncement(),
|
Container(
|
||||||
|
height: 2,
|
||||||
|
color: Colors.white70,
|
||||||
|
),
|
||||||
|
|
||||||
|
Container(
|
||||||
|
|
||||||
|
margin: EdgeInsets.all(20),
|
||||||
|
|
||||||
|
child: Container(
|
||||||
|
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade900,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.3),
|
||||||
|
blurRadius: 2,
|
||||||
|
spreadRadius: 4
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
|
||||||
|
child: _QuickAnnouncements_IBUS(
|
||||||
|
backgroundColor: Colors.grey.shade900,
|
||||||
|
outlineColor: Colors.white70,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
),
|
||||||
|
|
||||||
|
Container(
|
||||||
|
height: 2,
|
||||||
|
color: Colors.white70,
|
||||||
|
),
|
||||||
|
|
||||||
|
Container(
|
||||||
|
|
||||||
|
margin: EdgeInsets.all(20),
|
||||||
|
|
||||||
|
height: 300-45,
|
||||||
|
|
||||||
|
child: ListView(
|
||||||
|
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
|
||||||
|
children: [
|
||||||
|
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
LiveInformation liveInformation = LiveInformation();
|
||||||
|
liveInformation.announceRouteVariant(liveInformation.getRouteVariant()!);
|
||||||
|
},
|
||||||
|
child: Text("Test announcement"),
|
||||||
|
),
|
||||||
|
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
LiveInformation liveInformation = LiveInformation();
|
||||||
|
liveInformation.updateServer();
|
||||||
|
},
|
||||||
|
child: Text("Update server"),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(
|
||||||
|
|
||||||
|
width: 100,
|
||||||
|
|
||||||
|
child: TextField(
|
||||||
|
onChanged: (String value) {
|
||||||
|
LiveInformation liveInformation = LiveInformation();
|
||||||
|
// liveInformation.documentID = value;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(
|
||||||
|
|
||||||
|
width: 200,
|
||||||
|
|
||||||
|
child: TextField(
|
||||||
|
onSubmitted: (String value) {
|
||||||
|
LiveInformation liveInformation = LiveInformation();
|
||||||
|
liveInformation.queueAnnouncement(AnnouncementQueueEntry(
|
||||||
|
displayText: value,
|
||||||
|
audioSources: []
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
LiveInformation liveInformation = LiveInformation();
|
||||||
|
liveInformation.pullServer();
|
||||||
|
},
|
||||||
|
child: Text("Pull server"),
|
||||||
|
),
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
),
|
||||||
|
|
||||||
|
),
|
||||||
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -48,8 +155,10 @@ class pages_Home extends StatelessWidget {
|
|||||||
|
|
||||||
class _QuickAnnouncements_IBUS extends StatefulWidget {
|
class _QuickAnnouncements_IBUS extends StatefulWidget {
|
||||||
|
|
||||||
|
final Color backgroundColor;
|
||||||
|
final Color outlineColor;
|
||||||
|
|
||||||
_QuickAnnouncements_IBUS({super.key});
|
_QuickAnnouncements_IBUS({super.key, required this.backgroundColor, required this.outlineColor});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_QuickAnnouncements_IBUS> createState() => _QuickAnnouncementsState_IBUS();
|
State<_QuickAnnouncements_IBUS> createState() => _QuickAnnouncementsState_IBUS();
|
||||||
@@ -61,16 +170,23 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
|
|
||||||
int _currentIndex = 0;
|
int _currentIndex = 0;
|
||||||
|
|
||||||
|
|
||||||
_QuickAnnouncementsState_IBUS() {
|
_QuickAnnouncementsState_IBUS() {
|
||||||
LiveInformation liveInformation = LiveInformation();
|
LiveInformation liveInformation = LiveInformation();
|
||||||
|
|
||||||
for (ManualAnnouncementEntry announcement in liveInformation.manualAnnouncements) {
|
for (ManualAnnouncementEntry announcement in liveInformation.manualAnnouncements) {
|
||||||
announcements.add(
|
announcements.add(
|
||||||
_QuickAnnouncement_IBUS(announcement: announcement, index: liveInformation.manualAnnouncements.indexOf(announcement))
|
_QuickAnnouncement_IBUS(
|
||||||
|
announcement: announcement,
|
||||||
|
index: liveInformation.manualAnnouncements.indexOf(announcement),
|
||||||
|
outlineColor: Colors.white70
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
@@ -78,9 +194,9 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
return Container(
|
return Container(
|
||||||
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.lightGreen.shade100,
|
color: widget.backgroundColor,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
width: 2
|
width: 2
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -88,13 +204,19 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
|
|
||||||
),
|
),
|
||||||
|
|
||||||
padding: const EdgeInsets.all(2),
|
padding: const EdgeInsets.all(4),
|
||||||
|
|
||||||
|
width: double.infinity,
|
||||||
|
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 400
|
||||||
|
),
|
||||||
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
height: 2,
|
height: 2,
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
),
|
),
|
||||||
|
|
||||||
if (_currentIndex < announcements.length)
|
if (_currentIndex < announcements.length)
|
||||||
@@ -103,10 +225,10 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
Container(
|
Container(
|
||||||
height: 50,
|
height: 50,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.lightGreen.shade100,
|
color: widget.backgroundColor,
|
||||||
border: const Border.symmetric(
|
border: Border.symmetric(
|
||||||
vertical: BorderSide(
|
vertical: BorderSide(
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
width: 2
|
width: 2
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -115,7 +237,7 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
|
|
||||||
Container(
|
Container(
|
||||||
height: 2,
|
height: 2,
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
),
|
),
|
||||||
|
|
||||||
if (_currentIndex + 1 < announcements.length)
|
if (_currentIndex + 1 < announcements.length)
|
||||||
@@ -124,10 +246,10 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
Container(
|
Container(
|
||||||
height: 50,
|
height: 50,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.lightGreen.shade100,
|
color: widget.backgroundColor,
|
||||||
border: const Border.symmetric(
|
border: Border.symmetric(
|
||||||
vertical: BorderSide(
|
vertical: BorderSide(
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
width: 2
|
width: 2
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -136,7 +258,7 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
|
|
||||||
Container(
|
Container(
|
||||||
height: 2,
|
height: 2,
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
),
|
),
|
||||||
|
|
||||||
if (_currentIndex + 2 < announcements.length)
|
if (_currentIndex + 2 < announcements.length)
|
||||||
@@ -145,10 +267,10 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
Container(
|
Container(
|
||||||
height: 50,
|
height: 50,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.lightGreen.shade100,
|
color: widget.backgroundColor,
|
||||||
border: const Border.symmetric(
|
border: Border.symmetric(
|
||||||
vertical: BorderSide(
|
vertical: BorderSide(
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
width: 2
|
width: 2
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -157,7 +279,7 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
|
|
||||||
Container(
|
Container(
|
||||||
height: 2,
|
height: 2,
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
),
|
),
|
||||||
|
|
||||||
if (_currentIndex + 3 < announcements.length)
|
if (_currentIndex + 3 < announcements.length)
|
||||||
@@ -166,10 +288,10 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
Container(
|
Container(
|
||||||
height: 50,
|
height: 50,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.lightGreen.shade100,
|
color: widget.backgroundColor,
|
||||||
border: const Border.symmetric(
|
border: Border.symmetric(
|
||||||
vertical: BorderSide(
|
vertical: BorderSide(
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
width: 2
|
width: 2
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -178,16 +300,16 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
|
|
||||||
Container(
|
Container(
|
||||||
height: 2,
|
height: 2,
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
),
|
),
|
||||||
|
|
||||||
Container(
|
Container(
|
||||||
height: 40,
|
height: 40,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.lightGreen.shade100,
|
color: widget.backgroundColor,
|
||||||
border: const Border.symmetric(
|
border: Border.symmetric(
|
||||||
vertical: BorderSide(
|
vertical: BorderSide(
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
width: 2
|
width: 2
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -205,10 +327,10 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.lightGreen.shade100,
|
color: widget.backgroundColor,
|
||||||
border: const Border.symmetric(
|
border: Border.symmetric(
|
||||||
vertical: BorderSide(
|
vertical: BorderSide(
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
width: 2
|
width: 2
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -226,7 +348,7 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
height: 40,
|
height: 40,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.arrow_upward,
|
Icons.arrow_upward,
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
@@ -258,10 +380,10 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.lightGreen.shade100,
|
color: widget.backgroundColor,
|
||||||
border: const Border.symmetric(
|
border: Border.symmetric(
|
||||||
vertical: BorderSide(
|
vertical: BorderSide(
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
width: 2
|
width: 2
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -279,7 +401,7 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
height: 40,
|
height: 40,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.arrow_downward,
|
Icons.arrow_downward,
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
@@ -314,7 +436,7 @@ class _QuickAnnouncementsState_IBUS extends State<_QuickAnnouncements_IBUS> {
|
|||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: 2,
|
height: 2,
|
||||||
color: Colors.black,
|
color: widget.outlineColor,
|
||||||
),
|
),
|
||||||
|
|
||||||
]
|
]
|
||||||
@@ -333,8 +455,9 @@ class _QuickAnnouncement_IBUS extends StatelessWidget {
|
|||||||
|
|
||||||
final ManualAnnouncementEntry announcement;
|
final ManualAnnouncementEntry announcement;
|
||||||
final int index;
|
final int index;
|
||||||
|
final Color outlineColor;
|
||||||
|
|
||||||
const _QuickAnnouncement_IBUS({super.key, required this.announcement, required this.index});
|
_QuickAnnouncement_IBUS({super.key, required this.announcement, required this.index, required this.outlineColor});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -344,48 +467,52 @@ class _QuickAnnouncement_IBUS extends StatelessWidget {
|
|||||||
Container(
|
Container(
|
||||||
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.lightGreen.shade100,
|
color: Colors.transparent,
|
||||||
border: const Border.symmetric(
|
border: Border.symmetric(
|
||||||
vertical: BorderSide(
|
vertical: BorderSide(
|
||||||
color: Colors.black,
|
color: outlineColor,
|
||||||
width: 2
|
width: 2
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
padding: const EdgeInsets.all(5),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 10,
|
||||||
|
vertical: 5
|
||||||
|
),
|
||||||
|
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 50,
|
|
||||||
|
|
||||||
child: Row(
|
|
||||||
children: [
|
child: Transform.translate(
|
||||||
Text(
|
|
||||||
announcement.shortName,
|
offset: Offset(0, 4),
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 20,
|
child: Row(
|
||||||
color: Colors.black,
|
children: [
|
||||||
fontFamily: "lcd",
|
Text(
|
||||||
height: 1,
|
announcement.shortName,
|
||||||
|
style: GoogleFonts.teko(
|
||||||
|
fontSize: 25,
|
||||||
|
color: outlineColor,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
Expanded(
|
child: Container(
|
||||||
child: Container(
|
|
||||||
|
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
|
|
||||||
child: Text(
|
child: Text(
|
||||||
(index+1).toString(),
|
(index+1).toString(),
|
||||||
style: const TextStyle(
|
style: GoogleFonts.teko(
|
||||||
fontSize: 20,
|
fontSize: 25,
|
||||||
color: Colors.black,
|
color: outlineColor,
|
||||||
fontFamily: "lcd",
|
)
|
||||||
height: 1,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
)
|
],
|
||||||
],
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,16 +1,629 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import 'package:bus_infotainment/singletons/live_information.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
|
class pages_Settings extends StatefulWidget {
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<pages_Settings> createState() => _pages_SettingsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _pages_SettingsState extends State<pages_Settings> {
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
|
if (LiveInformation().auth.isAuthenticated()){
|
||||||
|
return Container(
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return _LoginPage(
|
||||||
|
onLogin: () {
|
||||||
|
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum _LoginType {
|
||||||
|
login,
|
||||||
|
signup
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class _LoginPage extends StatefulWidget {
|
||||||
|
|
||||||
|
_LoginType type = _LoginType.login;
|
||||||
|
|
||||||
|
final Function() onLogin;
|
||||||
|
|
||||||
|
_LoginPage({super.key, required this.onLogin, });
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_LoginPage> createState() => _LoginPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LoginPageState extends State<_LoginPage> {
|
||||||
|
|
||||||
|
|
||||||
class pages_Settings extends StatelessWidget {
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
|
color: Color.fromRGBO(19, 19, 19, 1),
|
||||||
|
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
|
||||||
child: Column(
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
|
||||||
|
// Login form
|
||||||
|
widget.type == _LoginType.login ?
|
||||||
|
LoginForm(
|
||||||
|
handleSubmit: (form) {
|
||||||
|
print("Login form submitted");
|
||||||
|
|
||||||
|
LiveInformation().auth.createEmailSession(
|
||||||
|
email: form.emailController.text,
|
||||||
|
password: form.passwordController.text
|
||||||
|
).then((value) {
|
||||||
|
widget.onLogin();
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
requestSignup: () {
|
||||||
|
setState(() {
|
||||||
|
widget.type = _LoginType.signup;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
) :
|
||||||
|
SignupForm(
|
||||||
|
handleSubmit: (form) {
|
||||||
|
print("Signup form submitted");
|
||||||
|
|
||||||
|
LiveInformation().auth.createUser(
|
||||||
|
displayName: form.dispnameController.text,
|
||||||
|
username: form.usernameController.text,
|
||||||
|
email: form.emailController.text,
|
||||||
|
password: form.passwordController.text
|
||||||
|
).then((value) {
|
||||||
|
// login
|
||||||
|
LiveInformation().auth.createEmailSession(
|
||||||
|
email: form.emailController.text,
|
||||||
|
password: form.passwordController.text
|
||||||
|
).then((value) {
|
||||||
|
widget.onLogin();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
requestSignin: () {
|
||||||
|
setState(() {
|
||||||
|
widget.type = _LoginType.login;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoginForm extends StatefulWidget {
|
||||||
|
|
||||||
|
final Function(LoginForm) handleSubmit;
|
||||||
|
|
||||||
|
final Function() requestSignup;
|
||||||
|
|
||||||
|
/* TextControllers */
|
||||||
|
final TextEditingController emailController = TextEditingController();
|
||||||
|
|
||||||
|
final TextEditingController passwordController = TextEditingController();
|
||||||
|
|
||||||
|
|
||||||
|
LoginForm({super.key, required this.handleSubmit, required this.requestSignup});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<LoginForm> createState() => _LoginFormState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LoginFormState extends State<LoginForm> {
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
|
||||||
|
child: Column(
|
||||||
|
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
||||||
|
children: [
|
||||||
|
|
||||||
|
Text(
|
||||||
|
"Sign In",
|
||||||
|
style: GoogleFonts.montserrat(
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
letterSpacing: -1
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 20),
|
||||||
|
|
||||||
|
PW_TextField(
|
||||||
|
title: "Email",
|
||||||
|
controller: widget.emailController,
|
||||||
|
handleSubmit: (form) {
|
||||||
|
print("Signup form submitted");
|
||||||
|
widget.handleSubmit(widget);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 20),
|
||||||
|
|
||||||
|
PW_TextField(
|
||||||
|
title: "Password",
|
||||||
|
obscure: true,
|
||||||
|
controller: widget.passwordController,
|
||||||
|
handleSubmit: (form) {
|
||||||
|
print("Signup form submitted");
|
||||||
|
widget.handleSubmit(widget);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 20),
|
||||||
|
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: (){
|
||||||
|
widget.handleSubmit(widget);
|
||||||
|
},
|
||||||
|
|
||||||
|
// make the corner radius 4, background color match the theme, and text colour white, fill to width of parent
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4)
|
||||||
|
),
|
||||||
|
minimumSize: Size(double.infinity, 48)
|
||||||
|
),
|
||||||
|
|
||||||
|
child: Text(
|
||||||
|
"Sign in",
|
||||||
|
style: GoogleFonts.interTight(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.white,
|
||||||
|
letterSpacing: 0.5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 20),
|
||||||
|
|
||||||
|
Row(
|
||||||
|
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|
||||||
|
children: [
|
||||||
|
|
||||||
|
TextButton(
|
||||||
|
onPressed: (){
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
// Make border radius 4, background transparent, and text colour white
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4)
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.transparent
|
||||||
|
),
|
||||||
|
|
||||||
|
child: Text(
|
||||||
|
"Forgot password?",
|
||||||
|
|
||||||
|
style: GoogleFonts.interTight(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
letterSpacing: 0.5
|
||||||
|
),
|
||||||
|
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
Container(
|
||||||
|
width: 1,
|
||||||
|
height: 24,
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
color: Colors.grey.shade800,
|
||||||
|
),
|
||||||
|
|
||||||
|
TextButton(
|
||||||
|
onPressed: (){
|
||||||
|
widget.requestSignup();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Make border radius 4, background transparent, and text colour white
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4)
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.transparent
|
||||||
|
),
|
||||||
|
|
||||||
|
child: Text(
|
||||||
|
"Sign Up",
|
||||||
|
|
||||||
|
style: GoogleFonts.interTight(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
letterSpacing: 0.5
|
||||||
|
),
|
||||||
|
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SignupForm extends StatelessWidget {
|
||||||
|
|
||||||
|
final Function(SignupForm) handleSubmit;
|
||||||
|
|
||||||
|
final Function() requestSignin;
|
||||||
|
|
||||||
|
/* TextControllers */
|
||||||
|
final TextEditingController dispnameController = TextEditingController();
|
||||||
|
final TextEditingController usernameController = TextEditingController();
|
||||||
|
final TextEditingController emailController = TextEditingController();
|
||||||
|
final TextEditingController passwordController = TextEditingController();
|
||||||
|
final TextEditingController confirmPasswordController = TextEditingController();
|
||||||
|
|
||||||
|
SignupForm({super.key, required this.handleSubmit, required this.requestSignin});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
|
||||||
|
child: Column(
|
||||||
|
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
||||||
|
children: [
|
||||||
|
|
||||||
|
Text(
|
||||||
|
"Sign Up",
|
||||||
|
style: GoogleFonts.montserrat(
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
letterSpacing: -1
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 20),
|
||||||
|
|
||||||
|
PW_TextField(
|
||||||
|
title: "Display Name",
|
||||||
|
controller: dispnameController,
|
||||||
|
handleSubmit: (form) {
|
||||||
|
print("Signup form submitted");
|
||||||
|
handleSubmit(this);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 20),
|
||||||
|
|
||||||
|
PW_TextField(
|
||||||
|
title: "Username",
|
||||||
|
controller: usernameController,
|
||||||
|
handleSubmit: (form) {
|
||||||
|
print("Signup form submitted");
|
||||||
|
handleSubmit(this);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 20),
|
||||||
|
|
||||||
|
PW_TextField(
|
||||||
|
title: "Email",
|
||||||
|
controller: emailController,
|
||||||
|
handleSubmit: (form) {
|
||||||
|
print("Signup form submitted");
|
||||||
|
handleSubmit(this);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 20),
|
||||||
|
|
||||||
|
PW_TextField(
|
||||||
|
title: "Password",
|
||||||
|
obscure: true,
|
||||||
|
controller: passwordController,
|
||||||
|
handleSubmit: (form) {
|
||||||
|
print("Signup form submitted");
|
||||||
|
handleSubmit(this);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 20),
|
||||||
|
|
||||||
|
PW_TextField(
|
||||||
|
title: "Confirm Password",
|
||||||
|
obscure: true,
|
||||||
|
controller: confirmPasswordController,
|
||||||
|
handleSubmit: (form) {
|
||||||
|
print("Signup form submitted");
|
||||||
|
handleSubmit(this);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 20),
|
||||||
|
|
||||||
|
// Terms and conditions with hyperlink to terms and conditions, with checkbox
|
||||||
|
// use TextSpan
|
||||||
|
// use check box
|
||||||
|
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Checkbox(
|
||||||
|
value: false,
|
||||||
|
onChanged: (value) {
|
||||||
|
print("Checkbox changed to $value");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: RichText(
|
||||||
|
// wrap text
|
||||||
|
text: TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: "By registering, you agree to our ",
|
||||||
|
style: GoogleFonts.interTight(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: "Terms and Conditions",
|
||||||
|
style: GoogleFonts.interTight(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
recognizer: new TapGestureRecognizer()
|
||||||
|
..onTap = () {
|
||||||
|
launchUrlString("https://google.co.uk/");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 20),
|
||||||
|
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: (){
|
||||||
|
handleSubmit(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
// make the corner radius 4, background color match the theme, and text colour white, fill to width of parent
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4)
|
||||||
|
),
|
||||||
|
minimumSize: Size(double.infinity, 48)
|
||||||
|
),
|
||||||
|
|
||||||
|
child: Text(
|
||||||
|
"Sign up",
|
||||||
|
style: GoogleFonts.interTight(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.white,
|
||||||
|
letterSpacing: 0.5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 20),
|
||||||
|
|
||||||
|
// Already have an account? Sign in
|
||||||
|
|
||||||
|
Row(
|
||||||
|
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|
||||||
|
children: [
|
||||||
|
|
||||||
|
Text(
|
||||||
|
"Already have an account? ",
|
||||||
|
style: GoogleFonts.interTight(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
TextButton(
|
||||||
|
onPressed: (){
|
||||||
|
requestSignin();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Make border radius 4, background transparent, and text colour white
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4)
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.transparent
|
||||||
|
),
|
||||||
|
|
||||||
|
child: Text(
|
||||||
|
"Sign in",
|
||||||
|
|
||||||
|
style: GoogleFonts.interTight(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
letterSpacing: 0.5
|
||||||
|
),
|
||||||
|
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
),
|
||||||
|
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class PW_TextField extends StatelessWidget {
|
||||||
|
|
||||||
|
String title = "field";
|
||||||
|
bool obscure = false;
|
||||||
|
bool longText = false;
|
||||||
|
|
||||||
|
late TextEditingController controller;
|
||||||
|
|
||||||
|
Function(String)? handleTapOutside;
|
||||||
|
Function(String)? handleEditingComplete;
|
||||||
|
Function(String)? handleChanged;
|
||||||
|
Function(String)? handleSubmit;
|
||||||
|
|
||||||
|
PW_TextField({super.key, this.title = "field", required this.controller, this.obscure = false, this.longText = false, this.handleSubmit, this.handleTapOutside, this.handleEditingComplete, this.handleChanged});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// TODO: implement build
|
||||||
|
return Column(
|
||||||
|
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
||||||
|
children: [
|
||||||
|
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: GoogleFonts.interTight(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
letterSpacing: 0.1
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 4),
|
||||||
|
|
||||||
|
// field with uniform padding and margin
|
||||||
|
SizedBox(
|
||||||
|
|
||||||
|
child: TextField(
|
||||||
|
|
||||||
|
onEditingComplete: () {
|
||||||
|
if (handleEditingComplete != null) {
|
||||||
|
handleEditingComplete!(controller.text);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onChanged: (value) {
|
||||||
|
if (handleChanged != null) {
|
||||||
|
handleChanged!(controller.text);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onSubmitted: (value) {
|
||||||
|
if (handleSubmit != null) {
|
||||||
|
handleSubmit!(controller.text);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onTapOutside: (value) {
|
||||||
|
if (handleTapOutside != null) {
|
||||||
|
handleTapOutside!(controller.text);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
decoration: InputDecoration(
|
||||||
|
contentPadding: const EdgeInsets.all(12),
|
||||||
|
isDense: true,
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
|
||||||
|
),
|
||||||
|
filled: true,
|
||||||
|
// fillColor: STUDIOS_DEFAULT_BACKGROUND_COLOR,
|
||||||
|
hintText: title,
|
||||||
|
hintStyle: GoogleFonts.interTight(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.grey.shade700,
|
||||||
|
letterSpacing: 0.1
|
||||||
|
),
|
||||||
|
|
||||||
|
),
|
||||||
|
|
||||||
|
// wrap text
|
||||||
|
minLines: longText ? 3 : 1,
|
||||||
|
maxLines: longText ? null : 1,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
|
||||||
|
obscureText: obscure,
|
||||||
|
|
||||||
|
style: GoogleFonts.interTight(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
letterSpacing: 0.1,
|
||||||
|
),
|
||||||
|
|
||||||
|
controller: controller,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -48,68 +48,73 @@ class TfL_Dataset_TestState extends State<TfL_Dataset_Test> {
|
|||||||
|
|
||||||
surfaceTintColor: Colors.transparent,
|
surfaceTintColor: Colors.transparent,
|
||||||
|
|
||||||
title: Column(
|
title: Container(
|
||||||
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
|
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
||||||
Text(
|
children: [
|
||||||
"Bus Infotainment",
|
|
||||||
style: GoogleFonts.montserrat(
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Row(
|
Text(
|
||||||
|
"Bus Infotainment",
|
||||||
children: [
|
style: GoogleFonts.teko(
|
||||||
|
fontSize: 25,
|
||||||
Text(
|
fontWeight: FontWeight.bold,
|
||||||
"Selected: ",
|
color: Colors.white,
|
||||||
style: GoogleFonts.montserrat(
|
height: 1,
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
if (liveInformation.getRouteVariant() != null)
|
Row(
|
||||||
Container(
|
|
||||||
|
|
||||||
decoration: BoxDecoration(
|
children: [
|
||||||
color: Colors.black,
|
|
||||||
|
Text(
|
||||||
|
"Selected: ",
|
||||||
|
style: GoogleFonts.teko(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.white,
|
||||||
|
height: 1,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2),
|
if (liveInformation.getRouteVariant() != null)
|
||||||
|
Container(
|
||||||
|
|
||||||
child: Text(
|
decoration: BoxDecoration(
|
||||||
"${liveInformation.getRouteVariant()!.busRoute.routeNumber} to ${liveInformation.getRouteVariant()!.busStops.last.formattedStopName}",
|
color: Colors.black,
|
||||||
style: GoogleFonts.montserrat(
|
),
|
||||||
fontSize: 15,
|
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2),
|
||||||
|
|
||||||
|
child: Text(
|
||||||
|
"${liveInformation.getRouteVariant()!.busRoute.routeNumber} to ${liveInformation.getRouteVariant()!.busStops.last.formattedStopName}",
|
||||||
|
style: GoogleFonts.montserrat(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.orange.shade900,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Text(
|
||||||
|
"None",
|
||||||
|
style: GoogleFonts.teko(
|
||||||
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.orange.shade900,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
|
||||||
)
|
)
|
||||||
else
|
|
||||||
Text(
|
|
||||||
"None",
|
|
||||||
style: GoogleFonts.montserrat(
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
|
|
||||||
)
|
],
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
|
),
|
||||||
),
|
),
|
||||||
) : null,
|
) : null,
|
||||||
body: Pages[_selectedIndex],
|
body: Pages[_selectedIndex],
|
||||||
|
|||||||
@@ -2,7 +2,11 @@
|
|||||||
// Singleton
|
// Singleton
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:appwrite/appwrite.dart' as appwrite;
|
||||||
|
import 'package:appwrite/models.dart' as models;
|
||||||
import 'package:bus_infotainment/audio_cache.dart';
|
import 'package:bus_infotainment/audio_cache.dart';
|
||||||
|
import 'package:bus_infotainment/auth/api_constants.dart';
|
||||||
|
import 'package:bus_infotainment/auth/auth_api.dart';
|
||||||
import 'package:bus_infotainment/tfl_datasets.dart';
|
import 'package:bus_infotainment/tfl_datasets.dart';
|
||||||
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
||||||
import 'package:bus_infotainment/utils/delegates.dart';
|
import 'package:bus_infotainment/utils/delegates.dart';
|
||||||
@@ -19,7 +23,7 @@ class LiveInformation {
|
|||||||
|
|
||||||
LiveInformation._internal();
|
LiveInformation._internal();
|
||||||
|
|
||||||
Future<void> LoadDatasets() async {
|
Future<void> Initialize() async {
|
||||||
|
|
||||||
{
|
{
|
||||||
// Load the bus sequences
|
// Load the bus sequences
|
||||||
@@ -41,6 +45,16 @@ class LiveInformation {
|
|||||||
print("Loaded bus sequences from assets");
|
print("Loaded bus sequences from assets");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auth.isAuthenticated()){
|
||||||
|
print("Auth is authenticated");
|
||||||
|
setupRealtime();
|
||||||
|
} else {
|
||||||
|
print("Auth is not authenticated");
|
||||||
|
auth.onLogin.addListener((value) {
|
||||||
|
setupRealtime();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshTimer();
|
refreshTimer();
|
||||||
@@ -54,28 +68,33 @@ class LiveInformation {
|
|||||||
AudioWrapper audioPlayer = AudioWrapper();
|
AudioWrapper audioPlayer = AudioWrapper();
|
||||||
AnnouncementCache announcementCache = AnnouncementCache();
|
AnnouncementCache announcementCache = AnnouncementCache();
|
||||||
List<AnnouncementQueueEntry> announcementQueue = [];
|
List<AnnouncementQueueEntry> announcementQueue = [];
|
||||||
|
DateTime lastAnnouncement = DateTime.now();
|
||||||
EventDelegate<AnnouncementQueueEntry> announcementDelegate = EventDelegate();
|
EventDelegate<AnnouncementQueueEntry> announcementDelegate = EventDelegate();
|
||||||
String CurrentAnnouncement = "*** NO MESSAGE ***";
|
String _currentAnnouncement = "*** NO MESSAGE ***";
|
||||||
|
|
||||||
|
String get currentAnnouncement => _currentAnnouncement;
|
||||||
|
void set currentAnnouncement(String value) {
|
||||||
|
_currentAnnouncement = value;
|
||||||
|
}
|
||||||
|
|
||||||
void _handleAnnouncementQueue() async {
|
void _handleAnnouncementQueue() async {
|
||||||
print("Handling announcement queue");
|
print("Handling announcement queue");
|
||||||
if (audioPlayer.state != AudioWrapper_State.Playing) {
|
if (audioPlayer.state != AudioWrapper_State.Playing) {
|
||||||
if (announcementQueue.isNotEmpty) {
|
if (announcementQueue.isNotEmpty) {
|
||||||
AnnouncementQueueEntry announcement = announcementQueue.first;
|
AnnouncementQueueEntry announcement = announcementQueue.first;
|
||||||
announcementDelegate.trigger(announcement);
|
announcementDelegate.trigger(announcement);
|
||||||
CurrentAnnouncement = announcement.displayText;
|
_currentAnnouncement = announcement.displayText;
|
||||||
|
|
||||||
|
lastAnnouncement = DateTime.now();
|
||||||
|
|
||||||
|
|
||||||
for (AudioWrapperSource source in announcement.audioSources) {
|
for (AudioWrapperSource source in announcement.audioSources) {
|
||||||
|
|
||||||
Duration? duration = await audioPlayer.play(source);
|
Duration? duration = await audioPlayer.play(source);
|
||||||
if (source == announcement.audioSources.last) {
|
|
||||||
announcementQueue.removeAt(0);
|
|
||||||
}
|
|
||||||
await Future.delayed(duration!);
|
await Future.delayed(duration!);
|
||||||
await Future.delayed(Duration(milliseconds: 150));
|
await Future.delayed(Duration(milliseconds: 150));
|
||||||
}
|
}
|
||||||
audioPlayer.stop();
|
audioPlayer.stop();
|
||||||
|
announcementQueue.removeAt(0);
|
||||||
print("Popped announcement queue");
|
print("Popped announcement queue");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,6 +138,38 @@ class LiveInformation {
|
|||||||
|
|
||||||
void queueAnnouncement(AnnouncementQueueEntry announcement) {
|
void queueAnnouncement(AnnouncementQueueEntry announcement) {
|
||||||
announcementQueue.add(announcement);
|
announcementQueue.add(announcement);
|
||||||
|
|
||||||
|
// Make sure the timestamp of the announcement is after the last announcement
|
||||||
|
// If so, dont queue it
|
||||||
|
// If timestamp is null, then skip this check
|
||||||
|
if (announcement.timestamp != null && announcement.timestamp!.isBefore(lastAnnouncement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!announcement.sendToServer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final databases = appwrite.Databases(auth.client);
|
||||||
|
|
||||||
|
if (announcement is ManualAnnouncementEntry) {
|
||||||
|
|
||||||
|
final document = databases.createDocument(
|
||||||
|
documentId: appwrite.ID.unique(),
|
||||||
|
databaseId: ApiConstants.INFO_Q_DATABASE_ID,
|
||||||
|
collectionId: ApiConstants.MANUAL_Q_COLLECTION_ID,
|
||||||
|
data: {
|
||||||
|
"ManualAnnouncementIndex": manualAnnouncements.indexOf(announcement),
|
||||||
|
"SessionID": sessionID,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
print("Queued manual announcement: ${announcement.shortName}");
|
||||||
|
|
||||||
|
} else if (announcement is AnnouncementQueueEntry) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ManualAnnouncementEntry> manualAnnouncements = [
|
List<ManualAnnouncementEntry> manualAnnouncements = [
|
||||||
@@ -204,17 +255,206 @@ class LiveInformation {
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
AuthAPI auth = AuthAPI();
|
||||||
|
String sessionID = "65de648aa7f44684ecce";
|
||||||
|
void updateServer() async {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
final databases = appwrite.Databases(auth.client);
|
||||||
|
|
||||||
|
// final document = databases.updateDocument(
|
||||||
|
// databaseId: ApiConstants.INFO_DATABASE_ID,
|
||||||
|
// collectionId: ApiConstants.INFO_COLLECTION_ID,
|
||||||
|
// documentId: documentID,
|
||||||
|
// data: {
|
||||||
|
// "Display": _currentAnnouncement,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
print("Updated server with announcement: $_currentAnnouncement");
|
||||||
|
|
||||||
|
}
|
||||||
|
void pullServer() async {
|
||||||
|
|
||||||
|
if (auth.status == AuthStatus.UNAUTHENTICATED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final databases = appwrite.Databases(auth.client);
|
||||||
|
|
||||||
|
// final document = await databases.getDocument(
|
||||||
|
// databaseId: ApiConstants.INFO_DATABASE_ID,
|
||||||
|
// collectionId: ApiConstants.INFO_COLLECTION_ID,
|
||||||
|
// documentId: documentID,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// queueAnnouncement(AnnouncementQueueEntry(
|
||||||
|
// displayText: document.data['Display'],
|
||||||
|
// audioSources: [],
|
||||||
|
// sendToServer: false, // Don't send this back to the server, else we'll get an infinite loop
|
||||||
|
// ));
|
||||||
|
|
||||||
|
// print("Pulled announcement from server: ${document.data['Display']}");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool purgeRunning = false;
|
||||||
|
Future<void> deleteAllManualQueueEntries() async {
|
||||||
|
|
||||||
|
if (purgeRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
purgeRunning = true;
|
||||||
|
|
||||||
|
final databases = appwrite.Databases(auth.client);
|
||||||
|
int offset = 0;
|
||||||
|
const int limit = 25; // Maximum number of documents that can be fetched at once
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Fetch a page of documents from the manual queue collection
|
||||||
|
print("Deleting manual queue entries");
|
||||||
|
final manual_q = await databases.listDocuments(
|
||||||
|
databaseId: ApiConstants.INFO_Q_DATABASE_ID,
|
||||||
|
collectionId: ApiConstants.MANUAL_Q_COLLECTION_ID,
|
||||||
|
queries: [
|
||||||
|
appwrite.Query.search("SessionID", sessionID),
|
||||||
|
appwrite.Query.limit(limit),
|
||||||
|
appwrite.Query.offset(offset),
|
||||||
|
appwrite.Query.orderDesc('\$createdAt')
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// If there are no documents in the fetched page, break the loop
|
||||||
|
if (manual_q.documents.isEmpty) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete each document in the fetched page
|
||||||
|
for (models.Document doc in manual_q.documents) {
|
||||||
|
await databases.deleteDocument(
|
||||||
|
databaseId: ApiConstants.INFO_Q_DATABASE_ID,
|
||||||
|
collectionId: ApiConstants.MANUAL_Q_COLLECTION_ID,
|
||||||
|
documentId: doc.$id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to the next page
|
||||||
|
offset += limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Deleted all manual queue entries");
|
||||||
|
}
|
||||||
|
|
||||||
|
void pullQueue() async {
|
||||||
|
|
||||||
|
if (auth.status == AuthStatus.UNAUTHENTICATED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final databases = appwrite.Databases(auth.client);
|
||||||
|
|
||||||
|
// Pull the manual queue
|
||||||
|
final manual_q = await databases.listDocuments(
|
||||||
|
databaseId: ApiConstants.INFO_Q_DATABASE_ID,
|
||||||
|
collectionId: ApiConstants.MANUAL_Q_COLLECTION_ID,
|
||||||
|
queries: [
|
||||||
|
appwrite.Query.search("SessionID", sessionID),
|
||||||
|
appwrite.Query.limit(25),
|
||||||
|
appwrite.Query.offset(0),
|
||||||
|
appwrite.Query.orderDesc('\$createdAt')
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
List<AnnouncementQueueEntry> queue = [];
|
||||||
|
|
||||||
|
for (models.Document doc in manual_q.documents) {
|
||||||
|
int index = doc.data['ManualAnnouncementIndex'];
|
||||||
|
|
||||||
|
ManualAnnouncementEntry announcement_clone = ManualAnnouncementEntry(
|
||||||
|
shortName: manualAnnouncements[index].shortName,
|
||||||
|
informationText: manualAnnouncements[index].displayText,
|
||||||
|
audioSources: manualAnnouncements[index].audioSources,
|
||||||
|
timestamp: DateTime.parse(doc.$createdAt),
|
||||||
|
sendToServer: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
// sort the queue by timestamp, so the oldest announcements are at the front
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
queue.add(announcement_clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (AnnouncementQueueEntry entry in queue) {
|
||||||
|
queueAnnouncement(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
appwrite.RealtimeSubscription? manual_q_subscription;
|
||||||
|
Future<void> setupRealtime() async {
|
||||||
|
|
||||||
|
if (manual_q_subscription != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// await deleteAllManualQueueEntries(); //todo
|
||||||
|
|
||||||
|
print("Setting up realtime");
|
||||||
|
|
||||||
|
// Websocket
|
||||||
|
appwrite.Realtime realtime = appwrite.Realtime(auth.client);
|
||||||
|
|
||||||
|
manual_q_subscription = realtime.subscribe(
|
||||||
|
['databases.${ApiConstants.INFO_Q_DATABASE_ID}.collections.${ApiConstants.MANUAL_Q_COLLECTION_ID}.documents'],
|
||||||
|
);
|
||||||
|
manual_q_subscription?.stream.listen((event) {
|
||||||
|
print("Manual queue entry added");
|
||||||
|
|
||||||
|
pullQueue();
|
||||||
|
});
|
||||||
|
|
||||||
|
print("Subscribed to servers");
|
||||||
|
|
||||||
|
await Future.delayed(Duration(seconds: 90));
|
||||||
|
|
||||||
|
manual_q_subscription?.close();
|
||||||
|
manual_q_subscription = null;
|
||||||
|
setupRealtime();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AnnouncementQueueEntry {
|
class AnnouncementQueueEntry {
|
||||||
final String displayText;
|
final String displayText;
|
||||||
final List<AudioWrapperSource> audioSources;
|
final List<AudioWrapperSource> audioSources;
|
||||||
|
bool sendToServer = true;
|
||||||
|
DateTime? timestamp;
|
||||||
|
|
||||||
AnnouncementQueueEntry({required this.displayText, required this.audioSources});
|
AnnouncementQueueEntry({required this.displayText, required this.audioSources, this.sendToServer = true, this.timestamp});
|
||||||
}
|
}
|
||||||
|
|
||||||
class ManualAnnouncementEntry extends AnnouncementQueueEntry {
|
class ManualAnnouncementEntry extends AnnouncementQueueEntry {
|
||||||
final String shortName;
|
final String shortName;
|
||||||
|
|
||||||
ManualAnnouncementEntry({required this.shortName, required String informationText, required List<AudioWrapperSource> audioSources}) : super(displayText: informationText, audioSources: audioSources);
|
ManualAnnouncementEntry({
|
||||||
|
required this.shortName,
|
||||||
|
required String informationText,
|
||||||
|
required List<AudioWrapperSource> audioSources,
|
||||||
|
DateTime? timestamp,
|
||||||
|
bool sendToServer = true,
|
||||||
|
}) : super(
|
||||||
|
displayText: informationText,
|
||||||
|
audioSources: audioSources,
|
||||||
|
sendToServer: sendToServer,
|
||||||
|
timestamp: timestamp,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -7,9 +7,17 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
||||||
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
#include <window_to_front/window_to_front_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
|
||||||
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
|
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) window_to_front_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowToFrontPlugin");
|
||||||
|
window_to_front_plugin_register_with_registrar(window_to_front_registrar);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
audioplayers_linux
|
audioplayers_linux
|
||||||
|
url_launcher_linux
|
||||||
|
window_to_front
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
@@ -7,12 +7,24 @@ import Foundation
|
|||||||
|
|
||||||
import audio_session
|
import audio_session
|
||||||
import audioplayers_darwin
|
import audioplayers_darwin
|
||||||
|
import device_info_plus
|
||||||
|
import flutter_web_auth_2
|
||||||
import just_audio
|
import just_audio
|
||||||
|
import package_info_plus
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
|
import shared_preferences_foundation
|
||||||
|
import url_launcher_macos
|
||||||
|
import window_to_front
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
||||||
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||||
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
|
FlutterWebAuth2Plugin.register(with: registry.registrar(forPlugin: "FlutterWebAuth2Plugin"))
|
||||||
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
|
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
|
||||||
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
|
WindowToFrontPlugin.register(with: registry.registrar(forPlugin: "WindowToFrontPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
326
pubspec.lock
326
pubspec.lock
@@ -1,6 +1,14 @@
|
|||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
|
appwrite:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: appwrite
|
||||||
|
sha256: "1a602cfc6122ec9f179403b44526ff60a6baac9a8f4e8578061fc5e85df67211"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "11.0.1"
|
||||||
archive:
|
archive:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -97,6 +105,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
|
charcode:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: charcode
|
||||||
|
sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.1"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -121,6 +137,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
|
cookie_jar:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cookie_jar
|
||||||
|
sha256: a6ac027d3ed6ed756bfce8f3ff60cb479e266f3b0fdabd6242b804b6765e52de
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.8"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -129,6 +153,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.3"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
csv:
|
csv:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -145,6 +177,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.6"
|
version: "1.0.6"
|
||||||
|
device_info_plus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: device_info_plus
|
||||||
|
sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.1.2"
|
||||||
|
device_info_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: device_info_plus_platform_interface
|
||||||
|
sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.0"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -190,11 +238,35 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.3"
|
version: "2.0.3"
|
||||||
|
flutter_map:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_map
|
||||||
|
sha256: cda8d72135b697f519287258b5294a57ce2f2a5ebf234f0e406aad4dc14c9399
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.0"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_web_auth_2:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_web_auth_2
|
||||||
|
sha256: "0da41e631a368e02366fc1a9b79dd8da191e700a836878bc54466fff51c07df2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1"
|
||||||
|
flutter_web_auth_2_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_web_auth_2_platform_interface
|
||||||
|
sha256: f6fa7059ff3428c19cd756c02fef8eb0147131c7e64591f9060c90b5ab84f094
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
flutter_web_plugins:
|
flutter_web_plugins:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -208,14 +280,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.0"
|
version: "6.1.0"
|
||||||
|
html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.4"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba
|
sha256: "4c3f04bfb64d3efd508d06b41b825542f08122d30bda4933fb95c069d22a4fa3"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.0.0"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -272,6 +352,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "0.2.0"
|
||||||
|
latlong2:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: latlong2
|
||||||
|
sha256: "18712164760cee655bc790122b0fd8f3d5b3c36da2cb7bf94b68a197fbb0811b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.0"
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -280,6 +368,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
lists:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: lists
|
||||||
|
sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
|
logger:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: logger
|
||||||
|
sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.2+1"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -304,6 +408,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.10.0"
|
||||||
|
mgrs_dart:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: mgrs_dart
|
||||||
|
sha256: fb89ae62f05fa0bb90f70c31fc870bcbcfd516c843fb554452ab3396f78586f7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
|
package_info_plus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_info_plus
|
||||||
|
sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.2.0"
|
||||||
|
package_info_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_info_plus_platform_interface
|
||||||
|
sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -384,6 +512,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.7.4"
|
version: "3.7.4"
|
||||||
|
polylabel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: polylabel
|
||||||
|
sha256: "41b9099afb2aa6c1730bdd8a0fab1400d287694ec7615dd8516935fa3144214b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
|
proj4dart:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: proj4dart
|
||||||
|
sha256: c8a659ac9b6864aa47c171e78d41bbe6f5e1d7bd790a5814249e6b68bc44324e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
rxdart:
|
rxdart:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -392,6 +536,62 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.27.7"
|
version: "0.27.7"
|
||||||
|
shared_preferences:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: shared_preferences
|
||||||
|
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.2"
|
||||||
|
shared_preferences_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_android
|
||||||
|
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1"
|
||||||
|
shared_preferences_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_foundation
|
||||||
|
sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.5"
|
||||||
|
shared_preferences_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_linux
|
||||||
|
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
shared_preferences_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_platform_interface
|
||||||
|
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
shared_preferences_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_web
|
||||||
|
sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.2"
|
||||||
|
shared_preferences_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_windows
|
||||||
|
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -477,6 +677,94 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2"
|
version: "1.3.2"
|
||||||
|
unicode:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: unicode
|
||||||
|
sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.1"
|
||||||
|
universal_html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: universal_html
|
||||||
|
sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.4"
|
||||||
|
universal_io:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: universal_io
|
||||||
|
sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.2"
|
||||||
|
url_launcher:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: url_launcher
|
||||||
|
sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.2.5"
|
||||||
|
url_launcher_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_android
|
||||||
|
sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.3.0"
|
||||||
|
url_launcher_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_ios
|
||||||
|
sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.2.4"
|
||||||
|
url_launcher_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_linux
|
||||||
|
sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.1"
|
||||||
|
url_launcher_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_macos
|
||||||
|
sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
|
url_launcher_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_platform_interface
|
||||||
|
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
url_launcher_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_web
|
||||||
|
sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.3"
|
||||||
|
url_launcher_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_windows
|
||||||
|
sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.1"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -501,6 +789,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.0"
|
version: "0.3.0"
|
||||||
|
web_socket_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web_socket_channel
|
||||||
|
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -509,6 +805,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.2.0"
|
version: "5.2.0"
|
||||||
|
win32_registry:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32_registry
|
||||||
|
sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
window_to_front:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: window_to_front
|
||||||
|
sha256: "7aef379752b7190c10479e12b5fd7c0b9d92adc96817d9e96c59937929512aee"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.3"
|
||||||
|
wkt_parser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: wkt_parser
|
||||||
|
sha256: "8a555fc60de3116c00aad67891bcab20f81a958e4219cc106e3c037aa3937f13"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -519,4 +839,4 @@ packages:
|
|||||||
version: "1.0.4"
|
version: "1.0.4"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.2.0 <4.0.0"
|
dart: ">=3.2.0 <4.0.0"
|
||||||
flutter: ">=3.10.0"
|
flutter: ">=3.16.0"
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ dependencies:
|
|||||||
google_fonts: ^6.1.0
|
google_fonts: ^6.1.0
|
||||||
intl: any
|
intl: any
|
||||||
text_scroll: ^0.2.0
|
text_scroll: ^0.2.0
|
||||||
|
flutter_map: ^6.1.0
|
||||||
|
appwrite: ^11.0.1
|
||||||
|
shared_preferences: ^2.2.2
|
||||||
|
url_launcher: ^6.2.2
|
||||||
|
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
|
|||||||
17
server/server.sh
Normal file
17
server/server.sh
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
sh
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Set the port
|
||||||
|
PORT=5000
|
||||||
|
|
||||||
|
# Stop any program currently running on the set port
|
||||||
|
echo 'preparing port' $PORT '...'
|
||||||
|
fuser -k 5000/tcp
|
||||||
|
|
||||||
|
# switch directories
|
||||||
|
cd build/web/
|
||||||
|
|
||||||
|
# Start the server
|
||||||
|
echo 'Server starting on port' $PORT '...'
|
||||||
|
python3 -m http.server $PORT
|
||||||
@@ -8,10 +8,16 @@
|
|||||||
|
|
||||||
#include <audioplayers_windows/audioplayers_windows_plugin.h>
|
#include <audioplayers_windows/audioplayers_windows_plugin.h>
|
||||||
#include <just_audio_windows/just_audio_windows_plugin.h>
|
#include <just_audio_windows/just_audio_windows_plugin.h>
|
||||||
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
#include <window_to_front/window_to_front_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
AudioplayersWindowsPluginRegisterWithRegistrar(
|
AudioplayersWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
|
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
|
||||||
JustAudioWindowsPluginRegisterWithRegistrar(
|
JustAudioWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("JustAudioWindowsPlugin"));
|
registry->GetRegistrarForPlugin("JustAudioWindowsPlugin"));
|
||||||
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
|
WindowToFrontPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("WindowToFrontPlugin"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
audioplayers_windows
|
audioplayers_windows
|
||||||
just_audio_windows
|
just_audio_windows
|
||||||
|
url_launcher_windows
|
||||||
|
window_to_front
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
Reference in New Issue
Block a user