Merge branch 'dev' into link_space_model_spaces

This commit is contained in:
mohammad
2025-03-06 11:50:21 +03:00
82 changed files with 2484 additions and 813 deletions

View File

@ -1,5 +1,9 @@
plugins { plugins {
id "com.android.application" id "com.android.application"
// START: FlutterFire Configuration
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
// END: FlutterFire Configuration
id "kotlin-android" id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin" id "dev.flutter.flutter-gradle-plugin"
} }

View File

@ -0,0 +1,68 @@
{
"project_info": {
"project_number": "427332280600",
"firebase_url": "https://test2-8a3d2-default-rtdb.firebaseio.com",
"project_id": "test2-8a3d2",
"storage_bucket": "test2-8a3d2.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:427332280600:android:550f67441246cb1a0c7e6d",
"android_client_info": {
"package_name": "com.example.syncrow.app"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyA5qOErxdm0zJmoHIB0TixfebYEsNRpwV0"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:427332280600:android:bb6047adeeb80fb00c7e6d",
"android_client_info": {
"package_name": "com.example.syncrow_application"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyA5qOErxdm0zJmoHIB0TixfebYEsNRpwV0"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:427332280600:android:2bc36fbe82994a3e0c7e6d",
"android_client_info": {
"package_name": "com.example.syncrow_web"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyA5qOErxdm0zJmoHIB0TixfebYEsNRpwV0"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View File

@ -20,6 +20,10 @@ pluginManagement {
plugins { plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false id "com.android.application" version "7.3.0" apply false
// START: FlutterFire Configuration
id "com.google.gms.google-services" version "4.3.15" apply false
id "com.google.firebase.crashlytics" version "2.8.1" apply false
// END: FlutterFire Configuration
id "org.jetbrains.kotlin.android" version "1.7.10" apply false id "org.jetbrains.kotlin.android" version "1.7.10" apply false
} }

View File

@ -0,0 +1,6 @@
<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.4 2.10938H11.7333V1.58203C11.7333 0.709699 11.0156 0 10.1333 0H5.86667C4.98443 0 4.26667 0.709699 4.26667 1.58203V2.10938H1.6C0.71776 2.10938 0 2.81907 0 3.69141C0 4.392 0.463111 4.9873 1.1024 5.19472L2.05369 16.5493C2.1222 17.3628 2.82258 18 3.64814 18H12.3519C13.1775 18 13.8778 17.3628 13.9464 16.5491L14.8976 5.19469C15.5369 4.9873 16 4.392 16 3.69141C16 2.81907 15.2822 2.10938 14.4 2.10938ZM5.33333 1.58203C5.33333 1.29125 5.57259 1.05469 5.86667 1.05469H10.1333C10.4274 1.05469 10.6667 1.29125 10.6667 1.58203V2.10938H5.33333V1.58203ZM12.8833 16.4618C12.8605 16.7329 12.6271 16.9453 12.3519 16.9453H3.64814C3.37298 16.9453 3.13952 16.7329 3.11673 16.462L2.17934 5.27344H13.8207L12.8833 16.4618ZM14.4 4.21875H1.6C1.30592 4.21875 1.06667 3.98218 1.06667 3.69141C1.06667 3.40063 1.30592 3.16406 1.6 3.16406H14.4C14.6941 3.16406 14.9333 3.40063 14.9333 3.69141C14.9333 3.98218 14.6941 4.21875 14.4 4.21875Z" fill="#999999"/>
<path d="M5.86561 15.3307L5.33228 6.82286C5.31404 6.53215 5.05957 6.31106 4.76698 6.32916C4.47297 6.3472 4.24943 6.59744 4.26764 6.88811L4.80097 15.396C4.8185 15.6756 5.05331 15.8907 5.33278 15.8907C5.64165 15.8907 5.88456 15.6335 5.86561 15.3307Z" fill="#999999"/>
<path d="M7.99989 6.32812C7.70534 6.32812 7.46655 6.56423 7.46655 6.85547V15.3633C7.46655 15.6545 7.70534 15.8906 7.99989 15.8906C8.29443 15.8906 8.53322 15.6545 8.53322 15.3633V6.85547C8.53322 6.56423 8.29443 6.32812 7.99989 6.32812Z" fill="#999999"/>
<path d="M11.233 6.32915C10.9396 6.31112 10.6859 6.53215 10.6677 6.82285L10.1343 15.3307C10.1162 15.6213 10.3397 15.8716 10.6337 15.8896C10.9278 15.9076 11.1808 15.6865 11.199 15.3959L11.7323 6.8881C11.7505 6.5974 11.527 6.34715 11.233 6.32915Z" fill="#999999"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

1
firebase.json Normal file
View File

@ -0,0 +1 @@
{"flutter":{"platforms":{"android":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:android:2bc36fbe82994a3e0c7e6d","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:ios:14346b200780dc760c7e6d","uploadDebugSymbols":true,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"macos":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:ios:14346b200780dc760c7e6d","uploadDebugSymbols":true,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"test2-8a3d2","configurations":{"android":"1:427332280600:android:2bc36fbe82994a3e0c7e6d","ios":"1:427332280600:ios:14346b200780dc760c7e6d","macos":"1:427332280600:ios:14346b200780dc760c7e6d","web":"1:427332280600:web:ad50516a87a35a1a0c7e6d","windows":"1:427332280600:web:f7a25537ccd5a7bd0c7e6d"}}}}}}

View File

@ -15,6 +15,7 @@
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
E44A9405B1EB1B638DD05A58 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7ABF0EC746A2D686A0ED574F /* Pods_RunnerTests.framework */; }; E44A9405B1EB1B638DD05A58 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7ABF0EC746A2D686A0ED574F /* Pods_RunnerTests.framework */; };
F2A3345EC3021060731668D3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B14AB50E8716720E10D074BD /* GoogleService-Info.plist */; };
FF49F60EC38658783D8D66DA /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2AFAE479A87ECDEBD5D6EB30 /* Pods_Runner.framework */; }; FF49F60EC38658783D8D66DA /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2AFAE479A87ECDEBD5D6EB30 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@ -64,6 +65,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B14AB50E8716720E10D074BD /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; };
D3AD250AADBF93406007C9EB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; D3AD250AADBF93406007C9EB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@ -138,6 +140,7 @@
331C8082294A63A400263BE5 /* RunnerTests */, 331C8082294A63A400263BE5 /* RunnerTests */,
1454C118FFCECEEDF59152D2 /* Pods */, 1454C118FFCECEEDF59152D2 /* Pods */,
20A3C64D2B1CFED5A81C3251 /* Frameworks */, 20A3C64D2B1CFED5A81C3251 /* Frameworks */,
B14AB50E8716720E10D074BD /* GoogleService-Info.plist */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -199,6 +202,7 @@
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
33590C9CD073D3D5EBA02CDE /* [CP] Embed Pods Frameworks */, 33590C9CD073D3D5EBA02CDE /* [CP] Embed Pods Frameworks */,
7A77858F6F15CB76D2D3A872 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */,
); );
buildRules = ( buildRules = (
); );
@ -264,6 +268,7 @@
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
F2A3345EC3021060731668D3 /* GoogleService-Info.plist in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -303,6 +308,24 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
}; };
7A77858F6F15CB76D2D3A872 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "FlutterFire: \"flutterfire upload-crashlytics-symbols\"";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\n#!/bin/bash\nPATH=\"${PATH}:$FLUTTER_ROOT/bin:$HOME/.pub-cache/bin\"\nflutterfire upload-crashlytics-symbols --upload-symbols-script-path=\"$PODS_ROOT/FirebaseCrashlytics/upload-symbols\" --platform=ios --apple-project-path=\"${SRCROOT}\" --env-platform-name=\"${PLATFORM_NAME}\" --env-configuration=\"${CONFIGURATION}\" --env-project-dir=\"${PROJECT_DIR}\" --env-built-products-dir=\"${BUILT_PRODUCTS_DIR}\" --env-dwarf-dsym-folder-path=\"${DWARF_DSYM_FOLDER_PATH}\" --env-dwarf-dsym-file-name=\"${DWARF_DSYM_FILE_NAME}\" --env-infoplist-path=\"${INFOPLIST_PATH}\" --default-config=default\n";
};
9740EEB61CF901F6004384FC /* Run Script */ = { 9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1; alwaysOutOfDate = 1;

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>API_KEY</key>
<string>AIzaSyABnpH6yo2RRjtkp4PlvtK84hKwRm2DhBw</string>
<key>GCM_SENDER_ID</key>
<string>427332280600</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>com.example.syncrowWeb</string>
<key>PROJECT_ID</key>
<string>test2-8a3d2</string>
<key>STORAGE_BUCKET</key>
<string>test2-8a3d2.firebasestorage.app</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:427332280600:ios:14346b200780dc760c7e6d</string>
<key>DATABASE_URL</key>
<string>https://test2-8a3d2-default-rtdb.firebaseio.com</string>
</dict>
</plist>

View File

@ -20,15 +20,22 @@ class DialogTextfieldDropdown extends StatefulWidget {
class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> { class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
bool _isOpen = false; bool _isOpen = false;
late OverlayEntry _overlayEntry; OverlayEntry? _overlayEntry;
final TextEditingController _controller = TextEditingController(); final TextEditingController _controller = TextEditingController();
late List<String> _filteredItems; // Filtered items list final FocusNode _focusNode = FocusNode();
List<String> _filteredItems = [];
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_controller.text = widget.initialValue ?? 'Select Tag'; _controller.text = widget.initialValue ?? '';
_filteredItems = List.from(widget.items); // Initialize filtered items _filteredItems = List.from(widget.items);
_focusNode.addListener(() {
if (!_focusNode.hasFocus) {
_closeDropdown();
}
});
} }
void _toggleDropdown() { void _toggleDropdown() {
@ -41,13 +48,16 @@ class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
void _openDropdown() { void _openDropdown() {
_overlayEntry = _createOverlayEntry(); _overlayEntry = _createOverlayEntry();
Overlay.of(context).insert(_overlayEntry); Overlay.of(context).insert(_overlayEntry!);
_isOpen = true; _isOpen = true;
} }
void _closeDropdown() { void _closeDropdown() {
_overlayEntry.remove(); if (_isOpen && _overlayEntry != null) {
_isOpen = false; _overlayEntry!.remove();
_overlayEntry = null;
_isOpen = false;
}
} }
OverlayEntry _createOverlayEntry() { OverlayEntry _createOverlayEntry() {
@ -58,9 +68,7 @@ class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
return OverlayEntry( return OverlayEntry(
builder: (context) { builder: (context) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: _closeDropdown,
_closeDropdown();
},
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
child: Stack( child: Stack(
children: [ children: [
@ -72,40 +80,44 @@ class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
elevation: 4.0, elevation: 4.0,
child: Container( child: Container(
color: ColorsManager.whiteColors, color: ColorsManager.whiteColors,
constraints: const BoxConstraints( constraints: const BoxConstraints(maxHeight: 200.0),
maxHeight: 200.0, child: StatefulBuilder(
), builder: (context, setStateDropdown) {
child: ListView.builder( return ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemCount: _filteredItems.length, itemCount: _filteredItems.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final item = _filteredItems[index]; final item = _filteredItems[index];
return Container(
decoration: const BoxDecoration( return Container(
border: Border( decoration: const BoxDecoration(
bottom: BorderSide( border: Border(
color: ColorsManager.lightGrayBorderColor, bottom: BorderSide(
width: 1.0, color: ColorsManager.lightGrayBorderColor,
width: 1.0,
),
),
), ),
), child: ListTile(
), title: Text(item,
child: ListTile( style: Theme.of(context)
title: Text(item, .textTheme
style: Theme.of(context) .bodyMedium
.textTheme ?.copyWith(
.bodyMedium color: ColorsManager
?.copyWith( .textPrimaryColor)),
color: ColorsManager.textPrimaryColor)), onTap: () {
onTap: () { _controller.text = item;
_controller.text = item; widget.onSelected(item);
widget.onSelected(item); setState(() {
setState(() { _filteredItems
_filteredItems .remove(item); // Remove selected item
.remove(item); // Remove selected item });
}); _closeDropdown();
_closeDropdown(); },
}, ),
), );
},
); );
}, },
), ),
@ -122,7 +134,8 @@ class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: _toggleDropdown, onTap: () => FocusScope.of(context).unfocus(),
behavior: HitTestBehavior.opaque,
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -135,23 +148,26 @@ class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
Expanded( Expanded(
child: TextFormField( child: TextFormField(
controller: _controller, controller: _controller,
onChanged: (value) { focusNode: _focusNode,
setState(() { onFieldSubmitted: (value) {
_filteredItems = widget.items
.where((item) =>
item.toLowerCase().contains(value.toLowerCase()))
.toList(); // Filter items dynamically
});
widget.onSelected(value); widget.onSelected(value);
_closeDropdown();
},
onTapOutside: (event) {
widget.onSelected(_controller.text);
_closeDropdown();
}, },
style: Theme.of(context).textTheme.bodyMedium, style: Theme.of(context).textTheme.bodyMedium,
decoration: const InputDecoration( decoration: const InputDecoration(
hintText: 'Enter or Select tag', hintText: 'Enter or Select a tag',
border: InputBorder.none, border: InputBorder.none,
), ),
), ),
), ),
const Icon(Icons.arrow_drop_down), GestureDetector(
onTap: _toggleDropdown,
child: const Icon(Icons.arrow_drop_down),
),
], ],
), ),
), ),

View File

@ -0,0 +1,93 @@
// File generated by FlutterFire CLI.
// ignore_for_file: type=lint
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, kIsWeb, TargetPlatform;
/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptionsDev {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
return web;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
return macos;
case TargetPlatform.windows:
return windows;
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions web = FirebaseOptions(
apiKey: 'AIzaSyCVEvKsJYzhWDFM-9Od68FE0nPpP933st0',
appId: '1:427332280600:web:ad50516a87a35a1a0c7e6d',
messagingSenderId: '427332280600',
projectId: 'test2-8a3d2',
authDomain: 'test2-8a3d2.firebaseapp.com',
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
storageBucket: 'test2-8a3d2.firebasestorage.app',
measurementId: 'G-Z1RTTTV5H9',
);
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyA5qOErxdm0zJmoHIB0TixfebYEsNRpwV0',
appId: '1:427332280600:android:2bc36fbe82994a3e0c7e6d',
messagingSenderId: '427332280600',
projectId: 'test2-8a3d2',
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
storageBucket: 'test2-8a3d2.firebasestorage.app',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyABnpH6yo2RRjtkp4PlvtK84hKwRm2DhBw',
appId: '1:427332280600:ios:14346b200780dc760c7e6d',
messagingSenderId: '427332280600',
projectId: 'test2-8a3d2',
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
storageBucket: 'test2-8a3d2.firebasestorage.app',
iosBundleId: 'com.example.syncrowWeb',
);
static const FirebaseOptions macos = FirebaseOptions(
apiKey: 'AIzaSyABnpH6yo2RRjtkp4PlvtK84hKwRm2DhBw',
appId: '1:427332280600:ios:14346b200780dc760c7e6d',
messagingSenderId: '427332280600',
projectId: 'test2-8a3d2',
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
storageBucket: 'test2-8a3d2.firebasestorage.app',
iosBundleId: 'com.example.syncrowWeb',
);
static const FirebaseOptions windows = FirebaseOptions(
apiKey: 'AIzaSyDizKjPC5rdkEjDxwXjM-RU5unB0Ziq3iw',
appId: '1:427332280600:web:f7a25537ccd5a7bd0c7e6d',
messagingSenderId: '427332280600',
projectId: 'test2-8a3d2',
authDomain: 'test2-8a3d2.firebaseapp.com',
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
storageBucket: 'test2-8a3d2.firebasestorage.app',
measurementId: 'G-4LFVXEXWKY',
);
}

View File

@ -0,0 +1,77 @@
// File generated by FlutterFire CLI.
// ignore_for_file: type=lint
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb, TargetPlatform;
/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptionsStaging {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
return web;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for macos - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.windows:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for windows - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyDP9GpYfLE8gHTj3kZ1hW8fx_FkJqOqSQk',
appId: '1:786692570726:android:0ef7079c2b978d4417b7a7',
messagingSenderId: '786692570726',
projectId: 'syncrow-staging',
databaseURL: 'https://syncrow-staging-default-rtdb.firebaseio.com',
storageBucket: 'syncrow-staging.appspot.com',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyAWlRiuJ75FMlf2_UDdri1voWKvkaSHtRg',
appId: '1:786692570726:ios:455a6fcff77e130f17b7a7',
messagingSenderId: '786692570726',
projectId: 'syncrow-staging',
databaseURL: 'https://syncrow-staging-default-rtdb.firebaseio.com',
storageBucket: 'syncrow-staging.appspot.com',
iosBundleId: 'com.example.syncrow.app',
);
static const FirebaseOptions web = FirebaseOptions(
apiKey: 'AIzaSyDyGaQ3sZhb4meaY6sGke-YglhdhJ2is8Q',
appId: '1:786692570726:web:93c931e6701797b317b7a7',
messagingSenderId: '786692570726',
projectId: 'syncrow-staging',
authDomain: 'syncrow-staging.firebaseapp.com',
databaseURL: 'https://syncrow-staging-default-rtdb.firebaseio.com',
storageBucket: 'syncrow-staging.appspot.com',
measurementId: 'G-CZ3J3G6LMQ',
);
}

View File

@ -1,8 +1,11 @@
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:syncrow_web/firebase_options_dev.dart';
import 'package:syncrow_web/firebase_options_prod.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart'; import 'package:syncrow_web/pages/home/bloc/home_event.dart';
@ -18,9 +21,13 @@ import 'package:syncrow_web/utils/theme/theme.dart';
Future<void> main() async { Future<void> main() async {
try { try {
const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development'); const environment =
String.fromEnvironment('FLAVOR', defaultValue: 'production');
await dotenv.load(fileName: '.env.$environment'); await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptionsStaging.currentPlatform,
);
initialSetup(); initialSetup();
} catch (_) {} } catch (_) {}
runApp(MyApp()); runApp(MyApp());
@ -49,7 +56,8 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MultiBlocProvider( return MultiBlocProvider(
providers: [ providers: [
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())), BlocProvider(
create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider<VisitorPasswordBloc>( BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(), create: (context) => VisitorPasswordBloc(),
), ),

84
lib/main_dev.dart Normal file
View File

@ -0,0 +1,84 @@
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:syncrow_web/firebase_options_dev.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/services/locator.dart';
import 'package:syncrow_web/utils/app_routes.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:syncrow_web/utils/theme/theme.dart';
Future<void> main() async {
try {
const environment =
String.fromEnvironment('FLAVOR', defaultValue: 'development');
await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptionsDev.currentPlatform,
);
initialSetup();
} catch (_) {}
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({
super.key,
});
final GoRouter _router = GoRouter(
initialLocation: RoutesConst.auth,
routes: AppRoutes.getRoutes(),
redirect: (context, state) async {
String checkToken = await AuthBloc.getTokenAndValidate();
final loggedIn = checkToken == 'Success';
final goingToLogin = state.uri.toString() == RoutesConst.auth;
if (!loggedIn && !goingToLogin) return RoutesConst.auth;
if (loggedIn && goingToLogin) return RoutesConst.home;
return null;
},
);
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(),
),
BlocProvider<RoutineBloc>(
create: (context) => RoutineBloc(),
),
BlocProvider<SpaceTreeBloc>(
create: (context) => SpaceTreeBloc()..add(InitialEvent()),
),
],
child: MaterialApp.router(
debugShowCheckedModeBanner: false,
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: {
PointerDeviceKind.mouse,
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.unknown,
},
),
theme: myTheme,
routerConfig: _router,
));
}
}

View File

@ -18,6 +18,7 @@ import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart';
class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout { class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
@ -33,11 +34,9 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
return WebScaffold( return WebScaffold(
enableMenuSidebar: false, enableMenuSidebar: false,
appBarTitle: FittedBox( appBarTitle: Text(
child: Text( 'Access Management',
'Access Management', style: ResponsiveTextTheme.of(context).deviceManagementTitle,
style: Theme.of(context).textTheme.headlineLarge,
),
), ),
rightBody: const NavigateHomeGridView(), rightBody: const NavigateHomeGridView(),
scaffoldBody: BlocProvider( scaffoldBody: BlocProvider(

View File

@ -10,9 +10,12 @@ import 'package:syncrow_web/pages/auth/model/region_model.dart';
import 'package:syncrow_web/pages/auth/model/token.dart'; import 'package:syncrow_web/pages/auth/model/token.dart';
import 'package:syncrow_web/pages/auth/model/user_model.dart'; import 'package:syncrow_web/pages/auth/model/user_model.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
import 'package:syncrow_web/services/auth_api.dart'; import 'package:syncrow_web/services/auth_api.dart';
import 'package:syncrow_web/utils/constants/strings_manager.dart'; import 'package:syncrow_web/utils/constants/strings_manager.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart'; import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/snack_bar.dart'; import 'package:syncrow_web/utils/snack_bar.dart';
class AuthBloc extends Bloc<AuthEvent, AuthState> { class AuthBloc extends Bloc<AuthEvent, AuthState> {
@ -32,8 +35,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
////////////////////////////// forget password ////////////////////////////////// ////////////////////////////// forget password //////////////////////////////////
final TextEditingController forgetEmailController = TextEditingController(); final TextEditingController forgetEmailController = TextEditingController();
final TextEditingController forgetPasswordController = final TextEditingController forgetPasswordController = TextEditingController();
TextEditingController();
final TextEditingController forgetOtp = TextEditingController(); final TextEditingController forgetOtp = TextEditingController();
final forgetFormKey = GlobalKey<FormState>(); final forgetFormKey = GlobalKey<FormState>();
final forgetEmailKey = GlobalKey<FormState>(); final forgetEmailKey = GlobalKey<FormState>();
@ -50,8 +52,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
return; return;
} }
_remainingTime = 1; _remainingTime = 1;
add(UpdateTimerEvent( add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
remainingTime: _remainingTime, isButtonEnabled: false));
try { try {
forgetEmailValidate = ''; forgetEmailValidate = '';
_remainingTime = (await AuthenticationAPI.sendOtp( _remainingTime = (await AuthenticationAPI.sendOtp(
@ -88,8 +89,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
_timer?.cancel(); _timer?.cancel();
add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true)); add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
} else { } else {
add(UpdateTimerEvent( add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
remainingTime: _remainingTime, isButtonEnabled: false));
} }
}); });
} }
@ -99,8 +99,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
emit(const TimerState(isButtonEnabled: true, remainingTime: 0)); emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
} }
Future<void> changePassword( Future<void> changePassword(ChangePasswordEvent event, Emitter<AuthState> emit) async {
ChangePasswordEvent event, Emitter<AuthState> emit) async {
emit(LoadingForgetState()); emit(LoadingForgetState());
try { try {
var response = await AuthenticationAPI.verifyOtp( var response = await AuthenticationAPI.verifyOtp(
@ -116,8 +115,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
} }
} on DioException catch (e) { } on DioException catch (e) {
final errorData = e.response!.data; final errorData = e.response!.data;
String errorMessage = String errorMessage = errorData['error']['message'] ?? 'something went wrong';
errorData['error']['message'] ?? 'something went wrong';
validate = errorMessage; validate = errorMessage;
emit(AuthInitialState()); emit(AuthInitialState());
} }
@ -131,9 +129,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
} }
void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) { void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) {
emit(TimerState( emit(TimerState(isButtonEnabled: event.isButtonEnabled, remainingTime: event.remainingTime));
isButtonEnabled: event.isButtonEnabled,
remainingTime: event.remainingTime));
} }
///////////////////////////////////// login ///////////////////////////////////// ///////////////////////////////////// login /////////////////////////////////////
@ -183,15 +179,13 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
if (token.accessTokenIsNotEmpty) { if (token.accessTokenIsNotEmpty) {
FlutterSecureStorage storage = const FlutterSecureStorage(); FlutterSecureStorage storage = const FlutterSecureStorage();
await storage.write( await storage.write(key: Token.loginAccessTokenKey, value: token.accessToken);
key: Token.loginAccessTokenKey, value: token.accessToken);
const FlutterSecureStorage().write( const FlutterSecureStorage().write(
key: UserModel.userUuidKey, key: UserModel.userUuidKey,
value: Token.decodeToken(token.accessToken)['uuid'].toString()); value: Token.decodeToken(token.accessToken)['uuid'].toString());
user = UserModel.fromToken(token); user = UserModel.fromToken(token);
loginEmailController.clear(); loginEmailController.clear();
loginPasswordController.clear(); loginPasswordController.clear();
debugPrint("token " + token.accessToken);
emit(LoginSuccess()); emit(LoginSuccess());
} else { } else {
emit(const LoginFailure(error: 'Something went wrong')); emit(const LoginFailure(error: 'Something went wrong'));
@ -342,14 +336,12 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
static Future<String> getTokenAndValidate() async { static Future<String> getTokenAndValidate() async {
try { try {
const storage = FlutterSecureStorage(); const storage = FlutterSecureStorage();
final firstLaunch = await SharedPreferencesHelper.readBoolFromSP( final firstLaunch =
StringsManager.firstLaunch) ?? await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true;
true;
if (firstLaunch) { if (firstLaunch) {
storage.deleteAll(); storage.deleteAll();
} }
await SharedPreferencesHelper.saveBoolToSP( await SharedPreferencesHelper.saveBoolToSP(StringsManager.firstLaunch, false);
StringsManager.firstLaunch, false);
final value = await storage.read(key: Token.loginAccessTokenKey) ?? ''; final value = await storage.read(key: Token.loginAccessTokenKey) ?? '';
if (value.isEmpty) { if (value.isEmpty) {
return 'Token not found'; return 'Token not found';
@ -402,9 +394,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
final String formattedTime = [ final String formattedTime = [
if (days > 0) '${days}d', // Append 'd' for days if (days > 0) '${days}d', // Append 'd' for days
if (days > 0 || hours > 0) if (days > 0 || hours > 0)
hours hours.toString().padLeft(2, '0'), // Show hours if there are days or hours
.toString()
.padLeft(2, '0'), // Show hours if there are days or hours
minutes.toString().padLeft(2, '0'), minutes.toString().padLeft(2, '0'),
seconds.toString().padLeft(2, '0'), seconds.toString().padLeft(2, '0'),
].join(':'); ].join(':');
@ -445,6 +435,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
static Future<void> logout(BuildContext context) async { static Future<void> logout(BuildContext context) async {
final storage = FlutterSecureStorage(); final storage = FlutterSecureStorage();
ProjectManager.clearProjectUUID(); ProjectManager.clearProjectUUID();
context.read<SpaceTreeBloc>().add(ClearAllData());
storage.deleteAll(); storage.deleteAll();
} }
} }

View File

@ -8,6 +8,8 @@ class ForgetPasswordPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const ResponsiveLayout( return const ResponsiveLayout(
desktopBody: ForgetPasswordWebPage(), mobileBody: ForgetPasswordWebPage()); tablet: ForgetPasswordWebPage(),
desktopBody: ForgetPasswordWebPage(),
mobileBody: ForgetPasswordWebPage());
} }
} }

View File

@ -9,6 +9,8 @@ class LoginPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const ResponsiveLayout( return const ResponsiveLayout(
desktopBody: LoginWebPage(), mobileBody: LoginWebPage()); tablet: LoginWebPage(),
desktopBody: LoginWebPage(),
mobileBody: LoginWebPage());
} }
} }

