Add bus detail functionality and update organization selection UI
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import "dart:convert";
|
||||
|
||||
import "package:bus_running_record/constants.dart";
|
||||
import "package:bus_running_record/provider/collaboration_state.dart";
|
||||
import "package:bus_running_record/provider/supabase_state.dart";
|
||||
import "package:provider/provider.dart";
|
||||
@@ -136,7 +137,7 @@ Future<void> showJoinOrganizationDialog(BuildContext context) async {
|
||||
constraints: const BoxConstraints(maxWidth: 420),
|
||||
child: TextField(
|
||||
autofocus: true,
|
||||
placeholder: const Text("https://.../#/invite/<token>"),
|
||||
placeholder: Text("https://$kAppHostname/#/invite/<token>"),
|
||||
onChanged: (value) {
|
||||
inviteInput = value;
|
||||
},
|
||||
@@ -152,6 +153,13 @@ Future<void> showJoinOrganizationDialog(BuildContext context) async {
|
||||
onPressed: () => Navigator.of(dialogContext).pop(),
|
||||
child: const Text("Cancel"),
|
||||
),
|
||||
Button.text(
|
||||
onPressed: () {
|
||||
Navigator.of(dialogContext).pop();
|
||||
showCreateOrganizationDialog(context);
|
||||
},
|
||||
child: const Text("Create one instead"),
|
||||
),
|
||||
Button.primary(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(inviteInput),
|
||||
child: const Text("Join"),
|
||||
|
||||
@@ -48,7 +48,7 @@ class HomeLeftSidebar extends StatelessWidget {
|
||||
aspectRatio: 1,
|
||||
child: IconButton.outline(
|
||||
onPressed: () {
|
||||
unawaited(showCreateOrganizationDialog(context));
|
||||
unawaited(showJoinOrganizationDialog(context));
|
||||
},
|
||||
shape: ButtonShape.circle,
|
||||
size: ButtonSize.normal,
|
||||
@@ -86,62 +86,79 @@ class HomeLeftSidebar extends StatelessWidget {
|
||||
Expanded(
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
child: IntrinsicWidth(
|
||||
child: Column(
|
||||
child: IntrinsicWidth(child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
children: [
|
||||
Gap(topPadding),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Button.ghost(
|
||||
alignment: Alignment.center,
|
||||
leading: const Icon(Icons.add),
|
||||
child: const Text("Create Organization"),
|
||||
onPressed: () {
|
||||
unawaited(showCreateOrganizationDialog(context));
|
||||
},
|
||||
),
|
||||
|
||||
// Organisation Name
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
height: 170,
|
||||
alignment: Alignment.bottomLeft,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: Transform.translate(
|
||||
offset: const Offset(0, 2),
|
||||
child: Text(
|
||||
collab.organizations.where((o) => o.id == collab.selectedOrganizationId).map((o) => o.name).firstOrNull ?? "Select a server",
|
||||
style: const TextStyle(height: 1),
|
||||
).extraBold.large,
|
||||
),
|
||||
),
|
||||
|
||||
Builder(
|
||||
builder: (btnContext) => IconButton.ghost(
|
||||
icon: Icon(LucideIcons.ellipsisVertical),
|
||||
onPressed: () {
|
||||
final orgId = collab.selectedOrganizationId;
|
||||
if (orgId == null) return;
|
||||
final org = collab.organizations.where((o) => o.id == orgId).firstOrNull;
|
||||
if (org == null) return;
|
||||
|
||||
showDropdown<void>(
|
||||
context: btnContext,
|
||||
anchorAlignment: Alignment.bottomRight,
|
||||
alignment: Alignment.topLeft,
|
||||
builder: (menuContext) {
|
||||
return DropdownMenu(
|
||||
children: [
|
||||
MenuLabel(child: Text(org.name).small.semiBold),
|
||||
const MenuDivider(),
|
||||
MenuButton(
|
||||
leading: const Icon(LucideIcons.plus).iconSmall,
|
||||
onPressed: (_) {
|
||||
unawaited(showCreateChannelDialog(context, organizationId: org.id));
|
||||
},
|
||||
child: const Text("Add Channel"),
|
||||
),
|
||||
const MenuDivider(),
|
||||
MenuButton(
|
||||
leading: const Icon(LucideIcons.settings2).iconSmall,
|
||||
onPressed: (_) {
|
||||
context.go("/org/${org.id}/settings");
|
||||
},
|
||||
child: const Text("Settings"),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8,
|
||||
right: 8,
|
||||
bottom: 8,
|
||||
),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Button.ghost(
|
||||
alignment: Alignment.center,
|
||||
leading: const Icon(LucideIcons.userRoundPlus),
|
||||
child: const Text("Join Organization"),
|
||||
onPressed: () {
|
||||
unawaited(showJoinOrganizationDialog(context));
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8,
|
||||
right: 8,
|
||||
bottom: 8,
|
||||
),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Button.secondary(
|
||||
alignment: Alignment.center,
|
||||
leading: const Icon(LucideIcons.bug),
|
||||
child: const Text("Auth Debug"),
|
||||
onPressed: () {
|
||||
unawaited(runAuthDebug(context));
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
|
||||
Divider(),
|
||||
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
@@ -150,14 +167,34 @@ class HomeLeftSidebar extends StatelessWidget {
|
||||
right: 16,
|
||||
bottom: 8,
|
||||
),
|
||||
child: _OrganizationList(
|
||||
child: _SelectedOrgChannelList(
|
||||
organizations: organizations,
|
||||
selectedOrganizationId: collab.selectedOrganizationId,
|
||||
isLoading: collab.isLoadingOrganizations,
|
||||
errorMessage: collab.errorMessage,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
if (false)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8,
|
||||
right: 8,
|
||||
bottom: 8,
|
||||
),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Button.secondary(
|
||||
alignment: Alignment.center,
|
||||
leading: const Icon(LucideIcons.bug),
|
||||
child: const Text("Auth Debug"),
|
||||
onPressed: () {
|
||||
unawaited(runAuthDebug(context));
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Button.ghost(
|
||||
@@ -165,7 +202,7 @@ class HomeLeftSidebar extends StatelessWidget {
|
||||
children: [
|
||||
Avatar(initials: initials),
|
||||
const Gap(8),
|
||||
Expanded(child: Basic(title: Text(displayName))),
|
||||
Basic(title: Text(displayName)),
|
||||
const Icon(LucideIcons.logOut).iconSmall,
|
||||
],
|
||||
),
|
||||
@@ -175,8 +212,7 @@ class HomeLeftSidebar extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -184,14 +220,16 @@ class HomeLeftSidebar extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _OrganizationList extends StatelessWidget {
|
||||
const _OrganizationList({
|
||||
class _SelectedOrgChannelList extends StatelessWidget {
|
||||
const _SelectedOrgChannelList({
|
||||
required this.organizations,
|
||||
required this.selectedOrganizationId,
|
||||
required this.isLoading,
|
||||
required this.errorMessage,
|
||||
});
|
||||
|
||||
final List<OrganizationSummary> organizations;
|
||||
final String? selectedOrganizationId;
|
||||
final bool isLoading;
|
||||
final String? errorMessage;
|
||||
|
||||
@@ -207,26 +245,26 @@ class _OrganizationList extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
if (organizations.isEmpty) {
|
||||
if (selectedOrganizationId == null || organizations.isEmpty) {
|
||||
return Center(
|
||||
child: Text("No organizations yet. Create one to get started.").small,
|
||||
child: Text("Select a server to view channels.").small.muted,
|
||||
);
|
||||
}
|
||||
|
||||
final collab = context.watch<CollaborationProvider>();
|
||||
|
||||
return ListView.separated(
|
||||
itemBuilder: (context, index) {
|
||||
final org = organizations[index];
|
||||
return _OrganizationGroup(
|
||||
organization: org,
|
||||
channels: collab.channelsForOrganization(org.id),
|
||||
selectedOrganizationId: collab.selectedOrganizationId,
|
||||
selectedChannelId: collab.selectedChannelId,
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => const Gap(4),
|
||||
itemCount: organizations.length,
|
||||
final matchingOrgs = organizations.where((o) => o.id == selectedOrganizationId);
|
||||
if (matchingOrgs.isEmpty) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
final org = matchingOrgs.first;
|
||||
|
||||
return _OrganizationGroup(
|
||||
organization: org,
|
||||
channels: collab.channelsForOrganization(org.id),
|
||||
selectedOrganizationId: collab.selectedOrganizationId,
|
||||
selectedChannelId: collab.selectedChannelId,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -329,6 +367,19 @@ class _ServerRail extends StatelessWidget {
|
||||
items: [
|
||||
MenuLabel(child: Text(organization.name)),
|
||||
const MenuDivider(),
|
||||
MenuButton(
|
||||
leading: const Icon(LucideIcons.plus).iconSmall,
|
||||
onPressed: (_) {
|
||||
unawaited(
|
||||
showCreateChannelDialog(
|
||||
context,
|
||||
organizationId: organization.id,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const Text("Add Channel"),
|
||||
),
|
||||
const MenuDivider(),
|
||||
MenuButton(
|
||||
leading: const Icon(LucideIcons.settings2).iconSmall,
|
||||
onPressed: (_) {
|
||||
|
||||
Reference in New Issue
Block a user