Skip to content

Commit

Permalink
Merge pull request #113 from sparcs-kaist/feat@design-issue
Browse files Browse the repository at this point in the history
Resolve #111, 배포 전 디자인 이슈 수정
  • Loading branch information
sboh1214 authored Jul 31, 2023
2 parents 8536f30 + fabbfd5 commit 148f48c
Show file tree
Hide file tree
Showing 15 changed files with 232 additions and 148 deletions.
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<application
android:name="${applicationName}"
Expand Down
14 changes: 7 additions & 7 deletions assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@
"speech": "Speech"
},
"days": {
"mon": "Monday",
"tue": "Tuesday",
"wed": "Wednesday",
"thu": "Thursday",
"fri": "Friday",
"sat": "Saturday",
"sun": "Sunday"
"mon": "Mon",
"tue": "Tue",
"wed": "Wed",
"thu": "Thu",
"fri": "Fri",
"sat": "Sat",
"sun": "Sun"
}
},
"dictionary": {
Expand Down
4 changes: 3 additions & 1 deletion lib/constants/url.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ const API_TIMETABLE_ADD_LECTURE_URL =
const API_TIMETABLE_REMOVE_LECTURE_URL =
API_TIMETABLE_URL + "/{timetable_id}/remove-lecture";
const API_LIKED_REVIEW_URL = API_URL + "/users/{user_id}/liked-reviews";
const API_SHARE_URL = API_URL + "share/timetable/{type}";
const API_SHARE_URL = API_URL + "share/timetable/{share_type}";

enum ShareType { image, ical }
3 changes: 3 additions & 0 deletions lib/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ class _OTLHomeState extends State<OTLHome> with SingleTickerProviderStateMixin {
padding: const EdgeInsets.only(left: 16),
child: SemesterPicker(
onSemesterChanged: () {
context
.read<LectureSearchModel>()
.setSelectedLecture(null);
context.read<LectureSearchModel>().lectureClear();
},
),
Expand Down
56 changes: 42 additions & 14 deletions lib/pages/timetable_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ class _TimetablePageState extends State<TimetablePage> {
final _selectedKey = GlobalKey();
final _paintKey = GlobalKey();

Lecture? _selectedLecture;

@override
Widget build(BuildContext context) {
if (context.select<TimetableModel, bool>((model) => model.isLoaded))
Expand All @@ -53,16 +51,46 @@ class _TimetablePageState extends State<TimetablePage> {
color: OTLColor.grayF,
child: Column(
children: <Widget>[
Container(
color: OTLColor.pinksLight,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 16),
decoration: BoxDecoration(
color: OTLColor.grayF,
borderRadius:
BorderRadius.only(topLeft: Radius.circular(16)),
),
child: _buildTimetableTabs(context),
SizedBox(
height: 60,
child: Row(
children: [
Expanded(
child: Container(
color: OTLColor.pinksLight,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 16),
decoration: BoxDecoration(
color: OTLColor.grayF,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16)),
),
child: _buildTimetableTabs(context),
),
),
),
if (mode == 0)
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
context
.read<LectureSearchModel>()
.resetLectureFilter();
Navigator.push(
context, buildLectureSearchPageRoute());
},
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 18, 16, 18),
child: Icon(
Icons.search,
size: 24,
color: OTLColor.pinksMain,
),
),
)
else
const SizedBox(width: 16),
],
),
),
Expanded(
Expand Down Expand Up @@ -115,7 +143,7 @@ class _TimetablePageState extends State<TimetablePage> {
if (!isExamTime)
TimetableSummary(
lectures: lectures,
tempLecture: _selectedLecture,
tempLecture: context.watch<LectureSearchModel>().selectedLecture,
),
],
);
Expand All @@ -129,7 +157,7 @@ class _TimetablePageState extends State<TimetablePage> {
return Timetable(
lectures: (lectureSearchModel.selectedLecture == null)
? lectures
: [...lectures, _selectedLecture!],
: [...lectures, lectureSearchModel.selectedLecture!],
isExamTime: isExamTime,
builder: (lecture, classTimeIndex, blockHeight) {
final isSelected = lectureSearchModel.selectedLecture == lecture;
Expand Down
38 changes: 16 additions & 22 deletions lib/providers/timetable_model.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widgetkit/flutter_widgetkit.dart';
import 'package:otlplus/constants/url.dart';
Expand All @@ -10,8 +10,7 @@ import 'package:otlplus/models/lecture.dart';
import 'package:otlplus/models/semester.dart';
import 'package:otlplus/models/timetable.dart';
import 'package:otlplus/models/user.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:otlplus/utils/export_file.dart';

class TimetableModel extends ChangeNotifier {
late User _user;
Expand All @@ -36,9 +35,6 @@ class TimetableModel extends ChangeNotifier {
bool _isLoaded = false;
bool get isLoaded => _isLoaded;

String get shareApiParameter =>
"?timetable=${currentTimetable.id}&year=${selectedSemester.year}&semester=${selectedSemester.semester}&language=";

TimetableModel({bool forTest = false}) {
if (forTest) {
_user = User(
Expand Down Expand Up @@ -248,23 +244,21 @@ class TimetableModel extends ChangeNotifier {
return false;
}

Future<bool> shareTimetable(String type, String language) async {
Future<bool> shareTimetable(ShareType type, String language) async {
try {
final status = await [
Permission.storage,
].request();

if (status[Permission.storage]!.isGranted) {
final dir = Platform.isAndroid
? '/storage/emulated/0/Download'
: (await getApplicationDocumentsDirectory()).path;
await DioProvider().dio.download(
API_SHARE_URL.replaceFirst('{type}', type) +
shareApiParameter +
language,
dir + '/timetable.${type == 'image' ? 'png' : 'ics'}',
);
}
final response = await DioProvider().dio.get(
API_SHARE_URL.replaceFirst(
'{share_type}', type == ShareType.image ? 'image' : 'ical'),
queryParameters: {
'timetable': currentTimetable.id,
'year': selectedSemester.year,
'semester': selectedSemester.semester,
'language': language,
},
options: Options(responseType: ResponseType.bytes),
);

writeFile(type, response.data);
return true;
} catch (exception) {
print(exception);
Expand Down
48 changes: 48 additions & 0 deletions lib/utils/export_file.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'dart:io';
import 'dart:ui';

import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:otlplus/constants/url.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:open_app_file/open_app_file.dart';

const MethodChannel _channel = const MethodChannel("org.sparcs.otlplus");

Future<void> exportImage(RenderRepaintBoundary boundary) async {
final image = await boundary.toImage(pixelRatio: 3.0);
final byteData = await image.toByteData(format: ImageByteFormat.png);

writeFile(ShareType.image, byteData?.buffer.asUint8List());
}

Future<void> writeFile(ShareType type, Uint8List? bytes) async {
final fileName =
"OTL-${DateTime.now().millisecondsSinceEpoch}.${type == ShareType.image ? 'png' : 'ics'}";

if (Platform.isAndroid) {
if (await Permission.storage.request().isGranted) {
switch (type) {
case ShareType.image:
_channel.invokeMethod("writeImageAsBytes", <String, dynamic>{
"fileName": fileName,
"bytes": bytes,
});
break;
case ShareType.ical:
final directory = await getExternalStorageDirectory();
final path =
"${directory?.path ?? '/storage/emulated/0/Android/data/org.sparcs.otlplus/files'}/$fileName";
File(path).writeAsBytesSync(bytes as List<int>);
OpenAppFile.open(path);
break;
}
}
} else {
final directory = await getApplicationDocumentsDirectory();
final path = "${directory.path}/$fileName";
File(path).writeAsBytesSync(bytes as List<int>);
OpenAppFile.open(path);
}
}
30 changes: 0 additions & 30 deletions lib/utils/export_image.dart

This file was deleted.

21 changes: 21 additions & 0 deletions lib/utils/get_text_height.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'package:flutter/material.dart';

double getTextHeight(
BuildContext context, {
required String text,
required TextStyle style,
double maxWidth = double.infinity,
}) {
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: style),
textDirection: TextDirection.ltr,
textScaleFactor: MediaQuery.of(context).textScaleFactor,
)..layout(maxWidth: maxWidth);
return textPainter.size.height;
}

double singleHeight(BuildContext context, TextStyle style) {
return style.fontSize! *
style.height! *
MediaQuery.of(context).textScaleFactor;
}
41 changes: 27 additions & 14 deletions lib/widgets/map_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:otlplus/constants/color.dart';
import 'package:otlplus/constants/text_styles.dart';
import 'package:otlplus/models/classtime.dart';
import 'package:otlplus/models/lecture.dart';
import 'package:otlplus/utils/get_text_height.dart';

const POSITION_OF_LOCATIONS = {
'E2': {'left': 0.599, 'top': 0.802},
Expand Down Expand Up @@ -285,23 +286,35 @@ class _MapViewState extends State<MapView> {
),
if (classtime.roomName.isNotEmpty)
Padding(
padding: const EdgeInsets.fromLTRB(16, 2.5, 0, 2.5),
child: Container(
height: 23,
padding: const EdgeInsets.symmetric(horizontal: 6),
alignment: Alignment.center,
decoration: BoxDecoration(
color: OTLColor.blockColors[lecture.course % 16],
borderRadius: BorderRadius.circular(100),
),
child: Text(
(buildingCode == '기타')
padding: const EdgeInsets.fromLTRB(16, 4, 0, 0),
child: Builder(
builder: (context) {
final location = (buildingCode == '기타')
? _isKo
? classtime.classroom
: classtime.classroomEn
: classtime.roomName,
style: labelRegular,
),
: classtime.roomName;
final isMultiLine = (getTextHeight(context,
text: location,
style: labelRegular,
maxWidth: 143) ~/
singleHeight(context, labelRegular)) >
1;

return Container(
width: isMultiLine ? 155 : null,
padding: const EdgeInsets.fromLTRB(6, 2, 6, 4),
alignment: Alignment.center,
decoration: BoxDecoration(
color: OTLColor.blockColors[lecture.course % 16],
borderRadius: BorderRadius.circular(isMultiLine ? 13 : 100),
),
child: Text(
location,
style: labelRegular,
),
);
},
),
),
],
Expand Down
Loading

0 comments on commit 148f48c

Please sign in to comment.