Commit 009cf00df190ae47e21695b202bb86ee682ba403
1 parent
8df5fbf9
feat:环节&单元解锁逻辑
Showing
5 changed files
with
126 additions
and
28 deletions
.gitignore
| @@ -68,6 +68,7 @@ | @@ -68,6 +68,7 @@ | ||
| 68 | **/ios/xcode_build_ipa_adh | 68 | **/ios/xcode_build_ipa_adh |
| 69 | **/ios/xcode_build_ipa_aps | 69 | **/ios/xcode_build_ipa_aps |
| 70 | **/ios/xcode_build_ipa_dev | 70 | **/ios/xcode_build_ipa_dev |
| 71 | +**/ios/build/ | ||
| 71 | 72 | ||
| 72 | # Exceptions to above rules. | 73 | # Exceptions to above rules. |
| 73 | !**/ios/**/default.mode1v3 | 74 | !**/ios/**/default.mode1v3 |
lib/pages/section/bloc/section_bloc.dart
| @@ -200,7 +200,7 @@ class SectionBloc extends Bloc<SectionEvent, SectionState> { | @@ -200,7 +200,7 @@ class SectionBloc extends Bloc<SectionEvent, SectionState> { | ||
| 200 | final curCourseSectionEntity = findCourseSectionById(courseLessonId); | 200 | final curCourseSectionEntity = findCourseSectionById(courseLessonId); |
| 201 | if (curCourseSectionEntity != null) { | 201 | if (curCourseSectionEntity != null) { |
| 202 | final curCourseUnitDetail = _courseUnitEntity.courseUnitVOList | 202 | final curCourseUnitDetail = _courseUnitEntity.courseUnitVOList |
| 203 | - ?.firstWhere( | 203 | + ?.firstWhereOrNull( |
| 204 | (element) => element.id == curCourseSectionEntity.courseUnitId); | 204 | (element) => element.id == curCourseSectionEntity.courseUnitId); |
| 205 | return curCourseUnitDetail; | 205 | return curCourseUnitDetail; |
| 206 | } | 206 | } |
| @@ -213,39 +213,86 @@ class SectionBloc extends Bloc<SectionEvent, SectionState> { | @@ -213,39 +213,86 @@ class SectionBloc extends Bloc<SectionEvent, SectionState> { | ||
| 213 | final curCourseSectionEntity = findCourseSectionById(courseLessonId); | 213 | final curCourseSectionEntity = findCourseSectionById(courseLessonId); |
| 214 | final curSectionSort = curCourseSectionEntity?.sortOrder ?? 0; | 214 | final curSectionSort = curCourseSectionEntity?.sortOrder ?? 0; |
| 215 | 215 | ||
| 216 | - ///查找下一个section | ||
| 217 | - final nextCourseSectionEntity = findCourseSectionBySort(curSectionSort + 1); | ||
| 218 | - if (nextCourseSectionEntity != null) { | ||
| 219 | - return nextCourseSectionEntity; | 216 | + try { |
| 217 | + ///查找当前unit的下一个section | ||
| 218 | + CourseSectionEntity? nextCourseSectionEntity = | ||
| 219 | + findCourseSectionBySort(curSectionSort + 1); | ||
| 220 | + return checkCourseSectionLocked(courseLessonId, nextCourseSectionEntity, emitter); | ||
| 221 | + } catch (e) { | ||
| 222 | + if (e is ApiException) { | ||
| 223 | + showToast(e.message.toString()); | ||
| 224 | + } | ||
| 225 | + return null; | ||
| 226 | + } | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + ///检查section是否锁定 | ||
| 230 | + Future<CourseSectionEntity?> checkCourseSectionLocked(int courseLessonId, CourseSectionEntity? courseSectionEntity, | ||
| 231 | + Emitter<SectionState> emitter) async { | ||
| 232 | + if (courseSectionEntity != null) { | ||
| 233 | + if (courseSectionEntity.lock == false) { | ||
| 234 | + ///如果section没锁,直接返回 | ||
| 235 | + return courseSectionEntity; | ||
| 236 | + } else { | ||
| 237 | + ///如果section锁了,请求当前unit下的section数据,查询解锁状态 | ||
| 238 | + int courseUnitId = courseSectionEntity.courseUnitId; | ||
| 239 | + await loading(() async { | ||
| 240 | + List<CourseSectionEntity>? tempSectionEntities = | ||
| 241 | + await LessonDao.courseSection(courseUnitId: courseUnitId); | ||
| 242 | + if (tempSectionEntities != null) { | ||
| 243 | + _courseSectionDatasMap[courseUnitId] = tempSectionEntities; | ||
| 244 | + emitter(LessonDataLoadState()); | ||
| 245 | + } | ||
| 246 | + courseSectionEntity = tempSectionEntities?.firstWhereOrNull( | ||
| 247 | + (element) => element.id == courseSectionEntity?.id); | ||
| 248 | + if (courseSectionEntity?.lock == false) { | ||
| 249 | + ///刷新后的数据如果解锁了,直接返回 | ||
| 250 | + return courseSectionEntity; | ||
| 251 | + } else { | ||
| 252 | + ///请求失败或者锁定状态没变(没变就感觉状态异常了,理论上不应该进入这条分支),返回null | ||
| 253 | + showToast('下个课程还没解锁哦'); | ||
| 254 | + return null; | ||
| 255 | + } | ||
| 256 | + }); | ||
| 257 | + } | ||
| 220 | } else { | 258 | } else { |
| 221 | - ///section为空说明当前unit学完了,找下一个unit。(跨unit选lesson) | 259 | + ///section为空说明当前unit学完了,找下一个unit。(跨unit选section) |
| 222 | ///先根据courseLessonId找出当前的unit | 260 | ///先根据courseLessonId找出当前的unit |
| 223 | final curCourseUnitDetail = findCourseUnitDetailById(courseLessonId); | 261 | final curCourseUnitDetail = findCourseUnitDetailById(courseLessonId); |
| 224 | if (curCourseUnitDetail != null) { | 262 | if (curCourseUnitDetail != null) { |
| 225 | - ///再根据当前unit找出下一个unit | ||
| 226 | - final nextCourseUnitDetail = _courseUnitEntity.courseUnitVOList | ||
| 227 | - ?.firstWhere((element) => | ||
| 228 | - element.sortOrder == (curCourseUnitDetail.sortOrder! + 1)); | 263 | + ///再根据当前unit的sortOrder找出下一个unit |
| 264 | + CourseUnitDetail? nextCourseUnitDetail = | ||
| 265 | + _courseUnitEntity.courseUnitVOList?.firstWhereOrNull((element) => | ||
| 266 | + element.sortOrder == (curCourseUnitDetail.sortOrder! + 1)); | ||
| 267 | + | ||
| 229 | if (nextCourseUnitDetail != null) { | 268 | if (nextCourseUnitDetail != null) { |
| 230 | - final courseUnitId = nextCourseUnitDetail.id!; | ||
| 231 | - try { | 269 | + if (nextCourseUnitDetail.lock == true) { |
| 270 | + ///如果下一个unit是锁定状态,请求数据刷新查询解锁状态 | ||
| 232 | await loading(() async { | 271 | await loading(() async { |
| 233 | - _courseSectionDatasMap[courseUnitId] = | ||
| 234 | - await LessonDao.courseSection(courseUnitId: courseUnitId); | ||
| 235 | - emitter(LessonDataLoadState()); | 272 | + CourseUnitEntity? newCourseUnitEntity = await LessonDao.courseUnit( |
| 273 | + _courseUnitEntity.nowCourseModuleId); | ||
| 274 | + | ||
| 275 | + ///拿到重新获取到的unit后,再次判断是否解锁 | ||
| 276 | + nextCourseUnitDetail = newCourseUnitEntity?.courseUnitVOList?.firstWhereOrNull( | ||
| 277 | + (element) => element.id == nextCourseUnitDetail?.id); | ||
| 278 | + if (nextCourseUnitDetail?.lock == false) { | ||
| 279 | + ///解锁状态从锁定到解锁,覆盖原unit数据并刷新ui | ||
| 280 | + _courseUnitEntity = newCourseUnitEntity!; | ||
| 281 | + emitter(LessonDataLoadState()); | ||
| 282 | + | ||
| 283 | + return checkCourseSectionLockedOfNextUnit(courseLessonId, nextCourseUnitDetail!.id!, emitter); | ||
| 284 | + } else { | ||
| 285 | + showToast('下个单元课程还没解锁哦'); | ||
| 286 | + | ||
| 287 | + ///如果还是锁定状态,返回null | ||
| 288 | + return null; | ||
| 289 | + } | ||
| 236 | }); | 290 | }); |
| 237 | - _pageController.nextPage( | ||
| 238 | - duration: const Duration(milliseconds: 500), | ||
| 239 | - curve: Curves.ease, | ||
| 240 | - ); | ||
| 241 | - return _courseSectionDatasMap[courseUnitId]!.first; | ||
| 242 | - } catch (e) { | ||
| 243 | - if (e is ApiException) { | ||
| 244 | - showToast(e.message.toString()); | ||
| 245 | - } | ||
| 246 | - return null; | 291 | + } else { |
| 292 | + return checkCourseSectionLockedOfNextUnit(courseLessonId, nextCourseUnitDetail.id!, emitter); | ||
| 247 | } | 293 | } |
| 248 | } else { | 294 | } else { |
| 295 | + showToast("恭喜你,本阶段学到顶啦"); | ||
| 249 | ///最后一个unit了 | 296 | ///最后一个unit了 |
| 250 | return null; | 297 | return null; |
| 251 | } | 298 | } |
| @@ -254,5 +301,45 @@ class SectionBloc extends Bloc<SectionEvent, SectionState> { | @@ -254,5 +301,45 @@ class SectionBloc extends Bloc<SectionEvent, SectionState> { | ||
| 254 | return null; | 301 | return null; |
| 255 | } | 302 | } |
| 256 | } | 303 | } |
| 304 | + return null; | ||
| 305 | + } | ||
| 306 | + | ||
| 307 | + ///检查下一个unit的(第一个)section | ||
| 308 | + Future<CourseSectionEntity?> checkCourseSectionLockedOfNextUnit(int courseLessonId, int nextCourseUnitDetailId, | ||
| 309 | + Emitter<SectionState> emitter) async { | ||
| 310 | + CourseSectionEntity? firstSectionNextUnit = await getFirstSectionByUnitId( | ||
| 311 | + nextCourseUnitDetailId, emitter); | ||
| 312 | + if (firstSectionNextUnit != null) { | ||
| 313 | + ///下个unit的第一个section如果不为空,再次检查是否锁定 | ||
| 314 | + CourseSectionEntity? courseSectionEntity = await checkCourseSectionLocked(courseLessonId, firstSectionNextUnit, emitter); | ||
| 315 | + if (courseSectionEntity != null) { | ||
| 316 | + ///只有是下一unit的第一个section并且解锁了,才跳转 | ||
| 317 | + _pageController.nextPage( | ||
| 318 | + duration: const Duration(milliseconds: 250), | ||
| 319 | + curve: Curves.ease, | ||
| 320 | + ); | ||
| 321 | + } | ||
| 322 | + return courseSectionEntity; | ||
| 323 | + } else { | ||
| 324 | + ///下个unit的第一个section如果为空,返回null | ||
| 325 | + showToast('下个课程暂未找到'); | ||
| 326 | + return null; | ||
| 327 | + } | ||
| 328 | + } | ||
| 329 | + | ||
| 330 | + ///根据unitId获取当前unit的第一个section | ||
| 331 | + Future<CourseSectionEntity?> getFirstSectionByUnitId( | ||
| 332 | + int courseUnitId, Emitter<SectionState> emitter) async { | ||
| 333 | + List<CourseSectionEntity>? courseSectionEntity = | ||
| 334 | + _courseSectionDatasMap[courseUnitId]; | ||
| 335 | + if (courseSectionEntity == null) { | ||
| 336 | + ///如果没下载过,请求数据 | ||
| 337 | + await loading(() async { | ||
| 338 | + _courseSectionDatasMap[courseUnitId] = | ||
| 339 | + await LessonDao.courseSection(courseUnitId: courseUnitId); | ||
| 340 | + emitter(LessonDataLoadState()); | ||
| 341 | + }); | ||
| 342 | + } | ||
| 343 | + return _courseSectionDatasMap[courseUnitId]?.first; | ||
| 257 | } | 344 | } |
| 258 | } | 345 | } |
lib/pages/unit/bloc.dart
| @@ -23,10 +23,10 @@ class UnitBloc extends Bloc<UnitEvent, UnitState> { | @@ -23,10 +23,10 @@ class UnitBloc extends Bloc<UnitEvent, UnitState> { | ||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | UnitBloc(CourseModuleEntity? courseEntity) : super(UnitState().init()) { | 25 | UnitBloc(CourseModuleEntity? courseEntity) : super(UnitState().init()) { |
| 26 | - on<RequestUnitDataEvent>(_requestData); | 26 | + on<RequestUnitDataEvent>(_requestUnitDatas); |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | - void _requestData(RequestUnitDataEvent event, Emitter<UnitState> emitter) async { | 29 | + void _requestUnitDatas(RequestUnitDataEvent event, Emitter<UnitState> emitter) async { |
| 30 | try { | 30 | try { |
| 31 | await loading(() async { | 31 | await loading(() async { |
| 32 | _unitData = await LessonDao.courseUnit(event.moduleId); | 32 | _unitData = await LessonDao.courseUnit(event.moduleId); |
lib/pages/unit/view.dart
| @@ -57,7 +57,7 @@ class UnitPage extends StatelessWidget { | @@ -57,7 +57,7 @@ class UnitPage extends StatelessWidget { | ||
| 57 | return GestureDetector( | 57 | return GestureDetector( |
| 58 | onTap: () { | 58 | onTap: () { |
| 59 | if (data.lock == true) { | 59 | if (data.lock == true) { |
| 60 | - showToast('当前课程暂未解锁'); | 60 | + showToast('当前单元课程暂未解锁'); |
| 61 | return; | 61 | return; |
| 62 | } | 62 | } |
| 63 | 63 |
lib/utils/list_ext.dart
| @@ -8,4 +8,14 @@ extension ListExtension<E> on List<E> { | @@ -8,4 +8,14 @@ extension ListExtension<E> on List<E> { | ||
| 8 | } | 8 | } |
| 9 | return null; | 9 | return null; |
| 10 | } | 10 | } |
| 11 | + | ||
| 12 | + /// 获取数组中第一个匹配元素,没有就返回null | ||
| 13 | + E? firstWhereOrNull(bool Function(E element) test) { | ||
| 14 | + for (E element in this) { | ||
| 15 | + if (test(element)) { | ||
| 16 | + return element; | ||
| 17 | + } | ||
| 18 | + } | ||
| 19 | + return null; | ||
| 20 | + } | ||
| 11 | } | 21 | } |