View File

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
@ -19,6 +20,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
on<AcControlEvent>(_onAcControl); on<AcControlEvent>(_onAcControl);
on<AcBatchControlEvent>(_onAcBatchControl); on<AcBatchControlEvent>(_onAcBatchControl);
on<AcFactoryResetEvent>(_onFactoryReset); on<AcFactoryResetEvent>(_onFactoryReset);
on<AcStatusUpdated>(_onAcStatusUpdated);
} }
FutureOr<void> _onFetchAcStatus( FutureOr<void> _onFetchAcStatus(
@ -28,12 +30,64 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
final status = final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status); deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(ACStatusLoaded(deviceStatus)); emit(ACStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(AcsFailedState(error: e.toString())); emit(AcsFailedState(error: e.toString()));
} }
} }
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) async {
if (event.snapshot.value == null) return;
if (_timer != null) {
await Future.delayed(const Duration(seconds: 1));
}
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
AcStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(AcStatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onAcStatusUpdated(AcStatusUpdated event, Emitter<AcsState> emit) {
deviceStatus = event.deviceStatus;
emit(ACStatusLoaded(deviceStatus));
}
// Future<void> testFirebaseConnection() async {
// // Reference to a test node in your database
// final testRef = FirebaseDatabase.instance.ref("test");
// // Write a test value
// await testRef.set("Hello, Firebase!");
// // Listen for changes on the test node
// testRef.onValue.listen((DatabaseEvent event) {
// final data = event.snapshot.value;
// print("Data from Firebase: $data");
// // If you see "Hello, Firebase!" printed in your console, it means the connection works.
// });
// }
FutureOr<void> _onAcControl( FutureOr<void> _onAcControl(
AcControlEvent event, Emitter<AcsState> emit) async { AcControlEvent event, Emitter<AcsState> emit) async {
final oldValue = _getValueByCode(event.code); final oldValue = _getValueByCode(event.code);

View File

@ -1,4 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
sealed class AcsEvent extends Equatable { sealed class AcsEvent extends Equatable {
@ -7,6 +8,7 @@ sealed class AcsEvent extends Equatable {
@override @override
List<Object> get props => []; List<Object> get props => [];
} }
class AcUpdated extends AcsEvent {}
class AcFetchDeviceStatusEvent extends AcsEvent { class AcFetchDeviceStatusEvent extends AcsEvent {
final String deviceId; final String deviceId;
@ -16,7 +18,10 @@ class AcFetchDeviceStatusEvent extends AcsEvent {
@override @override
List<Object> get props => [deviceId]; List<Object> get props => [deviceId];
} }
class AcStatusUpdated extends AcsEvent {
final AcStatusModel deviceStatus;
AcStatusUpdated(this.deviceStatus);
}
class AcFetchBatchStatusEvent extends AcsEvent { class AcFetchBatchStatusEvent extends AcsEvent {
final List<String> devicesIds; final List<String> devicesIds;

View File

@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/routines/view/routines_view.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart';
class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout { class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
@ -25,13 +26,12 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
), ),
], ],
child: WebScaffold( child: WebScaffold(
appBarTitle: FittedBox( appBarTitle: Text(
child: Text( 'Device Management',
'Device Management', style: ResponsiveTextTheme.of(context).deviceManagementTitle,
style: Theme.of(context).textTheme.headlineLarge,
),
), ),
centerBody: BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) { centerBody:
BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) {
return Row( return Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@ -48,8 +48,11 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
child: Text( child: Text(
'Devices', 'Devices',
style: context.textTheme.titleMedium?.copyWith( style: context.textTheme.titleMedium?.copyWith(
color: !state.routineTab ? ColorsManager.whiteColors : ColorsManager.grayColor, color: !state.routineTab
fontWeight: !state.routineTab ? FontWeight.w700 : FontWeight.w400, ? ColorsManager.whiteColors
: ColorsManager.grayColor,
fontWeight:
!state.routineTab ? FontWeight.w700 : FontWeight.w400,
), ),
), ),
), ),
@ -58,13 +61,18 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
backgroundColor: null, backgroundColor: null,
), ),
onPressed: () { onPressed: () {
context.read<RoutineBloc>().add(const TriggerSwitchTabsEvent(isRoutineTab: true)); context
.read<RoutineBloc>()
.add(const TriggerSwitchTabsEvent(isRoutineTab: true));
}, },
child: Text( child: Text(
'Routines', 'Routines',
style: context.textTheme.titleMedium?.copyWith( style: context.textTheme.titleMedium?.copyWith(
color: state.routineTab ? ColorsManager.whiteColors : ColorsManager.grayColor, color: state.routineTab
fontWeight: state.routineTab ? FontWeight.w700 : FontWeight.w400, ? ColorsManager.whiteColors
: ColorsManager.grayColor,
fontWeight:
state.routineTab ? FontWeight.w700 : FontWeight.w400,
), ),
), ),
), ),
@ -72,7 +80,8 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
); );
}), }),
rightBody: const NavigateHomeGridView(), rightBody: const NavigateHomeGridView(),
scaffoldBody: BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) { scaffoldBody:
BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) {
if (state.routineTab) { if (state.routineTab) {
return const RoutinesView(); return const RoutinesView();
} }
@ -87,7 +96,8 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
} else if (deviceState is DeviceManagementLoaded) { } else if (deviceState is DeviceManagementLoaded) {
return DeviceManagementBody(devices: deviceState.devices); return DeviceManagementBody(devices: deviceState.devices);
} else if (deviceState is DeviceManagementFiltered) { } else if (deviceState is DeviceManagementFiltered) {
return DeviceManagementBody(devices: deviceState.filteredDevices); return DeviceManagementBody(
devices: deviceState.filteredDevices);
} else { } else {
return const Center(child: Text('Error fetching Devices')); return const Center(child: Text('Error fetching Devices'));
} }

View File

