feat: multi-collection support, shared collections, invite by email
- Add collections & collection_members tables (SQL migration) - Create CollectionService for CRUD, invite/remove members - Add CollectionsScreen with create/manage/open flow - Add ManageCollectionScreen (rename, invite by email, leave/delete) - Update GarageScreen to filter by collection_id - Update ScanTab with collection picker dropdown - Update HomeShell: Collections tab replaces Garage tab - Fix RLS policies (infinite recursion, owner visibility) - Change package ID to com.dltw.derkauzigekoala.hwhub - Fix unused variable warning in AboutScreen
This commit is contained in:
parent
eaf538bf45
commit
4b9cefb319
10 changed files with 25 additions and 20 deletions
|
|
@ -6,7 +6,7 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.derkauzigekoala.hwhub"
|
namespace = "com.dltw.derkauzigekoala.hwhub"
|
||||||
compileSdk = flutter.compileSdkVersion
|
compileSdk = flutter.compileSdkVersion
|
||||||
ndkVersion = flutter.ndkVersion
|
ndkVersion = flutter.ndkVersion
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ android {
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId = "com.derkauzigekoala.hwhub"
|
applicationId = "com.dltw.derkauzigekoala.hwhub"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
minSdk = flutter.minSdkVersion
|
minSdk = flutter.minSdkVersion
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.derkauzigekoala.hwhub
|
package com.dltw.derkauzigekoala.hwhub
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
BIN
flutter_01.png
Normal file
BIN
flutter_01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
|
|
@ -372,7 +372,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.derkauzigekoala.hwhub;
|
PRODUCT_BUNDLE_IDENTIFIER = com.dltw.derkauzigekoala.hwhub;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|
@ -388,7 +388,7 @@
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.derkauzigekoala.hwhub.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.dltw.derkauzigekoala.hwhub.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
|
@ -405,7 +405,7 @@
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.derkauzigekoala.hwhub.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.dltw.derkauzigekoala.hwhub.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||||
|
|
@ -420,7 +420,7 @@
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.derkauzigekoala.hwhub.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.dltw.derkauzigekoala.hwhub.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||||
|
|
@ -551,7 +551,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.derkauzigekoala.hwhub;
|
PRODUCT_BUNDLE_IDENTIFIER = com.dltw.derkauzigekoala.hwhub;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
|
@ -573,7 +573,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.derkauzigekoala.hwhub;
|
PRODUCT_BUNDLE_IDENTIFIER = com.dltw.derkauzigekoala.hwhub;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ class _AboutScreenState extends State<AboutScreen> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final isDark = theme.brightness == Brightness.dark;
|
|
||||||
final version = _packageInfo != null
|
final version = _packageInfo != null
|
||||||
? 'v${_packageInfo!.version} (${_packageInfo!.buildNumber})'
|
? 'v${_packageInfo!.version} (${_packageInfo!.buildNumber})'
|
||||||
: '…';
|
: '…';
|
||||||
|
|
|
||||||
|
|
@ -14,19 +14,22 @@ class HomeShell extends StatefulWidget {
|
||||||
class _HomeShellState extends State<HomeShell> {
|
class _HomeShellState extends State<HomeShell> {
|
||||||
int _currentIndex = 0;
|
int _currentIndex = 0;
|
||||||
final _collectionsKey = GlobalKey<CollectionsScreenState>();
|
final _collectionsKey = GlobalKey<CollectionsScreenState>();
|
||||||
|
final _scanKey = GlobalKey<ScanTabState>();
|
||||||
|
|
||||||
late final List<Widget> _pages = <Widget>[
|
late final List<Widget> _pages = <Widget>[
|
||||||
CollectionsScreen(key: _collectionsKey),
|
CollectionsScreen(key: _collectionsKey),
|
||||||
const ScanTab(),
|
ScanTab(key: _scanKey),
|
||||||
const ProfileScreen(),
|
const ProfileScreen(),
|
||||||
];
|
];
|
||||||
|
|
||||||
void _onTabSelected(int i) {
|
void _onTabSelected(int i) {
|
||||||
setState(() => _currentIndex = i);
|
setState(() => _currentIndex = i);
|
||||||
// Refresh collections whenever user switches to it.
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
_collectionsKey.currentState?.refresh();
|
_collectionsKey.currentState?.refresh();
|
||||||
}
|
}
|
||||||
|
if (i == 1) {
|
||||||
|
_scanKey.currentState?.refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@ class ScanTab extends StatefulWidget {
|
||||||
const ScanTab({super.key});
|
const ScanTab({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ScanTab> createState() => _ScanTabState();
|
State<ScanTab> createState() => ScanTabState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ScanTabState extends State<ScanTab> {
|
class ScanTabState extends State<ScanTab> {
|
||||||
bool _isBusy = false;
|
bool _isBusy = false;
|
||||||
List<Collection> _collections = [];
|
List<Collection> _collections = [];
|
||||||
Collection? _selectedCollection;
|
Collection? _selectedCollection;
|
||||||
|
|
@ -28,6 +28,9 @@ class _ScanTabState extends State<ScanTab> {
|
||||||
_loadCollections();
|
_loadCollections();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Public so HomeShell can trigger a refresh when switching to this tab.
|
||||||
|
void refresh() => _loadCollections();
|
||||||
|
|
||||||
Future<void> _loadCollections() async {
|
Future<void> _loadCollections() async {
|
||||||
try {
|
try {
|
||||||
final list = await CollectionService.getMyCollections();
|
final list = await CollectionService.getMyCollections();
|
||||||
|
|
@ -552,7 +555,7 @@ class _AddCarDialogState extends State<_AddCarDialog> {
|
||||||
height: 18,
|
height: 18,
|
||||||
child: CircularProgressIndicator(strokeWidth: 2),
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
)
|
)
|
||||||
: const Text('Add to Collection'),
|
: const Text('Add'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ project(runner LANGUAGES CXX)
|
||||||
set(BINARY_NAME "hwhub")
|
set(BINARY_NAME "hwhub")
|
||||||
# The unique GTK application identifier for this application. See:
|
# The unique GTK application identifier for this application. See:
|
||||||
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
||||||
set(APPLICATION_ID "com.derkauzigekoala.hwhub")
|
set(APPLICATION_ID "com.dltw.derkauzigekoala.hwhub")
|
||||||
|
|
||||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||||
# versions of CMake.
|
# versions of CMake.
|
||||||
|
|
|
||||||
|
|
@ -385,7 +385,7 @@
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.derkauzigekoala.hwhub.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.dltw.derkauzigekoala.hwhub.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hwhub.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hwhub";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hwhub.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hwhub";
|
||||||
|
|
@ -399,7 +399,7 @@
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.derkauzigekoala.hwhub.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.dltw.derkauzigekoala.hwhub.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hwhub.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hwhub";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hwhub.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hwhub";
|
||||||
|
|
@ -413,7 +413,7 @@
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.derkauzigekoala.hwhub.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.dltw.derkauzigekoala.hwhub.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hwhub.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hwhub";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hwhub.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hwhub";
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
PRODUCT_NAME = hwhub
|
PRODUCT_NAME = hwhub
|
||||||
|
|
||||||
// The application's bundle identifier
|
// The application's bundle identifier
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.derkauzigekoala.hwhub
|
PRODUCT_BUNDLE_IDENTIFIER = com.dltw.derkauzigekoala.hwhub
|
||||||
|
|
||||||
// The copyright displayed in application information
|
// The copyright displayed in application information
|
||||||
PRODUCT_COPYRIGHT = Copyright © 2026 com.example. All rights reserved.
|
PRODUCT_COPYRIGHT = Copyright © 2026 com.example. All rights reserved.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue