Skip to content

Commit

Permalink
feat: author setting (#382)
Browse files Browse the repository at this point in the history
* feat: author setting UI

* feat: add notice edit page

* feat: edit notice UI

* chore: wrong translation

* feat: confirm dialog extension

* feat: delete action

* feat: send notification
  • Loading branch information
2paperstar authored Sep 18, 2024
1 parent a1a71aa commit 689d8e8
Show file tree
Hide file tree
Showing 19 changed files with 478 additions and 4 deletions.
1 change: 1 addition & 0 deletions assets/i18n/common.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"back": "Back",
"skip": "Skip",
"done": "Done",
"confirm": "Confirm",
"duration": {
"day": {
"one": "$n day",
Expand Down
1 change: 1 addition & 0 deletions assets/i18n/common_ko.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"back": "이전",
"skip": "건너뛰기",
"done": "완료",
"confirm": "",
"duration": {
"day": "$n일",
"week": {
Expand Down
30 changes: 29 additions & 1 deletion assets/i18n/notice.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,33 @@
"description": "Enter search keyword"
},
"list": "List",
"viewMore(rich)": "... ${more(more)}"
"viewMore(rich)": "... ${more(more)}",
"settings": {
"edit": {
"action": "Edit / Write Additional Notice"
},
"delete": {
"action": "Delete",
"title": "Are you sure you want to delete the notice?",
"description": "This action cannot be undone."
},
"sendNotification": {
"action": "Send Notification Now",
"caution": "Editing is disabled after sending a notification.",
"title": "Send Notification Now?",
"description": "You cannot edit the notice after sending the notification."
}
},
"edit": {
"title": "Edit Notice",
"leftTime(rich)": "Time left until editing is disabled $bold",
"cannotEdit": "The deadline for editing has passed.",
"editBody": "Edit Content",
"addEnglish": "Write English Notice",
"additional": {
"action": "Add Notice / Change Deadline",
"title": "Add Notice",
"deadline(rich)": "Change Deadline ${small((Optional))}"
}
}
}
30 changes: 29 additions & 1 deletion assets/i18n/notice_ko.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,33 @@
"description": "검색 키워드를 입력해보세요"
},
"list": "목록",
"viewMore(rich)": "... ${more(더 보기)}"
"viewMore(rich)": "... ${more(더 보기)}",
"settings": {
"edit": {
"action": "수정 / 추가공지 작성"
},
"delete": {
"action": "삭제",
"title": "정말 공지를 삭제하시겠습니까?",
"description": "이 작업은 되돌릴 수 없습니다."
},
"sendNotification": {
"action": "지금 바로 알림 보내기",
"caution": "알림을 보내면 수정이 불가능합니다.",
"title": "지금 바로 알림을 보내시겠습니까?",
"description": "알림을 보내면 수정할 수 없습니다."
}
},
"edit": {
"title": "공지 수정",
"leftTime(rich)": "수정 불가능까지 남은 시간 $bold",
"cannotEdit": "수정 가능한 기한이 지났습니다.",
"editBody": "본문 수정",
"addEnglish": "영어공지 작성",
"additional": {
"action": "추가공지 작성 / 마감일 변경",
"title": "추가공지 작성",
"deadline(rich)": "마감시각 변경 ${small((선택))}"
}
}
}
6 changes: 6 additions & 0 deletions assets/icons/add.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/bell.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/body.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/delete.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/language.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions lib/app/modules/common/presentation/extensions/confirm.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import 'package:flutter/cupertino.dart';
import 'package:ziggle/app/values/palette.dart';
import 'package:ziggle/gen/strings.g.dart';

extension BuildContextX on BuildContext {
Future<T?> showDialog<T>({
required String title,
required String content,
required void Function(BuildContext) onConfirm,
}) =>
showCupertinoDialog(
context: this,
builder: (context) => CupertinoAlertDialog(
title: Text(title),
content: Text(content),
actions: [
CupertinoDialogAction(
onPressed: () => Navigator.of(context).pop(),
textStyle: const TextStyle(color: Palette.gray),
child: Text(context.t.common.cancel),
),
CupertinoDialogAction(
isDestructiveAction: true,
onPressed: () => onConfirm(context),
child: Text(context.t.common.confirm),
),
],
),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ abstract class NoticeApi {
@DELETE('{id}')
Future<void> deleteNotice(@Path('id') int id);

@POST('{id}/alarm')
Future<void> alarm(@Path('id') int id);

@POST('{id}/additional')
Future<NoticeModel> addAdditionalContent(
@Path('id') int id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ class RestNoticeRepository implements NoticeRepository {

@override
Future<void> deleteNotice(int id) {
// TODO: implement deleteNotice
throw UnimplementedError();
return _api.deleteNotice(id);
}

@override
Expand Down Expand Up @@ -145,4 +144,10 @@ class RestNoticeRepository implements NoticeRepository {
// TODO: implement writeForeign
throw UnimplementedError();
}

@override
Future<NoticeEntity> sendNotification(int id) async {
await _api.alarm(id);
return _api.getNotice(id);
}
}
25 changes: 25 additions & 0 deletions lib/app/modules/notices/domain/entities/notice_entity.dart
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,29 @@ extension NoticeEntityExtension on NoticeEntity {
}

bool get isCertified => false;

NoticeEntity copyWith({
DateTime? publishedAt,
}) =>
NoticeEntity(
id: id,
views: views,
langs: langs,
deadline: deadline,
currentDeadline: currentDeadline,
createdAt: createdAt,
deletedAt: deletedAt,
tags: tags,
title: title,
content: content,
additionalContents: additionalContents,
reactions: reactions,
author: author,
images: images,
documentUrls: documentUrls,
isReminded: isReminded,
publishedAt: publishedAt ?? this.publishedAt,
groupName: groupName,
category: category,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ abstract class NoticeRepository {
DateTime? deadline,
});
Future<void> deleteNotice(int id);
Future<NoticeEntity> sendNotification(int id);
Future<NoticeEntity> addAdditionalContent({
required int id,
required String content,
Expand Down
18 changes: 18 additions & 0 deletions lib/app/modules/notices/presentation/bloc/notice_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,37 @@ class NoticeBloc extends Bloc<NoticeEvent, NoticeState> {
final entity = await _repository.getNotice(event.entity.id);
emit(_Loaded(entity));
});
on<_SendNotification>((event, emit) async {
if (state.entity == null) return;
emit(_Loading(state.entity!.copyWith(publishedAt: DateTime.now())));
final entity = await _repository.sendNotification(state.entity!.id);
emit(_Loaded(entity));
});
on<_Delete>((event, emit) async {
if (state.entity == null) return;
emit(_Loading(state.entity!));
await _repository.deleteNotice(state.entity!.id);
emit(const _Deleted());
});
}
}

@freezed
sealed class NoticeEvent with _$NoticeEvent {
const factory NoticeEvent.load(NoticeEntity entity) = _Load;
const factory NoticeEvent.sendNotification() = _SendNotification;
const factory NoticeEvent.delete() = _Delete;
}

@freezed
sealed class NoticeState with _$NoticeState {
const NoticeState._();
const factory NoticeState.initial() = _Initial;
const factory NoticeState.loaded(NoticeEntity entity) = _Loaded;
const factory NoticeState.loading(NoticeEntity entity) = _Loading;
const factory NoticeState.deleted() = _Deleted;

NoticeEntity? get entity => mapOrNull(loaded: (state) => state.entity);
bool get isLoaded => this is _Loaded;
bool get isDeleted => this is _Deleted;
}
Loading

0 comments on commit 689d8e8

Please sign in to comment.