@ -8,7 +8,6 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_search_filters.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_search_filters.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_batch_control_dialog.dart'; import 'package:syncrow_web/pages/device_managment/shared/device_batch_control_dialog.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_control_dialog.dart'; import 'package:syncrow_web/pages/device_managment/shared/device_control_dialog.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart'; import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
import 'package:syncrow_web/utils/format_date_time.dart'; import 'package:syncrow_web/utils/format_date_time.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -95,7 +94,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
const DeviceSearchFilters(), const DeviceSearchFilters(),
const SizedBox(height: 12), const SizedBox(height: 12),
Container( Container(
height: 45, // height: 45,
width: 125, width: 125,
decoration: containerDecoration, decoration: containerDecoration,
child: Center( child: Center(

View File

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_event.dart'; import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_event.dart';
@ -22,42 +23,55 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
on<ShowCeilingDescriptionEvent>(_showDescription); on<ShowCeilingDescriptionEvent>(_showDescription);
on<BackToCeilingGridViewEvent>(_backToGridView); on<BackToCeilingGridViewEvent>(_backToGridView);
on<CeilingFactoryResetEvent>(_onFactoryReset); on<CeilingFactoryResetEvent>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
void _fetchCeilingSensorStatus( void _fetchCeilingSensorStatus(
CeilingInitialEvent event, Emitter<CeilingSensorState> emit) async { CeilingInitialEvent event, Emitter<CeilingSensorState> emit) async {
emit(CeilingLoadingInitialState()); emit(CeilingLoadingInitialState());
try { try {
var response = await DevicesManagementApi().getDeviceStatus(event.deviceId); var response =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = CeilingSensorModel.fromJson(response.status); deviceStatus = CeilingSensorModel.fromJson(response.status);
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus)); emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
// _listenToChanges(); _listenToChanges(event.deviceId);
} catch (e) { } catch (e) {
emit(CeilingFailedState(error: e.toString())); emit(CeilingFailedState(error: e.toString()));
return; return;
} }
} }
// _listenToChanges() { _listenToChanges(deviceId) {
// try { try {
// DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); DatabaseReference ref =
// Stream<DatabaseEvent> stream = ref.onValue; FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
// stream.listen((DatabaseEvent event) { stream.listen((DatabaseEvent event) {
// Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>; Map<dynamic, dynamic> usersMap =
// List<StatusModel> statusList = []; event.snapshot.value as Map<dynamic, dynamic>;
// usersMap['status'].forEach((element) { List<Status> statusList = [];
// statusList.add(StatusModel(code: element['code'], value: element['value'])); usersMap['status'].forEach((element) {
// }); statusList
.add(Status(code: element['code'], value: element['value']));
});
// deviceStatus = WallSensorModel.fromJson(statusList); deviceStatus = CeilingSensorModel.fromJson(statusList);
// add(WallSensorUpdatedEvent()); if (!isClosed) {
// }); add(StatusUpdated(deviceStatus));
// } catch (_) {} }
// } });
} catch (_) {}
}
void _changeValue(CeilingChangeValueEvent event, Emitter<CeilingSensorState> emit) async { void _onStatusUpdated(StatusUpdated event, Emitter<CeilingSensorState> emit) {
deviceStatus = event.deviceStatus;
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
}
void _changeValue(
CeilingChangeValueEvent event, Emitter<CeilingSensorState> emit) async {
emit(CeilingLoadingNewSate(ceilingSensorModel: deviceStatus)); emit(CeilingLoadingNewSate(ceilingSensorModel: deviceStatus));
if (event.code == 'sensitivity') { if (event.code == 'sensitivity') {
deviceStatus.sensitivity = event.value; deviceStatus.sensitivity = event.value;
@ -122,7 +136,8 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
try { try {
late bool response; late bool response;
if (isBatch) { if (isBatch) {
response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value); response = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else { } else {
response = await DevicesManagementApi() response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value)); .deviceControl(deviceId, Status(code: code, value: value));
@ -143,8 +158,8 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
}); });
} }
FutureOr<void> _getDeviceReports( FutureOr<void> _getDeviceReports(GetCeilingDeviceReportsEvent event,
GetCeilingDeviceReportsEvent event, Emitter<CeilingSensorState> emit) async { Emitter<CeilingSensorState> emit) async {
if (event.code.isEmpty) { if (event.code.isEmpty) {
emit(ShowCeilingDescriptionState(description: reportString)); emit(ShowCeilingDescriptionState(description: reportString));
return; return;
@ -155,7 +170,8 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
try { try {
// await DevicesManagementApi.getDeviceReportsByDate(deviceId, event.code, from.toString(), to.toString()) // await DevicesManagementApi.getDeviceReportsByDate(deviceId, event.code, from.toString(), to.toString())
await DevicesManagementApi.getDeviceReports(deviceId, event.code).then((value) { await DevicesManagementApi.getDeviceReports(deviceId, event.code)
.then((value) {
emit(CeilingReportsState(deviceReport: value)); emit(CeilingReportsState(deviceReport: value));
}); });
} catch (e) { } catch (e) {
@ -165,19 +181,23 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
} }
} }
void _showDescription(ShowCeilingDescriptionEvent event, Emitter<CeilingSensorState> emit) { void _showDescription(
ShowCeilingDescriptionEvent event, Emitter<CeilingSensorState> emit) {
emit(ShowCeilingDescriptionState(description: event.description)); emit(ShowCeilingDescriptionState(description: event.description));
} }
void _backToGridView(BackToCeilingGridViewEvent event, Emitter<CeilingSensorState> emit) { void _backToGridView(
BackToCeilingGridViewEvent event, Emitter<CeilingSensorState> emit) {
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus)); emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
} }
FutureOr<void> _fetchCeilingSensorBatchControl( FutureOr<void> _fetchCeilingSensorBatchControl(
CeilingFetchDeviceStatusEvent event, Emitter<CeilingSensorState> emit) async { CeilingFetchDeviceStatusEvent event,
Emitter<CeilingSensorState> emit) async {
emit(CeilingLoadingInitialState()); emit(CeilingLoadingInitialState());
try { try {
var response = await DevicesManagementApi().getBatchStatus(event.devicesIds); var response =
await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus = CeilingSensorModel.fromJson(response.status); deviceStatus = CeilingSensorModel.fromJson(response.status);
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus)); emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
} catch (e) { } catch (e) {

View File

@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart';
abstract class CeilingSensorEvent extends Equatable { abstract class CeilingSensorEvent extends Equatable {
const CeilingSensorEvent(); const CeilingSensorEvent();
@ -83,3 +84,12 @@ class CeilingFactoryResetEvent extends CeilingSensorEvent {
@override @override
List<Object> get props => [devicesId, factoryResetModel]; List<Object> get props => [devicesId, factoryResetModel];
} }
class StatusUpdated extends CeilingSensorEvent {
final CeilingSensorModel deviceStatus;
const StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart';
@ -16,6 +17,7 @@ class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
on<CurtainControl>(_onCurtainControl); on<CurtainControl>(_onCurtainControl);
on<CurtainBatchControl>(_onCurtainBatchControl); on<CurtainBatchControl>(_onCurtainBatchControl);
on<CurtainFactoryReset>(_onFactoryReset); on<CurtainFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
FutureOr<void> _onFetchDeviceStatus( FutureOr<void> _onFetchDeviceStatus(
@ -24,7 +26,7 @@ class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
try { try {
final status = final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
_listenToChanges(event.deviceId);
deviceStatus = _checkStatus(status.status[0].value); deviceStatus = _checkStatus(status.status[0].value);
emit(CurtainStatusLoaded(deviceStatus)); emit(CurtainStatusLoaded(deviceStatus));
@ -33,6 +35,48 @@ class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
} }
} }
void _listenToChanges(String deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
final data = event.snapshot.value as Map<dynamic, dynamic>?;
if (data == null) return;
List<Status> statusList = [];
if (data['status'] != null) {
for (var element in data['status']) {
statusList.add(
Status(
code: element['code'].toString(),
value: element['value'].toString(),
),
);
}
}
if (statusList.isNotEmpty) {
bool newStatus = _checkStatus(statusList[0].value);
if (newStatus != deviceStatus) {
deviceStatus = newStatus;
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
}
}
});
} catch (e) {
emit(CurtainError('Failed to listen to changes: $e'));
}
}
void _onStatusUpdated(StatusUpdated event, Emitter<CurtainState> emit) {
emit(CurtainStatusLoading());
deviceStatus = event.deviceStatus;
emit(CurtainStatusLoaded(deviceStatus));
}
FutureOr<void> _onCurtainControl( FutureOr<void> _onCurtainControl(
CurtainControl event, Emitter<CurtainState> emit) async { CurtainControl event, Emitter<CurtainState> emit) async {
final oldValue = deviceStatus; final oldValue = deviceStatus;

View File

@ -60,3 +60,7 @@ class CurtainFactoryReset extends CurtainEvent {
@override @override
List<Object> get props => [deviceId, factoryReset]; List<Object> get props => [deviceId, factoryReset];
} }
class StatusUpdated extends CurtainEvent {
final bool deviceStatus;
const StatusUpdated(this.deviceStatus);
}

View File

@ -1,6 +1,7 @@
// ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: invalid_use_of_visible_for_testing_member
import 'dart:async'; import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/bloc/door_lock_event.dart'; import 'package:syncrow_web/pages/device_managment/door_lock/bloc/door_lock_event.dart';
@ -18,6 +19,39 @@ class DoorLockBloc extends Bloc<DoorLockEvent, DoorLockState> {
//on<DoorLockControl>(_onDoorLockControl); //on<DoorLockControl>(_onDoorLockControl);
on<UpdateLockEvent>(_updateLock); on<UpdateLockEvent>(_updateLock);
on<DoorLockFactoryReset>(_onFactoryReset); on<DoorLockFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
}
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
DoorLockStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(StatusUpdated event, Emitter<DoorLockState> emit) {
emit(DoorLockStatusLoading());
deviceStatus = event.deviceStatus;
emit(DoorLockStatusLoaded(deviceStatus));
} }
FutureOr<void> _onFetchDeviceStatus( FutureOr<void> _onFetchDeviceStatus(
@ -28,6 +62,8 @@ class DoorLockBloc extends Bloc<DoorLockEvent, DoorLockState> {
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = deviceStatus =
DoorLockStatusModel.fromJson(event.deviceId, status.status); DoorLockStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(DoorLockStatusLoaded(deviceStatus)); emit(DoorLockStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(DoorLockControlError(e.toString())); emit(DoorLockControlError(e.toString()));

View File

@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/models/door_lock_status_model.dart';
sealed class DoorLockEvent extends Equatable { sealed class DoorLockEvent extends Equatable {
const DoorLockEvent(); const DoorLockEvent();
@ -51,3 +52,10 @@ class DoorLockFactoryReset extends DoorLockEvent {
@override @override
List<Object> get props => [deviceId, factoryReset]; List<Object> get props => [deviceId, factoryReset];
} }
class StatusUpdated extends DoorLockEvent {
final DoorLockStatusModel deviceStatus;
const StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_event.dart'; import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_event.dart';
@ -39,31 +40,68 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
on<GarageDoorFetchBatchStatusEvent>(_onFetchBatchStatus); on<GarageDoorFetchBatchStatusEvent>(_onFetchBatchStatus);
on<GarageDoorFactoryResetEvent>(_onFactoryReset); on<GarageDoorFactoryResetEvent>(_onFactoryReset);
on<EditGarageDoorScheduleEvent>(_onEditSchedule); on<EditGarageDoorScheduleEvent>(_onEditSchedule);
on<StatusUpdated>(_onStatusUpdated);
}
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
GarageDoorStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
} }
void _fetchGarageDoorStatus(GarageDoorInitialEvent event, Emitter<GarageDoorState> emit) async { void _onStatusUpdated(StatusUpdated event, Emitter<GarageDoorState> emit) {
deviceStatus = event.deviceStatus;
emit(GarageDoorLoadedState(status: deviceStatus));
}
void _fetchGarageDoorStatus(
GarageDoorInitialEvent event, Emitter<GarageDoorState> emit) async {
emit(GarageDoorLoadingState()); emit(GarageDoorLoadingState());
try { try {
var response = await DevicesManagementApi().getDeviceStatus(event.deviceId); var response =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = GarageDoorStatusModel.fromJson(deviceId, response.status); deviceStatus = GarageDoorStatusModel.fromJson(deviceId, response.status);
_listenToChanges(deviceId);
emit(GarageDoorLoadedState(status: deviceStatus)); emit(GarageDoorLoadedState(status: deviceStatus));
} catch (e) { } catch (e) {
emit(GarageDoorErrorState(message: e.toString())); emit(GarageDoorErrorState(message: e.toString()));
} }
} }
Future<void> _onFetchBatchStatus(GarageDoorFetchBatchStatusEvent event, Emitter<GarageDoorState> emit) async { Future<void> _onFetchBatchStatus(GarageDoorFetchBatchStatusEvent event,
Emitter<GarageDoorState> emit) async {
emit(GarageDoorLoadingState()); emit(GarageDoorLoadingState());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds); final status =
deviceStatus = GarageDoorStatusModel.fromJson(event.deviceIds.first, status.status); await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus =
GarageDoorStatusModel.fromJson(event.deviceIds.first, status.status);
emit(GarageDoorBatchStatusLoaded(deviceStatus)); emit(GarageDoorBatchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(GarageDoorBatchControlError(e.toString())); emit(GarageDoorBatchControlError(e.toString()));
} }
} }
Future<void> _addSchedule(AddGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async { Future<void> _addSchedule(
AddGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async {
try { try {
ScheduleEntry newSchedule = ScheduleEntry( ScheduleEntry newSchedule = ScheduleEntry(
category: event.category, category: event.category,
@ -71,9 +109,11 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
function: Status(code: 'switch_1', value: event.functionOn), function: Status(code: 'switch_1', value: event.functionOn),
days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays), days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays),
); );
bool success = await DevicesManagementApi().addScheduleRecord(newSchedule, deviceId); bool success =
await DevicesManagementApi().addScheduleRecord(newSchedule, deviceId);
if (success) { if (success) {
add(FetchGarageDoorSchedulesEvent(deviceId: deviceId, category: 'switch_1')); add(FetchGarageDoorSchedulesEvent(
deviceId: deviceId, category: 'switch_1'));
} else { } else {
emit(GarageDoorLoadedState(status: deviceStatus)); emit(GarageDoorLoadedState(status: deviceStatus));
} }
@ -82,16 +122,19 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
} }
} }
void _onUpdateCountdownAlarm(UpdateCountdownAlarmEvent event, Emitter<GarageDoorState> emit) { void _onUpdateCountdownAlarm(
UpdateCountdownAlarmEvent event, Emitter<GarageDoorState> emit) {
final currentState = state; final currentState = state;
if (currentState is GarageDoorLoadedState) { if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith( emit(currentState.copyWith(
status: currentState.status.copyWith(countdownAlarm: event.countdownAlarm), status:
currentState.status.copyWith(countdownAlarm: event.countdownAlarm),
)); ));
} }
} }
void _onUpdateTrTimeCon(UpdateTrTimeConEvent event, Emitter<GarageDoorState> emit) { void _onUpdateTrTimeCon(
UpdateTrTimeConEvent event, Emitter<GarageDoorState> emit) {
final currentState = state; final currentState = state;
if (currentState is GarageDoorLoadedState) { if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith( emit(currentState.copyWith(
@ -100,7 +143,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
} }
} }
Future<void> _updateSchedule(UpdateGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async { Future<void> _updateSchedule(UpdateGarageDoorScheduleEvent event,
Emitter<GarageDoorState> emit) async {
try { try {
final updatedSchedules = deviceStatus.schedules?.map((schedule) { final updatedSchedules = deviceStatus.schedules?.map((schedule) {
if (schedule.scheduleId == event.scheduleId) { if (schedule.scheduleId == event.scheduleId) {
@ -127,12 +171,15 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
} }
} }
Future<void> _deleteSchedule(DeleteGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async { Future<void> _deleteSchedule(DeleteGarageDoorScheduleEvent event,
Emitter<GarageDoorState> emit) async {
try { try {
bool success = await DevicesManagementApi().deleteScheduleRecord(deviceStatus.uuid, event.scheduleId); bool success = await DevicesManagementApi()
.deleteScheduleRecord(deviceStatus.uuid, event.scheduleId);
if (success) { if (success) {
final updatedSchedules = final updatedSchedules = deviceStatus.schedules
deviceStatus.schedules?.where((schedule) => schedule.scheduleId != event.scheduleId).toList(); ?.where((schedule) => schedule.scheduleId != event.scheduleId)
.toList();
deviceStatus = deviceStatus.copyWith(schedules: updatedSchedules); deviceStatus = deviceStatus.copyWith(schedules: updatedSchedules);
emit(GarageDoorLoadedState(status: deviceStatus)); emit(GarageDoorLoadedState(status: deviceStatus));
} else { } else {
@ -143,11 +190,12 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
} }
} }
Future<void> _fetchSchedules(FetchGarageDoorSchedulesEvent event, Emitter<GarageDoorState> emit) async { Future<void> _fetchSchedules(FetchGarageDoorSchedulesEvent event,
Emitter<GarageDoorState> emit) async {
emit(ScheduleGarageLoadingState()); emit(ScheduleGarageLoadingState());
try { try {
List<ScheduleModel> schedules = List<ScheduleModel> schedules = await DevicesManagementApi()
await DevicesManagementApi().getDeviceSchedules(deviceStatus.uuid, event.category); .getDeviceSchedules(deviceStatus.uuid, event.category);
deviceStatus = deviceStatus.copyWith(schedules: schedules); deviceStatus = deviceStatus.copyWith(schedules: schedules);
emit( emit(
GarageDoorLoadedState( GarageDoorLoadedState(
@ -165,30 +213,37 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
} }
} }
Future<void> _updateSelectedTime(UpdateSelectedTimeEvent event, Emitter<GarageDoorState> emit) async { Future<void> _updateSelectedTime(
UpdateSelectedTimeEvent event, Emitter<GarageDoorState> emit) async {
final currentState = state; final currentState = state;
if (currentState is GarageDoorLoadedState) { if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith(selectedTime: event.selectedTime)); emit(currentState.copyWith(selectedTime: event.selectedTime));
} }
} }
Future<void> _updateSelectedDay(UpdateSelectedDayEvent event, Emitter<GarageDoorState> emit) async { Future<void> _updateSelectedDay(
UpdateSelectedDayEvent event, Emitter<GarageDoorState> emit) async {
final currentState = state; final currentState = state;
if (currentState is GarageDoorLoadedState) { if (currentState is GarageDoorLoadedState) {
List<bool> updatedDays = List.from(currentState.selectedDays); List<bool> updatedDays = List.from(currentState.selectedDays);
updatedDays[event.dayIndex] = event.isSelected; updatedDays[event.dayIndex] = event.isSelected;
emit(currentState.copyWith(selectedDays: updatedDays, selectedTime: currentState.selectedTime)); emit(currentState.copyWith(
selectedDays: updatedDays, selectedTime: currentState.selectedTime));
} }
} }
Future<void> _updateFunctionOn(UpdateFunctionOnEvent event, Emitter<GarageDoorState> emit) async { Future<void> _updateFunctionOn(
UpdateFunctionOnEvent event, Emitter<GarageDoorState> emit) async {
final currentState = state; final currentState = state;
if (currentState is GarageDoorLoadedState) { if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith(functionOn: event.functionOn, selectedTime: currentState.selectedTime)); emit(currentState.copyWith(
functionOn: event.functionOn,
selectedTime: currentState.selectedTime));
} }
} }
Future<void> _initializeAddSchedule(InitializeAddScheduleEvent event, Emitter<GarageDoorState> emit) async { Future<void> _initializeAddSchedule(
InitializeAddScheduleEvent event, Emitter<GarageDoorState> emit) async {
final currentState = state; final currentState = state;
if (currentState is GarageDoorLoadedState) { if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith( emit(currentState.copyWith(
@ -200,20 +255,25 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
} }
} }
Future<void> _fetchRecords(FetchGarageDoorRecordsEvent event, Emitter<GarageDoorState> emit) async { Future<void> _fetchRecords(
FetchGarageDoorRecordsEvent event, Emitter<GarageDoorState> emit) async {
emit(GarageDoorReportsLoadingState()); emit(GarageDoorReportsLoadingState());
try { try {
final from = DateTime.now().subtract(const Duration(days: 30)).millisecondsSinceEpoch; final from = DateTime.now()
.subtract(const Duration(days: 30))
.millisecondsSinceEpoch;
final to = DateTime.now().millisecondsSinceEpoch; final to = DateTime.now().millisecondsSinceEpoch;
final DeviceReport records = final DeviceReport records =
await DevicesManagementApi.getDeviceReportsByDate(event.deviceId, 'switch_1', from.toString(), to.toString()); await DevicesManagementApi.getDeviceReportsByDate(
event.deviceId, 'switch_1', from.toString(), to.toString());
emit(GarageDoorReportsState(deviceReport: records)); emit(GarageDoorReportsState(deviceReport: records));
} catch (e) { } catch (e) {
emit(GarageDoorReportsFailedState(error: e.toString())); emit(GarageDoorReportsFailedState(error: e.toString()));
} }
} }
Future<void> _onBatchControl(GarageDoorBatchControlEvent event, Emitter<GarageDoorState> emit) async { Future<void> _onBatchControl(
GarageDoorBatchControlEvent event, Emitter<GarageDoorState> emit) async {
final oldValue = event.code == 'switch_1' ? deviceStatus.switch1 : false; final oldValue = event.code == 'switch_1' ? deviceStatus.switch1 : false;
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
@ -233,11 +293,13 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
} }
} }
void _backToGridView(BackToGarageDoorGridViewEvent event, Emitter<GarageDoorState> emit) { void _backToGridView(
BackToGarageDoorGridViewEvent event, Emitter<GarageDoorState> emit) {
emit(GarageDoorLoadedState(status: deviceStatus)); emit(GarageDoorLoadedState(status: deviceStatus));
} }
void _handleUpdate(GarageDoorUpdatedEvent event, Emitter<GarageDoorState> emit) { void _handleUpdate(
GarageDoorUpdatedEvent event, Emitter<GarageDoorState> emit) {
emit(GarageDoorLoadedState(status: deviceStatus)); emit(GarageDoorLoadedState(status: deviceStatus));
} }
@ -253,9 +315,11 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
late bool status; late bool status;
await Future.delayed(const Duration(milliseconds: 500)); await Future.delayed(const Duration(milliseconds: 500));
if (isBatch) { if (isBatch) {
status = await DevicesManagementApi().deviceBatchControl(deviceId, code, value); status = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else { } else {
status = await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value)); status = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
} }
if (!status) { if (!status) {
@ -270,10 +334,12 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
} }
} }
Future<void> _onFactoryReset(GarageDoorFactoryResetEvent event, Emitter<GarageDoorState> emit) async { Future<void> _onFactoryReset(
GarageDoorFactoryResetEvent event, Emitter<GarageDoorState> emit) async {
emit(GarageDoorLoadingState()); emit(GarageDoorLoadingState());
try { try {
final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId); final response = await DevicesManagementApi()
.factoryReset(event.factoryReset, event.deviceId);
if (!response) { if (!response) {
emit(const GarageDoorErrorState(message: 'Failed to reset device')); emit(const GarageDoorErrorState(message: 'Failed to reset device'));
} else { } else {
@ -284,34 +350,47 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
} }
} }
void _increaseDelay(IncreaseGarageDoorDelayEvent event, Emitter<GarageDoorState> emit) async { void _increaseDelay(
IncreaseGarageDoorDelayEvent event, Emitter<GarageDoorState> emit) async {
// if (deviceStatus.countdown1 != 0) { // if (deviceStatus.countdown1 != 0) {
try { try {
deviceStatus = deviceStatus.copyWith(delay: deviceStatus.delay + Duration(minutes: 10)); deviceStatus = deviceStatus.copyWith(
delay: deviceStatus.delay + Duration(minutes: 10));
emit(GarageDoorLoadedState(status: deviceStatus)); emit(GarageDoorLoadedState(status: deviceStatus));
add(GarageDoorControlEvent(deviceId: event.deviceId, value: deviceStatus.delay.inSeconds, code: 'countdown_1')); add(GarageDoorControlEvent(
deviceId: event.deviceId,
value: deviceStatus.delay.inSeconds,
code: 'countdown_1'));
} catch (e) { } catch (e) {
emit(GarageDoorErrorState(message: e.toString())); emit(GarageDoorErrorState(message: e.toString()));
} }
// } // }
} }
void _decreaseDelay(DecreaseGarageDoorDelayEvent event, Emitter<GarageDoorState> emit) async { void _decreaseDelay(
DecreaseGarageDoorDelayEvent event, Emitter<GarageDoorState> emit) async {
// if (deviceStatus.countdown1 != 0) { // if (deviceStatus.countdown1 != 0) {
try { try {
if (deviceStatus.delay.inMinutes > 10) { if (deviceStatus.delay.inMinutes > 10) {
deviceStatus = deviceStatus.copyWith(delay: deviceStatus.delay - Duration(minutes: 10)); deviceStatus = deviceStatus.copyWith(
delay: deviceStatus.delay - Duration(minutes: 10));
} }
emit(GarageDoorLoadedState(status: deviceStatus)); emit(GarageDoorLoadedState(status: deviceStatus));
add(GarageDoorControlEvent(deviceId: event.deviceId, value: deviceStatus.delay.inSeconds, code: 'countdown_1')); add(GarageDoorControlEvent(
deviceId: event.deviceId,
value: deviceStatus.delay.inSeconds,
code: 'countdown_1'));
} catch (e) { } catch (e) {
emit(GarageDoorErrorState(message: e.toString())); emit(GarageDoorErrorState(message: e.toString()));
} }
//} //}
} }
void _garageDoorControlEvent(GarageDoorControlEvent event, Emitter<GarageDoorState> emit) async { void _garageDoorControlEvent(
final oldValue = event.code == 'countdown_1' ? deviceStatus.countdown1 : deviceStatus.switch1; GarageDoorControlEvent event, Emitter<GarageDoorState> emit) async {
final oldValue = event.code == 'countdown_1'
? deviceStatus.countdown1
: deviceStatus.switch1;
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
emit(GarageDoorLoadedState(status: deviceStatus)); emit(GarageDoorLoadedState(status: deviceStatus));
final success = await _runDeBouncer( final success = await _runDeBouncer(
@ -327,7 +406,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
} }
} }
void _revertValue(String code, dynamic oldValue, Emitter<GarageDoorState> emit) { void _revertValue(
String code, dynamic oldValue, Emitter<GarageDoorState> emit) {
switch (code) { switch (code) {
case 'switch_1': case 'switch_1':
if (oldValue is bool) { if (oldValue is bool) {
@ -336,7 +416,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
break; break;
case 'countdown_1': case 'countdown_1':
if (oldValue is int) { if (oldValue is int) {
deviceStatus = deviceStatus.copyWith(countdown1: oldValue, delay: Duration(seconds: oldValue)); deviceStatus = deviceStatus.copyWith(
countdown1: oldValue, delay: Duration(seconds: oldValue));
} }
break; break;
// Add other cases if needed // Add other cases if needed
@ -358,7 +439,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
break; break;
case 'countdown_1': case 'countdown_1':
if (value is int) { if (value is int) {
deviceStatus = deviceStatus.copyWith(countdown1: value, delay: Duration(seconds: value)); deviceStatus = deviceStatus.copyWith(
countdown1: value, delay: Duration(seconds: value));
} }
break; break;
case 'countdown_alarm': case 'countdown_alarm':
@ -401,7 +483,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
return super.close(); return super.close();
} }
FutureOr<void> _onEditSchedule(EditGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async { FutureOr<void> _onEditSchedule(
EditGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async {
try { try {
ScheduleEntry newSchedule = ScheduleEntry( ScheduleEntry newSchedule = ScheduleEntry(
scheduleId: event.scheduleId, scheduleId: event.scheduleId,
@ -410,9 +493,11 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
function: Status(code: 'switch_1', value: event.functionOn), function: Status(code: 'switch_1', value: event.functionOn),
days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays), days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays),
); );
bool success = await DevicesManagementApi().editScheduleRecord(deviceId, newSchedule); bool success = await DevicesManagementApi()
.editScheduleRecord(deviceId, newSchedule);
if (success) { if (success) {
add(FetchGarageDoorSchedulesEvent(deviceId: deviceId, category: 'switch_1')); add(FetchGarageDoorSchedulesEvent(
deviceId: deviceId, category: 'switch_1'));
} else { } else {
emit(GarageDoorLoadedState(status: deviceStatus)); emit(GarageDoorLoadedState(status: deviceStatus));
} }

View File

@ -3,6 +3,7 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/garage_door/models/garage_door_model.dart';
abstract class GarageDoorEvent extends Equatable { abstract class GarageDoorEvent extends Equatable {
const GarageDoorEvent(); const GarageDoorEvent();
@ -25,7 +26,8 @@ class GarageDoorControlEvent extends GarageDoorEvent {
final dynamic value; final dynamic value;
final String code; final String code;
const GarageDoorControlEvent({required this.deviceId, required this.value, required this.code}); const GarageDoorControlEvent(
{required this.deviceId, required this.value, required this.code});
@override @override
List<Object?> get props => [deviceId, value]; List<Object?> get props => [deviceId, value];
@ -121,7 +123,8 @@ class FetchGarageDoorRecordsEvent extends GarageDoorEvent {
final String deviceId; final String deviceId;
final String code; final String code;
const FetchGarageDoorRecordsEvent({required this.deviceId, required this.code}); const FetchGarageDoorRecordsEvent(
{required this.deviceId, required this.code});
@override @override
List<Object?> get props => [deviceId, code]; List<Object?> get props => [deviceId, code];
@ -232,3 +235,10 @@ class GarageDoorFactoryResetEvent extends GarageDoorEvent {
@override @override
List<Object?> get props => [factoryReset, deviceId]; List<Object?> get props => [factoryReset, deviceId];
} }
class StatusUpdated extends GarageDoorEvent {
final GarageDoorStatusModel deviceStatus;
const StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
@ -16,6 +17,7 @@ class MainDoorSensorBloc
on<MainDoorSensorFetchBatchEvent>(_onFetchBatchStatus); on<MainDoorSensorFetchBatchEvent>(_onFetchBatchStatus);
on<MainDoorSensorReportsEvent>(_fetchReports); on<MainDoorSensorReportsEvent>(_fetchReports);
on<MainDoorSensorFactoryReset>(_factoryReset); on<MainDoorSensorFactoryReset>(_factoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
late MainDoorSensorStatusModel deviceStatus; late MainDoorSensorStatusModel deviceStatus;
@ -28,7 +30,7 @@ class MainDoorSensorBloc
final status = await DevicesManagementApi() final status = await DevicesManagementApi()
.getDeviceStatus(event.deviceId) .getDeviceStatus(event.deviceId)
.then((value) => value.status); .then((value) => value.status);
_listenToChanges(event.deviceId);
deviceStatus = MainDoorSensorStatusModel.fromJson(event.deviceId, status); deviceStatus = MainDoorSensorStatusModel.fromJson(event.deviceId, status);
emit(MainDoorSensorDeviceStatusLoaded(deviceStatus)); emit(MainDoorSensorDeviceStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
@ -156,4 +158,35 @@ class MainDoorSensorBloc
emit(MainDoorSensorFailedState(error: e.toString())); emit(MainDoorSensorFailedState(error: e.toString()));
} }
} }
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus = MainDoorSensorStatusModel.fromJson(
usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(
StatusUpdated event, Emitter<MainDoorSensorState> emit) {
deviceStatus = event.deviceStatus;
emit(MainDoorSensorDeviceStatusLoaded(deviceStatus));
}
} }

View File

@ -1,4 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/main_door_sensor/models/main_door_status_model.dart';
import '../../all_devices/models/factory_reset_model.dart'; import '../../all_devices/models/factory_reset_model.dart';
@ -71,3 +72,10 @@ class MainDoorSensorFactoryReset extends MainDoorSensorEvent {
MainDoorSensorFactoryReset( MainDoorSensorFactoryReset(
{required this.deviceId, required this.factoryReset}); {required this.deviceId, required this.factoryReset});
} }
class StatusUpdated extends MainDoorSensorEvent {
final MainDoorSensorStatusModel deviceStatus;
StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
@ -10,33 +11,71 @@ import 'package:syncrow_web/services/devices_mang_api.dart';
part 'one_gang_glass_switch_event.dart'; part 'one_gang_glass_switch_event.dart';
part 'one_gang_glass_switch_state.dart'; part 'one_gang_glass_switch_state.dart';
class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassSwitchState> { class OneGangGlassSwitchBloc
extends Bloc<OneGangGlassSwitchEvent, OneGangGlassSwitchState> {
OneGangGlassStatusModel deviceStatus; OneGangGlassStatusModel deviceStatus;
Timer? _timer; Timer? _timer;
OneGangGlassSwitchBloc({required String deviceId}) OneGangGlassSwitchBloc({required String deviceId})
: deviceStatus = OneGangGlassStatusModel(uuid: deviceId, switch1: false, countDown: 0), : deviceStatus = OneGangGlassStatusModel(
uuid: deviceId, switch1: false, countDown: 0),
super(OneGangGlassSwitchInitial()) { super(OneGangGlassSwitchInitial()) {
on<OneGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus); on<OneGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
on<OneGangGlassSwitchControl>(_onControl); on<OneGangGlassSwitchControl>(_onControl);
on<OneGangGlassSwitchBatchControl>(_onBatchControl); on<OneGangGlassSwitchBatchControl>(_onBatchControl);
on<OneGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus); on<OneGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus);
on<OneGangGlassFactoryResetEvent>(_onFactoryReset); on<OneGangGlassFactoryResetEvent>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchDeviceStatus( Future<void> _onFetchDeviceStatus(OneGangGlassSwitchFetchDeviceEvent event,
OneGangGlassSwitchFetchDeviceEvent event, Emitter<OneGangGlassSwitchState> emit) async { Emitter<OneGangGlassSwitchState> emit) async {
emit(OneGangGlassSwitchLoading()); emit(OneGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
deviceStatus = OneGangGlassStatusModel.fromJson(event.deviceId, status.status); await DevicesManagementApi().getDeviceStatus(event.deviceId);
_listenToChanges(event.deviceId);
deviceStatus =
OneGangGlassStatusModel.fromJson(event.deviceId, status.status);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus)); emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(OneGangGlassSwitchError(e.toString())); emit(OneGangGlassSwitchError(e.toString()));
} }
} }
Future<void> _onControl(OneGangGlassSwitchControl event, Emitter<OneGangGlassSwitchState> emit) async { _listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus = OneGangGlassStatusModel.fromJson(
usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(
StatusUpdated event, Emitter<OneGangGlassSwitchState> emit) {
deviceStatus = event.deviceStatus;
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
}
Future<void> _onControl(OneGangGlassSwitchControl event,
Emitter<OneGangGlassSwitchState> emit) async {
final oldValue = _getValueByCode(event.code); final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
@ -52,10 +91,12 @@ class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassS
); );
} }
Future<void> _onFactoryReset(OneGangGlassFactoryResetEvent event, Emitter<OneGangGlassSwitchState> emit) async { Future<void> _onFactoryReset(OneGangGlassFactoryResetEvent event,
Emitter<OneGangGlassSwitchState> emit) async {
emit(OneGangGlassSwitchLoading()); emit(OneGangGlassSwitchLoading());
try { try {
final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId); final response = await DevicesManagementApi()
.factoryReset(event.factoryReset, event.deviceId);
if (!response) { if (!response) {
emit(OneGangGlassSwitchError('Failed to reset device')); emit(OneGangGlassSwitchError('Failed to reset device'));
} else { } else {
@ -66,7 +107,8 @@ class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassS
} }
} }
Future<void> _onBatchControl(OneGangGlassSwitchBatchControl event, Emitter<OneGangGlassSwitchState> emit) async { Future<void> _onBatchControl(OneGangGlassSwitchBatchControl event,
Emitter<OneGangGlassSwitchState> emit) async {
final oldValue = _getValueByCode(event.code); final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
@ -83,11 +125,14 @@ class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassS
} }
Future<void> _onFetchBatchStatus( Future<void> _onFetchBatchStatus(
OneGangGlassSwitchFetchBatchStatusEvent event, Emitter<OneGangGlassSwitchState> emit) async { OneGangGlassSwitchFetchBatchStatusEvent event,
Emitter<OneGangGlassSwitchState> emit) async {
emit(OneGangGlassSwitchLoading()); emit(OneGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds); final status =
deviceStatus = OneGangGlassStatusModel.fromJson(event.deviceIds.first, status.status); await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus = OneGangGlassStatusModel.fromJson(
event.deviceIds.first, status.status);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus)); emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(OneGangGlassSwitchError(e.toString())); emit(OneGangGlassSwitchError(e.toString()));
@ -117,9 +162,11 @@ class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassS
try { try {
late bool response; late bool response;
if (isBatch) { if (isBatch) {
response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value); response = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else { } else {
response = await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value)); response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
} }
if (!response) { if (!response) {
@ -131,7 +178,8 @@ class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassS
}); });
} }
void _revertValueAndEmit(String deviceId, String code, bool oldValue, Emitter<OneGangGlassSwitchState> emit) { void _revertValueAndEmit(String deviceId, String code, bool oldValue,
Emitter<OneGangGlassSwitchState> emit) {
_updateLocalValue(code, oldValue); _updateLocalValue(code, oldValue);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus)); emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
} }

View File

@ -39,6 +39,11 @@ class OneGangGlassSwitchFetchBatchStatusEvent extends OneGangGlassSwitchEvent {
OneGangGlassSwitchFetchBatchStatusEvent(this.deviceIds); OneGangGlassSwitchFetchBatchStatusEvent(this.deviceIds);
} }
class StatusUpdated extends OneGangGlassSwitchEvent {
final OneGangGlassStatusModel deviceStatus;
StatusUpdated(this.deviceStatus);
}
class OneGangGlassFactoryResetEvent extends OneGangGlassSwitchEvent { class OneGangGlassFactoryResetEvent extends OneGangGlassSwitchEvent {
final FactoryResetModel factoryReset; final FactoryResetModel factoryReset;
final String deviceId; final String deviceId;

View File

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart';
@ -16,6 +17,7 @@ class WallLightSwitchBloc
on<WallLightSwitchFetchBatchEvent>(_onFetchBatchStatus); on<WallLightSwitchFetchBatchEvent>(_onFetchBatchStatus);
on<WallLightSwitchBatchControl>(_onBatchControl); on<WallLightSwitchBatchControl>(_onBatchControl);
on<WallLightFactoryReset>(_onFactoryReset); on<WallLightFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
late WallLightStatusModel deviceStatus; late WallLightStatusModel deviceStatus;
@ -31,12 +33,44 @@ class WallLightSwitchBloc
deviceStatus = deviceStatus =
WallLightStatusModel.fromJson(event.deviceId, status.status); WallLightStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(WallLightSwitchStatusLoaded(deviceStatus)); emit(WallLightSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(WallLightSwitchError(e.toString())); emit(WallLightSwitchError(e.toString()));
} }
} }
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
WallLightStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(
StatusUpdated event, Emitter<WallLightSwitchState> emit) {
deviceStatus = event.deviceStatus;
emit(WallLightSwitchStatusLoaded(deviceStatus));
}
FutureOr<void> _onControl( FutureOr<void> _onControl(
WallLightSwitchControl event, Emitter<WallLightSwitchState> emit) async { WallLightSwitchControl event, Emitter<WallLightSwitchState> emit) async {
final oldValue = _getValueByCode(event.code); final oldValue = _getValueByCode(event.code);

View File

@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/models/wall_light_status_model.dart';
class WallLightSwitchEvent extends Equatable { class WallLightSwitchEvent extends Equatable {
@override @override
@ -57,3 +58,10 @@ class WallLightFactoryReset extends WallLightSwitchEvent {
@override @override
List<Object> get props => [deviceId, factoryReset]; List<Object> get props => [deviceId, factoryReset];
} }
class StatusUpdated extends WallLightSwitchEvent {
final WallLightStatusModel deviceStatus;
StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
@ -10,7 +11,8 @@ import 'package:syncrow_web/services/devices_mang_api.dart';
part 'three_gang_glass_switch_event.dart'; part 'three_gang_glass_switch_event.dart';
part 'three_gang_glass_switch_state.dart'; part 'three_gang_glass_switch_state.dart';
class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGangGlassSwitchState> { class ThreeGangGlassSwitchBloc
extends Bloc<ThreeGangGlassSwitchEvent, ThreeGangGlassSwitchState> {
ThreeGangGlassStatusModel deviceStatus; ThreeGangGlassStatusModel deviceStatus;
Timer? _timer; Timer? _timer;
@ -29,21 +31,57 @@ class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGang
on<ThreeGangGlassSwitchBatchControl>(_onBatchControl); on<ThreeGangGlassSwitchBatchControl>(_onBatchControl);
on<ThreeGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus); on<ThreeGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus);
on<ThreeGangGlassFactoryReset>(_onFactoryReset); on<ThreeGangGlassFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchDeviceStatus( Future<void> _onFetchDeviceStatus(ThreeGangGlassSwitchFetchDeviceEvent event,
ThreeGangGlassSwitchFetchDeviceEvent event, Emitter<ThreeGangGlassSwitchState> emit) async { Emitter<ThreeGangGlassSwitchState> emit) async {
emit(ThreeGangGlassSwitchLoading()); emit(ThreeGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
deviceStatus = ThreeGangGlassStatusModel.fromJson(event.deviceId, status.status); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus =
ThreeGangGlassStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus)); emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(ThreeGangGlassSwitchError(e.toString())); emit(ThreeGangGlassSwitchError(e.toString()));
} }
} }
Future<void> _onControl(ThreeGangGlassSwitchControl event, Emitter<ThreeGangGlassSwitchState> emit) async { _listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus = ThreeGangGlassStatusModel.fromJson(
usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(
StatusUpdated event, Emitter<ThreeGangGlassSwitchState> emit) {
deviceStatus = event.deviceStatus;
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
}
Future<void> _onControl(ThreeGangGlassSwitchControl event,
Emitter<ThreeGangGlassSwitchState> emit) async {
final oldValue = _getValueByCode(event.code); final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
@ -59,7 +97,8 @@ class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGang
); );
} }
Future<void> _onBatchControl(ThreeGangGlassSwitchBatchControl event, Emitter<ThreeGangGlassSwitchState> emit) async { Future<void> _onBatchControl(ThreeGangGlassSwitchBatchControl event,
Emitter<ThreeGangGlassSwitchState> emit) async {
final oldValue = _getValueByCode(event.code); final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
@ -76,21 +115,26 @@ class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGang
} }
Future<void> _onFetchBatchStatus( Future<void> _onFetchBatchStatus(
ThreeGangGlassSwitchFetchBatchStatusEvent event, Emitter<ThreeGangGlassSwitchState> emit) async { ThreeGangGlassSwitchFetchBatchStatusEvent event,
Emitter<ThreeGangGlassSwitchState> emit) async {
emit(ThreeGangGlassSwitchLoading()); emit(ThreeGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds); final status =
deviceStatus = ThreeGangGlassStatusModel.fromJson(event.deviceIds.first, status.status); await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus = ThreeGangGlassStatusModel.fromJson(
event.deviceIds.first, status.status);
emit(ThreeGangGlassSwitchBatchStatusLoaded(deviceStatus)); emit(ThreeGangGlassSwitchBatchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(ThreeGangGlassSwitchError(e.toString())); emit(ThreeGangGlassSwitchError(e.toString()));
} }
} }
Future<void> _onFactoryReset(ThreeGangGlassFactoryReset event, Emitter<ThreeGangGlassSwitchState> emit) async { Future<void> _onFactoryReset(ThreeGangGlassFactoryReset event,
Emitter<ThreeGangGlassSwitchState> emit) async {
emit(ThreeGangGlassSwitchLoading()); emit(ThreeGangGlassSwitchLoading());
try { try {
final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId); final response = await DevicesManagementApi()
.factoryReset(event.factoryReset, event.deviceId);
if (!response) { if (!response) {
emit(ThreeGangGlassSwitchError('Failed')); emit(ThreeGangGlassSwitchError('Failed'));
} else { } else {
@ -124,9 +168,11 @@ class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGang
try { try {
late bool response; late bool response;
if (isBatch) { if (isBatch) {
response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value); response = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else { } else {
response = await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value)); response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
} }
if (!response) { if (!response) {
@ -138,7 +184,8 @@ class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGang
}); });
} }
void _revertValueAndEmit(String deviceId, String code, bool oldValue, Emitter<ThreeGangGlassSwitchState> emit) { void _revertValueAndEmit(String deviceId, String code, bool oldValue,
Emitter<ThreeGangGlassSwitchState> emit) {
_updateLocalValue(code, oldValue); _updateLocalValue(code, oldValue);
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus)); emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
} }

View File

@ -49,3 +49,10 @@ class ThreeGangGlassFactoryReset extends ThreeGangGlassSwitchEvent {
required this.factoryReset, required this.factoryReset,
}); });
} }
class StatusUpdated extends ThreeGangGlassSwitchEvent {
final ThreeGangGlassStatusModel deviceStatus;
StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -3,6 +3,7 @@
import 'dart:async'; import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart';
@ -22,6 +23,7 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
on<LivingRoomBatchControl>(_livingRoomBatchControl); on<LivingRoomBatchControl>(_livingRoomBatchControl);
on<LivingRoomFetchBatchEvent>(_livingRoomFetchBatchControl); on<LivingRoomFetchBatchEvent>(_livingRoomFetchBatchControl);
on<LivingRoomFactoryResetEvent>(_livingRoomFactoryReset); on<LivingRoomFactoryResetEvent>(_livingRoomFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
FutureOr<void> _onFetchDeviceStatus(LivingRoomFetchDeviceStatusEvent event, FutureOr<void> _onFetchDeviceStatus(LivingRoomFetchDeviceStatusEvent event,
@ -32,6 +34,7 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = deviceStatus =
LivingRoomStatusModel.fromJson(event.deviceId, status.status); LivingRoomStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(deviceId);
emit(LivingRoomDeviceStatusLoaded(deviceStatus)); emit(LivingRoomDeviceStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(LivingRoomDeviceManagementError(e.toString())); emit(LivingRoomDeviceManagementError(e.toString()));
@ -144,6 +147,9 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
await DevicesManagementApi().getBatchStatus(event.devicesIds); await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus = deviceStatus =
LivingRoomStatusModel.fromJson(event.devicesIds.first, status.status); LivingRoomStatusModel.fromJson(event.devicesIds.first, status.status);
// for (var deviceId in event.devicesIds) {
// _listenToChanges(deviceId);
// }
emit(LivingRoomDeviceStatusLoaded(deviceStatus)); emit(LivingRoomDeviceStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(LivingRoomDeviceManagementError(e.toString())); emit(LivingRoomDeviceManagementError(e.toString()));
@ -185,4 +191,34 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
emit(LivingRoomDeviceManagementError(e.toString())); emit(LivingRoomDeviceManagementError(e.toString()));
} }
} }
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
LivingRoomStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(StatusUpdated event, Emitter<LivingRoomState> emit) {
deviceStatus = event.deviceStatus;
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
}
} }

View File

@ -58,3 +58,10 @@ class LivingRoomFactoryResetEvent extends LivingRoomEvent {
@override @override
List<Object> get props => [uuid, factoryReset]; List<Object> get props => [uuid, factoryReset];
} }
class StatusUpdated extends LivingRoomEvent {
final LivingRoomStatusModel deviceStatus;
const StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -1,12 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart';
import 'package:syncrow_web/services/devices_mang_api.dart'; import 'package:syncrow_web/services/devices_mang_api.dart';
part 'two_gang_glass_switch_event.dart'; part 'two_gang_glass_switch_event.dart';
part 'two_gang_glass_switch_state.dart'; part 'two_gang_glass_switch_state.dart';
@ -14,7 +13,6 @@ class TwoGangGlassSwitchBloc
extends Bloc<TwoGangGlassSwitchEvent, TwoGangGlassSwitchState> { extends Bloc<TwoGangGlassSwitchEvent, TwoGangGlassSwitchState> {
TwoGangGlassStatusModel deviceStatus; TwoGangGlassStatusModel deviceStatus;
Timer? _timer; Timer? _timer;
TwoGangGlassSwitchBloc({required String deviceId}) TwoGangGlassSwitchBloc({required String deviceId})
: deviceStatus = TwoGangGlassStatusModel( : deviceStatus = TwoGangGlassStatusModel(
uuid: deviceId, uuid: deviceId,
@ -28,6 +26,7 @@ class TwoGangGlassSwitchBloc
on<TwoGangGlassSwitchBatchControl>(_onBatchControl); on<TwoGangGlassSwitchBatchControl>(_onBatchControl);
on<TwoGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus); on<TwoGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus);
on<TwoGangGlassFactoryReset>(_onFactoryReset); on<TwoGangGlassFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchDeviceStatus(TwoGangGlassSwitchFetchDeviceEvent event, Future<void> _onFetchDeviceStatus(TwoGangGlassSwitchFetchDeviceEvent event,
@ -38,12 +37,44 @@ class TwoGangGlassSwitchBloc
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = deviceStatus =
TwoGangGlassStatusModel.fromJson(event.deviceId, status.status); TwoGangGlassStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus)); emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(TwoGangGlassSwitchError(e.toString())); emit(TwoGangGlassSwitchError(e.toString()));
} }
} }
void _listenToChanges(String deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
ref.onValue.listen((DatabaseEvent event) {
if (event.snapshot.value == null) return;
Map<dynamic, dynamic> data =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
data['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
// Parse the new status and add the event
final updatedStatus =
TwoGangGlassStatusModel.fromJson(data['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(updatedStatus));
}
});
} catch (e) {
// Handle errors and emit an error state if necessary
if (!isClosed) {
// add(TwoGangGlassSwitchError('Error listening to updates: $e'));
}
}
}
Future<void> _onControl(TwoGangGlassSwitchControl event, Future<void> _onControl(TwoGangGlassSwitchControl event,
Emitter<TwoGangGlassSwitchState> emit) async { Emitter<TwoGangGlassSwitchState> emit) async {
final oldValue = _getValueByCode(event.code); final oldValue = _getValueByCode(event.code);
@ -178,4 +209,37 @@ class TwoGangGlassSwitchBloc
_timer?.cancel(); _timer?.cancel();
return super.close(); return super.close();
} }
// _listenToChanges(deviceId) {
// try {
// DatabaseReference ref =
// FirebaseDatabase.instance.ref('device-status/$deviceId');
// Stream<DatabaseEvent> stream = ref.onValue;
// stream.listen((DatabaseEvent event) {
// Map<dynamic, dynamic> usersMap =
// event.snapshot.value as Map<dynamic, dynamic>;
// List<Status> statusList = [];
// usersMap['status'].forEach((element) {
// statusList
// .add(Status(code: element['code'], value: element['value']));
// });
// deviceStatus = TwoGangGlassStatusModel.fromJson(
// usersMap['productUuid'], statusList);
// if (!isClosed) {
// add(StatusUpdated(deviceStatus));
// }
// });
// } catch (_) {}
// }
void _onStatusUpdated(
StatusUpdated event, Emitter<TwoGangGlassSwitchState> emit) {
// Update the local deviceStatus with the new status from the event
deviceStatus = event.deviceStatus;
// Emit the new state with the updated status
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
}
} }

