💄 Adjusted developer hub
This commit is contained in:
		@@ -1,5 +1,6 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:gap/gap.dart';
 | 
			
		||||
import 'package:go_router/go_router.dart';
 | 
			
		||||
import 'package:google_fonts/google_fonts.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
@@ -7,6 +8,7 @@ import 'package:island/models/custom_app.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
import 'package:island/widgets/alert.dart';
 | 
			
		||||
import 'package:island/widgets/content/cloud_files.dart';
 | 
			
		||||
import 'package:island/widgets/extended_refresh_indicator.dart';
 | 
			
		||||
import 'package:island/widgets/response.dart';
 | 
			
		||||
import 'package:material_symbols_icons/symbols.dart';
 | 
			
		||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
			
		||||
@@ -83,129 +85,159 @@ class CustomAppsScreen extends HookConsumerWidget {
 | 
			
		||||
            ),
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
        return RefreshIndicator(
 | 
			
		||||
        return ExtendedRefreshIndicator(
 | 
			
		||||
          onRefresh:
 | 
			
		||||
              () => ref.refresh(
 | 
			
		||||
                customAppsProvider(publisherName, projectId).future,
 | 
			
		||||
              ),
 | 
			
		||||
          child: ListView.builder(
 | 
			
		||||
            padding: EdgeInsets.only(top: 4),
 | 
			
		||||
            itemCount: data.length,
 | 
			
		||||
            itemBuilder: (context, index) {
 | 
			
		||||
              final app = data[index];
 | 
			
		||||
              return Card(
 | 
			
		||||
                margin: const EdgeInsets.all(8.0),
 | 
			
		||||
                clipBehavior: Clip.antiAlias,
 | 
			
		||||
                child: InkWell(
 | 
			
		||||
                  onTap: () {
 | 
			
		||||
                    context.pushNamed(
 | 
			
		||||
                      'developerAppDetail',
 | 
			
		||||
                      pathParameters: {
 | 
			
		||||
                        'name': publisherName,
 | 
			
		||||
                        'projectId': projectId,
 | 
			
		||||
                        'appId': app.id,
 | 
			
		||||
                      },
 | 
			
		||||
                    );
 | 
			
		||||
                  },
 | 
			
		||||
                  child: Column(
 | 
			
		||||
                    children: [
 | 
			
		||||
                      SizedBox(
 | 
			
		||||
                        height: 150,
 | 
			
		||||
                        child: Stack(
 | 
			
		||||
                          fit: StackFit.expand,
 | 
			
		||||
          child: Column(
 | 
			
		||||
            children: [
 | 
			
		||||
              const Gap(8),
 | 
			
		||||
              Card(
 | 
			
		||||
                child: ListTile(
 | 
			
		||||
                  title: Text('customApps').tr().padding(horizontal: 8),
 | 
			
		||||
                  trailing: IconButton(
 | 
			
		||||
                    onPressed: () {
 | 
			
		||||
                      context.pushNamed(
 | 
			
		||||
                        'developerAppNew',
 | 
			
		||||
                        pathParameters: {
 | 
			
		||||
                          'name': publisherName,
 | 
			
		||||
                          'projectId': projectId,
 | 
			
		||||
                        },
 | 
			
		||||
                      );
 | 
			
		||||
                    },
 | 
			
		||||
                    icon: const Icon(Symbols.add),
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              Expanded(
 | 
			
		||||
                child: ListView.builder(
 | 
			
		||||
                  padding: EdgeInsets.zero,
 | 
			
		||||
                  itemCount: data.length,
 | 
			
		||||
                  itemBuilder: (context, index) {
 | 
			
		||||
                    final app = data[index];
 | 
			
		||||
                    return Card(
 | 
			
		||||
                      clipBehavior: Clip.antiAlias,
 | 
			
		||||
                      child: InkWell(
 | 
			
		||||
                        onTap: () {
 | 
			
		||||
                          context.pushNamed(
 | 
			
		||||
                            'developerAppDetail',
 | 
			
		||||
                            pathParameters: {
 | 
			
		||||
                              'name': publisherName,
 | 
			
		||||
                              'projectId': projectId,
 | 
			
		||||
                              'appId': app.id,
 | 
			
		||||
                            },
 | 
			
		||||
                          );
 | 
			
		||||
                        },
 | 
			
		||||
                        child: Column(
 | 
			
		||||
                          children: [
 | 
			
		||||
                            if (app.background != null)
 | 
			
		||||
                              CloudFileWidget(
 | 
			
		||||
                                item: app.background!,
 | 
			
		||||
                                fit: BoxFit.cover,
 | 
			
		||||
                              ).clipRRect(topLeft: 8, topRight: 8),
 | 
			
		||||
                            if (app.picture != null)
 | 
			
		||||
                              Positioned(
 | 
			
		||||
                                left: 16,
 | 
			
		||||
                                bottom: 16,
 | 
			
		||||
                                child: ProfilePictureWidget(
 | 
			
		||||
                                  fileId: app.picture!.id,
 | 
			
		||||
                                  radius: 40,
 | 
			
		||||
                                  fallbackIcon: Symbols.apps,
 | 
			
		||||
                                ),
 | 
			
		||||
                            SizedBox(
 | 
			
		||||
                              height: 150,
 | 
			
		||||
                              child: Stack(
 | 
			
		||||
                                fit: StackFit.expand,
 | 
			
		||||
                                children: [
 | 
			
		||||
                                  if (app.background != null)
 | 
			
		||||
                                    CloudFileWidget(
 | 
			
		||||
                                      item: app.background!,
 | 
			
		||||
                                      fit: BoxFit.cover,
 | 
			
		||||
                                    ).clipRRect(topLeft: 8, topRight: 8),
 | 
			
		||||
                                  if (app.picture != null)
 | 
			
		||||
                                    Positioned(
 | 
			
		||||
                                      left: 16,
 | 
			
		||||
                                      bottom: 16,
 | 
			
		||||
                                      child: ProfilePictureWidget(
 | 
			
		||||
                                        fileId: app.picture!.id,
 | 
			
		||||
                                        radius: 40,
 | 
			
		||||
                                        fallbackIcon: Symbols.apps,
 | 
			
		||||
                                      ),
 | 
			
		||||
                                    ),
 | 
			
		||||
                                ],
 | 
			
		||||
                              ),
 | 
			
		||||
                            ),
 | 
			
		||||
                            ListTile(
 | 
			
		||||
                              title: Text(app.name),
 | 
			
		||||
                              subtitle: Text(
 | 
			
		||||
                                app.slug,
 | 
			
		||||
                                style: GoogleFonts.robotoMono(fontSize: 12),
 | 
			
		||||
                              ),
 | 
			
		||||
                              contentPadding: EdgeInsets.only(
 | 
			
		||||
                                left: 20,
 | 
			
		||||
                                right: 12,
 | 
			
		||||
                              ),
 | 
			
		||||
                              trailing: PopupMenuButton(
 | 
			
		||||
                                itemBuilder:
 | 
			
		||||
                                    (context) => [
 | 
			
		||||
                                      PopupMenuItem(
 | 
			
		||||
                                        value: 'edit',
 | 
			
		||||
                                        child: Row(
 | 
			
		||||
                                          children: [
 | 
			
		||||
                                            const Icon(Symbols.edit),
 | 
			
		||||
                                            const SizedBox(width: 12),
 | 
			
		||||
                                            Text('edit').tr(),
 | 
			
		||||
                                          ],
 | 
			
		||||
                                        ),
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      PopupMenuItem(
 | 
			
		||||
                                        value: 'delete',
 | 
			
		||||
                                        child: Row(
 | 
			
		||||
                                          children: [
 | 
			
		||||
                                            const Icon(
 | 
			
		||||
                                              Symbols.delete,
 | 
			
		||||
                                              color: Colors.red,
 | 
			
		||||
                                            ),
 | 
			
		||||
                                            const SizedBox(width: 12),
 | 
			
		||||
                                            Text(
 | 
			
		||||
                                              'delete',
 | 
			
		||||
                                              style: TextStyle(
 | 
			
		||||
                                                color: Colors.red,
 | 
			
		||||
                                              ),
 | 
			
		||||
                                            ).tr(),
 | 
			
		||||
                                          ],
 | 
			
		||||
                                        ),
 | 
			
		||||
                                      ),
 | 
			
		||||
                                    ],
 | 
			
		||||
                                onSelected: (value) {
 | 
			
		||||
                                  if (value == 'edit') {
 | 
			
		||||
                                    context.pushNamed(
 | 
			
		||||
                                      'developerAppEdit',
 | 
			
		||||
                                      pathParameters: {
 | 
			
		||||
                                        'name': publisherName,
 | 
			
		||||
                                        'projectId': projectId,
 | 
			
		||||
                                        'id': app.id,
 | 
			
		||||
                                      },
 | 
			
		||||
                                    );
 | 
			
		||||
                                  } else if (value == 'delete') {
 | 
			
		||||
                                    showConfirmAlert(
 | 
			
		||||
                                      'deleteCustomAppHint'.tr(),
 | 
			
		||||
                                      'deleteCustomApp'.tr(),
 | 
			
		||||
                                    ).then((confirm) {
 | 
			
		||||
                                      if (confirm) {
 | 
			
		||||
                                        final client = ref.read(
 | 
			
		||||
                                          apiClientProvider,
 | 
			
		||||
                                        );
 | 
			
		||||
                                        client.delete(
 | 
			
		||||
                                          '/develop/developers/$publisherName/projects/$projectId/apps/${app.id}',
 | 
			
		||||
                                        );
 | 
			
		||||
                                        ref.invalidate(
 | 
			
		||||
                                          customAppsProvider(
 | 
			
		||||
                                            publisherName,
 | 
			
		||||
                                            projectId,
 | 
			
		||||
                                          ),
 | 
			
		||||
                                        );
 | 
			
		||||
                                      }
 | 
			
		||||
                                    });
 | 
			
		||||
                                  }
 | 
			
		||||
                                },
 | 
			
		||||
                              ),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ],
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                      ListTile(
 | 
			
		||||
                        title: Text(app.name),
 | 
			
		||||
                        subtitle: Text(
 | 
			
		||||
                          app.slug,
 | 
			
		||||
                          style: GoogleFonts.robotoMono(fontSize: 12),
 | 
			
		||||
                        ),
 | 
			
		||||
                        contentPadding: EdgeInsets.only(left: 20, right: 12),
 | 
			
		||||
                        trailing: PopupMenuButton(
 | 
			
		||||
                          itemBuilder:
 | 
			
		||||
                              (context) => [
 | 
			
		||||
                                PopupMenuItem(
 | 
			
		||||
                                  value: 'edit',
 | 
			
		||||
                                  child: Row(
 | 
			
		||||
                                    children: [
 | 
			
		||||
                                      const Icon(Symbols.edit),
 | 
			
		||||
                                      const SizedBox(width: 12),
 | 
			
		||||
                                      Text('edit').tr(),
 | 
			
		||||
                                    ],
 | 
			
		||||
                                  ),
 | 
			
		||||
                                ),
 | 
			
		||||
                                PopupMenuItem(
 | 
			
		||||
                                  value: 'delete',
 | 
			
		||||
                                  child: Row(
 | 
			
		||||
                                    children: [
 | 
			
		||||
                                      const Icon(
 | 
			
		||||
                                        Symbols.delete,
 | 
			
		||||
                                        color: Colors.red,
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      const SizedBox(width: 12),
 | 
			
		||||
                                      Text(
 | 
			
		||||
                                        'delete',
 | 
			
		||||
                                        style: TextStyle(color: Colors.red),
 | 
			
		||||
                                      ).tr(),
 | 
			
		||||
                                    ],
 | 
			
		||||
                                  ),
 | 
			
		||||
                                ),
 | 
			
		||||
                              ],
 | 
			
		||||
                          onSelected: (value) {
 | 
			
		||||
                            if (value == 'edit') {
 | 
			
		||||
                              context.pushNamed(
 | 
			
		||||
                                'developerAppEdit',
 | 
			
		||||
                                pathParameters: {
 | 
			
		||||
                                  'name': publisherName,
 | 
			
		||||
                                  'projectId': projectId,
 | 
			
		||||
                                  'id': app.id,
 | 
			
		||||
                                },
 | 
			
		||||
                              );
 | 
			
		||||
                            } else if (value == 'delete') {
 | 
			
		||||
                              showConfirmAlert(
 | 
			
		||||
                                'deleteCustomAppHint'.tr(),
 | 
			
		||||
                                'deleteCustomApp'.tr(),
 | 
			
		||||
                              ).then((confirm) {
 | 
			
		||||
                                if (confirm) {
 | 
			
		||||
                                  final client = ref.read(apiClientProvider);
 | 
			
		||||
                                  client.delete(
 | 
			
		||||
                                    '/develop/developers/$publisherName/projects/$projectId/apps/${app.id}',
 | 
			
		||||
                                  );
 | 
			
		||||
                                  ref.invalidate(
 | 
			
		||||
                                    customAppsProvider(
 | 
			
		||||
                                      publisherName,
 | 
			
		||||
                                      projectId,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                  );
 | 
			
		||||
                                }
 | 
			
		||||
                              });
 | 
			
		||||
                            }
 | 
			
		||||
                          },
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
                    );
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:gap/gap.dart';
 | 
			
		||||
import 'package:go_router/go_router.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/bot.dart';
 | 
			
		||||
@@ -10,6 +11,7 @@ import 'package:island/widgets/response.dart';
 | 
			
		||||
import 'package:material_symbols_icons/symbols.dart';
 | 
			
		||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
			
		||||
import 'package:island/widgets/extended_refresh_indicator.dart';
 | 
			
		||||
import 'package:styled_widget/styled_widget.dart';
 | 
			
		||||
 | 
			
		||||
part 'bots.g.dart';
 | 
			
		||||
 | 
			
		||||
@@ -64,95 +66,122 @@ class BotsScreen extends HookConsumerWidget {
 | 
			
		||||
        return ExtendedRefreshIndicator(
 | 
			
		||||
          onRefresh:
 | 
			
		||||
              () => ref.refresh(botsProvider(publisherName, projectId).future),
 | 
			
		||||
          child: ListView.builder(
 | 
			
		||||
            padding: const EdgeInsets.only(top: 4),
 | 
			
		||||
            itemCount: data.length,
 | 
			
		||||
            itemBuilder: (context, index) {
 | 
			
		||||
              final bot = data[index];
 | 
			
		||||
              return Card(
 | 
			
		||||
                margin: const EdgeInsets.all(8.0),
 | 
			
		||||
          child: Column(
 | 
			
		||||
            children: [
 | 
			
		||||
              const Gap(8),
 | 
			
		||||
              Card(
 | 
			
		||||
                child: ListTile(
 | 
			
		||||
                  shape: const RoundedRectangleBorder(
 | 
			
		||||
                    borderRadius: BorderRadius.all(Radius.circular(8.0)),
 | 
			
		||||
                  ),
 | 
			
		||||
                  leading: CircleAvatar(
 | 
			
		||||
                    child:
 | 
			
		||||
                        bot.account.profile.picture != null
 | 
			
		||||
                            ? ProfilePictureWidget(
 | 
			
		||||
                              file: bot.account.profile.picture!,
 | 
			
		||||
                            )
 | 
			
		||||
                            : const Icon(Symbols.smart_toy),
 | 
			
		||||
                  ),
 | 
			
		||||
                  title: Text(bot.account.nick),
 | 
			
		||||
                  subtitle: Text(bot.account.name),
 | 
			
		||||
                  trailing: PopupMenuButton(
 | 
			
		||||
                    itemBuilder:
 | 
			
		||||
                        (context) => [
 | 
			
		||||
                          PopupMenuItem(
 | 
			
		||||
                            value: 'edit',
 | 
			
		||||
                            child: Row(
 | 
			
		||||
                              children: [
 | 
			
		||||
                                const Icon(Symbols.edit),
 | 
			
		||||
                                const SizedBox(width: 12),
 | 
			
		||||
                                Text('edit').tr(),
 | 
			
		||||
                              ],
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                          PopupMenuItem(
 | 
			
		||||
                            value: 'delete',
 | 
			
		||||
                            child: Row(
 | 
			
		||||
                              children: [
 | 
			
		||||
                                const Icon(Symbols.delete, color: Colors.red),
 | 
			
		||||
                                const SizedBox(width: 12),
 | 
			
		||||
                                Text(
 | 
			
		||||
                                  'delete',
 | 
			
		||||
                                  style: TextStyle(color: Colors.red),
 | 
			
		||||
                                ).tr(),
 | 
			
		||||
                              ],
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ],
 | 
			
		||||
                    onSelected: (value) {
 | 
			
		||||
                      if (value == 'edit') {
 | 
			
		||||
                        context.pushNamed(
 | 
			
		||||
                          'developerBotEdit',
 | 
			
		||||
                          pathParameters: {
 | 
			
		||||
                            'name': publisherName,
 | 
			
		||||
                            'projectId': projectId,
 | 
			
		||||
                            'id': bot.id,
 | 
			
		||||
                          },
 | 
			
		||||
                        );
 | 
			
		||||
                      } else if (value == 'delete') {
 | 
			
		||||
                        showConfirmAlert(
 | 
			
		||||
                          'deleteBotHint'.tr(),
 | 
			
		||||
                          'deleteBot'.tr(),
 | 
			
		||||
                        ).then((confirm) {
 | 
			
		||||
                          if (confirm) {
 | 
			
		||||
                            final client = ref.read(apiClientProvider);
 | 
			
		||||
                            client.delete(
 | 
			
		||||
                              '/develop/developers/$publisherName/projects/$projectId/bots/${bot.id}',
 | 
			
		||||
                            );
 | 
			
		||||
                            ref.invalidate(
 | 
			
		||||
                              botsProvider(publisherName, projectId),
 | 
			
		||||
                            );
 | 
			
		||||
                          }
 | 
			
		||||
                        });
 | 
			
		||||
                      }
 | 
			
		||||
                  title: Text('bots').tr().padding(horizontal: 8),
 | 
			
		||||
                  trailing: IconButton(
 | 
			
		||||
                    onPressed: () {
 | 
			
		||||
                      context.pushNamed(
 | 
			
		||||
                        'developerBotNew',
 | 
			
		||||
                        pathParameters: {
 | 
			
		||||
                          'name': publisherName,
 | 
			
		||||
                          'projectId': projectId,
 | 
			
		||||
                        },
 | 
			
		||||
                      );
 | 
			
		||||
                    },
 | 
			
		||||
                    icon: const Icon(Symbols.add),
 | 
			
		||||
                  ),
 | 
			
		||||
                  onTap: () {
 | 
			
		||||
                    context.pushNamed(
 | 
			
		||||
                      'developerBotDetail',
 | 
			
		||||
                      pathParameters: {
 | 
			
		||||
                        'name': publisherName,
 | 
			
		||||
                        'projectId': projectId,
 | 
			
		||||
                        'botId': bot.id,
 | 
			
		||||
                      },
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              Expanded(
 | 
			
		||||
                child: ListView.builder(
 | 
			
		||||
                  padding: EdgeInsets.zero,
 | 
			
		||||
                  itemCount: data.length,
 | 
			
		||||
                  itemBuilder: (context, index) {
 | 
			
		||||
                    final bot = data[index];
 | 
			
		||||
                    return Card(
 | 
			
		||||
                      margin: const EdgeInsets.all(8.0),
 | 
			
		||||
                      child: ListTile(
 | 
			
		||||
                        shape: const RoundedRectangleBorder(
 | 
			
		||||
                          borderRadius: BorderRadius.all(Radius.circular(8.0)),
 | 
			
		||||
                        ),
 | 
			
		||||
                        leading: CircleAvatar(
 | 
			
		||||
                          child:
 | 
			
		||||
                              bot.account.profile.picture != null
 | 
			
		||||
                                  ? ProfilePictureWidget(
 | 
			
		||||
                                    file: bot.account.profile.picture!,
 | 
			
		||||
                                  )
 | 
			
		||||
                                  : const Icon(Symbols.smart_toy),
 | 
			
		||||
                        ),
 | 
			
		||||
                        title: Text(bot.account.nick),
 | 
			
		||||
                        subtitle: Text(bot.account.name),
 | 
			
		||||
                        trailing: PopupMenuButton(
 | 
			
		||||
                          itemBuilder:
 | 
			
		||||
                              (context) => [
 | 
			
		||||
                                PopupMenuItem(
 | 
			
		||||
                                  value: 'edit',
 | 
			
		||||
                                  child: Row(
 | 
			
		||||
                                    children: [
 | 
			
		||||
                                      const Icon(Symbols.edit),
 | 
			
		||||
                                      const SizedBox(width: 12),
 | 
			
		||||
                                      Text('edit').tr(),
 | 
			
		||||
                                    ],
 | 
			
		||||
                                  ),
 | 
			
		||||
                                ),
 | 
			
		||||
                                PopupMenuItem(
 | 
			
		||||
                                  value: 'delete',
 | 
			
		||||
                                  child: Row(
 | 
			
		||||
                                    children: [
 | 
			
		||||
                                      const Icon(
 | 
			
		||||
                                        Symbols.delete,
 | 
			
		||||
                                        color: Colors.red,
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      const SizedBox(width: 12),
 | 
			
		||||
                                      Text(
 | 
			
		||||
                                        'delete',
 | 
			
		||||
                                        style: TextStyle(color: Colors.red),
 | 
			
		||||
                                      ).tr(),
 | 
			
		||||
                                    ],
 | 
			
		||||
                                  ),
 | 
			
		||||
                                ),
 | 
			
		||||
                              ],
 | 
			
		||||
                          onSelected: (value) {
 | 
			
		||||
                            if (value == 'edit') {
 | 
			
		||||
                              context.pushNamed(
 | 
			
		||||
                                'developerBotEdit',
 | 
			
		||||
                                pathParameters: {
 | 
			
		||||
                                  'name': publisherName,
 | 
			
		||||
                                  'projectId': projectId,
 | 
			
		||||
                                  'id': bot.id,
 | 
			
		||||
                                },
 | 
			
		||||
                              );
 | 
			
		||||
                            } else if (value == 'delete') {
 | 
			
		||||
                              showConfirmAlert(
 | 
			
		||||
                                'deleteBotHint'.tr(),
 | 
			
		||||
                                'deleteBot'.tr(),
 | 
			
		||||
                              ).then((confirm) {
 | 
			
		||||
                                if (confirm) {
 | 
			
		||||
                                  final client = ref.read(apiClientProvider);
 | 
			
		||||
                                  client.delete(
 | 
			
		||||
                                    '/develop/developers/$publisherName/projects/$projectId/bots/${bot.id}',
 | 
			
		||||
                                  );
 | 
			
		||||
                                  ref.invalidate(
 | 
			
		||||
                                    botsProvider(publisherName, projectId),
 | 
			
		||||
                                  );
 | 
			
		||||
                                }
 | 
			
		||||
                              });
 | 
			
		||||
                            }
 | 
			
		||||
                          },
 | 
			
		||||
                        ),
 | 
			
		||||
                        onTap: () {
 | 
			
		||||
                          context.pushNamed(
 | 
			
		||||
                            'developerBotDetail',
 | 
			
		||||
                            pathParameters: {
 | 
			
		||||
                              'name': publisherName,
 | 
			
		||||
                              'projectId': projectId,
 | 
			
		||||
                              'botId': bot.id,
 | 
			
		||||
                            },
 | 
			
		||||
                          );
 | 
			
		||||
                        },
 | 
			
		||||
                      ),
 | 
			
		||||
                    );
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
import 'dart:math';
 | 
			
		||||
 | 
			
		||||
import 'package:dropdown_button2/dropdown_button2.dart';
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
@@ -99,15 +97,6 @@ class DeveloperHubScreen extends HookConsumerWidget {
 | 
			
		||||
      ),
 | 
			
		||||
      body: Column(
 | 
			
		||||
        children: [
 | 
			
		||||
          if (currentProject.value == null)
 | 
			
		||||
            ...([
 | 
			
		||||
              // Welcome Section
 | 
			
		||||
              _WelcomeSection(currentDeveloper: currentDeveloper.value),
 | 
			
		||||
 | 
			
		||||
              // Navigation Tabs
 | 
			
		||||
              _NavigationTabs(),
 | 
			
		||||
            ]),
 | 
			
		||||
 | 
			
		||||
          // Main Content
 | 
			
		||||
          if (currentProject.value != null)
 | 
			
		||||
            Expanded(
 | 
			
		||||
@@ -195,162 +184,6 @@ class _ConsoleAppBar extends StatelessWidget implements PreferredSizeWidget {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Welcome Section
 | 
			
		||||
class _WelcomeSection extends StatelessWidget {
 | 
			
		||||
  final SnDeveloper? currentDeveloper;
 | 
			
		||||
 | 
			
		||||
  const _WelcomeSection({required this.currentDeveloper});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final isDark = Theme.of(context).brightness == Brightness.dark;
 | 
			
		||||
    return Stack(
 | 
			
		||||
      children: [
 | 
			
		||||
        Positioned.fill(
 | 
			
		||||
          child: Container(
 | 
			
		||||
            decoration: BoxDecoration(
 | 
			
		||||
              gradient: LinearGradient(
 | 
			
		||||
                colors:
 | 
			
		||||
                    isDark
 | 
			
		||||
                        ? [
 | 
			
		||||
                          Theme.of(context).colorScheme.surfaceContainerHighest,
 | 
			
		||||
                          Theme.of(context).colorScheme.surfaceContainerLow,
 | 
			
		||||
                        ]
 | 
			
		||||
                        : [const Color(0xFFE8F0FE), const Color(0xFFF1F3F4)],
 | 
			
		||||
                begin: Alignment.topLeft,
 | 
			
		||||
                end: Alignment.bottomRight,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
        Positioned(
 | 
			
		||||
          right: 16,
 | 
			
		||||
          top: 0,
 | 
			
		||||
          bottom: 0,
 | 
			
		||||
          child: _RandomStickerImage(
 | 
			
		||||
            width: 180,
 | 
			
		||||
            height: 180,
 | 
			
		||||
          ).opacity(isWideScreen(context) ? 1 : 0.5),
 | 
			
		||||
        ),
 | 
			
		||||
        Container(
 | 
			
		||||
          height: 180,
 | 
			
		||||
          width: double.infinity,
 | 
			
		||||
          padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
 | 
			
		||||
          child: Column(
 | 
			
		||||
            crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
            mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
            children: [
 | 
			
		||||
              Text(
 | 
			
		||||
                'Good morning!',
 | 
			
		||||
                style: TextStyle(
 | 
			
		||||
                  fontSize: 32,
 | 
			
		||||
                  fontWeight: FontWeight.w400,
 | 
			
		||||
                  color: Theme.of(context).colorScheme.onSurface,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              const Gap(4),
 | 
			
		||||
              Text(
 | 
			
		||||
                currentDeveloper != null
 | 
			
		||||
                    ? "You're working as ${currentDeveloper!.publisher!.nick}"
 | 
			
		||||
                    : "Choose a developer and continue.",
 | 
			
		||||
                style: TextStyle(
 | 
			
		||||
                  fontSize: 16,
 | 
			
		||||
                  color: Theme.of(context).colorScheme.onSurfaceVariant,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Random Sticker Image Widget
 | 
			
		||||
class _RandomStickerImage extends StatelessWidget {
 | 
			
		||||
  final double? width;
 | 
			
		||||
  final double? height;
 | 
			
		||||
 | 
			
		||||
  const _RandomStickerImage({this.width, this.height});
 | 
			
		||||
 | 
			
		||||
  static const List<String> _stickers = [
 | 
			
		||||
    'assets/images/stickers/clap.png',
 | 
			
		||||
    'assets/images/stickers/confuse.png',
 | 
			
		||||
    'assets/images/stickers/pray.png',
 | 
			
		||||
    'assets/images/stickers/thumb_up.png',
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  String _getRandomSticker() {
 | 
			
		||||
    final random = Random();
 | 
			
		||||
    return _stickers[random.nextInt(_stickers.length)];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Image.asset(
 | 
			
		||||
      _getRandomSticker(),
 | 
			
		||||
      width: width ?? 80,
 | 
			
		||||
      height: height ?? 80,
 | 
			
		||||
      fit: BoxFit.contain,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Navigation Tabs
 | 
			
		||||
class _NavigationTabs extends StatelessWidget {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Container(
 | 
			
		||||
      decoration: BoxDecoration(color: Theme.of(context).colorScheme.surface),
 | 
			
		||||
      child: Row(
 | 
			
		||||
        children: [
 | 
			
		||||
          const Gap(24),
 | 
			
		||||
          _NavTabItem(title: 'Dashboard', isActive: true),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _NavTabItem extends StatelessWidget {
 | 
			
		||||
  final String title;
 | 
			
		||||
  final bool isActive;
 | 
			
		||||
 | 
			
		||||
  const _NavTabItem({required this.title, this.isActive = false});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Container(
 | 
			
		||||
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
 | 
			
		||||
      decoration: BoxDecoration(
 | 
			
		||||
        border: Border(
 | 
			
		||||
          bottom: BorderSide(
 | 
			
		||||
            color:
 | 
			
		||||
                isActive
 | 
			
		||||
                    ? Theme.of(context).colorScheme.primary
 | 
			
		||||
                    : Colors.transparent,
 | 
			
		||||
            width: 2,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
      child: Row(
 | 
			
		||||
        children: [
 | 
			
		||||
          Text(
 | 
			
		||||
            title,
 | 
			
		||||
            style: TextStyle(
 | 
			
		||||
              color:
 | 
			
		||||
                  isActive
 | 
			
		||||
                      ? Theme.of(context).colorScheme.primary
 | 
			
		||||
                      : Theme.of(context).colorScheme.onSurface,
 | 
			
		||||
              fontWeight: isActive ? FontWeight.w500 : FontWeight.w400,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Main Content Section
 | 
			
		||||
class _MainContentSection extends HookConsumerWidget {
 | 
			
		||||
  final SnDeveloper? currentDeveloper;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_hooks/flutter_hooks.dart';
 | 
			
		||||
import 'package:gap/gap.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/dev_project.dart';
 | 
			
		||||
import 'package:island/screens/developers/apps.dart';
 | 
			
		||||
@@ -54,6 +55,36 @@ class ProjectDetailView extends HookConsumerWidget {
 | 
			
		||||
                    label: Text('bots'.tr()),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
                leading: Container(
 | 
			
		||||
                  width: 256,
 | 
			
		||||
                  padding: EdgeInsets.only(
 | 
			
		||||
                    left: 16,
 | 
			
		||||
                    right: 16,
 | 
			
		||||
                    bottom: 8,
 | 
			
		||||
                    top: 2,
 | 
			
		||||
                  ),
 | 
			
		||||
                  decoration: BoxDecoration(
 | 
			
		||||
                    border: Border(
 | 
			
		||||
                      bottom: BorderSide(
 | 
			
		||||
                        color: Theme.of(
 | 
			
		||||
                          context,
 | 
			
		||||
                        ).colorScheme.onSurface.withOpacity(0.12),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                  child: Row(
 | 
			
		||||
                    spacing: 8,
 | 
			
		||||
                    children: [
 | 
			
		||||
                      IconButton(
 | 
			
		||||
                        onPressed: onBackToHub,
 | 
			
		||||
                        icon: const Icon(Icons.arrow_back),
 | 
			
		||||
                        iconSize: 16,
 | 
			
		||||
                        visualDensity: VisualDensity.compact,
 | 
			
		||||
                      ),
 | 
			
		||||
                      Expanded(child: Text("backToHub").tr()),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
@@ -69,6 +100,7 @@ class ProjectDetailView extends HookConsumerWidget {
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          const Gap(4),
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -737,7 +737,7 @@ class ActivityListNotifier extends _$ActivityListNotifier
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    final response = await client.get(
 | 
			
		||||
      '/sphere/activities',
 | 
			
		||||
      '/sphere/timeline',
 | 
			
		||||
      queryParameters: queryParameters,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user