💄 Optimization styles
This commit is contained in:
@@ -18,6 +18,7 @@ import 'package:island/widgets/account/status.dart';
|
|||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/widgets/app_scaffold.dart';
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
import 'package:island/widgets/content/cloud_files.dart';
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
|
import 'package:island/widgets/content/markdown.dart';
|
||||||
import 'package:island/widgets/post/post_list.dart';
|
import 'package:island/widgets/post/post_list.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:palette_generator/palette_generator.dart';
|
import 'package:palette_generator/palette_generator.dart';
|
||||||
@@ -233,25 +234,36 @@ class PublisherProfileScreen extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
).padding(horizontal: 24, top: 24);
|
).padding(horizontal: 24, top: 24);
|
||||||
|
|
||||||
Widget publisherVerificationWidget(SnPublisher data) => Card(
|
Widget publisherBadgesWidget(SnPublisher data) =>
|
||||||
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
(badges.value?.isNotEmpty ?? false)
|
||||||
child: Column(
|
? Card(
|
||||||
children: [
|
child: BadgeList(
|
||||||
if (badges.value?.isNotEmpty ?? false)
|
badges: badges.value!,
|
||||||
BadgeList(badges: badges.value!).padding(top: 16),
|
).padding(horizontal: 26, vertical: 20),
|
||||||
if (data.verification != null)
|
).padding(horizontal: 4)
|
||||||
VerificationStatusCard(mark: data.verification!),
|
: const SizedBox.shrink();
|
||||||
],
|
|
||||||
),
|
|
||||||
).padding(top: 16);
|
|
||||||
|
|
||||||
Widget publisherDetailWidget(SnPublisher data) => Card(
|
Widget publisherVerificationWidget(SnPublisher data) =>
|
||||||
|
(data.verification != null)
|
||||||
|
? Card(
|
||||||
|
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
child: VerificationStatusCard(mark: data.verification!),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink();
|
||||||
|
|
||||||
|
Widget publisherBioWidget(SnPublisher data) => Card(
|
||||||
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
Text('bio').tr().bold().padding(bottom: 2),
|
Text('bio').tr().bold().fontSize(15).padding(bottom: 8),
|
||||||
Text(data.bio.isEmpty ? 'descriptionNone'.tr() : data.bio),
|
if (data.bio.isEmpty)
|
||||||
|
Text('descriptionNone').tr().italic()
|
||||||
|
else
|
||||||
|
MarkdownTextContent(
|
||||||
|
content: data.bio,
|
||||||
|
linesMargin: EdgeInsets.zero,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 20, vertical: 16),
|
).padding(horizontal: 20, vertical: 16),
|
||||||
);
|
);
|
||||||
@@ -325,8 +337,9 @@ class PublisherProfileScreen extends HookConsumerWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
publisherBasisWidget(data),
|
publisherBasisWidget(data),
|
||||||
|
publisherBadgesWidget(data),
|
||||||
publisherVerificationWidget(data),
|
publisherVerificationWidget(data),
|
||||||
publisherDetailWidget(data),
|
publisherBioWidget(data),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -377,11 +390,14 @@ class PublisherProfileScreen extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(child: publisherBasisWidget(data)),
|
SliverToBoxAdapter(
|
||||||
|
child: publisherBasisWidget(data).padding(bottom: 8),
|
||||||
|
),
|
||||||
|
SliverToBoxAdapter(child: publisherBadgesWidget(data)),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: publisherVerificationWidget(data),
|
child: publisherVerificationWidget(data),
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(child: publisherDetailWidget(data)),
|
SliverToBoxAdapter(child: publisherBioWidget(data)),
|
||||||
SliverPostList(pubName: name),
|
SliverPostList(pubName: name),
|
||||||
SliverGap(MediaQuery.of(context).padding.bottom + 16),
|
SliverGap(MediaQuery.of(context).padding.bottom + 16),
|
||||||
],
|
],
|
||||||
|
@@ -32,12 +32,12 @@ class BadgeItem extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(4),
|
padding: const EdgeInsets.all(4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: (template?.color ?? Colors.blue).withOpacity(0.1),
|
color: (template?.color ?? Colors.blue).withOpacity(0.2),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
template?.icon ?? Icons.stars,
|
template?.icon ?? Icons.stars,
|
||||||
color: template?.color ?? Colors.orange,
|
color: template?.color ?? Colors.blue,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@@ -86,6 +86,7 @@ class AccountStatusCreationWidget extends HookConsumerWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
builder:
|
builder:
|
||||||
(context) => AccountStatusCreationSheet(
|
(context) => AccountStatusCreationSheet(
|
||||||
|
@@ -9,6 +9,7 @@ import 'package:island/pods/network.dart';
|
|||||||
import 'package:island/pods/userinfo.dart';
|
import 'package:island/pods/userinfo.dart';
|
||||||
import 'package:island/widgets/account/status.dart';
|
import 'package:island/widgets/account/status.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
|
||||||
class AccountStatusCreationSheet extends HookConsumerWidget {
|
class AccountStatusCreationSheet extends HookConsumerWidget {
|
||||||
@@ -71,178 +72,145 @@ class AccountStatusCreationSheet extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Container(
|
return SheetScaffold(
|
||||||
constraints: BoxConstraints(
|
heightFactor: 0.6,
|
||||||
maxHeight: MediaQuery.of(context).size.height * 0.8,
|
titleText:
|
||||||
),
|
initialStatus == null ? 'statusCreate'.tr() : 'statusUpdate'.tr(),
|
||||||
child: Column(
|
actions: [
|
||||||
children: [
|
TextButton.icon(
|
||||||
Padding(
|
onPressed:
|
||||||
padding: EdgeInsets.only(top: 16, left: 20, right: 16, bottom: 12),
|
submitting.value
|
||||||
child: Row(
|
? null
|
||||||
children: [
|
: () {
|
||||||
Text(
|
submitStatus();
|
||||||
initialStatus == null
|
},
|
||||||
? 'statusCreate'.tr()
|
icon: const Icon(Symbols.upload),
|
||||||
: 'statusUpdate'.tr(),
|
label: Text(initialStatus == null ? 'create' : 'update').tr(),
|
||||||
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
style: ButtonStyle(
|
||||||
fontWeight: FontWeight.w600,
|
visualDensity: VisualDensity(
|
||||||
letterSpacing: -0.5,
|
horizontal: VisualDensity.minimumDensity,
|
||||||
),
|
),
|
||||||
|
foregroundColor: WidgetStatePropertyAll(
|
||||||
|
Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (initialStatus != null)
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Symbols.delete),
|
||||||
|
onPressed: submitting.value ? null : () => clearStatus(),
|
||||||
|
style: IconButton.styleFrom(minimumSize: const Size(36, 36)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
const Gap(24),
|
||||||
|
TextField(
|
||||||
|
controller: labelController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'statusLabel'.tr(),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
),
|
||||||
TextButton.icon(
|
onTapOutside:
|
||||||
onPressed:
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
submitting.value
|
),
|
||||||
? null
|
const SizedBox(height: 24),
|
||||||
: () {
|
Text(
|
||||||
submitStatus();
|
'statusAttitude'.tr(),
|
||||||
},
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
icon: const Icon(Symbols.upload),
|
),
|
||||||
label: Text(initialStatus == null ? 'create' : 'update').tr(),
|
const SizedBox(height: 8),
|
||||||
style: ButtonStyle(
|
SegmentedButton(
|
||||||
visualDensity: VisualDensity(
|
segments: [
|
||||||
horizontal: VisualDensity.minimumDensity,
|
ButtonSegment(
|
||||||
),
|
value: 0,
|
||||||
foregroundColor: WidgetStatePropertyAll(
|
icon: const Icon(Symbols.sentiment_satisfied),
|
||||||
Theme.of(context).colorScheme.onSurface,
|
label: Text('attitudePositive'.tr()),
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (initialStatus != null)
|
ButtonSegment(
|
||||||
IconButton(
|
value: 1,
|
||||||
icon: const Icon(Symbols.delete),
|
icon: const Icon(Symbols.sentiment_stressed),
|
||||||
onPressed: submitting.value ? null : () => clearStatus(),
|
label: Text('attitudeNeutral'.tr()),
|
||||||
style: IconButton.styleFrom(
|
),
|
||||||
minimumSize: const Size(36, 36),
|
ButtonSegment(
|
||||||
),
|
value: 2,
|
||||||
),
|
icon: const Icon(Symbols.sentiment_sad),
|
||||||
IconButton(
|
label: Text('attitudeNegative'.tr()),
|
||||||
icon: const Icon(Symbols.close),
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
style: IconButton.styleFrom(minimumSize: const Size(36, 36)),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
selected: {attitude.value},
|
||||||
|
onSelectionChanged: (Set<int> newSelection) {
|
||||||
|
attitude.value = newSelection.first;
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
const Gap(12),
|
||||||
const Divider(height: 1),
|
SwitchListTile(
|
||||||
Expanded(
|
title: Text('statusInvisible'.tr()),
|
||||||
child: SingleChildScrollView(
|
subtitle: Text('statusInvisibleDescription'.tr()),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
value: isInvisible.value,
|
||||||
child: Column(
|
contentPadding: EdgeInsets.symmetric(horizontal: 8),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
onChanged: (bool value) {
|
||||||
children: [
|
isInvisible.value = value;
|
||||||
const Gap(24),
|
},
|
||||||
TextField(
|
),
|
||||||
controller: labelController,
|
SwitchListTile(
|
||||||
decoration: InputDecoration(
|
title: Text('statusNotDisturb'.tr()),
|
||||||
labelText: 'statusLabel'.tr(),
|
subtitle: Text('statusNotDisturbDescription'.tr()),
|
||||||
border: const OutlineInputBorder(
|
value: isNotDisturb.value,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
contentPadding: EdgeInsets.symmetric(horizontal: 8),
|
||||||
),
|
onChanged: (bool value) {
|
||||||
),
|
isNotDisturb.value = value;
|
||||||
onTapOutside:
|
},
|
||||||
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
),
|
||||||
),
|
const SizedBox(height: 24),
|
||||||
const SizedBox(height: 24),
|
Text(
|
||||||
Text(
|
'statusClearTime'.tr(),
|
||||||
'statusAttitude'.tr(),
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
),
|
||||||
),
|
const SizedBox(height: 8),
|
||||||
const SizedBox(height: 8),
|
ListTile(
|
||||||
SegmentedButton(
|
title: Text(
|
||||||
segments: [
|
clearedAt.value == null
|
||||||
ButtonSegment(
|
? 'statusNoAutoClear'.tr()
|
||||||
value: 0,
|
: DateFormat.yMMMd().add_jm().format(clearedAt.value!),
|
||||||
icon: const Icon(Symbols.sentiment_satisfied),
|
|
||||||
label: Text('attitudePositive'.tr()),
|
|
||||||
),
|
|
||||||
ButtonSegment(
|
|
||||||
value: 1,
|
|
||||||
icon: const Icon(Symbols.sentiment_stressed),
|
|
||||||
label: Text('attitudeNeutral'.tr()),
|
|
||||||
),
|
|
||||||
ButtonSegment(
|
|
||||||
value: 2,
|
|
||||||
icon: const Icon(Symbols.sentiment_sad),
|
|
||||||
label: Text('attitudeNegative'.tr()),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
selected: {attitude.value},
|
|
||||||
onSelectionChanged: (Set<int> newSelection) {
|
|
||||||
attitude.value = newSelection.first;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const Gap(12),
|
|
||||||
SwitchListTile(
|
|
||||||
title: Text('statusInvisible'.tr()),
|
|
||||||
subtitle: Text('statusInvisibleDescription'.tr()),
|
|
||||||
value: isInvisible.value,
|
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 8),
|
|
||||||
onChanged: (bool value) {
|
|
||||||
isInvisible.value = value;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
SwitchListTile(
|
|
||||||
title: Text('statusNotDisturb'.tr()),
|
|
||||||
subtitle: Text('statusNotDisturbDescription'.tr()),
|
|
||||||
value: isNotDisturb.value,
|
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 8),
|
|
||||||
onChanged: (bool value) {
|
|
||||||
isNotDisturb.value = value;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
Text(
|
|
||||||
'statusClearTime'.tr(),
|
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
ListTile(
|
|
||||||
title: Text(
|
|
||||||
clearedAt.value == null
|
|
||||||
? 'statusNoAutoClear'.tr()
|
|
||||||
: DateFormat.yMMMd().add_jm().format(
|
|
||||||
clearedAt.value!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
trailing: const Icon(Symbols.schedule),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
side: BorderSide(
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onTap: () async {
|
|
||||||
final now = DateTime.now();
|
|
||||||
final date = await showDatePicker(
|
|
||||||
context: context,
|
|
||||||
initialDate: now,
|
|
||||||
firstDate: now,
|
|
||||||
lastDate: now.add(const Duration(days: 365)),
|
|
||||||
);
|
|
||||||
if (date == null) return;
|
|
||||||
if (!context.mounted) return;
|
|
||||||
final time = await showTimePicker(
|
|
||||||
context: context,
|
|
||||||
initialTime: TimeOfDay.now(),
|
|
||||||
);
|
|
||||||
if (time == null) return;
|
|
||||||
clearedAt.value = DateTime(
|
|
||||||
date.year,
|
|
||||||
date.month,
|
|
||||||
date.day,
|
|
||||||
time.hour,
|
|
||||||
time.minute,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Gap(MediaQuery.of(context).padding.bottom + 24),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
trailing: const Icon(Symbols.schedule),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
side: BorderSide(color: Theme.of(context).colorScheme.outline),
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
final now = DateTime.now();
|
||||||
|
final date = await showDatePicker(
|
||||||
|
context: context,
|
||||||
|
initialDate: now,
|
||||||
|
firstDate: now,
|
||||||
|
lastDate: now.add(const Duration(days: 365)),
|
||||||
|
);
|
||||||
|
if (date == null) return;
|
||||||
|
if (!context.mounted) return;
|
||||||
|
final time = await showTimePicker(
|
||||||
|
context: context,
|
||||||
|
initialTime: TimeOfDay.now(),
|
||||||
|
);
|
||||||
|
if (time == null) return;
|
||||||
|
clearedAt.value = DateTime(
|
||||||
|
date.year,
|
||||||
|
date.month,
|
||||||
|
date.day,
|
||||||
|
time.hour,
|
||||||
|
time.minute,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
Gap(MediaQuery.of(context).padding.bottom + 24),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -670,11 +670,9 @@ class ComposeLogic {
|
|||||||
// Send request
|
// Send request
|
||||||
await client.request(
|
await client.request(
|
||||||
endpoint,
|
endpoint,
|
||||||
|
queryParameters: {'pub': state.currentPublisher.value?.name},
|
||||||
data: payload,
|
data: payload,
|
||||||
options: Options(
|
options: Options(method: isNewPost ? 'POST' : 'PATCH'),
|
||||||
headers: {'X-Pub': state.currentPublisher.value?.name},
|
|
||||||
method: isNewPost ? 'POST' : 'PATCH',
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Delete draft after successful submission
|
// Delete draft after successful submission
|
||||||
|
Reference in New Issue
Block a user