View File

@ -48,3 +48,11 @@ class TwoGangGlassFactoryReset extends TwoGangGlassSwitchEvent {
required this.factoryReset, required this.factoryReset,
}); });
} }
class StatusUpdated extends TwoGangGlassSwitchEvent {
final TwoGangGlassStatusModel deviceStatus;
StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -6,7 +6,8 @@ import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class TwoGangGlassSwitchControlView extends StatelessWidget with HelperResponsiveLayout { class TwoGangGlassSwitchControlView extends StatelessWidget
with HelperResponsiveLayout {
final String deviceId; final String deviceId;
const TwoGangGlassSwitchControlView({required this.deviceId, super.key}); const TwoGangGlassSwitchControlView({required this.deviceId, super.key});
@ -14,25 +15,25 @@ class TwoGangGlassSwitchControlView extends StatelessWidget with HelperResponsiv
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => create: (context) => TwoGangGlassSwitchBloc(deviceId: deviceId)
TwoGangGlassSwitchBloc(deviceId: deviceId)..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)), ..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)),
child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>( child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>(
builder: (context, state) { builder: (context, state) {
if (state is TwoGangGlassSwitchLoading) { if (state is TwoGangGlassSwitchLoading) {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} else if (state is TwoGangGlassSwitchStatusLoaded) { } else if (state is TwoGangGlassSwitchStatusLoaded) {
return _buildStatusControls(context, state.status); return _buildStatusControls(context, state.status);
} else if (state is TwoGangGlassSwitchError) { } else if (state is TwoGangGlassSwitchError) {
return const Center(child: Text('Error fetching status')); return Center(child: Text(state.message));
} else { } else {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} }
}, },
), ));
);
} }
Widget _buildStatusControls(BuildContext context, TwoGangGlassStatusModel status) { Widget _buildStatusControls(
BuildContext context, TwoGangGlassStatusModel status) {
final isExtraLarge = isExtraLargeScreenSize(context); final isExtraLarge = isExtraLargeScreenSize(context);
final isLarge = isLargeScreenSize(context); final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context); final isMedium = isMediumScreenSize(context);

View File

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.dart';
@ -14,6 +15,7 @@ class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
on<TwoGangSwitchFetchBatchEvent>(_onFetchBatchStatus); on<TwoGangSwitchFetchBatchEvent>(_onFetchBatchStatus);
on<TwoGangSwitchBatchControl>(_onBatchControl); on<TwoGangSwitchBatchControl>(_onBatchControl);
on<TwoGangFactoryReset>(_onFactoryReset); on<TwoGangFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
late TwoGangStatusModel deviceStatus; late TwoGangStatusModel deviceStatus;
@ -26,8 +28,8 @@ class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
try { try {
final status = final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = TwoGangStatusModel.fromJson(event.deviceId, status.status); deviceStatus = TwoGangStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(emit);
emit(TwoGangSwitchStatusLoaded(deviceStatus)); emit(TwoGangSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(TwoGangSwitchError(e.toString())); emit(TwoGangSwitchError(e.toString()));
@ -174,4 +176,34 @@ class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
emit(TwoGangSwitchError(e.toString())); emit(TwoGangSwitchError(e.toString()));
} }
} }
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
TwoGangStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(StatusUpdated event, Emitter<TwoGangSwitchState> emit) {
deviceStatus = event.deviceStatus;
emit(TwoGangSwitchStatusLoaded(deviceStatus));
}
} }

View File

