Skip to content

Commit

Permalink
feat: add NoticeCard (#338)
Browse files Browse the repository at this point in the history
* feat: notice card UI

* feat: show notice content with NoticeSummary entity

* feat: add DDay widget

* feat: add behavior property in ZigglePressable

* feat: add press events in NoticeCard
  • Loading branch information
2paperstar authored Sep 13, 2024
1 parent 57297f3 commit 3f8910a
Show file tree
Hide file tree
Showing 9 changed files with 321 additions and 0 deletions.
19 changes: 19 additions & 0 deletions assets/i18n/notice.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,24 @@
"title": "Please read and agree to the following.",
"upload": "Publish Notice"
}
},
"dDay": {
"daysLeft(rich)": {
"one": "$n day left",
"other": "$n days left"
},
"hoursLeft(rich)": {
"one": "$n hour left",
"other": "$n hours left"
},
"minutesLeft(rich)": {
"one": "$n minute left",
"other": "$n minutes left"
},
"secondsLeft(rich)": {
"one": "$n second left",
"other": "$n seconds left"
},
"overdue": "OVERDUE"
}
}
19 changes: 19 additions & 0 deletions assets/i18n/notice_ko.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,24 @@
"title": "아래 내용을 모두 읽고,\n동의해주세요.",
"upload": "공지 게시하기"
}
},
"dDay": {
"daysLeft(rich)": {
"one": "$n day left",
"other": "$n days left"
},
"hoursLeft(rich)": {
"one": "$n hour left",
"other": "$n hours left"
},
"minutesLeft(rich)": {
"one": "$n minute left",
"other": "$n minutes left"
},
"secondsLeft(rich)": {
"one": "$n second left",
"other": "$n seconds left"
},
"overdue": "OVERDUE"
}
}
6 changes: 6 additions & 0 deletions assets/icons/certificated-badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions assets/icons/fire.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions assets/icons/share.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ class ZigglePressable extends StatefulWidget {
required this.onPressed,
required this.child,
this.decoration = const BoxDecoration(),
this.behavior,
});

final VoidCallback? onPressed;
final Widget child;
final BoxDecoration decoration;
final HitTestBehavior? behavior;

@override
State<ZigglePressable> createState() => _ZigglePressableState();
Expand All @@ -24,6 +26,7 @@ class _ZigglePressableState extends State<ZigglePressable> {
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: widget.behavior,
onTapDown: (_) {
if (widget.onPressed == null) return;
setState(() {
Expand Down
21 changes: 21 additions & 0 deletions lib/app/modules/notice/domain/entities/notice_summary.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class NoticeSummary {
final int id;
final String title;
final String authorName;
final DateTime? deadline;
final String content;
final List<String> images;
final int likes;
final bool authorIsCertificated;

const NoticeSummary({
required this.id,
required this.title,
required this.authorName,
required this.deadline,
required this.content,
required this.images,
required this.likes,
required this.authorIsCertificated,
});
}
92 changes: 92 additions & 0 deletions lib/app/modules/notice/presentation/widgets/d_day.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:ziggle/app/values/palette.dart';
import 'package:ziggle/gen/strings.g.dart';

class DDay extends StatefulWidget {
const DDay({super.key, required this.deadline});

final DateTime deadline;

@override
State<DDay> createState() => _DDayState();
}

class _DDayState extends State<DDay> {
late final Timer _timer;

@override
void initState() {
super.initState();
_timer = Timer.periodic(const Duration(seconds: 1), (_) => setState(() {}));
}

@override
void dispose() {
_timer.cancel();
super.dispose();
}

(
int?,
TextSpan Function(
{required num n, required InlineSpan Function(num p1) nBuilder})?
) _getN() {
final now = DateTime.now();
final diff = widget.deadline.difference(now);
if (diff.isNegative) {
return (null, null);
}
final daysLeft = diff.inDays;
if (daysLeft > 0) {
return (daysLeft, t.notice.dDay.daysLeft);
}
final hoursLeft = diff.inHours;
if (hoursLeft > 0) {
return (hoursLeft, t.notice.dDay.hoursLeft);
}
final minutesLeft = diff.inMinutes;
if (minutesLeft > 0) {
return (minutesLeft, t.notice.dDay.minutesLeft);
}
final secondsLeft = diff.inSeconds;
return (secondsLeft, t.notice.dDay.secondsLeft);
}

@override
Widget build(BuildContext context) {
final (n, builder) = _getN();

return Container(
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: n != null ? Palette.primary : Palette.grayText,
),
child: n != null
? Text.rich(
builder!(
n: n,
nBuilder: (n) => TextSpan(
text: n.toString(),
style: const TextStyle(fontWeight: FontWeight.w700),
),
),
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Palette.white,
),
)
: Text(
t.notice.dDay.overdue,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Palette.white,
),
),
);
}
}
134 changes: 134 additions & 0 deletions lib/app/modules/notice/presentation/widgets/notice_card.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import 'package:flutter/material.dart';
import 'package:ziggle/app/modules/common/presentation/widgets/ziggle_pressable.dart';
import 'package:ziggle/app/modules/notice/domain/entities/notice_summary.dart';
import 'package:ziggle/app/modules/notice/presentation/widgets/d_day.dart';
import 'package:ziggle/app/values/palette.dart';
import 'package:ziggle/gen/assets.gen.dart';

class NoticeCard extends StatelessWidget {
const NoticeCard({
super.key,
required this.notice,
required this.onPressed,
required this.onLike,
required this.onShare,
});

final NoticeSummary notice;
final VoidCallback onPressed;
final VoidCallback onLike;
final VoidCallback onShare;

@override
Widget build(BuildContext context) {
return ZigglePressable(
onPressed: onPressed,
decoration: const BoxDecoration(
color: Palette.white,
borderRadius: BorderRadius.all(Radius.circular(15)),
),
child: Padding(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Assets.images.defaultProfile.image(width: 40),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
notice.authorName,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Palette.black,
height: 1,
leadingDistribution: TextLeadingDistribution.even,
),
),
const SizedBox(width: 5),
if (notice.authorIsCertificated)
Assets.icons.certificatedBadge.svg(width: 20),
],
),
const SizedBox(height: 2),
const Text(
'10분 전',
style: TextStyle(
fontSize: 12,
color: Palette.gray,
),
),
],
),
),
if (notice.deadline != null) DDay(deadline: notice.deadline!),
],
),
const SizedBox(height: 8),
Text(
notice.title,
style: const TextStyle(
color: Palette.black,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 8),
if (notice.images.isNotEmpty)
Image.network(
notice.images.first,
height: 250,
width: double.infinity,
fit: BoxFit.cover,
)
else
Text(
notice.content,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Palette.black,
),
),
const SizedBox(height: 8),
Row(
children: [
ZigglePressable(
behavior: HitTestBehavior.translucent,
onPressed: onLike,
child: Row(
children: [
Assets.icons.fire.svg(width: 30),
const SizedBox(width: 5),
Text(
'${notice.likes}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Palette.black,
),
),
],
),
),
const Spacer(),
ZigglePressable(
behavior: HitTestBehavior.translucent,
onPressed: onShare,
child: Assets.icons.share.svg(width: 30),
),
],
)
],
),
),
);
}
}

0 comments on commit 3f8910a

Please sign in to comment.