@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/models/two_gang_status_model.dart';
class TwoGangSwitchEvent extends Equatable { class TwoGangSwitchEvent extends Equatable {
@override @override
@ -57,3 +58,10 @@ class TwoGangFactoryReset extends TwoGangSwitchEvent {
@override @override
List<Object> get props => [deviceId, factoryReset]; List<Object> get props => [deviceId, factoryReset];
} }
class StatusUpdated extends TwoGangSwitchEvent {
final TwoGangStatusModel deviceStatus;
StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_event.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_event.dart';
@ -29,7 +30,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
var response = await DevicesManagementApi().getDeviceStatus(deviceId); var response = await DevicesManagementApi().getDeviceStatus(deviceId);
deviceStatus = WallSensorModel.fromJson(response.status); deviceStatus = WallSensorModel.fromJson(response.status);
emit(WallSensorUpdateState(wallSensorModel: deviceStatus)); emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
// _listenToChanges(); _listenToChanges(emit);
} catch (e) { } catch (e) {
emit(WallSensorFailedState(error: e.toString())); emit(WallSensorFailedState(error: e.toString()));
return; return;
@ -38,10 +39,12 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
// Fetch batch status // Fetch batch status
FutureOr<void> _fetchWallSensorBatchControl( FutureOr<void> _fetchWallSensorBatchControl(
WallSensorFetchBatchStatusEvent event, Emitter<WallSensorState> emit) async { WallSensorFetchBatchStatusEvent event,
Emitter<WallSensorState> emit) async {
emit(WallSensorLoadingInitialState()); emit(WallSensorLoadingInitialState());
try { try {
var response = await DevicesManagementApi().getBatchStatus(event.devicesIds); var response =
await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus = WallSensorModel.fromJson(response.status); deviceStatus = WallSensorModel.fromJson(response.status);
emit(WallSensorUpdateState(wallSensorModel: deviceStatus)); emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
} catch (e) { } catch (e) {
@ -49,26 +52,30 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
} }
} }
// _listenToChanges() { _listenToChanges(Emitter<WallSensorState> emit) {
// try { try {
// DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); DatabaseReference ref =
// Stream<DatabaseEvent> stream = ref.onValue; FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
// stream.listen((DatabaseEvent event) { stream.listen((DatabaseEvent event) {
// Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>; Map<dynamic, dynamic> usersMap =
// List<StatusModel> statusList = []; event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
// usersMap['status'].forEach((element) { usersMap['status'].forEach((element) {
// statusList.add(StatusModel(code: element['code'], value: element['value'])); statusList
// }); .add(Status(code: element['code'], value: element['value']));
});
// deviceStatus = WallSensorModel.fromJson(statusList); deviceStatus = WallSensorModel.fromJson(statusList);
// add(WallSensorUpdatedEvent()); emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
// }); });
// } catch (_) {} } catch (_) {}
// } }
void _changeValue(WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async { void _changeValue(
WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async {
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus)); emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
if (event.code == 'far_detection') { if (event.code == 'far_detection') {
deviceStatus.farDetection = event.value; deviceStatus.farDetection = event.value;
@ -125,7 +132,8 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
try { try {
late bool response; late bool response;
if (isBatch) { if (isBatch) {
response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value); response = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else { } else {
response = await DevicesManagementApi() response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value)); .deviceControl(deviceId, Status(code: code, value: value));
@ -150,7 +158,8 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
try { try {
// await DevicesManagementApi.getDeviceReportsByDate( // await DevicesManagementApi.getDeviceReportsByDate(
// deviceId, event.code, from.toString(), to.toString()) // deviceId, event.code, from.toString(), to.toString())
await DevicesManagementApi.getDeviceReports(deviceId, event.code).then((value) { await DevicesManagementApi.getDeviceReports(deviceId, event.code)
.then((value) {
emit(DeviceReportsState(deviceReport: value, code: event.code)); emit(DeviceReportsState(deviceReport: value, code: event.code));
}); });
} catch (e) { } catch (e) {
@ -159,11 +168,13 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
} }
} }
void _showDescription(ShowDescriptionEvent event, Emitter<WallSensorState> emit) { void _showDescription(
ShowDescriptionEvent event, Emitter<WallSensorState> emit) {
emit(WallSensorShowDescriptionState(description: event.description)); emit(WallSensorShowDescriptionState(description: event.description));
} }
void _backToGridView(BackToGridViewEvent event, Emitter<WallSensorState> emit) { void _backToGridView(
BackToGridViewEvent event, Emitter<WallSensorState> emit) {
emit(WallSensorUpdateState(wallSensorModel: deviceStatus)); emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
} }

View File

@ -4,6 +4,7 @@ import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart';
@ -34,6 +35,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
on<EditWaterHeaterScheduleEvent>(_onEditSchedule); on<EditWaterHeaterScheduleEvent>(_onEditSchedule);
on<DeleteScheduleEvent>(_onDeleteSchedule); on<DeleteScheduleEvent>(_onDeleteSchedule);
on<UpdateScheduleEntryEvent>(_onUpdateSchedule); on<UpdateScheduleEntryEvent>(_onUpdateSchedule);
on<StatusUpdated>(_onStatusUpdated);
} }
late WaterHeaterStatusModel deviceStatus; late WaterHeaterStatusModel deviceStatus;
@ -78,7 +80,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
final updatedDays = List<bool>.from(currentState.selectedDays); final updatedDays = List<bool>.from(currentState.selectedDays);
updatedDays[event.index] = event.value; updatedDays[event.index] = event.value;
emit(currentState.copyWith(selectedDays: updatedDays, selectedTime: currentState.selectedTime)); emit(currentState.copyWith(
selectedDays: updatedDays, selectedTime: currentState.selectedTime));
} }
FutureOr<void> _updateFunctionOn( FutureOr<void> _updateFunctionOn(
@ -86,7 +89,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
) { ) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
emit(currentState.copyWith(functionOn: event.isOn, selectedTime: currentState.selectedTime)); emit(currentState.copyWith(
functionOn: event.isOn, selectedTime: currentState.selectedTime));
} }
FutureOr<void> _updateScheduleEvent( FutureOr<void> _updateScheduleEvent(
@ -101,7 +105,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
)); ));
} }
if (event.scheduleMode == ScheduleModes.countdown) { if (event.scheduleMode == ScheduleModes.countdown) {
final countdownRemaining = Duration(hours: event.hours, minutes: event.minutes); final countdownRemaining =
Duration(hours: event.hours, minutes: event.minutes);
emit(currentState.copyWith( emit(currentState.copyWith(
scheduleMode: ScheduleModes.countdown, scheduleMode: ScheduleModes.countdown,
@ -111,11 +116,13 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
countdownRemaining: countdownRemaining, countdownRemaining: countdownRemaining,
)); ));
if (!currentState.isCountdownActive! && countdownRemaining > Duration.zero) { if (!currentState.isCountdownActive! &&
countdownRemaining > Duration.zero) {
_startCountdownTimer(emit, countdownRemaining); _startCountdownTimer(emit, countdownRemaining);
} }
} else if (event.scheduleMode == ScheduleModes.inching) { } else if (event.scheduleMode == ScheduleModes.inching) {
final inchingDuration = Duration(hours: event.hours, minutes: event.minutes); final inchingDuration =
Duration(hours: event.hours, minutes: event.minutes);
emit(currentState.copyWith( emit(currentState.copyWith(
scheduleMode: ScheduleModes.inching, scheduleMode: ScheduleModes.inching,
@ -217,7 +224,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
try { try {
final status = await DevicesManagementApi().deviceControl( final status = await DevicesManagementApi().deviceControl(
event.deviceId, event.deviceId,
Status(code: isCountDown ? 'countdown_1' : 'switch_inching', value: 0), Status(
code: isCountDown ? 'countdown_1' : 'switch_inching', value: 0),
); );
if (!status) { if (!status) {
emit(const WaterHeaterFailedState(error: 'Failed to stop schedule.')); emit(const WaterHeaterFailedState(error: 'Failed to stop schedule.'));
@ -235,8 +243,10 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
emit(WaterHeaterLoadingState()); emit(WaterHeaterLoadingState());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
deviceStatus = WaterHeaterStatusModel.fromJson(event.deviceId, status.status); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus =
WaterHeaterStatusModel.fromJson(event.deviceId, status.status);
if (deviceStatus.scheduleMode == ScheduleModes.countdown) { if (deviceStatus.scheduleMode == ScheduleModes.countdown) {
final countdownRemaining = Duration( final countdownRemaining = Duration(
@ -300,11 +310,42 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
isInchingActive: false, isInchingActive: false,
)); ));
} }
_listenToChanges(event.deviceId);
} catch (e) { } catch (e) {
emit(WaterHeaterFailedState(error: e.toString())); emit(WaterHeaterFailedState(error: e.toString()));
} }
} }
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus = WaterHeaterStatusModel.fromJson(
usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(StatusUpdated event, Emitter<WaterHeaterState> emit) {
deviceStatus = event.deviceStatus;
emit(WaterHeaterDeviceStatusLoaded(deviceStatus));
}
void _startCountdownTimer( void _startCountdownTimer(
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
Duration countdownRemaining, Duration countdownRemaining,
@ -334,8 +375,10 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
if (state is WaterHeaterDeviceStatusLoaded) { if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
if (currentState.countdownRemaining != null && currentState.countdownRemaining! > Duration.zero) { if (currentState.countdownRemaining != null &&
final newRemaining = currentState.countdownRemaining! - const Duration(minutes: 1); currentState.countdownRemaining! > Duration.zero) {
final newRemaining =
currentState.countdownRemaining! - const Duration(minutes: 1);
if (newRemaining <= Duration.zero) { if (newRemaining <= Duration.zero) {
_countdownTimer?.cancel(); _countdownTimer?.cancel();
@ -430,7 +473,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
} }
void _revertValue(String code, dynamic oldValue, void Function(WaterHeaterState state) emit) { void _revertValue(String code, dynamic oldValue,
void Function(WaterHeaterState state) emit) {
_updateLocalValue(code, oldValue); _updateLocalValue(code, oldValue);
if (state is WaterHeaterDeviceStatusLoaded) { if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
@ -477,12 +521,13 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
return super.close(); return super.close();
} }
FutureOr<void> _getSchedule(GetSchedulesEvent event, Emitter<WaterHeaterState> emit) async { FutureOr<void> _getSchedule(
GetSchedulesEvent event, Emitter<WaterHeaterState> emit) async {
emit(ScheduleLoadingState()); emit(ScheduleLoadingState());
try { try {
List<ScheduleModel> schedules = List<ScheduleModel> schedules = await DevicesManagementApi()
await DevicesManagementApi().getDeviceSchedules(deviceStatus.uuid, event.category); .getDeviceSchedules(deviceStatus.uuid, event.category);
emit(WaterHeaterDeviceStatusLoaded( emit(WaterHeaterDeviceStatusLoaded(
deviceStatus, deviceStatus,
@ -514,7 +559,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
// emit(ScheduleLoadingState()); // emit(ScheduleLoadingState());
bool success = await DevicesManagementApi().addScheduleRecord(newSchedule, currentState.status.uuid); bool success = await DevicesManagementApi()
.addScheduleRecord(newSchedule, currentState.status.uuid);
if (success) { if (success) {
add(GetSchedulesEvent(category: 'switch_1', uuid: deviceStatus.uuid)); add(GetSchedulesEvent(category: 'switch_1', uuid: deviceStatus.uuid));
@ -525,7 +571,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
} }
FutureOr<void> _onEditSchedule(EditWaterHeaterScheduleEvent event, Emitter<WaterHeaterState> emit) async { FutureOr<void> _onEditSchedule(EditWaterHeaterScheduleEvent event,
Emitter<WaterHeaterState> emit) async {
if (state is WaterHeaterDeviceStatusLoaded) { if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
@ -594,11 +641,13 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
// emit(ScheduleLoadingState()); // emit(ScheduleLoadingState());
bool success = await DevicesManagementApi().deleteScheduleRecord(currentState.status.uuid, event.scheduleId); bool success = await DevicesManagementApi()
.deleteScheduleRecord(currentState.status.uuid, event.scheduleId);
if (success) { if (success) {
final updatedSchedules = final updatedSchedules = currentState.schedules
currentState.schedules.where((schedule) => schedule.scheduleId != event.scheduleId).toList(); .where((schedule) => schedule.scheduleId != event.scheduleId)
.toList();
emit(currentState.copyWith(schedules: updatedSchedules)); emit(currentState.copyWith(schedules: updatedSchedules));
} else { } else {
@ -608,12 +657,15 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
} }
FutureOr<void> _batchFetchWaterHeater(FetchWaterHeaterBatchStatusEvent event, Emitter<WaterHeaterState> emit) async { FutureOr<void> _batchFetchWaterHeater(FetchWaterHeaterBatchStatusEvent event,
Emitter<WaterHeaterState> emit) async {
emit(WaterHeaterLoadingState()); emit(WaterHeaterLoadingState());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.devicesUuid); final status =
deviceStatus = WaterHeaterStatusModel.fromJson(event.devicesUuid.first, status.status); await DevicesManagementApi().getBatchStatus(event.devicesUuid);
deviceStatus = WaterHeaterStatusModel.fromJson(
event.devicesUuid.first, status.status);
emit(WaterHeaterDeviceStatusLoaded(deviceStatus)); emit(WaterHeaterDeviceStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
@ -621,7 +673,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
} }
FutureOr<void> _batchControlWaterHeater(ControlWaterHeaterBatchEvent event, Emitter<WaterHeaterState> emit) async { FutureOr<void> _batchControlWaterHeater(ControlWaterHeaterBatchEvent event,
Emitter<WaterHeaterState> emit) async {
if (state is WaterHeaterDeviceStatusLoaded) { if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;

View File

@ -54,6 +54,15 @@ final class WaterHeaterFetchStatusEvent extends WaterHeaterEvent {
final class DecrementCountdownEvent extends WaterHeaterEvent {} final class DecrementCountdownEvent extends WaterHeaterEvent {}
class StatusUpdated extends WaterHeaterEvent {
final WaterHeaterStatusModel deviceStatus;
const StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}
final class AddScheduleEvent extends WaterHeaterEvent { final class AddScheduleEvent extends WaterHeaterEvent {
final List<bool> selectedDays; final List<bool> selectedDays;
final TimeOfDay time; final TimeOfDay time;

View File

@ -1,4 +1,5 @@
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_event.dart'; import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_event.dart';
@ -21,6 +22,7 @@ class WaterLeakBloc extends Bloc<WaterLeakEvent, WaterLeakState> {
on<FetchWaterLeakBatchStatusEvent>(_onFetchBatchStatus); on<FetchWaterLeakBatchStatusEvent>(_onFetchBatchStatus);
on<FetchWaterLeakReportsEvent>(_onFetchWaterLeakReports); on<FetchWaterLeakReportsEvent>(_onFetchWaterLeakReports);
on<WaterLeakFactoryResetEvent>(_onFactoryReset); on<WaterLeakFactoryResetEvent>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchWaterLeakStatus( Future<void> _onFetchWaterLeakStatus(
@ -30,12 +32,43 @@ class WaterLeakBloc extends Bloc<WaterLeakEvent, WaterLeakState> {
final response = final response =
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = WaterLeakStatusModel.fromJson(deviceId, response.status); deviceStatus = WaterLeakStatusModel.fromJson(deviceId, response.status);
_listenToChanges();
emit(WaterLeakLoadedState(deviceStatus!)); emit(WaterLeakLoadedState(deviceStatus!));
} catch (e) { } catch (e) {
emit(WaterLeakErrorState(e.toString())); emit(WaterLeakErrorState(e.toString()));
} }
} }
_listenToChanges() {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
WaterLeakStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus!));
}
});
} catch (_) {}
}
void _onStatusUpdated(StatusUpdated event, Emitter<WaterLeakState> emit) {
deviceStatus = event.deviceStatus;
emit(WaterLeakLoadedState(deviceStatus!));
}
Future<void> _onControl( Future<void> _onControl(
WaterLeakControlEvent event, Emitter<WaterLeakState> emit) async { WaterLeakControlEvent event, Emitter<WaterLeakState> emit) async {
final oldValue = deviceStatus!.watersensorState; final oldValue = deviceStatus!.watersensorState;

View File

@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/model/water_leak_status_model.dart';
abstract class WaterLeakEvent extends Equatable { abstract class WaterLeakEvent extends Equatable {
const WaterLeakEvent(); const WaterLeakEvent();
@ -17,6 +18,13 @@ class FetchWaterLeakStatusEvent extends WaterLeakEvent {
List<Object> get props => [deviceId]; List<Object> get props => [deviceId];
} }
class StatusUpdated extends WaterLeakEvent {
final WaterLeakStatusModel deviceStatus;
const StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}
class WaterLeakControlEvent extends WaterLeakEvent { class WaterLeakControlEvent extends WaterLeakEvent {
final String deviceId; final String deviceId;
final String code; final String code;

View File

@ -2,20 +2,19 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:shared_preferences/shared_preferences.dart';
// import 'package:graphview/GraphView.dart';
import 'package:syncrow_web/pages/auth/model/user_model.dart'; import 'package:syncrow_web/pages/auth/model/user_model.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart'; import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/home/bloc/home_state.dart'; import 'package:syncrow_web/pages/home/bloc/home_state.dart';
import 'package:syncrow_web/pages/home/home_model/home_item_model.dart'; import 'package:syncrow_web/pages/home/home_model/home_item_model.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
import 'package:syncrow_web/services/home_api.dart'; import 'package:syncrow_web/services/home_api.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:syncrow_web/utils/constants/strings_manager.dart'; import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
class HomeBloc extends Bloc<HomeEvent, HomeState> { class HomeBloc extends Bloc<HomeEvent, HomeState> {
// final Graph graph = Graph()..isTree = true; // final Graph graph = Graph()..isTree = true;
@ -52,12 +51,12 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async { Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async {
try { try {
var uuid = var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
user = await HomeApi().fetchUserInfo(uuid); user = await HomeApi().fetchUserInfo(uuid);
if (user != null && user!.project != null) { if (user != null && user!.project != null) {
await ProjectManager.setProjectUUID(user!.project!.uuid); await ProjectManager.setProjectUUID(user!.project!.uuid);
NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>().add(InitialEvent());
} }
add(FetchTermEvent()); add(FetchTermEvent());
add(FetchPolicyEvent()); add(FetchPolicyEvent());
@ -73,8 +72,6 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
emit(LoadingHome()); emit(LoadingHome());
terms = await HomeApi().fetchTerms(); terms = await HomeApi().fetchTerms();
emit(HomeInitial()); emit(HomeInitial());
// emit(PolicyAgreement());
} catch (e) { } catch (e) {
return; return;
} }
@ -84,8 +81,6 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
try { try {
emit(LoadingHome()); emit(LoadingHome());
policy = await HomeApi().fetchPolicy(); policy = await HomeApi().fetchPolicy();
debugPrint("Fetched policy: $policy");
// Emit a state to trigger the UI update
emit(HomeInitial()); emit(HomeInitial());
} catch (e) { } catch (e) {
debugPrint("Error fetching policy: $e"); debugPrint("Error fetching policy: $e");
@ -93,12 +88,10 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
} }
} }
Future _confirmUserAgreement( Future _confirmUserAgreement(ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
try { try {
emit(LoadingHome()); emit(LoadingHome());
var uuid = var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
policy = await HomeApi().confirmUserAgreements(uuid); policy = await HomeApi().confirmUserAgreements(uuid);
emit(PolicyAgreement()); emit(PolicyAgreement());
} catch (e) { } catch (e) {
@ -118,10 +111,11 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
List<HomeItemModel> homeItems = [ List<HomeItemModel> homeItems = [
HomeItemModel( HomeItemModel(
title: 'Access', title: 'Access Management',
icon: Assets.accessIcon, icon: Assets.accessIcon,
active: true, active: true,
onPress: (context) { onPress: (context) {
context.read<SpaceTreeBloc>().add(ClearCachedData());
context.go(RoutesConst.accessManagementPage); context.go(RoutesConst.accessManagementPage);
}, },
color: null, color: null,
@ -131,21 +125,24 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
icon: Assets.spaseManagementIcon, icon: Assets.spaseManagementIcon,
active: true, active: true,
onPress: (context) { onPress: (context) {
context.read<SpaceTreeBloc>().add(ClearCachedData());
context.go(RoutesConst.spacesManagementPage); context.go(RoutesConst.spacesManagementPage);
}, },
color: ColorsManager.primaryColor, color: ColorsManager.primaryColor,
), ),
HomeItemModel( HomeItemModel(
title: 'Devices', title: 'Devices Management',
icon: Assets.devicesIcon, icon: Assets.devicesIcon,
active: true, active: true,
onPress: (context) { onPress: (context) {
context.read<SpaceTreeBloc>().add(ClearCachedData());
BlocProvider.of<RoutineBloc>(context) BlocProvider.of<RoutineBloc>(context)
.add(const TriggerSwitchTabsEvent(isRoutineTab: false)); .add(const TriggerSwitchTabsEvent(isRoutineTab: false));
context.go(RoutesConst.deviceManagementPage); context.go(RoutesConst.deviceManagementPage);
}, },
color: ColorsManager.primaryColor, color: ColorsManager.primaryColor,
), ),
// HomeItemModel( // HomeItemModel(
// title: 'Move in', // title: 'Move in',
// icon: Assets.moveinIcon, // icon: Assets.moveinIcon,

View File

@ -219,6 +219,9 @@ class UserSpaceModel {
final double x; final double x;
final double y; final double y;
final String icon; final String icon;
final String communityUuid;
//communityUuid
UserSpaceModel({ UserSpaceModel({
required this.uuid, required this.uuid,
@ -231,22 +234,23 @@ class UserSpaceModel {
required this.x, required this.x,
required this.y, required this.y,
required this.icon, required this.icon,
required this.communityUuid,
}); });
/// Create a [UserSpaceModel] from JSON data /// Create a [UserSpaceModel] from JSON data
factory UserSpaceModel.fromJson(Map<String, dynamic> json) { factory UserSpaceModel.fromJson(Map<String, dynamic> json) {
return UserSpaceModel( return UserSpaceModel(
uuid: json['uuid'] as String, uuid: json['uuid'] as String,
createdAt: json['createdAt'] as String, createdAt: json['createdAt'] as String,
updatedAt: json['updatedAt'] as String, updatedAt: json['updatedAt'] as String,
spaceTuyaUuid: json['spaceTuyaUuid'] as String?, spaceTuyaUuid: json['spaceTuyaUuid'] as String?,
spaceName: json['spaceName'] as String, spaceName: json['spaceName'] as String,
invitationCode: json['invitationCode'] as String?, invitationCode: json['invitationCode'] as String?,
disabled: json['disabled'] as bool, disabled: json['disabled'] as bool,
x: (json['x'] as num).toDouble(), x: (json['x'] as num).toDouble(),
y: (json['y'] as num).toDouble(), y: (json['y'] as num).toDouble(),
icon: json['icon'] as String, icon: json['icon'] as String,
); communityUuid: json['communityUuid'] as String);
} }
/// Convert the [UserSpaceModel] to JSON /// Convert the [UserSpaceModel] to JSON
@ -262,6 +266,7 @@ class UserSpaceModel {
'x': x, 'x': x,
'y': y, 'y': y,
'icon': icon, 'icon': icon,
'communityUuid': communityUuid
}; };
} }
} }

View File

@ -1,5 +1,6 @@
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/common/custom_dialog.dart'; import 'package:syncrow_web/pages/common/custom_dialog.dart';
import 'package:syncrow_web/pages/roles_and_permission/model/edit_user_model.dart'; import 'package:syncrow_web/pages/roles_and_permission/model/edit_user_model.dart';
@ -8,14 +9,14 @@ import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialo
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart'; import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/model/permission_option_model.dart'; import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/model/permission_option_model.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/model/tree_node_model.dart'; import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/model/tree_node_model.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/services/space_mana_api.dart';
import 'package:syncrow_web/services/user_permission.dart'; import 'package:syncrow_web/services/user_permission.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/constants/strings_manager.dart'; import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
class UsersBloc extends Bloc<UsersEvent, UsersState> { class UsersBloc extends Bloc<UsersEvent, UsersState> {
UsersBloc() : super(UsersInitial()) { UsersBloc() : super(UsersInitial()) {
@ -65,8 +66,10 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
void isCompleteSpacesFun( void isCompleteSpacesFun(
CheckSpacesStepStatus event, Emitter<UsersState> emit) { CheckSpacesStepStatus event, Emitter<UsersState> emit) {
emit(UsersLoadingState()); emit(UsersLoadingState());
List<String> selectedIds = getSelectedIds(updatedCommunities); var spaceBloc =
isCompleteSpaces = selectedIds.isNotEmpty; NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>();
isCompleteSpaces = spaceBloc.state.selectedCommunities.isNotEmpty;
emit(ChangeStatusSteps()); emit(ChangeStatusSteps());
} }
@ -340,9 +343,11 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
final projectUuid = await ProjectManager.getProjectUUID() ?? ''; final projectUuid = await ProjectManager.getProjectUUID() ?? '';
emit(UsersLoadingState()); emit(UsersLoadingState());
List<String> selectedIds = getSelectedIds(updatedCommunities) // List<String> selectedIds =
.where((id) => !communityIds.contains(id)) // getSelectedIds(updatedCommunities).where((id) => !communityIds.contains(id)).toList();
.toList();
List<String> selectedSpacesId = getSelectedSpacesIds();
// List<String> selectedIds = getSelectedIds(updatedCommunities);
bool res = await UserPermissionApi().sendInviteUser( bool res = await UserPermissionApi().sendInviteUser(
email: emailController.text, email: emailController.text,
@ -351,7 +356,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
lastName: lastNameController.text, lastName: lastNameController.text,
phoneNumber: phoneController.text, phoneNumber: phoneController.text,
roleUuid: roleSelected, roleUuid: roleSelected,
spaceUuids: selectedIds, spaceUuids: selectedSpacesId,
projectUuid: projectUuid); projectUuid: projectUuid);
if (res) { if (res) {
@ -381,12 +386,20 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
} }
} }
List<String> getSelectedSpacesIds() {
List<String> selectedSpacesId = [];
var spaceBloc =
NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>();
for (var community in spaceBloc.state.selectedCommunities) {
selectedSpacesId
.addAll(spaceBloc.state.selectedCommunityAndSpaces[community] ?? []);
}
return selectedSpacesId;
}
_editInviteUser(EditInviteUsers event, Emitter<UsersState> emit) async { _editInviteUser(EditInviteUsers event, Emitter<UsersState> emit) async {
try { try {
emit(UsersLoadingState()); emit(UsersLoadingState());
List<String> selectedIds = getSelectedIds(updatedCommunities)
.where((id) => !communityIds.contains(id))
.toList();
final projectUuid = await ProjectManager.getProjectUUID() ?? ''; final projectUuid = await ProjectManager.getProjectUUID() ?? '';
bool res = await UserPermissionApi().editInviteUser( bool res = await UserPermissionApi().editInviteUser(
@ -396,7 +409,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
lastName: lastNameController.text, lastName: lastNameController.text,
phoneNumber: phoneController.text, phoneNumber: phoneController.text,
roleUuid: roleSelected, roleUuid: roleSelected,
spaceUuids: selectedIds, spaceUuids: getSelectedSpacesIds(),
projectUuid: projectUuid); projectUuid: projectUuid);
if (res == true) { if (res == true) {
showCustomDialog( showCustomDialog(
@ -502,6 +515,8 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
emit(UsersLoadingState()); emit(UsersLoadingState());
try { try {
var spaceBloc =
NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? ''; final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (event.uuid?.isNotEmpty ?? false) { if (event.uuid?.isNotEmpty ?? false) {
@ -516,13 +531,20 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
phoneController.text = res.phoneNumber ?? ''; phoneController.text = res.phoneNumber ?? '';
jobTitleController.text = res.jobTitle ?? ''; jobTitleController.text = res.jobTitle ?? '';
res.roleType; res.roleType;
if (updatedCommunities.isNotEmpty) { res.spaces.map((space) {
// Create a list of UUIDs to mark selectedIds.add(space.uuid);
final uuidsToMark = res.spaces.map((space) => space.uuid).toList(); CommunityModel community = spaceBloc.state.communityList
// Print all IDs and mark nodes in updatedCommunities .firstWhere((item) => item.uuid == space.communityUuid);
debugPrint('Printing and marking nodes in updatedCommunities:'); spaceBloc.add(OnSpaceSelected(community, space.uuid, []));
_printAndMarkNodes(updatedCommunities, uuidsToMark); }).toList();
}
// if (updatedCommunities.isNotEmpty) {
// // Create a list of UUIDs to mark
// final uuidsToMark = res.spaces.map((space) => space.uuid).toList();
// // Print all IDs and mark nodes in updatedCommunities
// debugPrint('Printing and marking nodes in updatedCommunities:');
// _printAndMarkNodes(updatedCommunities, uuidsToMark);
// }
final roleId = roles final roleId = roles
.firstWhere((element) => .firstWhere((element) =>
element.type == element.type ==
@ -615,4 +637,16 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
} }
return null; return null;
} }
@override
Future<void> close() {
emailController.dispose();
firstNameController.dispose();
lastNameController.dispose();
emailController.dispose();
phoneController.dispose();
jobTitleController.dispose();
roleSearchController.dispose();
return super.close();
}
} }

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart'; import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_event.dart'; import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_event.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart'; import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart';
@ -25,7 +24,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (BuildContext context) => UsersBloc() create: (BuildContext context) => UsersBloc()
..add(const LoadCommunityAndSpacesEvent()) // ..add(const LoadCommunityAndSpacesEvent())
..add(const RoleEvent()), ..add(const RoleEvent()),
child: BlocConsumer<UsersBloc, UsersState>( child: BlocConsumer<UsersBloc, UsersState>(
listener: (context, state) {}, listener: (context, state) {},

View File

@ -26,13 +26,12 @@ class _EditUserDialogState extends State<EditUserDialog> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (BuildContext context) => UsersBloc() create: (BuildContext context) => UsersBloc()
..add(const LoadCommunityAndSpacesEvent()) // ..add(const LoadCommunityAndSpacesEvent())
..add(const RoleEvent()) ..add(const RoleEvent())
..add(GetUserByIdEvent(uuid: widget.userId)), ..add(GetUserByIdEvent(uuid: widget.userId)),
child: BlocConsumer<UsersBloc, UsersState>(listener: (context, state) { child: BlocConsumer<UsersBloc, UsersState>(listener: (context, state) {
if (state is SpacesLoadedState) { if (state is SpacesLoadedState) {
BlocProvider.of<UsersBloc>(context) BlocProvider.of<UsersBloc>(context).add(GetUserByIdEvent(uuid: widget.userId));
.add(GetUserByIdEvent(uuid: widget.userId));
} }
}, builder: (context, state) { }, builder: (context, state) {
final _blocRole = BlocProvider.of<UsersBloc>(context); final _blocRole = BlocProvider.of<UsersBloc>(context);
@ -40,8 +39,7 @@ class _EditUserDialogState extends State<EditUserDialog> {
return Dialog( return Dialog(
child: Container( child: Container(
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Colors.white, color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(20))),
borderRadius: BorderRadius.all(Radius.circular(20))),
width: 900, width: 900,
child: Column( child: Column(
children: [ children: [
@ -70,8 +68,7 @@ class _EditUserDialogState extends State<EditUserDialog> {
children: [ children: [
_buildStep1Indicator(1, "Basics", _blocRole), _buildStep1Indicator(1, "Basics", _blocRole),
_buildStep2Indicator(2, "Spaces", _blocRole), _buildStep2Indicator(2, "Spaces", _blocRole),
_buildStep3Indicator( _buildStep3Indicator(3, "Role & Permissions", _blocRole),
3, "Role & Permissions", _blocRole),
], ],
), ),
), ),
@ -119,15 +116,13 @@ class _EditUserDialogState extends State<EditUserDialog> {
if (currentStep < 3) { if (currentStep < 3) {
currentStep++; currentStep++;
if (currentStep == 2) { if (currentStep == 2) {
_blocRole _blocRole.add(CheckStepStatus(isEditUser: true));
.add(CheckStepStatus(isEditUser: true));
} else if (currentStep == 3) { } else if (currentStep == 3) {
_blocRole.add(const CheckSpacesStepStatus()); _blocRole.add(const CheckSpacesStepStatus());
} }
} else { } else {
_blocRole.add(EditInviteUsers( _blocRole
context: context, .add(EditInviteUsers(context: context, userId: widget.userId!));
userId: widget.userId!));
} }
}); });
}, },
@ -136,8 +131,7 @@ class _EditUserDialogState extends State<EditUserDialog> {
style: TextStyle( style: TextStyle(
color: (_blocRole.isCompleteSpaces == false || color: (_blocRole.isCompleteSpaces == false ||
_blocRole.isCompleteBasics == false || _blocRole.isCompleteBasics == false ||
_blocRole.isCompleteRolePermissions == _blocRole.isCompleteRolePermissions == false) &&
false) &&
currentStep == 3 currentStep == 3
? ColorsManager.grayColor ? ColorsManager.grayColor
: ColorsManager.secondaryColor), : ColorsManager.secondaryColor),
@ -210,12 +204,8 @@ class _EditUserDialogState extends State<EditUserDialog> {
label, label,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
color: currentStep == step color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
? ColorsManager.blackColor fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
: ColorsManager.greyColor,
fontWeight: currentStep == step
? FontWeight.bold
: FontWeight.normal,
), ),
), ),
], ],
@ -273,12 +263,8 @@ class _EditUserDialogState extends State<EditUserDialog> {
label, label,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
color: currentStep == step color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
? ColorsManager.blackColor fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
: ColorsManager.greyColor,
fontWeight: currentStep == step
? FontWeight.bold
: FontWeight.normal,
), ),
), ),
], ],
@ -335,12 +321,8 @@ class _EditUserDialogState extends State<EditUserDialog> {
label, label,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
color: currentStep == step color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
? ColorsManager.blackColor fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
: ColorsManager.greyColor,
fontWeight: currentStep == step
? FontWeight.bold
: FontWeight.normal,
), ),
), ),
], ],

View File

@ -1,14 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart'; import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_event.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart'; import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/view/build_tree_view.dart'; import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/style.dart';
class SpacesAccessView extends StatelessWidget { class SpacesAccessView extends StatelessWidget {
final String? userId; final String? userId;
@ -27,10 +22,8 @@ class SpacesAccessView extends StatelessWidget {
children: [ children: [
Text( Text(
'Spaces access', 'Spaces access',
style: context.textTheme.bodyLarge?.copyWith( style: context.textTheme.bodyLarge
fontWeight: FontWeight.w700, ?.copyWith(fontWeight: FontWeight.w700, fontSize: 20, color: Colors.black),
fontSize: 20,
color: Colors.black),
), ),
const SizedBox( const SizedBox(
height: 35, height: 35,
@ -42,77 +35,78 @@ class SpacesAccessView extends StatelessWidget {
const SizedBox( const SizedBox(
height: 25, height: 25,
), ),
Expanded( Expanded(child: SpaceTreeView(onSelect: () {}))
child: SizedBox( // Expanded(
child: Column( // child: SizedBox(
children: [ // child: Column(
Expanded( // children: [
flex: 2, // Expanded(
child: Container( // flex: 2,
decoration: const BoxDecoration( // child: Container(
color: ColorsManager.circleRolesBackground, // decoration: const BoxDecoration(
borderRadius: BorderRadius.only( // color: ColorsManager.circleRolesBackground,
topRight: Radius.circular(20), // borderRadius: BorderRadius.only(
topLeft: Radius.circular(20)), // topRight: Radius.circular(20),
), // topLeft: Radius.circular(20)),
child: Padding( // ),
padding: const EdgeInsets.all(8.0), // child: Padding(
child: Row( // padding: const EdgeInsets.all(8.0),
children: [ // child: Row(
Expanded( // children: [
child: Container( // Expanded(
decoration: BoxDecoration( // child: Container(
borderRadius: const BorderRadius.all( // decoration: BoxDecoration(
Radius.circular(20)), // borderRadius: const BorderRadius.all(
border: Border.all( // Radius.circular(20)),
color: ColorsManager.grayBorder)), // border: Border.all(
child: TextFormField( // color: ColorsManager.grayBorder)),
style: // child: TextFormField(
const TextStyle(color: Colors.black), // style:
// controller: _blocRole.firstNameController, // const TextStyle(color: Colors.black),
onChanged: (value) { // // controller: _blocRole.firstNameController,
_blocRole.add(SearchAnode( // onChanged: (value) {
nodes: _blocRole.updatedCommunities, // _blocRole.add(SearchAnode(
searchTerm: value)); // nodes: _blocRole.updatedCommunities,
}, // searchTerm: value));
decoration: textBoxDecoration(radios: 20)! // },
.copyWith( // decoration: textBoxDecoration(radios: 20)!
fillColor: Colors.white, // .copyWith(
suffixIcon: Padding( // fillColor: Colors.white,
padding: // suffixIcon: Padding(
const EdgeInsets.only(right: 16), // padding:
child: SvgPicture.asset( // const EdgeInsets.only(right: 16),
Assets.textFieldSearch, // child: SvgPicture.asset(
width: 24, // Assets.textFieldSearch,
height: 24, // width: 24,
), // height: 24,
), // ),
hintStyle: context.textTheme.bodyMedium // ),
?.copyWith( // hintStyle: context.textTheme.bodyMedium
fontWeight: FontWeight.w400, // ?.copyWith(
fontSize: 12, // fontWeight: FontWeight.w400,
color: ColorsManager.textGray), // fontSize: 12,
), // color: ColorsManager.textGray),
), // ),
), // ),
), // ),
], // ),
), // ],
), // ),
), // ),
), // ),
Expanded( // ),
flex: 7, // Expanded(
child: Container( // flex: 7,
color: ColorsManager.circleRolesBackground, // child: Container(
padding: const EdgeInsets.all(8.0), // color: ColorsManager.circleRolesBackground,
child: Container( // padding: const EdgeInsets.all(8.0),
color: ColorsManager.whiteColors, // child: Container(
child: TreeView(userId: userId)))) // color: ColorsManager.whiteColors,
], // child: TreeView(userId: userId))))
), // ],
), // ),
), // ),
// ),
], ],
), ),
), ),

View File

@ -13,6 +13,8 @@ import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/vi
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/de_activate_filter.dart'; import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/de_activate_filter.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/name_filter.dart'; import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/name_filter.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/user_table.dart'; import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/user_table.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -25,8 +27,7 @@ class UsersPage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final TextEditingController searchController = TextEditingController(); final TextEditingController searchController = TextEditingController();
Widget actionButton( Widget actionButton({bool isActive = false, required String title, Function()? onTap}) {
{bool isActive = false, required String title, Function()? onTap}) {
return InkWell( return InkWell(
onTap: onTap, onTap: onTap,
child: Padding( child: Padding(
@ -59,8 +60,7 @@ class UsersPage extends StatelessWidget {
: ColorsManager.disabledPink.withOpacity(0.5), : ColorsManager.disabledPink.withOpacity(0.5),
), ),
child: Padding( child: Padding(
padding: padding: const EdgeInsets.only(left: 10, right: 10, bottom: 5, top: 5),
const EdgeInsets.only(left: 10, right: 10, bottom: 5, top: 5),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@ -84,15 +84,12 @@ class UsersPage extends StatelessWidget {
} }
Widget changeIconStatus( Widget changeIconStatus(
{required String userId, {required String userId, required String status, required Function()? onTap}) {
required String status,
required Function()? onTap}) {
return Center( return Center(
child: InkWell( child: InkWell(
onTap: onTap, onTap: onTap,
child: Padding( child: Padding(
padding: padding: const EdgeInsets.only(left: 5, right: 5, bottom: 5, top: 5),
const EdgeInsets.only(left: 5, right: 5, bottom: 5, top: 5),
child: SvgPicture.asset( child: SvgPicture.asset(
status == "invited" status == "invited"
? Assets.invitedIcon ? Assets.invitedIcon
@ -160,6 +157,7 @@ class UsersPage extends StatelessWidget {
const SizedBox(width: 20), const SizedBox(width: 20),
InkWell( InkWell(
onTap: () { onTap: () {
context.read<SpaceTreeBloc>().add(ClearCachedData());
showDialog( showDialog(
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
@ -198,14 +196,10 @@ class UsersPage extends StatelessWidget {
context: context, context: context,
isSelected: _blocRole.currentSortOrder, isSelected: _blocRole.currentSortOrder,
aToZTap: () { aToZTap: () {
context context.read<UserTableBloc>().add(const SortUsersByNameAsc());
.read<UserTableBloc>()
.add(const SortUsersByNameAsc());
}, },
zToaTap: () { zToaTap: () {
context context.read<UserTableBloc>().add(const SortUsersByNameDesc());
.read<UserTableBloc>()
.add(const SortUsersByNameDesc());
}, },
); );
} }
@ -214,9 +208,8 @@ class UsersPage extends StatelessWidget {
for (var item in _blocRole.jobTitle) for (var item in _blocRole.jobTitle)
item: _blocRole.selectedJobTitles.contains(item), item: _blocRole.selectedJobTitles.contains(item),
}; };
final RenderBox overlay = Overlay.of(context) final RenderBox overlay =
.context Overlay.of(context).context.findRenderObject() as RenderBox;
.findRenderObject() as RenderBox;
showPopUpFilterMenu( showPopUpFilterMenu(
position: RelativeRect.fromLTRB( position: RelativeRect.fromLTRB(
@ -256,9 +249,8 @@ class UsersPage extends StatelessWidget {
for (var item in _blocRole.roleTypes) for (var item in _blocRole.roleTypes)
item: _blocRole.selectedRoles.contains(item), item: _blocRole.selectedRoles.contains(item),
}; };
final RenderBox overlay = Overlay.of(context) final RenderBox overlay =
.context Overlay.of(context).context.findRenderObject() as RenderBox;
.findRenderObject() as RenderBox;
showPopUpFilterMenu( showPopUpFilterMenu(
position: RelativeRect.fromLTRB( position: RelativeRect.fromLTRB(
overlay.size.width / 4, overlay.size.width / 4,
@ -278,10 +270,9 @@ class UsersPage extends StatelessWidget {
.map((entry) => entry.key) .map((entry) => entry.key)
.toList(); .toList();
Navigator.of(context).pop(); Navigator.of(context).pop();
context.read<UserTableBloc>().add( context.read<UserTableBloc>().add(FilterUsersByRoleEvent(
FilterUsersByRoleEvent( selectedRoles: selectedItems,
selectedRoles: selectedItems, sortOrder: _blocRole.currentSortRole));
sortOrder: _blocRole.currentSortRole));
}, },
onSortAtoZ: (v) { onSortAtoZ: (v) {
_blocRole.currentSortRole = v; _blocRole.currentSortRole = v;
@ -296,14 +287,10 @@ class UsersPage extends StatelessWidget {
context: context, context: context,
isSelected: _blocRole.currentSortOrder, isSelected: _blocRole.currentSortOrder,
aToZTap: () { aToZTap: () {
context context.read<UserTableBloc>().add(const DateNewestToOldestEvent());
.read<UserTableBloc>()
.add(const DateNewestToOldestEvent());
}, },
zToaTap: () { zToaTap: () {
context context.read<UserTableBloc>().add(const DateOldestToNewestEvent());
.read<UserTableBloc>()
.add(const DateOldestToNewestEvent());
}, },
); );
} }
@ -312,9 +299,8 @@ class UsersPage extends StatelessWidget {
for (var item in _blocRole.createdBy) for (var item in _blocRole.createdBy)
item: _blocRole.selectedCreatedBy.contains(item), item: _blocRole.selectedCreatedBy.contains(item),
}; };
final RenderBox overlay = Overlay.of(context) final RenderBox overlay =
.context Overlay.of(context).context.findRenderObject() as RenderBox;
.findRenderObject() as RenderBox;
showPopUpFilterMenu( showPopUpFilterMenu(
position: RelativeRect.fromLTRB( position: RelativeRect.fromLTRB(
overlay.size.width / 1, overlay.size.width / 1,
@ -352,9 +338,8 @@ class UsersPage extends StatelessWidget {
item: _blocRole.selectedStatuses.contains(item), item: _blocRole.selectedStatuses.contains(item),
}; };
final RenderBox overlay = Overlay.of(context) final RenderBox overlay =
.context Overlay.of(context).context.findRenderObject() as RenderBox;
.findRenderObject() as RenderBox;
showPopUpFilterMenu( showPopUpFilterMenu(
position: RelativeRect.fromLTRB( position: RelativeRect.fromLTRB(
overlay.size.width / 0, overlay.size.width / 0,
@ -391,14 +376,10 @@ class UsersPage extends StatelessWidget {
context: context, context: context,
isSelected: _blocRole.currentSortOrderDate, isSelected: _blocRole.currentSortOrderDate,
aToZTap: () { aToZTap: () {
context context.read<UserTableBloc>().add(const DateNewestToOldestEvent());
.read<UserTableBloc>()
.add(const DateNewestToOldestEvent());
}, },
zToaTap: () { zToaTap: () {
context context.read<UserTableBloc>().add(const DateOldestToNewestEvent());
.read<UserTableBloc>()
.add(const DateOldestToNewestEvent());
}, },
); );
} }
@ -425,23 +406,17 @@ class UsersPage extends StatelessWidget {
Text(user.createdTime ?? ''), Text(user.createdTime ?? ''),
Text(user.invitedBy), Text(user.invitedBy),
status( status(
status: user.isEnabled == false status: user.isEnabled == false ? 'disabled' : user.status,
? 'disabled'
: user.status,
), ),
changeIconStatus( changeIconStatus(
status: user.isEnabled == false status: user.isEnabled == false ? 'disabled' : user.status,
? 'disabled'
: user.status,
userId: user.uuid, userId: user.uuid,
onTap: user.status != "invited" onTap: user.status != "invited"
? () { ? () {
context.read<UserTableBloc>().add( context.read<UserTableBloc>().add(ChangeUserStatus(
ChangeUserStatus( userId: user.uuid,
userId: user.uuid, newStatus:
newStatus: user.isEnabled == false user.isEnabled == false ? 'disabled' : user.status));
? 'disabled'
: user.status));
} }
: null, : null,
), ),
@ -452,12 +427,12 @@ class UsersPage extends StatelessWidget {
isActive: true, isActive: true,
title: "Edit", title: "Edit",
onTap: () { onTap: () {
context.read<SpaceTreeBloc>().add(ClearCachedData());
showDialog( showDialog(
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
builder: (BuildContext context) { builder: (BuildContext context) {
return EditUserDialog( return EditUserDialog(userId: user.uuid);
userId: user.uuid);
}, },
).then((v) { ).then((v) {
if (v != null) { if (v != null) {
@ -478,13 +453,10 @@ class UsersPage extends StatelessWidget {
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
builder: (BuildContext context) { builder: (BuildContext context) {
return DeleteUserDialog( return DeleteUserDialog(onTapDelete: () async {
onTapDelete: () async {
try { try {
_blocRole.add(DeleteUserEvent( _blocRole.add(DeleteUserEvent(user.uuid, context));
user.uuid, context)); await Future.delayed(const Duration(seconds: 2));
await Future.delayed(
const Duration(seconds: 2));
return true; return true;
} catch (e) { } catch (e) {
return false; return false;
@ -514,20 +486,14 @@ class UsersPage extends StatelessWidget {
visiblePagesCount: 4, visiblePagesCount: 4,
buttonRadius: 10, buttonRadius: 10,
selectedButtonColor: ColorsManager.secondaryColor, selectedButtonColor: ColorsManager.secondaryColor,
buttonUnSelectedBorderColor: buttonUnSelectedBorderColor: ColorsManager.grayBorder,
ColorsManager.grayBorder, lastPageIcon: const Icon(Icons.keyboard_double_arrow_right),
lastPageIcon: firstPageIcon: const Icon(Icons.keyboard_double_arrow_left),
const Icon(Icons.keyboard_double_arrow_right), totalPages:
firstPageIcon: (_blocRole.totalUsersCount.length / _blocRole.itemsPerPage).ceil(),
const Icon(Icons.keyboard_double_arrow_left),
totalPages: (_blocRole.totalUsersCount.length /
_blocRole.itemsPerPage)
.ceil(),
currentPage: _blocRole.currentPage, currentPage: _blocRole.currentPage,
onPageChanged: (int pageNumber) { onPageChanged: (int pageNumber) {
context context.read<UserTableBloc>().add(ChangePage(pageNumber));
.read<UserTableBloc>()
.add(ChangePage(pageNumber));
}, },
), ),
), ),

View File

@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bl
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/users_page.dart'; import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/users_page.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart';
class RolesAndPermissionPage extends StatelessWidget { class RolesAndPermissionPage extends StatelessWidget {
@ -27,11 +28,10 @@ class RolesAndPermissionPage extends StatelessWidget {
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: WebScaffold( : WebScaffold(
enableMenuSidebar: false, enableMenuSidebar: false,
appBarTitle: FittedBox( appBarTitle: Text(
child: Text( 'Roles & Permissions',
'Roles & Permissions', style:
style: Theme.of(context).textTheme.headlineLarge, ResponsiveTextTheme.of(context).deviceManagementTitle,
),
), ),
rightBody: const NavigateHomeGridView(), rightBody: const NavigateHomeGridView(),
centerBody: Row( centerBody: Row(

View File

@ -7,7 +7,6 @@ import 'package:syncrow_web/pages/routines/widgets/main_routine_view/routine_vie
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart'; import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/snack_bar.dart';
class RoutinesView extends StatefulWidget { class RoutinesView extends StatefulWidget {
const RoutinesView({super.key}); const RoutinesView({super.key});

View File

@ -1,4 +1,3 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
@ -6,18 +5,8 @@ import 'package:syncrow_web/pages/space_tree/bloc/space_tree_state.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/services/space_mana_api.dart';
import 'package:syncrow_web/utils/constants/strings_manager.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> { class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
// String selectedCommunityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9';
// String selectedSpaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6';
final TextEditingController textController = TextEditingController();
// String selectedCommunityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9';
// String selectedSpaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6';
SpaceTreeBloc() : super(const SpaceTreeState()) { SpaceTreeBloc() : super(const SpaceTreeState()) {
on<InitialEvent>(_fetchSpaces); on<InitialEvent>(_fetchSpaces);
on<OnCommunityExpanded>(_onCommunityExpanded); on<OnCommunityExpanded>(_onCommunityExpanded);
@ -25,6 +14,8 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
on<OnCommunitySelected>(_onCommunitySelected); on<OnCommunitySelected>(_onCommunitySelected);
on<OnSpaceSelected>(_onSpaceSelected); on<OnSpaceSelected>(_onSpaceSelected);
on<SearchQueryEvent>(_onSearch); on<SearchQueryEvent>(_onSearch);
on<ClearAllData>(_clearAllData);
on<ClearCachedData>(_clearCachedData);
} }
_fetchSpaces(InitialEvent event, Emitter<SpaceTreeState> emit) async { _fetchSpaces(InitialEvent event, Emitter<SpaceTreeState> emit) async {
@ -37,8 +28,8 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
List<CommunityModel> updatedCommunities = await Future.wait( List<CommunityModel> updatedCommunities = await Future.wait(
communities.map((community) async { communities.map((community) async {
List<SpaceModel> spaces = await CommunitySpaceManagementApi() List<SpaceModel> spaces =
.getSpaceHierarchy(community.uuid, projectUuid); await CommunitySpaceManagementApi().getSpaceHierarchy(community.uuid, projectUuid);
return CommunityModel( return CommunityModel(
uuid: community.uuid, uuid: community.uuid,
@ -53,19 +44,15 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
); );
emit(state.copyWith( emit(state.copyWith(
communitiesList: updatedCommunities, communitiesList: updatedCommunities, expandedCommunity: [], expandedSpaces: []));
expandedCommunity: [],
expandedSpaces: []));
} catch (e) { } catch (e) {
emit(SpaceTreeErrorState('Error loading communities and spaces: $e')); emit(SpaceTreeErrorState('Error loading communities and spaces: $e'));
} }
} }
_onCommunityExpanded( _onCommunityExpanded(OnCommunityExpanded event, Emitter<SpaceTreeState> emit) async {
OnCommunityExpanded event, Emitter<SpaceTreeState> emit) async {
try { try {
List<String> updatedExpandedCommunityList = List<String> updatedExpandedCommunityList = List.from(state.expandedCommunities);
List.from(state.expandedCommunities);
if (updatedExpandedCommunityList.contains(event.communityId)) { if (updatedExpandedCommunityList.contains(event.communityId)) {
updatedExpandedCommunityList.remove(event.communityId); updatedExpandedCommunityList.remove(event.communityId);
@ -97,17 +84,14 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
} }
} }
_onCommunitySelected( _onCommunitySelected(OnCommunitySelected event, Emitter<SpaceTreeState> emit) async {
OnCommunitySelected event, Emitter<SpaceTreeState> emit) async {
try { try {
List<String> updatedSelectedCommunities = List<String> updatedSelectedCommunities =
List.from(state.selectedCommunities.toSet().toList()); List.from(state.selectedCommunities.toSet().toList());
List<String> updatedSelectedSpaces = List<String> updatedSelectedSpaces = List.from(state.selectedSpaces.toSet().toList());
List.from(state.selectedSpaces.toSet().toList()); List<String> updatedSoldChecks = List.from(state.soldCheck.toSet().toList());
List<String> updatedSoldChecks = Map<String, List<String>> communityAndSpaces = Map.from(state.selectedCommunityAndSpaces);
List.from(state.soldCheck.toSet().toList()); List<String> selectedSpacesInCommunity = communityAndSpaces[event.communityId] ?? [];
Map<String, List<String>> communityAndSpaces =
Map.from(state.selectedCommunityAndSpaces);
List<String> childrenIds = _getAllChildIds(event.children); List<String> childrenIds = _getAllChildIds(event.children);
@ -115,14 +99,16 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
// Select the community and all its children // Select the community and all its children
updatedSelectedCommunities.add(event.communityId); updatedSelectedCommunities.add(event.communityId);
updatedSelectedSpaces.addAll(childrenIds); updatedSelectedSpaces.addAll(childrenIds);
selectedSpacesInCommunity.addAll(childrenIds);
} else { } else {
// Unselect the community and all its children // Unselect the community and all its children
updatedSelectedCommunities.remove(event.communityId); updatedSelectedCommunities.remove(event.communityId);
updatedSelectedSpaces.removeWhere(childrenIds.contains); updatedSelectedSpaces.removeWhere(childrenIds.contains);
updatedSoldChecks.removeWhere(childrenIds.contains); updatedSoldChecks.removeWhere(childrenIds.contains);
selectedSpacesInCommunity.removeWhere(childrenIds.contains);
} }
communityAndSpaces[event.communityId] = updatedSelectedSpaces; communityAndSpaces[event.communityId] = selectedSpacesInCommunity;
emit(state.copyWith( emit(state.copyWith(
selectedCommunities: updatedSelectedCommunities, selectedCommunities: updatedSelectedCommunities,
@ -138,12 +124,11 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
try { try {
List<String> updatedSelectedCommunities = List<String> updatedSelectedCommunities =
List.from(state.selectedCommunities.toSet().toList()); List.from(state.selectedCommunities.toSet().toList());
List<String> updatedSelectedSpaces = List<String> updatedSelectedSpaces = List.from(state.selectedSpaces.toSet().toList());
List.from(state.selectedSpaces.toSet().toList()); List<String> updatedSoldChecks = List.from(state.soldCheck.toSet().toList());
List<String> updatedSoldChecks = Map<String, List<String>> communityAndSpaces = Map.from(state.selectedCommunityAndSpaces);
List.from(state.soldCheck.toSet().toList());
Map<String, List<String>> communityAndSpaces = List<String> selectedSpacesInCommunity = communityAndSpaces[event.communityModel.uuid] ?? [];
Map.from(state.selectedCommunityAndSpaces);
List<String> childrenIds = _getAllChildIds(event.children); List<String> childrenIds = _getAllChildIds(event.children);
bool isChildSelected = false; bool isChildSelected = false;
@ -158,16 +143,17 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
!updatedSoldChecks.contains(event.spaceId)) { !updatedSoldChecks.contains(event.spaceId)) {
// First click: Select the space and all its children // First click: Select the space and all its children
updatedSelectedSpaces.add(event.spaceId); updatedSelectedSpaces.add(event.spaceId);
updatedSelectedCommunities.add(event.communityId); updatedSelectedCommunities.add(event.communityModel.uuid);
selectedSpacesInCommunity.add(event.spaceId);
if (childrenIds.isNotEmpty) { if (childrenIds.isNotEmpty) {
updatedSelectedSpaces.addAll(childrenIds); updatedSelectedSpaces.addAll(childrenIds);
selectedSpacesInCommunity.addAll(childrenIds);
} }
List<String> spaces = List<String> spaces = _getThePathToChild(event.communityModel.uuid, event.spaceId);
_getThePathToChild(event.communityId, event.spaceId);
for (String space in spaces) { for (String space in spaces) {
if (!updatedSelectedSpaces.contains(space) && if (!updatedSelectedSpaces.contains(space) && !updatedSoldChecks.contains(space)) {
!updatedSoldChecks.contains(space)) {
updatedSoldChecks.add(space); updatedSoldChecks.add(space);
} }
} }
@ -175,29 +161,42 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
childrenIds.isNotEmpty && childrenIds.isNotEmpty &&
isChildSelected) { isChildSelected) {
// Second click: Unselect space but keep children // Second click: Unselect space but keep children
selectedSpacesInCommunity.remove(event.spaceId);
updatedSelectedSpaces.remove(event.spaceId); updatedSelectedSpaces.remove(event.spaceId);
updatedSoldChecks.add(event.spaceId); updatedSoldChecks.add(event.spaceId);
} else { } else {
// Third click: Unselect space and all its children // Third click: Unselect space and all its children
selectedSpacesInCommunity.remove(event.spaceId);
updatedSelectedSpaces.remove(event.spaceId); updatedSelectedSpaces.remove(event.spaceId);
if (childrenIds.isNotEmpty) { if (childrenIds.isNotEmpty) {
updatedSelectedSpaces.removeWhere(childrenIds.contains); updatedSelectedSpaces.removeWhere(childrenIds.contains);
updatedSoldChecks.removeWhere(childrenIds.contains); updatedSoldChecks.removeWhere(childrenIds.contains);
selectedSpacesInCommunity.removeWhere(childrenIds.contains);
} }
updatedSoldChecks.remove(event.spaceId); updatedSoldChecks.remove(event.spaceId);
List<String> parents = List<String> parents =
_getThePathToChild(event.communityId, event.spaceId); _getThePathToChild(event.communityModel.uuid, event.spaceId).toSet().toList();
if (!_parentSelected(parents, updatedSelectedSpaces)) {
if (updatedSelectedSpaces.isEmpty) {
updatedSoldChecks.removeWhere(parents.contains); updatedSoldChecks.removeWhere(parents.contains);
} updatedSelectedCommunities.remove(event.communityModel.uuid);
if (!_anySpacesSelectedInCommunity( } else {
event.communityId, updatedSelectedSpaces, updatedSoldChecks)) { // Check if any parent has selected children
updatedSelectedCommunities.remove(event.communityId); for (String space in parents) {
if (!_noChildrenSelected(event.communityModel, space, updatedSelectedSpaces, parents)) {
updatedSoldChecks.remove(space);
}
}
if (!_anySpacesSelectedInCommunity(
event.communityModel, updatedSelectedSpaces, updatedSoldChecks)) {
updatedSelectedCommunities.remove(event.communityModel.uuid);
}
} }
} }
communityAndSpaces[event.communityId] = updatedSelectedSpaces; communityAndSpaces[event.communityModel.uuid] = selectedSpacesInCommunity;
emit(state.copyWith( emit(state.copyWith(
selectedCommunities: updatedSelectedCommunities.toSet().toList(), selectedCommunities: updatedSelectedCommunities.toSet().toList(),
@ -210,12 +209,24 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
} }
} }
_parentSelected(List<String> parents, List<String> selectedSpaces) { _noChildrenSelected(
for (String space in parents) { CommunityModel community, String spaceId, List<String> selectedSpaces, List<String> parents) {
if (selectedSpaces.contains(space)) { if (selectedSpaces.contains(spaceId)) {
return true; return true;
}
List<SpaceModel> children = _getAllChildSpaces(community.spaces);
for (var child in children) {
if (spaceId == child.uuid) {
List<String> ids = _getAllChildIds(child.children);
for (var id in ids) {
if (selectedSpaces.contains(id)) {
return true;
}
}
} }
} }
return false; return false;
} }
@ -226,11 +237,10 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
// Filter communities and expand only those that match the query // Filter communities and expand only those that match the query
filteredCommunity = communities.where((community) { filteredCommunity = communities.where((community) {
final containsQueryInCommunity = community.name final containsQueryInCommunity =
.toLowerCase() community.name.toLowerCase().contains(event.searchQuery.toLowerCase());
.contains(event.searchQuery.toLowerCase()); final containsQueryInSpaces =
final containsQueryInSpaces = community.spaces.any( community.spaces.any((space) => _containsQuery(space, event.searchQuery.toLowerCase()));
(space) => _containsQuery(space, event.searchQuery.toLowerCase()));
return containsQueryInCommunity || containsQueryInSpaces; return containsQueryInCommunity || containsQueryInSpaces;
}).toList(); }).toList();
@ -244,11 +254,47 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
} }
} }
_clearAllData(ClearAllData event, Emitter<SpaceTreeState> emit) async {
try {
emit(state.copyWith(
communitiesList: [],
filteredCommunity: [],
isSearching: false,
soldCheck: [],
selectedSpaces: [],
selectedCommunities: [],
selectedCommunityAndSpaces: {},
searchQuery: '',
expandedSpaces: [],
expandedCommunity: []));
} catch (e) {
emit(const SpaceTreeErrorState('Something went wrong'));
}
}
_clearCachedData(ClearCachedData event, Emitter<SpaceTreeState> emit) async {
try {
emit(state.copyWith(
communitiesList: state.communityList,
filteredCommunity: [],
isSearching: false,
soldCheck: [],
selectedSpaces: [],
selectedCommunities: [],
selectedCommunityAndSpaces: {},
searchQuery: '',
expandedSpaces: [],
expandedCommunity: []));
} catch (e) {
emit(const SpaceTreeErrorState('Something went wrong'));
}
}
// Helper function to determine if any space or its children match the search query // Helper function to determine if any space or its children match the search query
bool _containsQuery(SpaceModel space, String query) { bool _containsQuery(SpaceModel space, String query) {
final matchesSpace = space.name.toLowerCase().contains(query); final matchesSpace = space.name.toLowerCase().contains(query);
final matchesChildren = space.children.any((child) => final matchesChildren =
_containsQuery(child, query)); // Recursive check for children space.children.any((child) => _containsQuery(child, query)); // Recursive check for children
return matchesSpace || matchesChildren; return matchesSpace || matchesChildren;
} }
@ -259,22 +305,26 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
ids.add(child.uuid!); ids.add(child.uuid!);
ids.addAll(_getAllChildIds(child.children)); ids.addAll(_getAllChildIds(child.children));
} }
return ids; return ids.toSet().toList();
} }
bool _anySpacesSelectedInCommunity(String communityId, List<SpaceModel> _getAllChildSpaces(List<SpaceModel> spaces) {
List<String> selectedSpaces, List<String> partialCheckedList) { List<SpaceModel> children = [];
for (var child in spaces) {
children.add(child);
children.addAll(_getAllChildSpaces(child.children));
}
return children;
}
bool _anySpacesSelectedInCommunity(
CommunityModel community, List<String> selectedSpaces, List<String> partialCheckedList) {
bool result = false; bool result = false;
for (var community in state.communityList) { List<String> ids = _getAllChildIds(community.spaces);
if (community.uuid == communityId) { for (var id in ids) {
List<String> ids = _getAllChildIds(community.spaces); result = selectedSpaces.contains(id) || partialCheckedList.contains(id);
for (var id in ids) { if (result) {
result = return result;
selectedSpaces.contains(id) || partialCheckedList.contains(id);
if (result) {
return result;
}
}
} }
} }
return result; return result;
@ -297,8 +347,7 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
return ids; return ids;
} }
List<String> _getAllParentsIds( List<String> _getAllParentsIds(SpaceModel child, String spaceId, List<String> listIds) {
SpaceModel child, String spaceId, List<String> listIds) {
List<String> ids = listIds; List<String> ids = listIds;
ids.add(child.uuid ?? ''); ids.add(child.uuid ?? '');
@ -322,7 +371,6 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
@override @override
Future<void> close() async { Future<void> close() async {
textController.dispose();
super.close(); super.close();
} }
} }

View File

@ -1,4 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
class SpaceTreeEvent extends Equatable { class SpaceTreeEvent extends Equatable {
@ -49,14 +50,14 @@ class OnSpaceExpanded extends SpaceTreeEvent {
} }
class OnSpaceSelected extends SpaceTreeEvent { class OnSpaceSelected extends SpaceTreeEvent {
final String communityId;
final String spaceId; final String spaceId;
final List<SpaceModel> children; final List<SpaceModel> children;
final CommunityModel communityModel;
const OnSpaceSelected(this.communityId, this.spaceId, this.children); const OnSpaceSelected(this.communityModel, this.spaceId, this.children);
@override @override
List<Object> get props => [communityId, spaceId, children]; List<Object> get props => [communityModel, spaceId, children];
} }
class SearchQueryEvent extends SpaceTreeEvent { class SearchQueryEvent extends SpaceTreeEvent {
@ -67,3 +68,7 @@ class SearchQueryEvent extends SpaceTreeEvent {
@override @override
List<Object> get props => [searchQuery]; List<Object> get props => [searchQuery];
} }
class ClearAllData extends SpaceTreeEvent {}
class ClearCachedData extends SpaceTreeEvent {}

View File

@ -172,11 +172,8 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
.expandedSpaces .expandedSpaces
.contains(space.uuid), .contains(space.uuid),
onItemSelected: () { onItemSelected: () {
context context.read<SpaceTreeBloc>().add(
.read<SpaceTreeBloc>() OnSpaceSelected(community, space.uuid ?? '',
.add(OnSpaceSelected(
community.uuid,
space.uuid ?? '',
space.children)); space.children));
widget.onSelect(); widget.onSelect();
}, },
@ -196,10 +193,7 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
isSoldCheck: state.soldCheck isSoldCheck: state.soldCheck
.contains(space.uuid), .contains(space.uuid),
children: _buildNestedSpaces( children: _buildNestedSpaces(
context, context, state, space, community),
state,
space,
community.uuid),
); );
}).toList(), }).toList(),
), ),
@ -281,8 +275,8 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
}); });
} }
List<Widget> _buildNestedSpaces(BuildContext context, SpaceTreeState state, List<Widget> _buildNestedSpaces(
SpaceModel space, String communityId) { BuildContext context, SpaceTreeState state, SpaceModel space, CommunityModel community) {
return space.children.map((child) { return space.children.map((child) {
return CustomExpansionTileSpaceTree( return CustomExpansionTileSpaceTree(
isSelected: state.selectedSpaces.contains(child.uuid) || isSelected: state.selectedSpaces.contains(child.uuid) ||
@ -291,16 +285,15 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
title: child.name, title: child.name,
isExpanded: state.expandedSpaces.contains(child.uuid), isExpanded: state.expandedSpaces.contains(child.uuid),
onItemSelected: () { onItemSelected: () {
context.read<SpaceTreeBloc>().add( context
OnSpaceSelected(communityId, child.uuid ?? '', child.children)); .read<SpaceTreeBloc>()
.add(OnSpaceSelected(community, child.uuid ?? '', child.children));
widget.onSelect(); widget.onSelect();
}, },
onExpansionChanged: () { onExpansionChanged: () {
context context.read<SpaceTreeBloc>().add(OnSpaceExpanded(community.uuid, child.uuid ?? ''));
.read<SpaceTreeBloc>()
.add(OnSpaceExpanded(communityId, child.uuid ?? ''));
}, },
children: _buildNestedSpaces(context, state, child, communityId), children: _buildNestedSpaces(context, state, child, community),
); );
}).toList(); }).toList();
} }

View File

@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/spaces_management/structure_selector/view/cent
import 'package:syncrow_web/services/product_api.dart'; import 'package:syncrow_web/services/product_api.dart';
import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/services/space_mana_api.dart';
import 'package:syncrow_web/services/space_model_mang_api.dart'; import 'package:syncrow_web/services/space_model_mang_api.dart';
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart';
class SpaceManagementPage extends StatefulWidget { class SpaceManagementPage extends StatefulWidget {
@ -39,8 +40,10 @@ class SpaceManagementPageState extends State<SpaceManagementPage> {
), ),
], ],
child: WebScaffold( child: WebScaffold(
appBarTitle: Text('Space Management', appBarTitle: Text(
style: Theme.of(context).textTheme.headlineLarge), 'Space Management',
style: ResponsiveTextTheme.of(context).deviceManagementTitle,
),
enableMenuSidebar: false, enableMenuSidebar: false,
centerBody: CenterBodyWidget(), centerBody: CenterBodyWidget(),
rightBody: const NavigateHomeGridView(), rightBody: const NavigateHomeGridView(),

View File

@ -4,9 +4,6 @@ import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_state.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_state.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
import 'package:syncrow_web/services/space_model_mang_api.dart'; import 'package:syncrow_web/services/space_model_mang_api.dart';
import 'package:syncrow_web/utils/constants/strings_manager.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
class SpaceModelBloc extends Bloc<SpaceModelEvent, SpaceModelState> { class SpaceModelBloc extends Bloc<SpaceModelEvent, SpaceModelState> {
final SpaceModelManagementApi api; final SpaceModelManagementApi api;
@ -17,6 +14,7 @@ class SpaceModelBloc extends Bloc<SpaceModelEvent, SpaceModelState> {
}) : super(SpaceModelLoaded(spaceModels: initialSpaceModels)) { }) : super(SpaceModelLoaded(spaceModels: initialSpaceModels)) {
on<CreateSpaceModel>(_onCreateSpaceModel); on<CreateSpaceModel>(_onCreateSpaceModel);
on<UpdateSpaceModel>(_onUpdateSpaceModel); on<UpdateSpaceModel>(_onUpdateSpaceModel);
on<DeleteSpaceModel>(_onDeleteSpaceModel);
} }
Future<void> _onCreateSpaceModel( Future<void> _onCreateSpaceModel(
@ -50,7 +48,7 @@ class SpaceModelBloc extends Bloc<SpaceModelEvent, SpaceModelState> {
final projectUuid = await ProjectManager.getProjectUUID() ?? ''; final projectUuid = await ProjectManager.getProjectUUID() ?? '';
final newSpaceModel = final newSpaceModel =
await api.getSpaceModel(event.spaceModelUuid ?? '', projectUuid); await api.getSpaceModel(event.spaceModelUuid, projectUuid);
if (newSpaceModel != null) { if (newSpaceModel != null) {
final updatedSpaceModels = currentState.spaceModels.map((model) { final updatedSpaceModels = currentState.spaceModels.map((model) {
return model.uuid == event.spaceModelUuid ? newSpaceModel : model; return model.uuid == event.spaceModelUuid ? newSpaceModel : model;
@ -62,4 +60,28 @@ class SpaceModelBloc extends Bloc<SpaceModelEvent, SpaceModelState> {
} }
} }
} }
Future<void> _onDeleteSpaceModel(
DeleteSpaceModel event, Emitter<SpaceModelState> emit) async {
final currentState = state;
if (currentState is SpaceModelLoaded) {
try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
final deletedSuccessfully =
await api.deleteSpaceModel(event.spaceModelUuid, projectUuid);
if (deletedSuccessfully) {
final updatedSpaceModels = currentState.spaceModels
.where((model) => model.uuid != event.spaceModelUuid)
.toList();
emit(SpaceModelLoaded(spaceModels: updatedSpaceModels));
}
} catch (e) {
emit(SpaceModelError(message: e.toString()));
}
}
}
} }

View File

@ -34,3 +34,12 @@ class UpdateSpaceModel extends SpaceModelEvent {
@override @override
List<Object?> get props => [spaceModelUuid]; List<Object?> get props => [spaceModelUuid];
} }
class DeleteSpaceModel extends SpaceModelEvent {
final String spaceModelUuid;
DeleteSpaceModel({required this.spaceModelUuid});
@override
List<Object?> get props => [spaceModelUuid];
}

View File

@ -90,7 +90,11 @@ class SpaceModelPage extends StatelessWidget {
}, },
child: Container( child: Container(
margin: const EdgeInsets.all(8.0), margin: const EdgeInsets.all(8.0),
child: SpaceModelCardWidget(model: model), child: SpaceModelCardWidget(
model: model,
pageContext: context,
topActionsDisabled: false,
),
)); ));
}, },
), ),

View File

@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class DeleteSpaceModelDialog extends StatelessWidget {
final VoidCallback onConfirmDelete;
const DeleteSpaceModelDialog({Key? key, required this.onConfirmDelete})
: super(key: key);
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
backgroundColor: ColorsManager.whiteColors,
title: Center(
child: Text(
"Delete Space Model",
textAlign: TextAlign.center,
style: Theme.of(context)
.textTheme
.headlineLarge
?.copyWith(color: ColorsManager.blackColor),
),
),
content: SizedBox(
width: screenWidth * 0.4,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Are you sure you want to delete?",
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 15),
Text(
"The existing sub-spaces, devices, and routines will remain associated with the spaces, but the connection will be removed.",
textAlign: TextAlign.center,
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(color: ColorsManager.lightGrayColor),
),
],
),
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 200,
child: CancelButton(
onPressed: () {
Navigator.of(context).pop(); // Close dialog
},
label: "Cancel",
),
),
const SizedBox(width: 10),
SizedBox(
width: 200,
child: DefaultButton(
onPressed: () {
Navigator.of(context).pop(); // Close dialog
onConfirmDelete(); // Execute delete action
},
backgroundColor: ColorsManager.semiTransparentRed,
borderRadius: 10,
foregroundColor: ColorsManager.whiteColors,
child: const Text('Delete'),
),
),
],
),
],
);
}
}

View File

@ -10,6 +10,9 @@ import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/l
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/linking_attention_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/linking_attention_dialog.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/overwrite_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/overwrite_dialog.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_event.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/delete_space_model_dialog.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dynamic_product_widget.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dynamic_product_widget.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dynamic_room_widget.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dynamic_room_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
@ -17,8 +20,15 @@ import 'package:syncrow_web/utils/constants/assets.dart';
class SpaceModelCardWidget extends StatelessWidget { class SpaceModelCardWidget extends StatelessWidget {
final SpaceTemplateModel model; final SpaceTemplateModel model;
final BuildContext? pageContext;
final bool topActionsDisabled;
const SpaceModelCardWidget({Key? key, required this.model}) : super(key: key); const SpaceModelCardWidget({
Key? key,
required this.model,
this.pageContext,
this.topActionsDisabled = true,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -193,7 +203,48 @@ class SpaceModelCardWidget extends StatelessWidget {
), ),
), ),
], ],
) ),
Expanded(
child: Text(
model.modelName,
style:
Theme.of(context).textTheme.headlineMedium?.copyWith(
color: Colors.black,
fontWeight: FontWeight.bold,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
if (!topActionsDisabled)
GestureDetector(
onTap: () => _showDeleteDialog(context),
child: Container(
width: 36, // Adjust size as needed
height: 36,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
spreadRadius: 2,
blurRadius: 5,
offset: const Offset(0, 2),
),
],
),
child: Center(
child: SvgPicture.asset(
Assets.deleteSpaceModel, // Your actual SVG path
width: 20,
height: 20,
colorFilter: const ColorFilter.mode(
Colors.grey, BlendMode.srcIn),
),
),
),
),
], ],
), ),
if (!showOnlyName) ...[ if (!showOnlyName) ...[
@ -253,4 +304,22 @@ class SpaceModelCardWidget extends StatelessWidget {
}, },
); );
} }
void _showDeleteDialog(BuildContext context) {
showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext dialogContext) {
return DeleteSpaceModelDialog(
onConfirmDelete: () {
if (pageContext != null) {
pageContext!.read<SpaceModelBloc>().add(
DeleteSpaceModel(spaceModelUuid: model.uuid ?? ''),
);
}
},
);
},
);
}
} }

View File

@ -86,12 +86,23 @@ class SpaceModelManagementApi {
Future validateSpaceModel(String projectId, List<String> spaceUuids) async { Future validateSpaceModel(String projectId, List<String> spaceUuids) async {
final response = await HTTPService().post( final response = await HTTPService().post(
path: path: ApiEndpoints.validateSpaceModel
ApiEndpoints.validateSpaceModel.replaceAll('{projectId}', projectId), .replaceAll('{projectId}', projectId),
showServerMessage: true,
body: {"spacesUuids": spaceUuids},
expectedResponseModel: (json) {
return json;
});
}
Future<bool> deleteSpaceModel(String spaceModelUuid, String projectId) async {
final response = await HTTPService().delete(
path: ApiEndpoints.getSpaceModel
.replaceAll('{projectId}', projectId)
.replaceAll('{spaceModelUuid}', spaceModelUuid),
showServerMessage: true, showServerMessage: true,
body: {"spacesUuids": spaceUuids},
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json['success'] ?? false;
}, },
); );
return response; return response;

View File

@ -14,7 +14,7 @@ class UserPermissionApi {
Future<List<RolesUserModel>> fetchUsers(String projectId) async { Future<List<RolesUserModel>> fetchUsers(String projectId) async {
try { try {
final response = await _httpService.get( final response = await _httpService.get(
path: ApiEndpoints.getUsers.replaceAll('{projectUuid}', projectId), path: ApiEndpoints.getUsers.replaceAll('{projectId}', projectId),
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
debugPrint('fetchUsers Response: $json'); debugPrint('fetchUsers Response: $json');

View File

@ -71,4 +71,5 @@ abstract class ColorsManager {
static const Color lightGrayBorderColor = Color(0xB2D5D5D5); static const Color lightGrayBorderColor = Color(0xB2D5D5D5);
//background: #F8F8F8; //background: #F8F8F8;
static const Color vividBlue = Color(0xFF023DFE); static const Color vividBlue = Color(0xFF023DFE);
static const Color semiTransparentRed = Color(0x99FF0000);
} }

View File

@ -29,7 +29,7 @@ abstract class ApiEndpoints {
////// Devices Management //////////////// ////// Devices Management ////////////////
static const String getAllDevices = '/projects/{projectId}/device'; static const String getAllDevices = '/projects/{projectId}/devices';
static const String getSpaceDevices = static const String getSpaceDevices =
'/projects/{projectId}/communities/{communityUuid}/spaces/{spaceUuid}/devices'; '/projects/{projectId}/communities/{communityUuid}/spaces/{spaceUuid}/devices';
static const String getDeviceStatus = '/device/{uuid}/functions/status'; static const String getDeviceStatus = '/device/{uuid}/functions/status';
@ -120,8 +120,8 @@ abstract class ApiEndpoints {
static const String inviteUser = '/invite-user'; static const String inviteUser = '/invite-user';
static const String checkEmail = '/invite-user/check-email'; static const String checkEmail = '/invite-user/check-email';
static const String getUsers = '/projects/{projectUuid}/user'; static const String getUsers = '/projects/{projectId}/user';
static const String getUserById = '/projects/{projectUuid}/user/{userUuid}'; static const String getUserById = '/projects/{projectId}/user/{userUuid}';
static const String editUser = '/invite-user/{inviteUserUuid}'; static const String editUser = '/invite-user/{inviteUserUuid}';
static const String deleteUser = '/invite-user/{inviteUserUuid}'; static const String deleteUser = '/invite-user/{inviteUserUuid}';
static const String changeUserStatus = static const String changeUserStatus =

View File

@ -258,6 +258,7 @@ class Assets {
static const String doorSensor = 'assets/icons/door_sensor.svg'; static const String doorSensor = 'assets/icons/door_sensor.svg';
static const String delete = 'assets/icons/delete.svg'; static const String delete = 'assets/icons/delete.svg';
static const String deleteSpaceModel = 'assets/icons/delete_space_model.svg';
static const String edit = 'assets/icons/edit.svg'; static const String edit = 'assets/icons/edit.svg';
static const String editSpace = 'assets/icons/edit_space.svg'; static const String editSpace = 'assets/icons/edit_space.svg';
//assets/icons/routine/tab_to_run.svg //assets/icons/routine/tab_to_run.svg

View File

@ -1,18 +1,37 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ResponsiveLayout extends StatelessWidget { class ResponsiveLayout extends StatelessWidget {
final Widget desktopBody;
final Widget mobileBody; final Widget mobileBody;
const ResponsiveLayout( final Widget? tablet;
{super.key, required this.desktopBody, required this.mobileBody}); final Widget desktopBody;
const ResponsiveLayout({
super.key,
required this.mobileBody,
this.tablet,
required this.desktopBody,
});
static bool isMobile(BuildContext context) =>
MediaQuery.of(context).size.width < 650;
static bool isTablet(BuildContext context) =>
MediaQuery.of(context).size.width < 1100 &&
MediaQuery.of(context).size.width >= 650;
static bool isDesktop(BuildContext context) =>
MediaQuery.of(context).size.width >= 1100;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return LayoutBuilder( return LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
if (constraints.maxWidth < 600) { if (constraints.maxWidth >= 1100) {
return mobileBody;
} else {
return desktopBody; return desktopBody;
} else if (constraints.maxWidth >= 650) {
return tablet!;
} else {
return mobileBody;
} }
}, },
); );

View File

@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/responsive_layout.dart';
class ResponsiveTextTheme extends ThemeExtension<ResponsiveTextTheme> {
final TextStyle deviceManagementTitle;
ResponsiveTextTheme({
required this.deviceManagementTitle,
});
@override
ThemeExtension<ResponsiveTextTheme> copyWith() => this;
@override
ThemeExtension<ResponsiveTextTheme> lerp(
ThemeExtension<ResponsiveTextTheme>? other, double t) =>
this;
static ResponsiveTextTheme of(BuildContext context) {
final isMobile = ResponsiveLayout.isMobile(context);
return Theme.of(context).extension<ResponsiveTextTheme>() ??
ResponsiveTextTheme(
deviceManagementTitle: TextStyle(
fontSize: isMobile ? 20 : 30,
fontWeight: FontWeight.w700,
color: ColorsManager.whiteColors),
);
}
}

View File

@ -5,10 +5,10 @@ import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_state.dart'; import 'package:syncrow_web/pages/home/bloc/home_state.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/responsive_layout.dart';
import 'package:syncrow_web/utils/user_drop_down_menu.dart'; import 'package:syncrow_web/utils/user_drop_down_menu.dart';
class WebAppBar extends StatefulWidget { class WebAppBar extends StatelessWidget {
final Widget? title; final Widget? title;
final Widget? centerBody; final Widget? centerBody;
final Widget? rightBody; final Widget? rightBody;
@ -16,178 +16,234 @@ class WebAppBar extends StatefulWidget {
const WebAppBar({super.key, this.title, this.centerBody, this.rightBody}); const WebAppBar({super.key, this.title, this.centerBody, this.rightBody});
@override @override
State<WebAppBar> createState() => _WebAppBarState(); Widget build(BuildContext context) {
return BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) {
final user = context.read<HomeBloc>().user;
return ResponsiveLayout(
mobileBody: MobileAppBar(
title: title,
centerBody: centerBody,
rightBody: rightBody,
user: user,
),
tablet: TabletAppBar(
title: title,
centerBody: centerBody,
rightBody: rightBody,
user: user,
),
desktopBody: DesktopAppBar(
title: title,
centerBody: centerBody,
rightBody: rightBody,
user: user,
),
);
},
);
}
} }
class _WebAppBarState extends State<WebAppBar> with HelperResponsiveLayout { class DesktopAppBar extends StatelessWidget {
@override final Widget? title;
void initState() { final Widget? centerBody;
super.initState(); final Widget? rightBody;
} final dynamic user;
const DesktopAppBar({
super.key,
this.title,
this.centerBody,
this.rightBody,
required this.user,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isSmallScreen = isSmallScreenSize(context); return Container(
bool isHalfMediumScreen = isHafMediumScreenSize(context); height: 100,
return BlocBuilder<HomeBloc, HomeState>(builder: (context, state) { decoration: const BoxDecoration(color: ColorsManager.secondaryColor),
final user = context.read<HomeBloc>().user; padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
return Container( child: Row(
height: (isSmallScreen || isHalfMediumScreen) ? 130 : 100, mainAxisAlignment: MainAxisAlignment.spaceBetween,
decoration: const BoxDecoration(color: ColorsManager.secondaryColor), children: [
padding: const EdgeInsets.all(10), Expanded(
child: isSmallScreen || isHalfMediumScreen child: Row(
? Column( children: [
crossAxisAlignment: CrossAxisAlignment.start, if (title != null) title!,
children: [ if (centerBody != null)
if (widget.title != null) Padding(
Align( padding: const EdgeInsets.only(left: 80),
alignment: Alignment.centerLeft, child: centerBody!,
child: widget.title!,
),
if (widget.centerBody != null)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: widget.centerBody,
),
if (widget.rightBody != null || user != null)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (widget.rightBody != null) widget.rightBody!,
Row(
children: [
SizedBox.square(
dimension: 40,
child: CircleAvatar(
backgroundColor: ColorsManager.whiteColors,
child: SizedBox.square(
dimension: 35,
child: SvgPicture.asset(
Assets.logoGrey,
fit: BoxFit.cover,
),
),
),
),
const SizedBox(
width: 10,
),
if (user != null)
Text(
'${user.firstName} ${user.lastName}',
style: Theme.of(context).textTheme.bodyLarge,
),
],
),
],
),
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Row(
children: [
widget.title!,
if (widget.centerBody != null)
Padding(
padding: const EdgeInsets.only(left: 80),
child: widget.centerBody!,
),
],
),
), ),
Row( ],
mainAxisSize: MainAxisSize.min, ),
children: [ ),
if (widget.rightBody != null) _buildUserSection(context),
Align( ],
alignment: Alignment.centerRight, ),
child: widget.rightBody, );
), }
const SizedBox(
width: 10, Widget _buildUserSection(BuildContext context) {
), return Row(
SizedBox.square( children: [
dimension: 40, if (rightBody != null) rightBody!,
child: CircleAvatar( const SizedBox(width: 24),
backgroundColor: ColorsManager.whiteColors, _UserAvatar(),
child: SizedBox.square( const SizedBox(width: 12),
dimension: 35, if (user != null)
child: SvgPicture.asset( Text(
Assets.logoGrey, '${user.firstName} ${user.lastName}',
fit: BoxFit.cover, style: Theme.of(context).textTheme.bodyLarge,
), ),
), const SizedBox(width: 12),
), UserDropdownMenu(user: user),
), ],
const SizedBox( );
width: 10, }
), }
if (user != null)
Text( class TabletAppBar extends StatelessWidget {
'${user.firstName} ${user.lastName}', final Widget? title;
style: Theme.of(context).textTheme.bodyLarge, final Widget? centerBody;
), final Widget? rightBody;
const SizedBox( final dynamic user;
width: 10,
), const TabletAppBar({
UserDropdownMenu(user: user), super.key,
// GestureDetector( this.title,
// onTap: () { this.centerBody,
// showCustomDialog( this.rightBody,
// context: context, required this.user,
// barrierDismissible: true, });
// title: 'Logout',
// message: 'Are you sure you want to logout?', @override
// actions: [ Widget build(BuildContext context) {
// GestureDetector( return Container(
// onTap: () { height: 100,
// AuthBloc.logout(); decoration: const BoxDecoration(color: ColorsManager.secondaryColor),
// context.go(RoutesConst.auth); padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
// }, child: Column(
// child: DefaultButton( children: [
// child: Text( Row(
// 'Ok', mainAxisAlignment: MainAxisAlignment.spaceBetween,
// style: Theme.of(context) children: [
// .textTheme if (title != null) Expanded(child: title!),
// .bodyMedium! _buildUserSection(context),
// .copyWith(fontSize: 12, color: Colors.white), ],
// ), ),
// ), if (centerBody != null) Expanded(child: centerBody!),
// ), ],
// const SizedBox( ),
// height: 10, );
// ), }
// GestureDetector(
// onTap: () { Widget _buildUserSection(BuildContext context) {
// context.pop(); return Row(
// }, children: [
// child: DefaultButton( if (rightBody != null) rightBody!,
// child: Text( const SizedBox(width: 16),
// 'Cancel', _UserAvatar(),
// style: Theme.of(context) if (user != null) ...[
// .textTheme const SizedBox(width: 8),
// .bodyMedium! Text(
// .copyWith(fontSize: 12, color: Colors.white), '${user.firstName} ${user.lastName}',
// ), style:
// ), Theme.of(context).textTheme.bodyLarge?.copyWith(fontSize: 14),
// ), ),
// ], ],
// ); UserDropdownMenu(user: user),
// }, ],
// child: const Icon( );
// Icons.logout, }
// color: ColorsManager.whiteColors, }
// ),
// ) class MobileAppBar extends StatelessWidget {
], final Widget? title;
), final Widget? centerBody;
], final Widget? rightBody;
), final dynamic user;
);
}); const MobileAppBar({
super.key,
this.title,
this.centerBody,
this.rightBody,
required this.user,
});
@override
Widget build(BuildContext context) {
return Container(
height: 135,
decoration: const BoxDecoration(color: ColorsManager.secondaryColor),
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (title != null) title!,
_buildUserSection(context),
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (centerBody != null)
Padding(
padding: const EdgeInsets.only(top: 8),
child: centerBody!,
),
if (rightBody != null)
Padding(
padding: const EdgeInsets.only(top: 8),
child: rightBody!,
),
],
),
],
),
);
}
Widget _buildUserSection(BuildContext context) {
return Row(
children: [
_UserAvatar(),
if (user != null) ...[
const SizedBox(width: 8),
Text(
'${user.firstName} ${user.lastName}',
style:
Theme.of(context).textTheme.bodyLarge?.copyWith(fontSize: 14),
),
],
UserDropdownMenu(user: user),
],
);
}
}
class _UserAvatar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox.square(
dimension: 40,
child: CircleAvatar(
backgroundColor: ColorsManager.whiteColors,
child: SizedBox.square(
dimension: 35,
child: SvgPicture.asset(
Assets.logoGrey,
fit: BoxFit.cover,
),
),
),
);
} }
} }

View File

@ -5,12 +5,20 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import firebase_analytics
import firebase_core
import firebase_crashlytics
import firebase_database
import flutter_secure_storage_macos import flutter_secure_storage_macos
import path_provider_foundation import path_provider_foundation
import shared_preferences_foundation import shared_preferences_foundation
import url_launcher_macos import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FLTFirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAnalyticsPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin"))
FLTFirebaseDatabasePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseDatabasePlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))

View File

@ -22,6 +22,7 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
108157F896CD9F637B06D7C0 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DAF1C60594A51D692304366 /* Pods_Runner.framework */; }; 108157F896CD9F637B06D7C0 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DAF1C60594A51D692304366 /* Pods_Runner.framework */; };
2901225E5FAB0C696EE79F77 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 996A2A515D007C9FED5396A5 /* GoogleService-Info.plist */; };
2D0F1F294F673EF0DB5E4CA1 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E148CBDFFE42BF88E8C34DE0 /* Pods_RunnerTests.framework */; }; 2D0F1F294F673EF0DB5E4CA1 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E148CBDFFE42BF88E8C34DE0 /* Pods_RunnerTests.framework */; };
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
@ -84,6 +85,7 @@
81F2F315AC5109F6F5D27BE6 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; }; 81F2F315AC5109F6F5D27BE6 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
96C46007EE0A4E9E1D6D74CE /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; }; 96C46007EE0A4E9E1D6D74CE /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
996A2A515D007C9FED5396A5 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; };
A604E311B663FBF4B7C54DC5 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; }; A604E311B663FBF4B7C54DC5 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
AB949539E0D0A8E2BDAB9ADF /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; AB949539E0D0A8E2BDAB9ADF /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
E148CBDFFE42BF88E8C34DE0 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E148CBDFFE42BF88E8C34DE0 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -138,6 +140,7 @@
33CC10EE2044A3C60003C045 /* Products */, 33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */, D73912EC22F37F3D000D13A0 /* Frameworks */,
75DCDFECC7757C5159E8F0C5 /* Pods */, 75DCDFECC7757C5159E8F0C5 /* Pods */,
996A2A515D007C9FED5396A5 /* GoogleService-Info.plist */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -241,6 +244,7 @@
33CC110E2044A8840003C045 /* Bundle Framework */, 33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */, 3399D490228B24CF009A79C7 /* ShellScript */,
92D754792F50A5D35F6D5AEE /* [CP] Embed Pods Frameworks */, 92D754792F50A5D35F6D5AEE /* [CP] Embed Pods Frameworks */,
7E188D2155D07A3E9E027C0F /* FlutterFire: "flutterfire upload-crashlytics-symbols" */,
); );
buildRules = ( buildRules = (
); );
@ -317,6 +321,7 @@
files = ( files = (
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
2901225E5FAB0C696EE79F77 /* GoogleService-Info.plist in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -361,6 +366,24 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
}; };
7E188D2155D07A3E9E027C0F /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "FlutterFire: \"flutterfire upload-crashlytics-symbols\"";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\n#!/bin/bash\nPATH=\"${PATH}:$FLUTTER_ROOT/bin:$HOME/.pub-cache/bin\"\nflutterfire upload-crashlytics-symbols --upload-symbols-script-path=\"$PODS_ROOT/FirebaseCrashlytics/upload-symbols\" --platform=macos --apple-project-path=\"${SRCROOT}\" --env-platform-name=\"${PLATFORM_NAME}\" --env-configuration=\"${CONFIGURATION}\" --env-project-dir=\"${PROJECT_DIR}\" --env-built-products-dir=\"${BUILT_PRODUCTS_DIR}\" --env-dwarf-dsym-folder-path=\"${DWARF_DSYM_FOLDER_PATH}\" --env-dwarf-dsym-file-name=\"${DWARF_DSYM_FILE_NAME}\" --env-infoplist-path=\"${INFOPLIST_PATH}\" --default-config=default\n";
};
8ECFD939A4D371A145DBA191 /* [CP] Check Pods Manifest.lock */ = { 8ECFD939A4D371A145DBA191 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>API_KEY</key>
<string>AIzaSyABnpH6yo2RRjtkp4PlvtK84hKwRm2DhBw</string>
<key>GCM_SENDER_ID</key>
<string>427332280600</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>com.example.syncrowWeb</string>
<key>PROJECT_ID</key>
<string>test2-8a3d2</string>
<key>STORAGE_BUCKET</key>
<string>test2-8a3d2.firebasestorage.app</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:427332280600:ios:14346b200780dc760c7e6d</string>
<key>DATABASE_URL</key>
<string>https://test2-8a3d2-default-rtdb.firebaseio.com</string>
</dict>
</plist>

View File

@ -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:
_flutterfire_internals:
dependency: transitive
description:
name: _flutterfire_internals
sha256: e051259913915ea5bc8fe18664596bea08592fd123930605d562969cd7315fcd
url: "https://pub.dev"
source: hosted
version: "1.3.51"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -153,6 +161,94 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.0.0" version: "7.0.0"
firebase_analytics:
dependency: "direct main"
description:
name: firebase_analytics
sha256: "47428047a0778f72af53a3c7cb5d556e1cb25e2327cc8aa40d544971dc6245b2"
url: "https://pub.dev"
source: hosted
version: "11.4.2"
firebase_analytics_platform_interface:
dependency: transitive
description:
name: firebase_analytics_platform_interface
sha256: "1076f4b041f76143e14878c70f0758f17fe5910c0cd992db9e93bd3c3584512b"
url: "https://pub.dev"
source: hosted
version: "4.3.2"
firebase_analytics_web:
dependency: transitive
description:
name: firebase_analytics_web
sha256: "8f6dd64ea6d28b7f5b9e739d183a9e1c7f17027794a3e9aba1879621d42426ef"
url: "https://pub.dev"
source: hosted
version: "0.5.10+8"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
sha256: "93dc4dd12f9b02c5767f235307f609e61ed9211047132d07f9e02c668f0bfc33"
url: "https://pub.dev"
source: hosted
version: "3.11.0"
firebase_core_platform_interface:
dependency: transitive
description:
name: firebase_core_platform_interface
sha256: d7253d255ff10f85cfd2adaba9ac17bae878fa3ba577462451163bd9f1d1f0bf
url: "https://pub.dev"
source: hosted
version: "5.4.0"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
sha256: "0e13c80f0de8acaa5d0519cbe23c8b4cc138a2d5d508b5755c861bdfc9762678"
url: "https://pub.dev"
source: hosted
version: "2.20.0"
firebase_crashlytics:
dependency: "direct main"
description:
name: firebase_crashlytics
sha256: "6273ed71bcd8a6fb4d0ca13d3abddbb3301796807efaad8782b5f90156f26f03"
url: "https://pub.dev"
source: hosted
version: "4.3.2"
firebase_crashlytics_platform_interface:
dependency: transitive
description:
name: firebase_crashlytics_platform_interface
sha256: "94f3986e1a10e5a883f2ad5e3d719aef98a8a0f9a49357f6e45b7d3696ea6a97"
url: "https://pub.dev"
source: hosted
version: "3.8.2"
firebase_database:
dependency: "direct main"
description:
name: firebase_database
sha256: cd2354dfef68e52c0713b5efbb7f4e10dfc2aff2f945c7bc8db34d1934170627
url: "https://pub.dev"
source: hosted
version: "11.3.2"
firebase_database_platform_interface:
dependency: transitive
description:
name: firebase_database_platform_interface
sha256: d430983f4d877c9f72f88b3d715cca9a50021dd7ccd8e3ae6fb79603853317de
url: "https://pub.dev"
source: hosted
version: "0.2.6+2"
firebase_database_web:
dependency: transitive
description:
name: firebase_database_web
sha256: f64edae62c5beaa08e9e611a0736d64ab11a812983a0aa132695d2d191311ea7
url: "https://pub.dev"
source: hosted
version: "0.2.6+8"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
@ -572,10 +668,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_web name: shared_preferences_web
sha256: "3a293170d4d9403c3254ee05b84e62e8a9b3c5808ebd17de6a33fe9ea6457936" sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.0" version: "2.4.2"
shared_preferences_windows: shared_preferences_windows:
dependency: transitive dependency: transitive
description: description:
@ -777,10 +873,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: web name: web
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.1" version: "1.1.0"
win32: win32:
dependency: transitive dependency: transitive
description: description:

View File

@ -56,6 +56,11 @@ dependencies:
number_pagination: ^1.1.6 number_pagination: ^1.1.6
url_launcher: ^6.2.5 url_launcher: ^6.2.5
flutter_html: ^3.0.0-beta.2 flutter_html: ^3.0.0-beta.2
firebase_analytics: ^11.4.0
firebase_core: ^3.11.0
firebase_crashlytics: ^4.3.2
firebase_database: ^11.3.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@ -40,6 +40,8 @@
<script src="flutter.js" defer></script> <script src="flutter.js" defer></script>
</head> </head>
<body> <body>
<script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"></script>
<script> <script>
window.addEventListener('load', function(ev) { window.addEventListener('load', function(ev) {
// Download main.dart.js // Download main.dart.js
@ -54,6 +56,20 @@
} }
}); });
}); });
var firebaseConfig = {
apiKey: "AIzaSyCVEvKsJYzhWDFM-9Od68FE0nPpP933st0",
authDomain: "test2-8a3d2.firebaseapp.com",
databaseURL: "https://test2-8a3d2-default-rtdb.firebaseio.com",
projectId: "test2-8a3d2",
storageBucket: "test2-8a3d2.firebasestorage.app",
messagingSenderId: "427332280600",
appId: "1:427332280600:web:ad50516a87a35a1a0c7e6d",
measurementId: "G-Z1RTTTV5H9"
};
firebase.initializeApp(firebaseConfig);
</script> </script>
</body> </body>
</html> </html>

View File

@ -6,10 +6,13 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <firebase_core/firebase_core_plugin_c_api.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h> #include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
FirebaseCorePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar( FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar( UrlLauncherWindowsRegisterWithRegistrar(

View File

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
firebase_core
flutter_secure_storage_windows flutter_secure_storage_windows
url_launcher_windows url_launcher_windows
) )