From 20cad6487a22fb6923642d24fe892fc5e44086ef Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 28 Aug 2024 01:14:51 +0000 Subject: [PATCH 01/18] Set version number to 1.70 --- gradle/app.versions.toml | 2 +- iosHyperskillApp/NotificationServiceExtension/Info.plist | 2 +- iosHyperskillApp/iosHyperskillApp/Info.plist | 2 +- iosHyperskillApp/iosHyperskillAppTests/Info.plist | 2 +- iosHyperskillApp/iosHyperskillAppUITests/Info.plist | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gradle/app.versions.toml b/gradle/app.versions.toml index 5f32c35ca..83dda90bf 100644 --- a/gradle/app.versions.toml +++ b/gradle/app.versions.toml @@ -2,5 +2,5 @@ minSdk = '24' targetSdk = '34' compileSdk = '34' -versionName = '1.69' +versionName = '1.70' versionCode = '534' \ No newline at end of file diff --git a/iosHyperskillApp/NotificationServiceExtension/Info.plist b/iosHyperskillApp/NotificationServiceExtension/Info.plist index 6462e3dd2..82dd30279 100644 --- a/iosHyperskillApp/NotificationServiceExtension/Info.plist +++ b/iosHyperskillApp/NotificationServiceExtension/Info.plist @@ -11,7 +11,7 @@ CFBundleVersion 562 CFBundleShortVersionString - 1.69 + 1.70 CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleExecutable diff --git a/iosHyperskillApp/iosHyperskillApp/Info.plist b/iosHyperskillApp/iosHyperskillApp/Info.plist index e628ac6cd..e37f60386 100644 --- a/iosHyperskillApp/iosHyperskillApp/Info.plist +++ b/iosHyperskillApp/iosHyperskillApp/Info.plist @@ -21,7 +21,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.69 + 1.70 CFBundleURLTypes diff --git a/iosHyperskillApp/iosHyperskillAppTests/Info.plist b/iosHyperskillApp/iosHyperskillAppTests/Info.plist index 87306c182..724219c89 100644 --- a/iosHyperskillApp/iosHyperskillAppTests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppTests/Info.plist @@ -13,7 +13,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.69 + 1.70 CFBundleVersion 562 diff --git a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist index 7fd912af0..25b45832a 100644 --- a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist @@ -13,7 +13,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.69 + 1.70 CFBundleVersion 562 From 73e49404e8c697900295913fb6a0625bd1774be4 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 28 Aug 2024 01:14:53 +0000 Subject: [PATCH 02/18] Bump build number --- gradle/app.versions.toml | 2 +- .../NotificationServiceExtension/Info.plist | 2 +- .../iosHyperskillApp.xcodeproj/project.pbxproj | 16 ++++++++-------- iosHyperskillApp/iosHyperskillApp/Info.plist | 2 +- .../iosHyperskillAppTests/Info.plist | 2 +- .../iosHyperskillAppUITests/Info.plist | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/gradle/app.versions.toml b/gradle/app.versions.toml index 83dda90bf..042b5523b 100644 --- a/gradle/app.versions.toml +++ b/gradle/app.versions.toml @@ -3,4 +3,4 @@ minSdk = '24' targetSdk = '34' compileSdk = '34' versionName = '1.70' -versionCode = '534' \ No newline at end of file +versionCode = '535' \ No newline at end of file diff --git a/iosHyperskillApp/NotificationServiceExtension/Info.plist b/iosHyperskillApp/NotificationServiceExtension/Info.plist index 82dd30279..1b5f450d7 100644 --- a/iosHyperskillApp/NotificationServiceExtension/Info.plist +++ b/iosHyperskillApp/NotificationServiceExtension/Info.plist @@ -9,7 +9,7 @@ CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleVersion - 562 + 563 CFBundleShortVersionString 1.70 CFBundlePackageType diff --git a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj index 006cef760..b1d2f09c8 100644 --- a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj +++ b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj @@ -5673,7 +5673,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 562; + CURRENT_PROJECT_VERSION = 563; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; @@ -5694,7 +5694,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 562; + CURRENT_PROJECT_VERSION = 563; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = iosHyperskillAppUITests/Info.plist; @@ -5715,7 +5715,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 562; + CURRENT_PROJECT_VERSION = 563; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; @@ -5736,7 +5736,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 562; + CURRENT_PROJECT_VERSION = 563; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5757,7 +5757,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 562; + CURRENT_PROJECT_VERSION = 563; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; @@ -5786,7 +5786,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 562; + CURRENT_PROJECT_VERSION = 563; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5932,7 +5932,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 562; + CURRENT_PROJECT_VERSION = 563; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; @@ -5968,7 +5968,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 562; + CURRENT_PROJECT_VERSION = 563; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; diff --git a/iosHyperskillApp/iosHyperskillApp/Info.plist b/iosHyperskillApp/iosHyperskillApp/Info.plist index e37f60386..5bf005cf5 100644 --- a/iosHyperskillApp/iosHyperskillApp/Info.plist +++ b/iosHyperskillApp/iosHyperskillApp/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 562 + 563 FirebaseAppDelegateProxyEnabled FirebaseMessagingAutoInitEnabled diff --git a/iosHyperskillApp/iosHyperskillAppTests/Info.plist b/iosHyperskillApp/iosHyperskillAppTests/Info.plist index 724219c89..1fed0c49c 100644 --- a/iosHyperskillApp/iosHyperskillAppTests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppTests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 562 + 563 diff --git a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist index 25b45832a..c9f62a74c 100644 --- a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 562 + 563 From e15c706e738cc4a8a3e28eeee67dd3f98604b744 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 12:58:01 +0800 Subject: [PATCH 03/18] Bump rexml from 3.2.9 to 3.3.6 in /iosHyperskillApp (#1162) * Bump rexml from 3.2.9 to 3.3.6 in /iosHyperskillApp Bumps [rexml](https://github.com/ruby/rexml) from 3.2.9 to 3.3.6. - [Release notes](https://github.com/ruby/rexml/releases) - [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md) - [Commits](https://github.com/ruby/rexml/compare/v3.2.9...v3.3.6) --- updated-dependencies: - dependency-name: rexml dependency-type: indirect ... Signed-off-by: dependabot[bot] * Run bundle update --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ivan Magda --- iosHyperskillApp/Gemfile.lock | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/iosHyperskillApp/Gemfile.lock b/iosHyperskillApp/Gemfile.lock index 3df87ac80..5ae3d2d1a 100644 --- a/iosHyperskillApp/Gemfile.lock +++ b/iosHyperskillApp/Gemfile.lock @@ -18,16 +18,17 @@ GEM base64 nkf rexml - activesupport (7.1.3.4) + activesupport (7.2.1) base64 bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) + concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - mutex_m - tzinfo (~> 2.0) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) algoliasearch (1.27.5) @@ -36,16 +37,16 @@ GEM artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.960.0) - aws-sdk-core (3.201.3) + aws-partitions (1.968.0) + aws-sdk-core (3.202.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) aws-sdk-kms (1.88.0) aws-sdk-core (~> 3, >= 3.201.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) + aws-sdk-s3 (1.159.0) aws-sdk-core (~> 3, >= 3.201.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) @@ -96,7 +97,7 @@ GEM colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) connection_pool (2.4.1) declarative (0.0.20) digest-crc (0.6.5) @@ -213,7 +214,7 @@ GEM google-apis-core (>= 0.11.0, < 2.a) google-apis-storage_v1 (0.31.0) google-apis-core (>= 0.11.0, < 2.a) - google-cloud-core (1.7.0) + google-cloud-core (1.7.1) google-cloud-env (>= 1.0, < 3.a) google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) @@ -234,7 +235,7 @@ GEM os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.6) + http-cookie (1.0.7) domain_name (~> 0.5) httpclient (2.8.3) i18n (1.14.5) @@ -244,13 +245,13 @@ GEM jwt (2.8.2) base64 liquid (4.0.4) + logger (1.6.0) mini_magick (4.13.2) mini_mime (1.1.5) - minitest (5.24.1) + minitest (5.25.1) molinillo (0.8.0) multi_json (1.15.0) multipart-post (2.4.1) - mutex_m (0.2.0) nanaimo (0.3.0) nap (1.1.0) naturally (2.2.1) @@ -267,12 +268,13 @@ GEM trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.9) + rexml (3.3.6) strscan rouge (2.0.7) ruby-macho (2.5.1) ruby2_keywords (0.0.5) rubyzip (2.3.2) + securerandom (0.3.1) security (0.1.5) signet (0.19.0) addressable (~> 2.8) @@ -299,13 +301,13 @@ GEM uber (0.1.0) unicode-display_width (2.5.0) word_wrap (1.0.0) - xcodeproj (1.24.0) + xcodeproj (1.25.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) - rexml (~> 3.2.4) + rexml (>= 3.3.2, < 4.0) xcpretty (0.3.0) rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) From 07c51df51566855586aafa7964da1a5d95ea41e7 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 28 Aug 2024 04:58:47 +0000 Subject: [PATCH 04/18] iOS: Bump build number --- .../NotificationServiceExtension/Info.plist | 2 +- .../iosHyperskillApp.xcodeproj/project.pbxproj | 16 ++++++++-------- iosHyperskillApp/iosHyperskillApp/Info.plist | 2 +- .../iosHyperskillAppTests/Info.plist | 2 +- .../iosHyperskillAppUITests/Info.plist | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/iosHyperskillApp/NotificationServiceExtension/Info.plist b/iosHyperskillApp/NotificationServiceExtension/Info.plist index 1b5f450d7..07ded51ee 100644 --- a/iosHyperskillApp/NotificationServiceExtension/Info.plist +++ b/iosHyperskillApp/NotificationServiceExtension/Info.plist @@ -9,7 +9,7 @@ CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleVersion - 563 + 564 CFBundleShortVersionString 1.70 CFBundlePackageType diff --git a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj index b1d2f09c8..afe3c340c 100644 --- a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj +++ b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj @@ -5673,7 +5673,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 563; + CURRENT_PROJECT_VERSION = 564; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; @@ -5694,7 +5694,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 563; + CURRENT_PROJECT_VERSION = 564; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = iosHyperskillAppUITests/Info.plist; @@ -5715,7 +5715,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 563; + CURRENT_PROJECT_VERSION = 564; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; @@ -5736,7 +5736,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 563; + CURRENT_PROJECT_VERSION = 564; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5757,7 +5757,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 563; + CURRENT_PROJECT_VERSION = 564; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; @@ -5786,7 +5786,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 563; + CURRENT_PROJECT_VERSION = 564; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5932,7 +5932,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 563; + CURRENT_PROJECT_VERSION = 564; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; @@ -5968,7 +5968,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 563; + CURRENT_PROJECT_VERSION = 564; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; diff --git a/iosHyperskillApp/iosHyperskillApp/Info.plist b/iosHyperskillApp/iosHyperskillApp/Info.plist index 5bf005cf5..5cbec8f4f 100644 --- a/iosHyperskillApp/iosHyperskillApp/Info.plist +++ b/iosHyperskillApp/iosHyperskillApp/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 563 + 564 FirebaseAppDelegateProxyEnabled FirebaseMessagingAutoInitEnabled diff --git a/iosHyperskillApp/iosHyperskillAppTests/Info.plist b/iosHyperskillApp/iosHyperskillAppTests/Info.plist index 1fed0c49c..28cf58e3c 100644 --- a/iosHyperskillApp/iosHyperskillAppTests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppTests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 563 + 564 diff --git a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist index c9f62a74c..72f9b323a 100644 --- a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 563 + 564 From d63f8cae9b59d09fefe5abec8856b257f6d532d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 13:03:06 +0800 Subject: [PATCH 05/18] Bump rexml from 3.3.4 to 3.3.6 in /androidHyperskillApp (#1169) * Bump rexml from 3.3.4 to 3.3.6 in /androidHyperskillApp Bumps [rexml](https://github.com/ruby/rexml) from 3.3.4 to 3.3.6. - [Release notes](https://github.com/ruby/rexml/releases) - [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md) - [Commits](https://github.com/ruby/rexml/compare/v3.3.4...v3.3.6) --- updated-dependencies: - dependency-name: rexml dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Run bundle update --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ivan Magda --- androidHyperskillApp/Gemfile | 2 +- androidHyperskillApp/Gemfile.lock | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/androidHyperskillApp/Gemfile b/androidHyperskillApp/Gemfile index a64e4672e..873ac8973 100644 --- a/androidHyperskillApp/Gemfile +++ b/androidHyperskillApp/Gemfile @@ -2,6 +2,6 @@ source "https://rubygems.org" ruby "3.3.0" gem "fastlane", "2.222.0" -gem "rexml", ">= 3.3.3" +gem "rexml", ">= 3.3.6" eval_gemfile("fastlane/Pluginfile") diff --git a/androidHyperskillApp/Gemfile.lock b/androidHyperskillApp/Gemfile.lock index c8fbca5dd..5b77bf954 100644 --- a/androidHyperskillApp/Gemfile.lock +++ b/androidHyperskillApp/Gemfile.lock @@ -10,16 +10,16 @@ GEM artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.962.0) - aws-sdk-core (3.201.3) + aws-partitions (1.968.0) + aws-sdk-core (3.202.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) aws-sdk-kms (1.88.0) aws-sdk-core (~> 3, >= 3.201.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.157.0) + aws-sdk-s3 (1.159.0) aws-sdk-core (~> 3, >= 3.201.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) @@ -154,7 +154,7 @@ GEM os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.6) + http-cookie (1.0.7) domain_name (~> 0.5) httpclient (2.8.3) jmespath (1.6.2) @@ -178,7 +178,7 @@ GEM trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.3.4) + rexml (3.3.6) strscan rouge (2.0.7) ruby2_keywords (0.0.5) @@ -204,12 +204,13 @@ GEM uber (0.1.0) unicode-display_width (2.5.0) word_wrap (1.0.0) - xcodeproj (1.19.0) + xcodeproj (1.25.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) + rexml (>= 3.3.2, < 4.0) xcpretty (0.3.0) rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) @@ -225,7 +226,7 @@ PLATFORMS DEPENDENCIES fastlane (= 2.222.0) fastlane-plugin-firebase_app_distribution - rexml (>= 3.3.3) + rexml (>= 3.3.6) RUBY VERSION ruby 3.3.0p0 From a6cef174bf2d8a5876fae1040e586aca3ba29b8a Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 28 Aug 2024 05:03:55 +0000 Subject: [PATCH 06/18] Android: Bump build number --- gradle/app.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/app.versions.toml b/gradle/app.versions.toml index 042b5523b..46dad80cf 100644 --- a/gradle/app.versions.toml +++ b/gradle/app.versions.toml @@ -3,4 +3,4 @@ minSdk = '24' targetSdk = '34' compileSdk = '34' versionName = '1.70' -versionCode = '535' \ No newline at end of file +versionCode = '536' \ No newline at end of file From fa7029291903321a7207aa7f6414c86320512353 Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Wed, 28 Aug 2024 19:21:53 +0800 Subject: [PATCH 07/18] iOS: CommentsReactionsView update menuItemText --- .../Views/Content/CommentsReactionsView.swift | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/Comments/Views/Content/CommentsReactionsView.swift b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/Comments/Views/Content/CommentsReactionsView.swift index bc7a98405..4ec63156c 100644 --- a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/Comments/Views/Content/CommentsReactionsView.swift +++ b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/Comments/Views/Content/CommentsReactionsView.swift @@ -53,7 +53,7 @@ struct CommentsReactionsView: View { action(commentReaction) }, label: { - Text(commentReaction.emoji) + Text(commentReaction.menuItemText) } ) } @@ -85,7 +85,22 @@ private extension ReactionType { default: "" } } + + var emojiText: String { + switch self { + case .smile: "Haha" + case .plus: "Like" + case .minus: "Dislike" + case .confused: "Confused" + case .thinking: "Hmm" + case .fire: "Awesome" + case .clap: "Applause" + default: "" + } + } // swiftlint:enable switch_case_on_newline + + var menuItemText: String { "\(emoji) \(emojiText)" } } private extension CommentReaction { From 6fe80ace460ad2053c51b7de234119ccf7517388 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 28 Aug 2024 11:22:37 +0000 Subject: [PATCH 08/18] iOS: Bump build number --- .../NotificationServiceExtension/Info.plist | 2 +- .../iosHyperskillApp.xcodeproj/project.pbxproj | 16 ++++++++-------- iosHyperskillApp/iosHyperskillApp/Info.plist | 2 +- .../iosHyperskillAppTests/Info.plist | 2 +- .../iosHyperskillAppUITests/Info.plist | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/iosHyperskillApp/NotificationServiceExtension/Info.plist b/iosHyperskillApp/NotificationServiceExtension/Info.plist index 07ded51ee..79c171f2f 100644 --- a/iosHyperskillApp/NotificationServiceExtension/Info.plist +++ b/iosHyperskillApp/NotificationServiceExtension/Info.plist @@ -9,7 +9,7 @@ CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleVersion - 564 + 565 CFBundleShortVersionString 1.70 CFBundlePackageType diff --git a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj index afe3c340c..feff43abd 100644 --- a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj +++ b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj @@ -5673,7 +5673,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 564; + CURRENT_PROJECT_VERSION = 565; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; @@ -5694,7 +5694,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 564; + CURRENT_PROJECT_VERSION = 565; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = iosHyperskillAppUITests/Info.plist; @@ -5715,7 +5715,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 564; + CURRENT_PROJECT_VERSION = 565; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; @@ -5736,7 +5736,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 564; + CURRENT_PROJECT_VERSION = 565; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5757,7 +5757,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 564; + CURRENT_PROJECT_VERSION = 565; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; @@ -5786,7 +5786,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 564; + CURRENT_PROJECT_VERSION = 565; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5932,7 +5932,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 564; + CURRENT_PROJECT_VERSION = 565; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; @@ -5968,7 +5968,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 564; + CURRENT_PROJECT_VERSION = 565; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; diff --git a/iosHyperskillApp/iosHyperskillApp/Info.plist b/iosHyperskillApp/iosHyperskillApp/Info.plist index 5cbec8f4f..d7567d675 100644 --- a/iosHyperskillApp/iosHyperskillApp/Info.plist +++ b/iosHyperskillApp/iosHyperskillApp/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 564 + 565 FirebaseAppDelegateProxyEnabled FirebaseMessagingAutoInitEnabled diff --git a/iosHyperskillApp/iosHyperskillAppTests/Info.plist b/iosHyperskillApp/iosHyperskillAppTests/Info.plist index 28cf58e3c..3b22fcaaa 100644 --- a/iosHyperskillApp/iosHyperskillAppTests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppTests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 564 + 565 diff --git a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist index 72f9b323a..0eb089718 100644 --- a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 564 + 565 From 52713f55c1d2b221888c7d7f05b979a75c1fa7ee Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Wed, 28 Aug 2024 22:55:44 +0800 Subject: [PATCH 09/18] Shared: Decrease wrong submission count for skip suggestion (#1168) ^ALTAPPS-1343 --- .../view/mapper/StepQuizFeedbackMapper.kt | 4 ++-- .../step_quiz/StepQuizFeedbackMapperTest.kt | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz/view/mapper/StepQuizFeedbackMapper.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz/view/mapper/StepQuizFeedbackMapper.kt index 4dbfd1147..5ddcc2789 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz/view/mapper/StepQuizFeedbackMapper.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz/view/mapper/StepQuizFeedbackMapper.kt @@ -17,8 +17,8 @@ import org.hyperskill.app.submissions.domain.model.formattedText class StepQuizFeedbackMapper(private val resourcesProvider: ResourceProvider) { companion object { - private const val SEE_HINT_SUGGESTION_THRESHOLD = 2 - private const val SKIP_SUGGESTION_THRESHOLD = 4 + private const val SEE_HINT_SUGGESTION_THRESHOLD = 1 + private const val SKIP_SUGGESTION_THRESHOLD = 2 } fun map(state: StepQuizFeature.State): StepQuizFeedbackState = diff --git a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz/StepQuizFeedbackMapperTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz/StepQuizFeedbackMapperTest.kt index 9f33ecf88..19667bd6c 100644 --- a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz/StepQuizFeedbackMapperTest.kt +++ b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz/StepQuizFeedbackMapperTest.kt @@ -164,14 +164,14 @@ class StepQuizFeedbackMapperTest { } @Test - fun `Second wrong submission should trigger Wrong viewState with SEE_HINT actionType when hint is not opened`() { + fun `First wrong submission should trigger Wrong viewState with SEE_HINT actionType when hint is not opened`() { val stepId = 0L val state = getAttemptLoadedSubmissionState( step = Step.stub(id = stepId), submission = Submission.stub( status = SubmissionStatus.WRONG, ), - wrongSubmissionCount = 2, + wrongSubmissionCount = 1, stepQuizHintsState = StepQuizHintsFeature.State.Content( hintsIds = listOf(0, 1, 2), currentHint = null, @@ -189,7 +189,7 @@ class StepQuizFeedbackMapperTest { /*ktlint-disable*/ @Test - fun `Second wrong submission should trigger Wrong viewState with READ_COMMENTS actionType when hint is opened and there are comments`() { + fun `First wrong submission should trigger Wrong viewState with READ_COMMENTS actionType when hint is opened and there are comments`() { val stepId = 0L val state = getAttemptLoadedSubmissionState( step = Step.stub( @@ -204,7 +204,7 @@ class StepQuizFeedbackMapperTest { submission = Submission.stub( status = SubmissionStatus.WRONG, ), - wrongSubmissionCount = 2, + wrongSubmissionCount = 1, stepQuizHintsState = StepQuizHintsFeature.State.Content( hintsIds = listOf(0, 1, 2), currentHint = Comment.stub(id = 0), @@ -222,7 +222,7 @@ class StepQuizFeedbackMapperTest { /*ktlint-disable*/ @Test - fun `Second wrong submission should trigger Wrong viewState with null actionType when hint is opened and no comments available`() { + fun `First wrong submission should trigger Wrong viewState with null actionType when hint is opened and no comments available`() { val stepId = 0L val state = getAttemptLoadedSubmissionState( step = Step.stub( @@ -232,7 +232,7 @@ class StepQuizFeedbackMapperTest { submission = Submission.stub( status = SubmissionStatus.WRONG, ), - wrongSubmissionCount = 2, + wrongSubmissionCount = 1, stepQuizHintsState = StepQuizHintsFeature.State.Content( hintsIds = listOf(0, 1, 2), currentHint = Comment.stub(id = 0), @@ -249,14 +249,14 @@ class StepQuizFeedbackMapperTest { } @Test - fun `4th wrong submission should trigger Wrong viewState with SKIP_PROBLEM action type`() { + fun `Second wrong submission should trigger Wrong viewState with SKIP_PROBLEM action type`() { val stepId = 0L val state = getAttemptLoadedSubmissionState( step = Step.stub(id = stepId), submission = Submission.stub( status = SubmissionStatus.WRONG, ), - wrongSubmissionCount = 4 + wrongSubmissionCount = 2 ) val viewState = mapper.map(state) From 496754cb6f39abec5f86056ee10c9c236be7e2a5 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 28 Aug 2024 14:56:27 +0000 Subject: [PATCH 10/18] Bump build number --- gradle/app.versions.toml | 2 +- .../NotificationServiceExtension/Info.plist | 2 +- .../iosHyperskillApp.xcodeproj/project.pbxproj | 16 ++++++++-------- iosHyperskillApp/iosHyperskillApp/Info.plist | 2 +- .../iosHyperskillAppTests/Info.plist | 2 +- .../iosHyperskillAppUITests/Info.plist | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/gradle/app.versions.toml b/gradle/app.versions.toml index 46dad80cf..914be9db2 100644 --- a/gradle/app.versions.toml +++ b/gradle/app.versions.toml @@ -3,4 +3,4 @@ minSdk = '24' targetSdk = '34' compileSdk = '34' versionName = '1.70' -versionCode = '536' \ No newline at end of file +versionCode = '537' \ No newline at end of file diff --git a/iosHyperskillApp/NotificationServiceExtension/Info.plist b/iosHyperskillApp/NotificationServiceExtension/Info.plist index 79c171f2f..0b75d9d15 100644 --- a/iosHyperskillApp/NotificationServiceExtension/Info.plist +++ b/iosHyperskillApp/NotificationServiceExtension/Info.plist @@ -9,7 +9,7 @@ CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleVersion - 565 + 566 CFBundleShortVersionString 1.70 CFBundlePackageType diff --git a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj index feff43abd..6269c8687 100644 --- a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj +++ b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj @@ -5673,7 +5673,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 565; + CURRENT_PROJECT_VERSION = 566; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; @@ -5694,7 +5694,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 565; + CURRENT_PROJECT_VERSION = 566; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = iosHyperskillAppUITests/Info.plist; @@ -5715,7 +5715,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 565; + CURRENT_PROJECT_VERSION = 566; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; @@ -5736,7 +5736,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 565; + CURRENT_PROJECT_VERSION = 566; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5757,7 +5757,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 565; + CURRENT_PROJECT_VERSION = 566; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; @@ -5786,7 +5786,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 565; + CURRENT_PROJECT_VERSION = 566; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5932,7 +5932,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 565; + CURRENT_PROJECT_VERSION = 566; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; @@ -5968,7 +5968,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 565; + CURRENT_PROJECT_VERSION = 566; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; diff --git a/iosHyperskillApp/iosHyperskillApp/Info.plist b/iosHyperskillApp/iosHyperskillApp/Info.plist index d7567d675..3b84a470a 100644 --- a/iosHyperskillApp/iosHyperskillApp/Info.plist +++ b/iosHyperskillApp/iosHyperskillApp/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 565 + 566 FirebaseAppDelegateProxyEnabled FirebaseMessagingAutoInitEnabled diff --git a/iosHyperskillApp/iosHyperskillAppTests/Info.plist b/iosHyperskillApp/iosHyperskillAppTests/Info.plist index 3b22fcaaa..73d47d750 100644 --- a/iosHyperskillApp/iosHyperskillAppTests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppTests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 565 + 566 diff --git a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist index 0eb089718..33ff5e514 100644 --- a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 565 + 566 From 6f257479242a72e34d3b746e2ed8c2b2b5185326 Mon Sep 17 00:00:00 2001 From: Aleksandr Zhukov Date: Thu, 29 Aug 2024 06:19:47 +0200 Subject: [PATCH 11/18] Android: Fix double resume coroutine when calling AndroidPurchaseManager.canMakePayments (#1171) ^ALTAPPS-1339 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b4e040cdd..7b9292932 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,7 +19,7 @@ coil = '2.2.0' lottie = '6.1.0' kermit = '2.0.4' androidxBrowser = "1.5.0" -revenueCat = "7.4.0" +revenueCat = "8.6.0" googlePlayReview = "2.0.1" googlePlayInstallReferrer = "2.2" From b057363ee7c94d66e9e080e03b7fb07ef2a95c53 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 29 Aug 2024 04:20:29 +0000 Subject: [PATCH 12/18] Bump build number --- gradle/app.versions.toml | 2 +- .../NotificationServiceExtension/Info.plist | 2 +- .../iosHyperskillApp.xcodeproj/project.pbxproj | 16 ++++++++-------- iosHyperskillApp/iosHyperskillApp/Info.plist | 2 +- .../iosHyperskillAppTests/Info.plist | 2 +- .../iosHyperskillAppUITests/Info.plist | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/gradle/app.versions.toml b/gradle/app.versions.toml index 914be9db2..eae0a9919 100644 --- a/gradle/app.versions.toml +++ b/gradle/app.versions.toml @@ -3,4 +3,4 @@ minSdk = '24' targetSdk = '34' compileSdk = '34' versionName = '1.70' -versionCode = '537' \ No newline at end of file +versionCode = '538' \ No newline at end of file diff --git a/iosHyperskillApp/NotificationServiceExtension/Info.plist b/iosHyperskillApp/NotificationServiceExtension/Info.plist index 0b75d9d15..6e915fda4 100644 --- a/iosHyperskillApp/NotificationServiceExtension/Info.plist +++ b/iosHyperskillApp/NotificationServiceExtension/Info.plist @@ -9,7 +9,7 @@ CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleVersion - 566 + 567 CFBundleShortVersionString 1.70 CFBundlePackageType diff --git a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj index 6269c8687..9249a5160 100644 --- a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj +++ b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj @@ -5673,7 +5673,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 566; + CURRENT_PROJECT_VERSION = 567; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; @@ -5694,7 +5694,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 566; + CURRENT_PROJECT_VERSION = 567; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = iosHyperskillAppUITests/Info.plist; @@ -5715,7 +5715,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 566; + CURRENT_PROJECT_VERSION = 567; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; @@ -5736,7 +5736,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 566; + CURRENT_PROJECT_VERSION = 567; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5757,7 +5757,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 566; + CURRENT_PROJECT_VERSION = 567; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; @@ -5786,7 +5786,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 566; + CURRENT_PROJECT_VERSION = 567; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5932,7 +5932,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 566; + CURRENT_PROJECT_VERSION = 567; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; @@ -5968,7 +5968,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 566; + CURRENT_PROJECT_VERSION = 567; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; diff --git a/iosHyperskillApp/iosHyperskillApp/Info.plist b/iosHyperskillApp/iosHyperskillApp/Info.plist index 3b84a470a..b4dc0e001 100644 --- a/iosHyperskillApp/iosHyperskillApp/Info.plist +++ b/iosHyperskillApp/iosHyperskillApp/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 566 + 567 FirebaseAppDelegateProxyEnabled FirebaseMessagingAutoInitEnabled diff --git a/iosHyperskillApp/iosHyperskillAppTests/Info.plist b/iosHyperskillApp/iosHyperskillAppTests/Info.plist index 73d47d750..17474055b 100644 --- a/iosHyperskillApp/iosHyperskillAppTests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppTests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 566 + 567 diff --git a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist index 33ff5e514..d9da95189 100644 --- a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 566 + 567 From fa913ede4a2d7ae7da51439fec0e87556dd8edda Mon Sep 17 00:00:00 2001 From: Aleksandr Zhukov Date: Thu, 29 Aug 2024 06:24:57 +0200 Subject: [PATCH 13/18] Shared: Fix load more bug with pagination for root topics section (#1170) ^ALTAPPS-1333 --- .../presentation/StudyPlanWidgetFeature.kt | 2 +- .../presentation/StudyPlanWidgetReducer.kt | 56 +++++++++++-------- .../mapper/StudyPlanWidgetViewStateMapper.kt | 23 +++++--- .../study_plan/widget/StudyPlanWidgetTest.kt | 17 +++--- 4 files changed, 59 insertions(+), 39 deletions(-) diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetFeature.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetFeature.kt index 122480a54..d4671e02f 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetFeature.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetFeature.kt @@ -65,8 +65,8 @@ object StudyPlanWidgetFeature { ERROR, FIRST_PAGE_LOADING, + FIRST_PAGE_LOADED, NEXT_PAGE_LOADING, - PAGE_LOADED, ALL_PAGES_LOADED } diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetReducer.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetReducer.kt index 6ce719407..393a8fc99 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetReducer.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetReducer.kt @@ -164,7 +164,7 @@ class StudyPlanWidgetReducer : StateReducer { studyPlanSection = studyPlanSection, isExpanded = studyPlanSection.id == currentSectionId, sectionContentStatus = if (studyPlanSection.id == currentSectionId) { - SectionContentStatus.PAGE_LOADED + SectionContentStatus.FIRST_PAGE_LOADED } else { SectionContentStatus.IDLE } @@ -245,29 +245,41 @@ class StudyPlanWidgetReducer : StateReducer { sectionId: Long, activities: List ): StudyPlanWidgetReducerResult { - val nextState = state.copy( - // ALTAPPS-786: We should hide sections without available activities to avoid blocking study plan - studyPlanSections = if (activities.isEmpty()) { - state.studyPlanSections.mutate { - remove(sectionId) + val sectionContentStatus = state.studyPlanSections[sectionId]?.sectionContentStatus + val nextState = + when { + // ALTAPPS-786: We should hide sections without available activities to avoid blocking study plan + activities.isEmpty() && sectionContentStatus == SectionContentStatus.FIRST_PAGE_LOADING -> { + state.copy( + studyPlanSections = state.studyPlanSections.mutate { + remove(sectionId) + } + ) } - } else { - state.studyPlanSections.update(sectionId) { sectionInfo -> - val canLoadMoreActivities = - sectionInfo - .studyPlanSection - .getActivitiesToBeLoaded(state.activities.values) - .isNotEmpty() - sectionInfo.copy( - sectionContentStatus = if (canLoadMoreActivities) { - SectionContentStatus.PAGE_LOADED - } else { - SectionContentStatus.ALL_PAGES_LOADED + else -> { + state.copy( + studyPlanSections = state.studyPlanSections.update(sectionId) { sectionInfo -> + sectionInfo.copy( + sectionContentStatus = when (sectionContentStatus) { + SectionContentStatus.FIRST_PAGE_LOADED -> { + val canLoadMoreActivities = + sectionInfo + .studyPlanSection + .getActivitiesToBeLoaded(state.activities.values) + .isNotEmpty() + if (canLoadMoreActivities) { + SectionContentStatus.FIRST_PAGE_LOADED + } else { + SectionContentStatus.ALL_PAGES_LOADED + } + } + else -> SectionContentStatus.ALL_PAGES_LOADED + } + ) } ) } } - ) val isFetchedActivitiesForCurrentSection = sectionId == state.getCurrentSection()?.id @@ -313,8 +325,8 @@ class StudyPlanWidgetReducer : StateReducer { SectionContentStatus.ERROR, SectionContentStatus.FIRST_PAGE_LOADING -> SectionContentStatus.ERROR SectionContentStatus.NEXT_PAGE_LOADING, - SectionContentStatus.PAGE_LOADED, - SectionContentStatus.ALL_PAGES_LOADED -> SectionContentStatus.PAGE_LOADED + SectionContentStatus.FIRST_PAGE_LOADED, + SectionContentStatus.ALL_PAGES_LOADED -> SectionContentStatus.FIRST_PAGE_LOADED } ) } @@ -408,7 +420,7 @@ class StudyPlanWidgetReducer : StateReducer { // activities are loading at the moment or already loaded SectionContentStatus.FIRST_PAGE_LOADING, SectionContentStatus.NEXT_PAGE_LOADING, - SectionContentStatus.PAGE_LOADED, + SectionContentStatus.FIRST_PAGE_LOADED, SectionContentStatus.ALL_PAGES_LOADED -> { updateSectionState(contentStatus) to setOfNotNull(logAnalyticEventAction) } diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/mapper/StudyPlanWidgetViewStateMapper.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/mapper/StudyPlanWidgetViewStateMapper.kt index 83f94adc5..b93ebb281 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/mapper/StudyPlanWidgetViewStateMapper.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/mapper/StudyPlanWidgetViewStateMapper.kt @@ -6,7 +6,12 @@ import org.hyperskill.app.learning_activities.domain.model.LearningActivity import org.hyperskill.app.learning_activities.domain.model.LearningActivityState import org.hyperskill.app.learning_activities.view.mapper.LearningActivityTextsMapper import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature -import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus.ALL_PAGES_LOADED +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus.ERROR +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus.FIRST_PAGE_LOADED +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus.FIRST_PAGE_LOADING +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus.IDLE +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus.NEXT_PAGE_LOADING import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionStatus import org.hyperskill.app.study_plan.widget.presentation.getCurrentActivity import org.hyperskill.app.study_plan.widget.presentation.getCurrentSection @@ -74,10 +79,10 @@ class StudyPlanWidgetViewStateMapper(private val dateFormatter: SharedDateFormat ): SectionContent = if (sectionInfo.isExpanded) { when (sectionInfo.sectionContentStatus) { - SectionContentStatus.IDLE -> SectionContent.Collapsed - SectionContentStatus.ERROR -> SectionContent.Error - SectionContentStatus.FIRST_PAGE_LOADING, - SectionContentStatus.NEXT_PAGE_LOADING -> { + IDLE -> SectionContent.Collapsed + ERROR -> SectionContent.Error + FIRST_PAGE_LOADING, + NEXT_PAGE_LOADING -> { getContent( state = state, sectionInfo = sectionInfo, @@ -85,8 +90,8 @@ class StudyPlanWidgetViewStateMapper(private val dateFormatter: SharedDateFormat emptyActivitiesState = SectionContent.Loading ) } - SectionContentStatus.PAGE_LOADED, - SectionContentStatus.ALL_PAGES_LOADED -> { + FIRST_PAGE_LOADED, + ALL_PAGES_LOADED -> { getContent( state = state, sectionInfo = sectionInfo, @@ -114,8 +119,8 @@ class StudyPlanWidgetViewStateMapper(private val dateFormatter: SharedDateFormat activities = loadedActivities, currentActivityId = currentActivityId, unlockedActivitiesCount = state.getUnlockedActivitiesCount(sectionId), - isLoadAllTopicsButtonVisible = sectionInfo.sectionContentStatus == SectionContentStatus.PAGE_LOADED, - isNextPageLoadingShowed = sectionInfo.sectionContentStatus == SectionContentStatus.NEXT_PAGE_LOADING + isLoadAllTopicsButtonVisible = sectionInfo.sectionContentStatus == FIRST_PAGE_LOADED, + isNextPageLoadingShowed = sectionInfo.sectionContentStatus == NEXT_PAGE_LOADING ) } } diff --git a/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanWidgetTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanWidgetTest.kt index 4c024675d..d185e87d6 100644 --- a/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanWidgetTest.kt +++ b/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanWidgetTest.kt @@ -310,7 +310,7 @@ class StudyPlanWidgetTest { studyPlanSections = mapOf( currentSectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(currentSectionId), - sectionContentStatus = SectionContentStatus.NEXT_PAGE_LOADING, + sectionContentStatus = SectionContentStatus.FIRST_PAGE_LOADING, isExpanded = true ), nextSectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( @@ -334,7 +334,7 @@ class StudyPlanWidgetTest { @Test fun `Not current section should be removed if no available activities loaded`() { val currentSectionId = 0L - val notCurrent = 1L + val notCurrentSectionId = 1L val initialState = StudyPlanWidgetFeature.State( studyPlanSections = mapOf( currentSectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( @@ -342,9 +342,9 @@ class StudyPlanWidgetTest { sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED, isExpanded = true ), - notCurrent to StudyPlanWidgetFeature.StudyPlanSectionInfo( - studyPlanSection = studyPlanSectionStub(notCurrent), - sectionContentStatus = SectionContentStatus.NEXT_PAGE_LOADING, + notCurrentSectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( + studyPlanSection = studyPlanSectionStub(notCurrentSectionId), + sectionContentStatus = SectionContentStatus.FIRST_PAGE_LOADING, isExpanded = false ) ) @@ -352,9 +352,12 @@ class StudyPlanWidgetTest { val (state, _) = reducer.reduce( initialState, - StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success(sectionId = notCurrent, emptyList()) + StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success( + sectionId = notCurrentSectionId, + activities = emptyList() + ) ) - assertTrue(state.studyPlanSections.containsKey(notCurrent).not()) + assertTrue(state.studyPlanSections.containsKey(notCurrentSectionId).not()) } @Test From cd36df0e04f29ac1587f7606d09f6b0a2e2b7c13 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 29 Aug 2024 04:25:39 +0000 Subject: [PATCH 14/18] Bump build number --- gradle/app.versions.toml | 2 +- .../NotificationServiceExtension/Info.plist | 2 +- .../iosHyperskillApp.xcodeproj/project.pbxproj | 16 ++++++++-------- iosHyperskillApp/iosHyperskillApp/Info.plist | 2 +- .../iosHyperskillAppTests/Info.plist | 2 +- .../iosHyperskillAppUITests/Info.plist | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/gradle/app.versions.toml b/gradle/app.versions.toml index eae0a9919..87e19a854 100644 --- a/gradle/app.versions.toml +++ b/gradle/app.versions.toml @@ -3,4 +3,4 @@ minSdk = '24' targetSdk = '34' compileSdk = '34' versionName = '1.70' -versionCode = '538' \ No newline at end of file +versionCode = '539' \ No newline at end of file diff --git a/iosHyperskillApp/NotificationServiceExtension/Info.plist b/iosHyperskillApp/NotificationServiceExtension/Info.plist index 6e915fda4..d78e6ac7e 100644 --- a/iosHyperskillApp/NotificationServiceExtension/Info.plist +++ b/iosHyperskillApp/NotificationServiceExtension/Info.plist @@ -9,7 +9,7 @@ CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleVersion - 567 + 568 CFBundleShortVersionString 1.70 CFBundlePackageType diff --git a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj index 9249a5160..5a021e39b 100644 --- a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj +++ b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj @@ -5673,7 +5673,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 567; + CURRENT_PROJECT_VERSION = 568; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; @@ -5694,7 +5694,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 567; + CURRENT_PROJECT_VERSION = 568; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = iosHyperskillAppUITests/Info.plist; @@ -5715,7 +5715,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 567; + CURRENT_PROJECT_VERSION = 568; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; @@ -5736,7 +5736,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 567; + CURRENT_PROJECT_VERSION = 568; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5757,7 +5757,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 567; + CURRENT_PROJECT_VERSION = 568; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; @@ -5786,7 +5786,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 567; + CURRENT_PROJECT_VERSION = 568; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5932,7 +5932,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 567; + CURRENT_PROJECT_VERSION = 568; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; @@ -5968,7 +5968,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 567; + CURRENT_PROJECT_VERSION = 568; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; diff --git a/iosHyperskillApp/iosHyperskillApp/Info.plist b/iosHyperskillApp/iosHyperskillApp/Info.plist index b4dc0e001..ff2742802 100644 --- a/iosHyperskillApp/iosHyperskillApp/Info.plist +++ b/iosHyperskillApp/iosHyperskillApp/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 567 + 568 FirebaseAppDelegateProxyEnabled FirebaseMessagingAutoInitEnabled diff --git a/iosHyperskillApp/iosHyperskillAppTests/Info.plist b/iosHyperskillApp/iosHyperskillAppTests/Info.plist index 17474055b..fe183c32e 100644 --- a/iosHyperskillApp/iosHyperskillAppTests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppTests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 567 + 568 diff --git a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist index d9da95189..5b5987e21 100644 --- a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 567 + 568 From 199897b4dc5ede06c2c1a8d98b97c34483ea18d5 Mon Sep 17 00:00:00 2001 From: Aleksandr Zhukov Date: Mon, 2 Sep 2024 06:37:56 +0200 Subject: [PATCH 15/18] Shared, Android: Support skipped and completed activities in study plan (#1172) ^ALTAPPS-1335 --- .../delegate/StudyPlanWidgetDelegate.kt | 140 +------- .../mapper/StudyPlanWidgetUIStateMapper.kt | 154 +++++++++ .../study_plan/model/StudyPlanRecyclerItem.kt | 6 + .../res/drawable/ic_study_plan_expand_all.xml | 9 + .../res/layout/item_study_plan_activity.xml | 8 +- ...tem_study_plan_expand_completed_button.xml | 19 ++ .../src/main/res/values/dimens.xml | 1 + config/detekt/baseline.xml | 1 - .../Views/Section/StudyPlanSectionView.swift | 13 +- .../hyperskill/HyperskillAnalyticTarget.kt | 3 +- ...mpletedActivitiesClickedHSAnalyticEvent.kt | 35 ++ .../MainStudyPlanWidgetActionDispatcher.kt | 10 +- .../presentation/StudyPlanWidgetFeature.kt | 44 ++- .../presentation/StudyPlanWidgetReducer.kt | 209 ++++++++---- .../StudyPlanWidgetStateExtensions.kt | 61 +++- .../mapper/StudyPlanWidgetViewStateMapper.kt | 137 ++++---- .../view/model/StudyPlanWidgetViewState.kt | 20 +- .../moko-resources/base/strings.xml | 1 + .../study_plan/screen/StudyPlanScreenTest.kt | 4 +- .../StudyPlanExpandCompletedActivitiesTest.kt | 139 ++++++++ .../widget/StudyPlanLoadMoreActivitiesTest.kt | 180 ++++++++++ .../StudyPlanWidgetStateExtensionsTest.kt | 124 +++---- .../study_plan/widget/StudyPlanWidgetTest.kt | 321 ++++++++++++------ 23 files changed, 1161 insertions(+), 478 deletions(-) create mode 100644 androidHyperskillApp/src/main/java/org/hyperskill/app/android/study_plan/mapper/StudyPlanWidgetUIStateMapper.kt create mode 100644 androidHyperskillApp/src/main/res/drawable/ic_study_plan_expand_all.xml create mode 100644 androidHyperskillApp/src/main/res/layout/item_study_plan_expand_completed_button.xml create mode 100644 shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/domain/analytic/StudyPlanExpandCompletedActivitiesClickedHSAnalyticEvent.kt create mode 100644 shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanExpandCompletedActivitiesTest.kt create mode 100644 shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanLoadMoreActivitiesTest.kt diff --git a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/study_plan/delegate/StudyPlanWidgetDelegate.kt b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/study_plan/delegate/StudyPlanWidgetDelegate.kt index c11757326..a9d627383 100644 --- a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/study_plan/delegate/StudyPlanWidgetDelegate.kt +++ b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/study_plan/delegate/StudyPlanWidgetDelegate.kt @@ -1,8 +1,6 @@ package org.hyperskill.app.android.study_plan.delegate import android.content.Context -import androidx.annotation.ColorInt -import androidx.core.content.ContextCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import org.hyperskill.app.android.R @@ -14,6 +12,7 @@ import org.hyperskill.app.android.study_plan.adapter.ActivityLoadingAdapterDeleg import org.hyperskill.app.android.study_plan.adapter.StudyPlanActivityAdapterDelegate import org.hyperskill.app.android.study_plan.adapter.StudyPlanItemAnimator import org.hyperskill.app.android.study_plan.adapter.StudyPlanSectionAdapterDelegate +import org.hyperskill.app.android.study_plan.mapper.StudyPlanWidgetUIStateMapper import org.hyperskill.app.android.study_plan.model.StudyPlanRecyclerItem import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature import org.hyperskill.app.study_plan.widget.view.model.StudyPlanWidgetViewState @@ -27,11 +26,6 @@ class StudyPlanWidgetDelegate( private val onNewMessage: (StudyPlanWidgetFeature.Message) -> Unit ) { - companion object { - private const val SECTIONS_LOADING_ITEMS_COUNT = 4 - private const val ACTIVITIES_LOADING_ITEMS_COUNT = 3 - } - private val studyPlanAdapter = DefaultDelegateAdapter().apply { addDelegate(StudyPlanSectionAdapterDelegate(onNewMessage)) addDelegate( @@ -46,6 +40,7 @@ class StudyPlanWidgetDelegate( ) addDelegate(sectionsLoadingAdapterDelegate()) addDelegate(loadAllTopicsButtonDelegate()) + addDelegate(expandCompletedActivitiesButtonDelegate()) addDelegate(paywallAdapterDelegate()) addDelegate(ActivityLoadingAdapterDelegate()) addDelegate( @@ -55,39 +50,15 @@ class StudyPlanWidgetDelegate( ) } - @ColorInt private val inactiveSectionTextColor: Int = - ContextCompat.getColor(context, org.hyperskill.app.R.color.color_on_surface_alpha_60) - - @ColorInt private val activeSectionTextColor: Int = - ContextCompat.getColor(context, org.hyperskill.app.R.color.color_on_surface) - - @ColorInt private val activeActivityTextColor: Int = - ContextCompat.getColor(context, org.hyperskill.app.R.color.color_on_surface_alpha_87) - - @ColorInt private val inactiveActivityTextColor: Int = - ContextCompat.getColor(context, org.hyperskill.app.R.color.color_on_surface_alpha_60) + private val uiStateMapper: StudyPlanWidgetUIStateMapper = StudyPlanWidgetUIStateMapper(context) private val sectionTopMargin = context.resources.getDimensionPixelOffset(R.dimen.study_plan_section_top_margin) private val activityTopMargin = context.resources.getDimensionPixelOffset(R.dimen.study_plan_activity_top_margin) - private val activeIcon = - ContextCompat.getDrawable(context, R.drawable.ic_home_screen_arrow_button) - private val skippedIcon = - ContextCompat.getDrawable(context, R.drawable.ic_topic_skipped) - private val completedIcon = - ContextCompat.getDrawable(context, R.drawable.ic_topic_completed) - private val lockedIcon = - ContextCompat.getDrawable(context, R.drawable.ic_activity_locked) - private var studyPlanViewStateDelegate: ViewStateDelegate? = null - private val sectionsLoadingItems: List = - List(SECTIONS_LOADING_ITEMS_COUNT) { index -> - StudyPlanRecyclerItem.SectionLoading(index) - } - fun setup(recyclerView: RecyclerView, errorViewBinding: ErrorNoConnectionWithButtonBinding) { studyPlanViewStateDelegate = ViewStateDelegate().apply { addState() @@ -138,7 +109,8 @@ class StudyPlanWidgetDelegate( is StudyPlanRecyclerItem.ActivityLoading, is StudyPlanRecyclerItem.Activity, is StudyPlanRecyclerItem.ActivitiesError, - is StudyPlanRecyclerItem.LoadAllTopicsButton -> activityTopMargin + is StudyPlanRecyclerItem.LoadAllTopicsButton, + is StudyPlanRecyclerItem.ExpandCompletedActivitiesButton -> activityTopMargin else -> 0 } @@ -148,17 +120,7 @@ class StudyPlanWidgetDelegate( fun render(state: StudyPlanWidgetViewState) { studyPlanViewStateDelegate?.switchState(state) - when (state) { - StudyPlanWidgetViewState.Loading -> { - studyPlanAdapter.items = sectionsLoadingItems - } - is StudyPlanWidgetViewState.Content -> { - studyPlanAdapter.items = mapContentToRecyclerItems(state) - } - else -> { - // no op - } - } + studyPlanAdapter.items = uiStateMapper.map(state) } private fun sectionsLoadingAdapterDelegate() = @@ -188,87 +150,17 @@ class StudyPlanWidgetDelegate( } } - private fun mapContentToRecyclerItems( - studyPlanContent: StudyPlanWidgetViewState.Content - ): List = - buildList { - if (studyPlanContent.isPaywallBannerShown) { - add(StudyPlanRecyclerItem.PaywallBanner) - } - studyPlanContent.sections.forEachIndexed { sectionIndex, section -> - add(mapSectionToRecyclerItem(sectionIndex, section)) - when (val sectionContent = section.content) { - StudyPlanWidgetViewState.SectionContent.Collapsed -> { - // no op - } - StudyPlanWidgetViewState.SectionContent.Loading -> { - addAll(getActivitiesLoadingItems(section.id)) - } - is StudyPlanWidgetViewState.SectionContent.Content -> { - addAll(mapSectionItemsToActivityItems(section.id, sectionContent.sectionItems)) - if (sectionContent.isLoadAllTopicsButtonShown) { - add(StudyPlanRecyclerItem.LoadAllTopicsButton(section.id)) - } - if (sectionContent.isNextPageLoadingShowed) { - addAll(getActivitiesLoadingItems(section.id)) - } - } - StudyPlanWidgetViewState.SectionContent.Error -> { - add(StudyPlanRecyclerItem.ActivitiesError(section.id)) - } + private fun expandCompletedActivitiesButtonDelegate() = + adapterDelegate( + R.layout.item_study_plan_expand_completed_button + ) { + itemView.setOnClickListener { + val sectionId = item?.sectionId + if (sectionId != null) { + onNewMessage( + StudyPlanWidgetFeature.Message.ExpandCompletedActivitiesClicked(sectionId) + ) } } } - - private fun mapSectionToRecyclerItem( - index: Int, - section: StudyPlanWidgetViewState.Section - ): StudyPlanRecyclerItem.Section = - StudyPlanRecyclerItem.Section( - id = section.id, - title = section.title, - titleTextColor = if (index == 0) { - activeSectionTextColor - } else { - inactiveSectionTextColor - }, - subtitle = section.subtitle, - formattedTopicsCount = section.formattedTopicsCount, - formattedTimeToComplete = section.formattedTimeToComplete, - isExpanded = section.content !is StudyPlanWidgetViewState.SectionContent.Collapsed, - isCurrentBadgeShown = section.isCurrentBadgeShown - ) - - private fun mapSectionItemsToActivityItems( - sectionId: Long, - sectionItems: List - ): List = - sectionItems.map { item -> - StudyPlanRecyclerItem.Activity( - id = item.id, - sectionId = sectionId, - title = item.title, - subtitle = item.subtitle, - titleTextColor = if (item.state == StudyPlanWidgetViewState.SectionItemState.NEXT) { - activeActivityTextColor - } else { - inactiveActivityTextColor - }, - progress = item.progress, - formattedProgress = item.formattedProgress, - endIcon = when (item.state) { - StudyPlanWidgetViewState.SectionItemState.IDLE -> null - StudyPlanWidgetViewState.SectionItemState.NEXT -> activeIcon - StudyPlanWidgetViewState.SectionItemState.SKIPPED -> skippedIcon - StudyPlanWidgetViewState.SectionItemState.COMPLETED -> completedIcon - StudyPlanWidgetViewState.SectionItemState.LOCKED -> lockedIcon - }, - isIdeRequired = item.isIdeRequired - ) - } - - private fun getActivitiesLoadingItems(sectionId: Long) = - List(ACTIVITIES_LOADING_ITEMS_COUNT) { index -> - StudyPlanRecyclerItem.ActivityLoading(sectionId, index) - } } \ No newline at end of file diff --git a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/study_plan/mapper/StudyPlanWidgetUIStateMapper.kt b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/study_plan/mapper/StudyPlanWidgetUIStateMapper.kt new file mode 100644 index 000000000..52a4af265 --- /dev/null +++ b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/study_plan/mapper/StudyPlanWidgetUIStateMapper.kt @@ -0,0 +1,154 @@ +package org.hyperskill.app.android.study_plan.mapper + +import android.content.Context +import androidx.annotation.ColorInt +import androidx.core.content.ContextCompat +import org.hyperskill.app.android.R +import org.hyperskill.app.android.study_plan.model.StudyPlanRecyclerItem +import org.hyperskill.app.study_plan.widget.view.model.StudyPlanWidgetViewState +import org.hyperskill.app.study_plan.widget.view.model.StudyPlanWidgetViewState.SectionContentPageLoadingState + +class StudyPlanWidgetUIStateMapper(context: Context) { + + companion object { + private const val SECTIONS_LOADING_ITEMS_COUNT = 4 + private const val ACTIVITIES_LOADING_ITEMS_COUNT = 3 + } + + private val sectionsLoadingItems: List = + List(SECTIONS_LOADING_ITEMS_COUNT) { index -> + StudyPlanRecyclerItem.SectionLoading(index) + } + + @ColorInt + private val inactiveSectionTextColor: Int = + ContextCompat.getColor(context, org.hyperskill.app.R.color.color_on_surface_alpha_60) + + @ColorInt + private val activeSectionTextColor: Int = + ContextCompat.getColor(context, org.hyperskill.app.R.color.color_on_surface) + + @ColorInt + private val activeActivityTextColor: Int = + ContextCompat.getColor(context, org.hyperskill.app.R.color.color_on_surface_alpha_87) + + @ColorInt + private val inactiveActivityTextColor: Int = + ContextCompat.getColor(context, org.hyperskill.app.R.color.color_on_surface_alpha_60) + + private val activeIcon = + ContextCompat.getDrawable(context, R.drawable.ic_home_screen_arrow_button) + private val skippedIcon = + ContextCompat.getDrawable(context, R.drawable.ic_topic_skipped) + private val completedIcon = + ContextCompat.getDrawable(context, R.drawable.ic_topic_completed) + private val lockedIcon = + ContextCompat.getDrawable(context, R.drawable.ic_activity_locked) + + fun map(state: StudyPlanWidgetViewState): List = + when (state) { + StudyPlanWidgetViewState.Loading -> sectionsLoadingItems + is StudyPlanWidgetViewState.Content -> mapContentToRecyclerItems(state) + else -> emptyList() + } + + private fun mapContentToRecyclerItems( + studyPlanContent: StudyPlanWidgetViewState.Content + ): List = + buildList { + if (studyPlanContent.isPaywallBannerShown) { + add(StudyPlanRecyclerItem.PaywallBanner) + } + studyPlanContent.sections.forEachIndexed { sectionIndex, section -> + add(mapSectionToRecyclerItem(sectionIndex, section)) + when (val sectionContent = section.content) { + StudyPlanWidgetViewState.SectionContent.Collapsed -> { + // no op + } + StudyPlanWidgetViewState.SectionContent.Loading -> { + addAll(getActivitiesLoadingItems(section.id)) + } + is StudyPlanWidgetViewState.SectionContent.Content -> { + when (sectionContent.completedPageLoadingState) { + SectionContentPageLoadingState.HIDDEN -> { + // no op + } + SectionContentPageLoadingState.LOAD_MORE -> { + add(StudyPlanRecyclerItem.ExpandCompletedActivitiesButton(section.id)) + } + SectionContentPageLoadingState.LOADING -> { + addAll(getActivitiesLoadingItems(section.id)) + } + } + addAll(mapSectionItemsToActivityItems(section.id, sectionContent.sectionItems)) + when (sectionContent.nextPageLoadingState) { + SectionContentPageLoadingState.HIDDEN -> { + // no op + } + SectionContentPageLoadingState.LOAD_MORE -> { + add(StudyPlanRecyclerItem.LoadAllTopicsButton(section.id)) + } + SectionContentPageLoadingState.LOADING -> { + addAll(getActivitiesLoadingItems(section.id)) + } + } + } + StudyPlanWidgetViewState.SectionContent.Error -> { + add(StudyPlanRecyclerItem.ActivitiesError(section.id)) + } + } + } + } + + private fun mapSectionToRecyclerItem( + index: Int, + section: StudyPlanWidgetViewState.Section + ): StudyPlanRecyclerItem.Section = + StudyPlanRecyclerItem.Section( + id = section.id, + title = section.title, + titleTextColor = if (index == 0) { + activeSectionTextColor + } else { + inactiveSectionTextColor + }, + subtitle = section.subtitle, + formattedTopicsCount = section.formattedTopicsCount, + formattedTimeToComplete = section.formattedTimeToComplete, + isExpanded = section.content !is StudyPlanWidgetViewState.SectionContent.Collapsed, + isCurrentBadgeShown = section.isCurrentBadgeShown + ) + + private fun mapSectionItemsToActivityItems( + sectionId: Long, + sectionItems: List + ): List = + sectionItems.map { item -> + StudyPlanRecyclerItem.Activity( + id = item.id, + sectionId = sectionId, + title = item.title, + subtitle = item.subtitle, + titleTextColor = if (item.state == StudyPlanWidgetViewState.SectionItemState.NEXT) { + activeActivityTextColor + } else { + inactiveActivityTextColor + }, + progress = item.progress, + formattedProgress = item.formattedProgress, + endIcon = when (item.state) { + StudyPlanWidgetViewState.SectionItemState.IDLE -> null + StudyPlanWidgetViewState.SectionItemState.NEXT -> activeIcon + StudyPlanWidgetViewState.SectionItemState.SKIPPED -> skippedIcon + StudyPlanWidgetViewState.SectionItemState.COMPLETED -> completedIcon + StudyPlanWidgetViewState.SectionItemState.LOCKED -> lockedIcon + }, + isIdeRequired = item.isIdeRequired + ) + } + + private fun getActivitiesLoadingItems(sectionId: Long) = + List(ACTIVITIES_LOADING_ITEMS_COUNT) { index -> + StudyPlanRecyclerItem.ActivityLoading(sectionId, index) + } +} \ No newline at end of file diff --git a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/study_plan/model/StudyPlanRecyclerItem.kt b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/study_plan/model/StudyPlanRecyclerItem.kt index 82b9864b4..259249e64 100644 --- a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/study_plan/model/StudyPlanRecyclerItem.kt +++ b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/study_plan/model/StudyPlanRecyclerItem.kt @@ -28,6 +28,12 @@ interface StudyPlanRecyclerItem { override val id: String = "study-plan-$sectionId-load-all-topics" } + data class ExpandCompletedActivitiesButton( + val sectionId: Long + ) : StudyPlanRecyclerItem, Identifiable { + override val id: String = "study-plan-$sectionId-expand-completed-activities" + } + object PaywallBanner : StudyPlanRecyclerItem, Identifiable { override val id: String = "study-plan-paywall-banner" } diff --git a/androidHyperskillApp/src/main/res/drawable/ic_study_plan_expand_all.xml b/androidHyperskillApp/src/main/res/drawable/ic_study_plan_expand_all.xml new file mode 100644 index 000000000..02763565b --- /dev/null +++ b/androidHyperskillApp/src/main/res/drawable/ic_study_plan_expand_all.xml @@ -0,0 +1,9 @@ + + + diff --git a/androidHyperskillApp/src/main/res/layout/item_study_plan_activity.xml b/androidHyperskillApp/src/main/res/layout/item_study_plan_activity.xml index 19eea6eba..4971581c4 100644 --- a/androidHyperskillApp/src/main/res/layout/item_study_plan_activity.xml +++ b/androidHyperskillApp/src/main/res/layout/item_study_plan_activity.xml @@ -15,28 +15,28 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" - app:layout_constraintGuide_begin="16dp"/> + app:layout_constraintGuide_begin="@dimen/study_plan_activity_padding"/> + app:layout_constraintGuide_end="@dimen/study_plan_activity_padding"/> + app:layout_constraintGuide_begin="@dimen/study_plan_activity_padding"/> + app:layout_constraintGuide_end="@dimen/study_plan_activity_padding"/> + + + + + diff --git a/androidHyperskillApp/src/main/res/values/dimens.xml b/androidHyperskillApp/src/main/res/values/dimens.xml index 6f28f801d..d590c8074 100644 --- a/androidHyperskillApp/src/main/res/values/dimens.xml +++ b/androidHyperskillApp/src/main/res/values/dimens.xml @@ -121,6 +121,7 @@ 20dp 16dp 8dp + 16dp 80dp diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index b2d1a348e..86ff7e88f 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -287,7 +287,6 @@ TooManyFunctions:Reply.kt$Reply$Companion TooManyFunctions:SharedDateFormatter.kt$SharedDateFormatter TooManyFunctions:StepActionDispatcher.kt$StepActionDispatcher : CoroutineActionDispatcher - TooManyFunctions:StudyPlanWidgetDelegate.kt$StudyPlanWidgetDelegate TooManyFunctions:SubscriptionsInteractor.kt$SubscriptionsInteractor TopLevelPropertyNaming:HyperskillNotificationChannel.kt$private const val dailyReminderId = "dailyReminderChannel" TopLevelPropertyNaming:HyperskillNotificationChannel.kt$private const val otherId = "otherChannel" diff --git a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/StudyPlanSectionView.swift b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/StudyPlanSectionView.swift index ace4745d1..e4f59580e 100644 --- a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/StudyPlanSectionView.swift +++ b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/StudyPlanSectionView.swift @@ -75,7 +75,7 @@ struct StudyPlanSectionView: View { #Preview { StudyPlanSectionView( section: StudyPlanWidgetViewStateSection.makePlaceholder( - isNextPageLoadingShowed: true + nextPageLoadingState: StudyPlanWidgetViewStateSectionContentPageLoadingState.loading ), onSectionTap: { _ in }, onActivityTap: { _, _ in }, @@ -89,7 +89,7 @@ struct StudyPlanSectionView: View { #Preview { StudyPlanSectionView( section: StudyPlanWidgetViewStateSection.makePlaceholder( - isLoadAllTopicsButtonShown: true + nextPageLoadingState: StudyPlanWidgetViewStateSectionContentPageLoadingState.loadMore ), onSectionTap: { _ in }, onActivityTap: { _, _ in }, @@ -102,8 +102,9 @@ struct StudyPlanSectionView: View { extension StudyPlanWidgetViewStateSection { static func makePlaceholder( - isNextPageLoadingShowed: Bool = false, - isLoadAllTopicsButtonShown: Bool = false + nextPageLoadingState: StudyPlanWidgetViewStateSectionContentPageLoadingState = StudyPlanWidgetViewStateSectionContentPageLoadingState.hidden, + completedPageLoadingState: StudyPlanWidgetViewStateSectionContentPageLoadingState = + StudyPlanWidgetViewStateSectionContentPageLoadingState.hidden ) -> StudyPlanWidgetViewStateSection { StudyPlanWidgetViewStateSection( id: 1, @@ -119,8 +120,8 @@ extension StudyPlanWidgetViewStateSection { .makePlaceholder(state: .completed), .makePlaceholder(state: .next) ], - isNextPageLoadingShowed: isNextPageLoadingShowed, - isLoadAllTopicsButtonShown: isLoadAllTopicsButtonShown + nextPageLoadingState: nextPageLoadingState, + completedPageLoadingState: completedPageLoadingState ) ) } diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/analytic/domain/model/hyperskill/HyperskillAnalyticTarget.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/analytic/domain/model/hyperskill/HyperskillAnalyticTarget.kt index 197087476..da52ad1af 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/analytic/domain/model/hyperskill/HyperskillAnalyticTarget.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/analytic/domain/model/hyperskill/HyperskillAnalyticTarget.kt @@ -138,5 +138,6 @@ enum class HyperskillAnalyticTarget(val targetName: String) { CODE_BLOCK("code_block"), CODE_BLOCK_SUGGESTION("code_block_suggestion"), CODE_BLOCK_CHILD("code_block_child"), - LOAD_MORE("load_more") + LOAD_MORE("load_more"), + EXPAND_COMPLETED("expand_completed") } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/domain/analytic/StudyPlanExpandCompletedActivitiesClickedHSAnalyticEvent.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/domain/analytic/StudyPlanExpandCompletedActivitiesClickedHSAnalyticEvent.kt new file mode 100644 index 000000000..7d62e9353 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/domain/analytic/StudyPlanExpandCompletedActivitiesClickedHSAnalyticEvent.kt @@ -0,0 +1,35 @@ +package org.hyperskill.app.study_plan.domain.analytic + +import org.hyperskill.app.analytic.domain.model.hyperskill.HyperskillAnalyticAction +import org.hyperskill.app.analytic.domain.model.hyperskill.HyperskillAnalyticEvent +import org.hyperskill.app.analytic.domain.model.hyperskill.HyperskillAnalyticPart +import org.hyperskill.app.analytic.domain.model.hyperskill.HyperskillAnalyticRoute +import org.hyperskill.app.analytic.domain.model.hyperskill.HyperskillAnalyticTarget + +/** + * Represents a click analytic event of the ExpandCompleted button in the section. + * + * JSON payload: + * ``` + * { + * "route": "/study-plan"", + * "action": "click", + * "part": "study_plan_section", + * "target": "expand_completed", + * "context": + * { + * "section_id": 123 + * } + * } + * ``` + * @see HyperskillAnalyticEvent + */ +class StudyPlanExpandCompletedActivitiesClickedHSAnalyticEvent( + val sectionId: Long +) : HyperskillAnalyticEvent( + route = HyperskillAnalyticRoute.StudyPlan(), + action = HyperskillAnalyticAction.CLICK, + part = HyperskillAnalyticPart.STUDY_PLAN_SECTION, + target = HyperskillAnalyticTarget.EXPAND_COMPLETED, + context = mapOf(StudyPlanAnalyticParams.PARAM_SECTION_ID to sectionId) +) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/MainStudyPlanWidgetActionDispatcher.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/MainStudyPlanWidgetActionDispatcher.kt index ca36f3484..231a52d9a 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/MainStudyPlanWidgetActionDispatcher.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/MainStudyPlanWidgetActionDispatcher.kt @@ -143,7 +143,12 @@ internal class MainStudyPlanWidgetActionDispatcher( ) { sentryInteractor.withTransaction( action.sentryTransaction, - onError = { StudyPlanWidgetFeature.LearningActivitiesFetchResult.Failed(action.sectionId) } + onError = { + StudyPlanWidgetFeature.LearningActivitiesFetchResult.Failed( + sectionId = action.sectionId, + targetPage = action.targetPage + ) + } ) { learningActivitiesRepository .getLearningActivities( @@ -155,7 +160,8 @@ internal class MainStudyPlanWidgetActionDispatcher( .let { learningActivities -> StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success( sectionId = action.sectionId, - activities = learningActivities + activities = learningActivities, + targetPage = action.targetPage ) } }.let(onNewMessage) diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetFeature.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetFeature.kt index d4671e02f..6fa8e9e89 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetFeature.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetFeature.kt @@ -24,7 +24,7 @@ object StudyPlanWidgetFeature { /** * Describes status of sections loading */ - val sectionsStatus: SectionStatus = SectionStatus.IDLE, + val sectionsStatus: ContentStatus = ContentStatus.IDLE, /** * Map of activity ids to activities @@ -53,31 +53,34 @@ object StudyPlanWidgetFeature { get() = profile?.features?.isLearningPathDividedTrackTopicsEnabled ?: false } - enum class SectionStatus { + enum class ContentStatus { IDLE, LOADING, ERROR, LOADED } - enum class SectionContentStatus { + enum class PageContentStatus { IDLE, + AWAIT_LOADING, + LOADING, ERROR, + LOADED + } - FIRST_PAGE_LOADING, - FIRST_PAGE_LOADED, - NEXT_PAGE_LOADING, - ALL_PAGES_LOADED + enum class SectionPage { + MAIN, + NEXT, + COMPLETED } data class StudyPlanSectionInfo( val studyPlanSection: StudyPlanSection, val isExpanded: Boolean, - /** - * Describes status of section's activities loading - * */ - val sectionContentStatus: SectionContentStatus + val mainPageContentStatus: ContentStatus, + val nextPageContentStatus: PageContentStatus, + val completedPageContentStatus: PageContentStatus ) sealed interface Message { @@ -89,6 +92,8 @@ object StudyPlanWidgetFeature { data class RetryActivitiesLoading(val sectionId: Long) : Message + data class ExpandCompletedActivitiesClicked(val sectionId: Long) : Message + data object PullToRefresh : Message data object SubscribeClicked : Message @@ -132,10 +137,14 @@ object StudyPlanWidgetFeature { internal sealed interface LearningActivitiesFetchResult : Message { data class Success( val sectionId: Long, - val activities: List + val activities: List, + val targetPage: SectionPage ) : LearningActivitiesFetchResult - data class Failed(val sectionId: Long) : LearningActivitiesFetchResult + data class Failed( + val sectionId: Long, + val targetPage: SectionPage + ) : LearningActivitiesFetchResult } internal sealed interface ProfileFetchResult : Message { @@ -165,8 +174,13 @@ object StudyPlanWidgetFeature { val sectionId: Long, val activitiesIds: List, val types: Set = LearningActivityType.supportedTypes(), - val states: Set = setOf(LearningActivityState.TODO), - val sentryTransaction: HyperskillSentryTransaction + val states: Set = setOf( + LearningActivityState.TODO, + LearningActivityState.COMPLETED, + LearningActivityState.SKIPPED + ), + val sentryTransaction: HyperskillSentryTransaction, + val targetPage: SectionPage ) : InternalAction data object FetchProfile : InternalAction diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetReducer.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetReducer.kt index 393a8fc99..d0dfda961 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetReducer.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetReducer.kt @@ -13,6 +13,7 @@ import org.hyperskill.app.study_plan.domain.analytic.StudyPlanClickedActivityHyp import org.hyperskill.app.study_plan.domain.analytic.StudyPlanClickedRetryActivitiesLoadingHyperskillAnalyticEvent import org.hyperskill.app.study_plan.domain.analytic.StudyPlanClickedSectionHyperskillAnalyticEvent import org.hyperskill.app.study_plan.domain.analytic.StudyPlanClickedSubscribeHyperskillAnalyticEvent +import org.hyperskill.app.study_plan.domain.analytic.StudyPlanExpandCompletedActivitiesClickedHSAnalyticEvent import org.hyperskill.app.study_plan.domain.analytic.StudyPlanLoadMoreActivitiesClickedHSAnalyticEvent import org.hyperskill.app.study_plan.domain.analytic.StudyPlanStageImplementUnsupportedModalClickedGoToHomeScreenHyperskillAnalyticEvent import org.hyperskill.app.study_plan.domain.analytic.StudyPlanStageImplementUnsupportedModalHiddenHyperskillAnalyticEvent @@ -22,10 +23,12 @@ import org.hyperskill.app.study_plan.domain.model.StudyPlanSectionType import org.hyperskill.app.study_plan.domain.model.firstRootTopicsActivityIndexToBeLoaded import org.hyperskill.app.study_plan.widget.domain.mapper.LearningActivityToTopicProgressMapper import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.Action +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.ContentStatus import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.InternalAction import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.InternalMessage import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.Message -import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.PageContentStatus +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionPage import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.State import ru.nobird.app.core.model.slice import ru.nobird.app.presentation.redux.reducer.StateReducer @@ -44,7 +47,7 @@ class StudyPlanWidgetReducer : StateReducer { is StudyPlanWidgetFeature.LearningActivitiesWithSectionsFetchResult.Success -> handleLearningActivitiesWithSectionsFetchSuccess(state, message) StudyPlanWidgetFeature.LearningActivitiesWithSectionsFetchResult.Failed -> { - state.copy(sectionsStatus = StudyPlanWidgetFeature.SectionStatus.ERROR) to emptySet() + state.copy(sectionsStatus = ContentStatus.ERROR) to emptySet() } is Message.RetryActivitiesLoading -> handleRetryActivitiesLoading(state, message) @@ -55,15 +58,17 @@ class StudyPlanWidgetReducer : StateReducer { state.copy( studyPlanSections = state.studyPlanSections.mapValues { (sectionId, sectionInfo) -> sectionInfo.copy( - sectionContentStatus = if (sectionId == currentSectionId) { - sectionInfo.sectionContentStatus + mainPageContentStatus = if (sectionId == currentSectionId) { + sectionInfo.mainPageContentStatus } else { - SectionContentStatus.IDLE + ContentStatus.IDLE } ) } ) to getContentFetchActions(forceUpdate = true) } + is Message.ExpandCompletedActivitiesClicked -> + handleExpandCompletedActivitiesClicked(state, message) is Message.PullToRefresh -> if (!state.isRefreshing) { state.copy(isRefreshing = true) to getContentFetchActions(forceUpdate = true) @@ -120,10 +125,10 @@ class StudyPlanWidgetReducer : StateReducer { } ?: (state to emptySet()) private fun coldContentFetch(state: State, message: InternalMessage.Initialize): StudyPlanWidgetReducerResult = - if (state.sectionsStatus == StudyPlanWidgetFeature.SectionStatus.IDLE || - state.sectionsStatus == StudyPlanWidgetFeature.SectionStatus.ERROR && message.forceUpdate + if (state.sectionsStatus == ContentStatus.IDLE || + state.sectionsStatus == ContentStatus.ERROR && message.forceUpdate ) { - State(sectionsStatus = StudyPlanWidgetFeature.SectionStatus.LOADING) to + State(sectionsStatus = ContentStatus.LOADING) to getContentFetchActions(forceUpdate = message.forceUpdate) } else { state to emptySet() @@ -144,7 +149,7 @@ class StudyPlanWidgetReducer : StateReducer { val visibleSections = getVisibleSections(message.studyPlanSections, learningActivitiesIds) val currentSectionId = visibleSections.firstOrNull()?.id ?: return state.copy( studyPlanSections = emptyMap(), - sectionsStatus = StudyPlanWidgetFeature.SectionStatus.LOADED, + sectionsStatus = ContentStatus.LOADED, isRefreshing = false, subscriptionLimitType = message.subscriptionLimitType ) to emptySet() @@ -163,17 +168,19 @@ class StudyPlanWidgetReducer : StateReducer { studyPlanSection.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSection, isExpanded = studyPlanSection.id == currentSectionId, - sectionContentStatus = if (studyPlanSection.id == currentSectionId) { - SectionContentStatus.FIRST_PAGE_LOADED + mainPageContentStatus = if (studyPlanSection.id == currentSectionId) { + ContentStatus.LOADED } else { - SectionContentStatus.IDLE - } + ContentStatus.IDLE + }, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) } val loadedSectionsState = state.copy( studyPlanSections = studyPlanSections, - sectionsStatus = StudyPlanWidgetFeature.SectionStatus.LOADED, + sectionsStatus = ContentStatus.LOADED, isRefreshing = false, learnedTopicsCount = message.learnedTopicsCount, activities = message.learningActivities.associateBy { it.id }, @@ -184,7 +191,8 @@ class StudyPlanWidgetReducer : StateReducer { handleNewActivities( loadedSectionsState, sectionId = currentSectionId, - activities = message.learningActivities + activities = message.learningActivities, + targetPage = SectionPage.MAIN ) } else { loadedSectionsState to emptySet() @@ -237,47 +245,25 @@ class StudyPlanWidgetReducer : StateReducer { } ), sectionId = message.sectionId, - activities = message.activities + activities = message.activities, + targetPage = message.targetPage ) private fun handleNewActivities( state: State, sectionId: Long, - activities: List + activities: List, + targetPage: SectionPage ): StudyPlanWidgetReducerResult { - val sectionContentStatus = state.studyPlanSections[sectionId]?.sectionContentStatus + val section = state.studyPlanSections[sectionId] ?: return state to emptySet() val nextState = when { // ALTAPPS-786: We should hide sections without available activities to avoid blocking study plan - activities.isEmpty() && sectionContentStatus == SectionContentStatus.FIRST_PAGE_LOADING -> { - state.copy( - studyPlanSections = state.studyPlanSections.mutate { - remove(sectionId) - } - ) + activities.isEmpty() && targetPage == SectionPage.MAIN -> { + state.hideSection(sectionId) } else -> { - state.copy( - studyPlanSections = state.studyPlanSections.update(sectionId) { sectionInfo -> - sectionInfo.copy( - sectionContentStatus = when (sectionContentStatus) { - SectionContentStatus.FIRST_PAGE_LOADED -> { - val canLoadMoreActivities = - sectionInfo - .studyPlanSection - .getActivitiesToBeLoaded(state.activities.values) - .isNotEmpty() - if (canLoadMoreActivities) { - SectionContentStatus.FIRST_PAGE_LOADED - } else { - SectionContentStatus.ALL_PAGES_LOADED - } - } - else -> SectionContentStatus.ALL_PAGES_LOADED - } - ) - } - ) + updateSectionContentStatus(state, section, targetPage) } } @@ -306,6 +292,53 @@ class StudyPlanWidgetReducer : StateReducer { ) } + private fun updateSectionContentStatus( + state: State, + section: StudyPlanWidgetFeature.StudyPlanSectionInfo, + targetPage: SectionPage + ): State = + state.copy( + studyPlanSections = state.studyPlanSections.update(section.studyPlanSection.id) { sectionInfo -> + sectionInfo.copy( + mainPageContentStatus = if (targetPage == SectionPage.MAIN) { + ContentStatus.LOADED + } else { + sectionInfo.mainPageContentStatus + }, + nextPageContentStatus = when (targetPage) { + SectionPage.MAIN -> { + val canLoadMoreActivities = + state + .getNextRootTopicsActivitiesToBeLoaded(section.studyPlanSection.id) + .isNotEmpty() + if (canLoadMoreActivities) { + PageContentStatus.AWAIT_LOADING + } else { + PageContentStatus.LOADED + } + } + SectionPage.NEXT -> PageContentStatus.LOADED + SectionPage.COMPLETED -> sectionInfo.nextPageContentStatus + }, + completedPageContentStatus = when (targetPage) { + SectionPage.MAIN -> { + val canLoadCompletedActivities = + state + .getActivitiesBeforeCurrentActivityToBeLoaded(section.studyPlanSection.id) + .isNotEmpty() + if (canLoadCompletedActivities) { + PageContentStatus.AWAIT_LOADING + } else { + PageContentStatus.LOADED + } + } + SectionPage.COMPLETED -> PageContentStatus.LOADED + SectionPage.NEXT -> section.completedPageContentStatus + } + ) + } + ) + private fun putFetchedLearningActivitiesProgressesToCacheAction( activities: List ): Action { @@ -319,16 +352,21 @@ class StudyPlanWidgetReducer : StateReducer { ): StudyPlanWidgetReducerResult = state.copy( studyPlanSections = state.studyPlanSections.update(message.sectionId) { sectionInfo -> - sectionInfo.copy( - sectionContentStatus = when (sectionInfo.sectionContentStatus) { - SectionContentStatus.IDLE, - SectionContentStatus.ERROR, - SectionContentStatus.FIRST_PAGE_LOADING -> SectionContentStatus.ERROR - SectionContentStatus.NEXT_PAGE_LOADING, - SectionContentStatus.FIRST_PAGE_LOADED, - SectionContentStatus.ALL_PAGES_LOADED -> SectionContentStatus.FIRST_PAGE_LOADED + when (message.targetPage) { + SectionPage.MAIN -> { + sectionInfo.copy( + mainPageContentStatus = ContentStatus.ERROR, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE + ) } - ) + SectionPage.NEXT -> { + sectionInfo.copy(nextPageContentStatus = PageContentStatus.ERROR) + } + SectionPage.COMPLETED -> { + sectionInfo.copy(completedPageContentStatus = PageContentStatus.ERROR) + } + } } ) to emptySet() @@ -340,7 +378,7 @@ class StudyPlanWidgetReducer : StateReducer { state.studyPlanSections[message.sectionId] ?: return state to emptySet() return state.copy( studyPlanSections = state.studyPlanSections.update(message.sectionId) { sectionInfo -> - sectionInfo.copy(sectionContentStatus = SectionContentStatus.FIRST_PAGE_LOADING) + sectionInfo.copy(mainPageContentStatus = ContentStatus.LOADING) } ) to setOf( InternalAction.FetchLearningActivities( @@ -349,7 +387,8 @@ class StudyPlanWidgetReducer : StateReducer { section = section, isLearningPathDividedTrackTopicsEnabled = state.isLearningPathDividedTrackTopicsEnabled ), - sentryTransaction = getFetchLearningActivitiesSentryTransaction(state, message.sectionId) + sentryTransaction = getFetchLearningActivitiesSentryTransaction(state, message.sectionId), + targetPage = SectionPage.MAIN ), InternalAction.LogAnalyticEvent( StudyPlanClickedRetryActivitiesLoadingHyperskillAnalyticEvent(message.sectionId) @@ -363,17 +402,44 @@ class StudyPlanWidgetReducer : StateReducer { ): StudyPlanWidgetReducerResult = state.copy( studyPlanSections = state.studyPlanSections.update(message.sectionId) { sectionInfo -> - sectionInfo.copy(sectionContentStatus = SectionContentStatus.NEXT_PAGE_LOADING) + sectionInfo.copy(nextPageContentStatus = PageContentStatus.LOADING) } ) to setOf( InternalAction.LogAnalyticEvent(StudyPlanLoadMoreActivitiesClickedHSAnalyticEvent(message.sectionId)), InternalAction.FetchLearningActivities( sectionId = message.sectionId, - activitiesIds = state.getActivitiesToBeLoaded(message.sectionId).toList(), - sentryTransaction = getFetchLearningActivitiesSentryTransaction(state, message.sectionId) + activitiesIds = state.getNextRootTopicsActivitiesToBeLoaded(message.sectionId), + sentryTransaction = getFetchLearningActivitiesSentryTransaction(state, message.sectionId), + targetPage = SectionPage.NEXT ) ) + private fun handleExpandCompletedActivitiesClicked( + state: State, + message: Message.ExpandCompletedActivitiesClicked + ): StudyPlanWidgetReducerResult { + val activitiesToBeLoaded = state.getActivitiesBeforeCurrentActivityToBeLoaded(message.sectionId) + return if (activitiesToBeLoaded.isNotEmpty()) { + state.copy( + studyPlanSections = state.studyPlanSections.update(message.sectionId) { sectionInfo -> + sectionInfo.copy(completedPageContentStatus = PageContentStatus.LOADING) + } + ) to setOf( + InternalAction.LogAnalyticEvent( + StudyPlanExpandCompletedActivitiesClickedHSAnalyticEvent(message.sectionId) + ), + InternalAction.FetchLearningActivities( + sectionId = message.sectionId, + activitiesIds = activitiesToBeLoaded, + sentryTransaction = getFetchLearningActivitiesSentryTransaction(state, message.sectionId), + targetPage = SectionPage.COMPLETED + ) + ) + } else { + state to emptySet() + } + } + private fun changeSectionExpanse( state: State, sectionId: Long, @@ -390,43 +456,42 @@ class StudyPlanWidgetReducer : StateReducer { } fun updateSectionState( - sectionContentStatus: SectionContentStatus + mainPageContentStatus: ContentStatus ): State = state.copy( studyPlanSections = state.studyPlanSections.update( sectionId, - section.copy(isExpanded = isExpanded, sectionContentStatus = sectionContentStatus) + section.copy(isExpanded = isExpanded, mainPageContentStatus = mainPageContentStatus) ) ) return if (isExpanded) { - when (val contentStatus = section.sectionContentStatus) { - SectionContentStatus.IDLE, - SectionContentStatus.ERROR -> { + when (val mainPageContentStatus = section.mainPageContentStatus) { + ContentStatus.IDLE, + ContentStatus.ERROR -> { val sentryTransaction = getFetchLearningActivitiesSentryTransaction(state, sectionId) - updateSectionState(SectionContentStatus.FIRST_PAGE_LOADING) to setOfNotNull( + updateSectionState(ContentStatus.LOADING) to setOfNotNull( InternalAction.FetchLearningActivities( sectionId = sectionId, activitiesIds = getPaginatedActivitiesIds( section = section, isLearningPathDividedTrackTopicsEnabled = state.isLearningPathDividedTrackTopicsEnabled ), - sentryTransaction = sentryTransaction + sentryTransaction = sentryTransaction, + targetPage = SectionPage.MAIN ), logAnalyticEventAction ) } // activities are loading at the moment or already loaded - SectionContentStatus.FIRST_PAGE_LOADING, - SectionContentStatus.NEXT_PAGE_LOADING, - SectionContentStatus.FIRST_PAGE_LOADED, - SectionContentStatus.ALL_PAGES_LOADED -> { - updateSectionState(contentStatus) to setOfNotNull(logAnalyticEventAction) + ContentStatus.LOADING, + ContentStatus.LOADED -> { + updateSectionState(mainPageContentStatus) to setOfNotNull(logAnalyticEventAction) } } } else { - updateSectionState(section.sectionContentStatus) to setOfNotNull(logAnalyticEventAction) + updateSectionState(section.mainPageContentStatus) to setOfNotNull(logAnalyticEventAction) } } diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetStateExtensions.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetStateExtensions.kt index d5e2904a3..6fa4b2486 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetStateExtensions.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/presentation/StudyPlanWidgetStateExtensions.kt @@ -1,11 +1,13 @@ package org.hyperskill.app.study_plan.widget.presentation +import kotlin.math.max +import org.hyperskill.app.core.utils.mutate import org.hyperskill.app.learning_activities.domain.model.LearningActivity import org.hyperskill.app.learning_activities.domain.model.LearningActivityState import org.hyperskill.app.study_plan.domain.model.StudyPlanSection import org.hyperskill.app.study_plan.domain.model.StudyPlanSectionType -import org.hyperskill.app.study_plan.domain.model.rootTopicsActivitiesToBeLoaded import org.hyperskill.app.subscriptions.domain.model.SubscriptionLimitType +import ru.nobird.app.core.model.slice /** * @return current [StudyPlanSection]. @@ -39,7 +41,7 @@ internal fun StudyPlanWidgetFeature.State.getCurrentActivity(): LearningActivity /** * @param sectionId target section id. - * @return a sequence of [LearningActivity] for the given section with [sectionId] + * @return A sequence of [LearningActivity] for the given section with [sectionId] * filtered by availability in [StudyPlanWidgetFeature.State.activities] */ internal fun StudyPlanWidgetFeature.State.getLoadedSectionActivities(sectionId: Long): Sequence = @@ -50,24 +52,50 @@ internal fun StudyPlanWidgetFeature.State.getLoadedSectionActivities(sectionId: ?.mapNotNull { id -> activities[id] } ?: emptySequence() -internal fun StudyPlanWidgetFeature.State.getActivitiesToBeLoaded(sectionId: Long): Set { - val sectionInfo = studyPlanSections[sectionId] ?: return emptySet() +/** + * @param sectionId target section id + * @return A list of activities to be loaded for ROOT_TOPICS section. + * Activities to be loaded are activities + * starting from next to the last loaded activity for ROOT_TOPICS section + * and ending with the last ROOT_TOPICS section activity. + */ +internal fun StudyPlanWidgetFeature.State.getNextRootTopicsActivitiesToBeLoaded(sectionId: Long): List { + val sectionInfo = studyPlanSections[sectionId] ?: return emptyList() val studyPlanSection = sectionInfo.studyPlanSection return if (studyPlanSection.type == StudyPlanSectionType.ROOT_TOPICS) { - val sectionLoadedActivity = getLoadedSectionActivities(sectionId).map { it.id }.toSet() - studyPlanSection.rootTopicsActivitiesToBeLoaded.subtract(sectionLoadedActivity) + val lastLoadedActivityId = getLoadedSectionActivities(sectionId).lastOrNull()?.id + val lastLoadedActivityIndex = if (lastLoadedActivityId != null) { + max(0, studyPlanSection.activities.indexOf(lastLoadedActivityId) + 1) + } else { + 0 + } + studyPlanSection.activities.slice(from = lastLoadedActivityIndex) } else { - emptySet() + emptyList() } } -internal fun StudyPlanSection.getActivitiesToBeLoaded(allLoadedActivities: Collection): Set = - if (type == StudyPlanSectionType.ROOT_TOPICS) { - val sectionActivities = activities.intersect(allLoadedActivities.map { it.id }.toSet()) - rootTopicsActivitiesToBeLoaded.subtract(sectionActivities) +/** + * @param sectionId target section id + * @return A list of activities to be loaded before first loaded activity for current section. + * If section with [sectionId] is not current, then returns empty list. + */ +internal fun StudyPlanWidgetFeature.State.getActivitiesBeforeCurrentActivityToBeLoaded(sectionId: Long): List { + val currentSection = getCurrentSection() + return if (currentSection?.id == sectionId) { + val sectionInfo = studyPlanSections[sectionId] ?: return emptyList() + val studyPlanSection = sectionInfo.studyPlanSection + val firstLoadedActivityId = getLoadedSectionActivities(sectionId).firstOrNull()?.id + val firstLoadedActivityIndex = if (firstLoadedActivityId != null) { + max(0, studyPlanSection.activities.indexOf(firstLoadedActivityId)) + } else { + 0 + } + studyPlanSection.activities.slice(from = 0, to = firstLoadedActivityIndex) } else { - emptySet() + emptyList() } +} /** * @param sectionId target section id. @@ -129,4 +157,11 @@ internal fun StudyPlanWidgetFeature.State.isPaywallShown(): Boolean { } else { false } -} \ No newline at end of file +} + +internal fun StudyPlanWidgetFeature.State.hideSection(sectionId: Long): StudyPlanWidgetFeature.State = + copy( + studyPlanSections = studyPlanSections.mutate { + remove(sectionId) + } + ) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/mapper/StudyPlanWidgetViewStateMapper.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/mapper/StudyPlanWidgetViewStateMapper.kt index b93ebb281..88b4c1734 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/mapper/StudyPlanWidgetViewStateMapper.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/mapper/StudyPlanWidgetViewStateMapper.kt @@ -6,13 +6,8 @@ import org.hyperskill.app.learning_activities.domain.model.LearningActivity import org.hyperskill.app.learning_activities.domain.model.LearningActivityState import org.hyperskill.app.learning_activities.view.mapper.LearningActivityTextsMapper import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature -import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus.ALL_PAGES_LOADED -import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus.ERROR -import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus.FIRST_PAGE_LOADED -import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus.FIRST_PAGE_LOADING -import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus.IDLE -import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus.NEXT_PAGE_LOADING -import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionStatus +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.ContentStatus +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.PageContentStatus import org.hyperskill.app.study_plan.widget.presentation.getCurrentActivity import org.hyperskill.app.study_plan.widget.presentation.getCurrentSection import org.hyperskill.app.study_plan.widget.presentation.getLoadedSectionActivities @@ -20,14 +15,15 @@ import org.hyperskill.app.study_plan.widget.presentation.getUnlockedActivitiesCo import org.hyperskill.app.study_plan.widget.presentation.isPaywallShown import org.hyperskill.app.study_plan.widget.view.model.StudyPlanWidgetViewState import org.hyperskill.app.study_plan.widget.view.model.StudyPlanWidgetViewState.SectionContent +import org.hyperskill.app.study_plan.widget.view.model.StudyPlanWidgetViewState.SectionContentPageLoadingState class StudyPlanWidgetViewStateMapper(private val dateFormatter: SharedDateFormatter) { fun map(state: StudyPlanWidgetFeature.State): StudyPlanWidgetViewState = when (state.sectionsStatus) { - SectionStatus.IDLE -> StudyPlanWidgetViewState.Idle - SectionStatus.LOADING -> StudyPlanWidgetViewState.Loading - SectionStatus.ERROR -> StudyPlanWidgetViewState.Error - SectionStatus.LOADED -> getLoadedWidgetContent(state) + ContentStatus.IDLE -> StudyPlanWidgetViewState.Idle + ContentStatus.LOADING -> StudyPlanWidgetViewState.Loading + ContentStatus.ERROR -> StudyPlanWidgetViewState.Error + ContentStatus.LOADED -> getLoadedWidgetContent(state) } private fun getLoadedWidgetContent(state: StudyPlanWidgetFeature.State): StudyPlanWidgetViewState.Content { @@ -78,27 +74,21 @@ class StudyPlanWidgetViewStateMapper(private val dateFormatter: SharedDateFormat currentActivityId: Long? ): SectionContent = if (sectionInfo.isExpanded) { - when (sectionInfo.sectionContentStatus) { - IDLE -> SectionContent.Collapsed - ERROR -> SectionContent.Error - FIRST_PAGE_LOADING, - NEXT_PAGE_LOADING -> { - getContent( - state = state, - sectionInfo = sectionInfo, - currentActivityId = currentActivityId, - emptyActivitiesState = SectionContent.Loading - ) - } - FIRST_PAGE_LOADED, - ALL_PAGES_LOADED -> { - getContent( - state = state, - sectionInfo = sectionInfo, - currentActivityId = currentActivityId, - emptyActivitiesState = SectionContent.Error - ) - } + when (sectionInfo.mainPageContentStatus) { + ContentStatus.IDLE -> SectionContent.Collapsed + ContentStatus.ERROR -> SectionContent.Error + ContentStatus.LOADING -> getContent( + state = state, + sectionInfo = sectionInfo, + currentActivityId = currentActivityId, + emptyActivitiesState = SectionContent.Loading + ) + ContentStatus.LOADED -> getContent( + state = state, + sectionInfo = sectionInfo, + currentActivityId = currentActivityId, + emptyActivitiesState = SectionContent.Error + ) } } else { SectionContent.Collapsed @@ -115,52 +105,57 @@ class StudyPlanWidgetViewStateMapper(private val dateFormatter: SharedDateFormat return if (loadedActivities.isEmpty()) { emptyActivitiesState } else { - getContent( - activities = loadedActivities, - currentActivityId = currentActivityId, - unlockedActivitiesCount = state.getUnlockedActivitiesCount(sectionId), - isLoadAllTopicsButtonVisible = sectionInfo.sectionContentStatus == FIRST_PAGE_LOADED, - isNextPageLoadingShowed = sectionInfo.sectionContentStatus == NEXT_PAGE_LOADING + val unlockedActivitiesCount = state.getUnlockedActivitiesCount(sectionId) + SectionContent.Content( + sectionItems = loadedActivities.mapIndexed { index, activity -> + mapSectionItem( + activity = activity, + currentActivityId = currentActivityId, + isLocked = unlockedActivitiesCount != null && index + 1 > unlockedActivitiesCount + ) + }, + nextPageLoadingState = mapPageContentStatusToViewState(sectionInfo.nextPageContentStatus), + completedPageLoadingState = mapPageContentStatusToViewState(sectionInfo.completedPageContentStatus) ) } } - private fun getContent( - activities: List, + private fun mapPageContentStatusToViewState(pageContentStatus: PageContentStatus): SectionContentPageLoadingState = + when (pageContentStatus) { + PageContentStatus.IDLE, + PageContentStatus.ERROR, + PageContentStatus.LOADED -> SectionContentPageLoadingState.HIDDEN + PageContentStatus.AWAIT_LOADING -> SectionContentPageLoadingState.LOAD_MORE + PageContentStatus.LOADING -> SectionContentPageLoadingState.LOADING + } + + private fun mapSectionItem( + activity: LearningActivity, currentActivityId: Long?, - unlockedActivitiesCount: Int?, - isLoadAllTopicsButtonVisible: Boolean, - isNextPageLoadingShowed: Boolean - ): SectionContent.Content = - SectionContent.Content( - sectionItems = activities.mapIndexed { index, activity -> - val isLocked = unlockedActivitiesCount != null && index + 1 > unlockedActivitiesCount - StudyPlanWidgetViewState.SectionItem( - id = activity.id, - title = LearningActivityTextsMapper.mapLearningActivityToTitle(activity), - subtitle = LearningActivityTextsMapper.mapLearningActivityToSubtitle(activity), - state = if (isLocked) { - StudyPlanWidgetViewState.SectionItemState.LOCKED + isLocked: Boolean + ): StudyPlanWidgetViewState.SectionItem = + StudyPlanWidgetViewState.SectionItem( + id = activity.id, + title = LearningActivityTextsMapper.mapLearningActivityToTitle(activity), + subtitle = LearningActivityTextsMapper.mapLearningActivityToSubtitle(activity), + state = if (isLocked) { + StudyPlanWidgetViewState.SectionItemState.LOCKED + } else { + when (activity.state) { + LearningActivityState.TODO -> if (activity.id == currentActivityId) { + StudyPlanWidgetViewState.SectionItemState.NEXT } else { - when (activity.state) { - LearningActivityState.TODO -> if (activity.id == currentActivityId) { - StudyPlanWidgetViewState.SectionItemState.NEXT - } else { - StudyPlanWidgetViewState.SectionItemState.IDLE - } - LearningActivityState.SKIPPED -> StudyPlanWidgetViewState.SectionItemState.SKIPPED - LearningActivityState.COMPLETED -> StudyPlanWidgetViewState.SectionItemState.COMPLETED - null -> StudyPlanWidgetViewState.SectionItemState.IDLE - } - }, - isIdeRequired = activity.isIdeRequired, - progress = activity.progressPercentage, - formattedProgress = LearningActivityTextsMapper.mapLearningActivityToProgressString(activity), - hypercoinsAward = activity.hypercoinsAward.takeIf { it > 0 } - ) + StudyPlanWidgetViewState.SectionItemState.IDLE + } + LearningActivityState.SKIPPED -> StudyPlanWidgetViewState.SectionItemState.SKIPPED + LearningActivityState.COMPLETED -> StudyPlanWidgetViewState.SectionItemState.COMPLETED + null -> StudyPlanWidgetViewState.SectionItemState.IDLE + } }, - isLoadAllTopicsButtonShown = isLoadAllTopicsButtonVisible, - isNextPageLoadingShowed = isNextPageLoadingShowed + isIdeRequired = activity.isIdeRequired, + progress = activity.progressPercentage, + formattedProgress = LearningActivityTextsMapper.mapLearningActivityToProgressString(activity), + hypercoinsAward = activity.hypercoinsAward.takeIf { it > 0 } ) private fun formatTopicsCount(completedTopicsCount: Int, topicsCount: Int): String? = diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/model/StudyPlanWidgetViewState.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/model/StudyPlanWidgetViewState.kt index 37a423a44..00e416742 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/model/StudyPlanWidgetViewState.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/model/StudyPlanWidgetViewState.kt @@ -34,9 +34,25 @@ sealed interface StudyPlanWidgetViewState { data class Content( val sectionItems: List, - val isNextPageLoadingShowed: Boolean, + val nextPageLoadingState: SectionContentPageLoadingState, + val completedPageLoadingState: SectionContentPageLoadingState + ) : SectionContent { + // TODO: ALTAPPS-1334 remove this property and use nextPageLoadingState directly + @Deprecated("Is used only for iOS compatibility") val isLoadAllTopicsButtonShown: Boolean - ) : SectionContent + get() = nextPageLoadingState == SectionContentPageLoadingState.LOAD_MORE + + // TODO: ALTAPPS-1334 remove this property and use completedPageLoadingState directly + @Deprecated("Is used only for iOS compatibility") + val isNextPageLoadingShowed: Boolean + get() = nextPageLoadingState == SectionContentPageLoadingState.LOADING + } + } + + enum class SectionContentPageLoadingState { + HIDDEN, + LOAD_MORE, + LOADING } data class SectionItem( diff --git a/shared/src/commonMain/moko-resources/base/strings.xml b/shared/src/commonMain/moko-resources/base/strings.xml index 70a7558a6..b5c9718f8 100644 --- a/shared/src/commonMain/moko-resources/base/strings.xml +++ b/shared/src/commonMain/moko-resources/base/strings.xml @@ -530,6 +530,7 @@ Learn unlimited with\nMobile only plan Subscribe now Load more + Expand completed Select project diff --git a/shared/src/commonTest/kotlin/org/hyperskill/study_plan/screen/StudyPlanScreenTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/study_plan/screen/StudyPlanScreenTest.kt index 8391ff24b..f1bf73491 100644 --- a/shared/src/commonTest/kotlin/org/hyperskill/study_plan/screen/StudyPlanScreenTest.kt +++ b/shared/src/commonTest/kotlin/org/hyperskill/study_plan/screen/StudyPlanScreenTest.kt @@ -14,7 +14,7 @@ import org.hyperskill.app.study_plan.domain.analytic.StudyPlanViewedHyperskillAn import org.hyperskill.app.study_plan.screen.presentation.StudyPlanScreenFeature import org.hyperskill.app.study_plan.screen.presentation.StudyPlanScreenReducer import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature -import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionStatus +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.ContentStatus import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetReducer import org.hyperskill.app.users_interview_widget.presentation.UsersInterviewWidgetFeature import org.hyperskill.app.users_interview_widget.presentation.UsersInterviewWidgetReducer @@ -62,7 +62,7 @@ class StudyPlanScreenTest { toolbarState = GamificationToolbarFeature.State.Loading, questionnaireWidgetState = UsersInterviewWidgetFeature.State.Loading, studyPlanWidgetState = StudyPlanWidgetFeature.State( - sectionsStatus = SectionStatus.LOADING + sectionsStatus = ContentStatus.LOADING ) ) diff --git a/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanExpandCompletedActivitiesTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanExpandCompletedActivitiesTest.kt new file mode 100644 index 000000000..fc83b3f5c --- /dev/null +++ b/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanExpandCompletedActivitiesTest.kt @@ -0,0 +1,139 @@ +package org.hyperskill.study_plan.widget + +import kotlin.test.Test +import kotlin.test.assertEquals +import org.hyperskill.app.learning_activities.domain.model.LearningActivity +import org.hyperskill.app.study_plan.domain.model.StudyPlanSection +import org.hyperskill.app.study_plan.domain.model.StudyPlanSectionType +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.ContentStatus +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.PageContentStatus +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetReducer +import org.hyperskill.app.study_plan.widget.presentation.getActivitiesBeforeCurrentActivityToBeLoaded +import org.hyperskill.learning_activities.domain.model.stub +import org.hyperskill.study_plan.domain.model.stub + +class StudyPlanExpandCompletedActivitiesTest { + + private val reducer = StudyPlanWidgetReducer() + + @Test + fun `getActivitiesBeforeCurrentActivityToBeLoaded should return all the activities before last loaded activity`() { + val sectionId = 1L + val sectionActivitiesIds = List(10) { it.toLong() } + val section = StudyPlanSection.stub( + id = sectionId, + type = StudyPlanSectionType.ROOT_TOPICS, + activities = sectionActivitiesIds + ) + val loadedActivitiesIds = listOf(4L, /*5L,*/ 6L, /*7L,*/ 8L) + val state = StudyPlanWidgetFeature.State( + studyPlanSections = mapOf( + section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( + studyPlanSection = section, + isExpanded = true, + mainPageContentStatus = ContentStatus.IDLE, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE + ) + ), + activities = loadedActivitiesIds.associateWith { id -> LearningActivity.stub(id = id) } + ) + + val expectedActivitiesToBeLoaded = listOf(0L, 1L, 2L, 3L) + val actualActivitiesToBeLoaded = state.getActivitiesBeforeCurrentActivityToBeLoaded(sectionId) + assertEquals( + expected = expectedActivitiesToBeLoaded.toList(), + actual = actualActivitiesToBeLoaded + ) + } + + @Test + fun `getActivitiesBeforeCurrentActivityToBeLoaded should return empty list for non current sections`() { + val allActivities = List(10) { it.toLong() } + + val currentSectionId = 0L + val currentSectionActivities = listOf(/*0L, 1L, 2L,*/ 3L, 4L) + val currentSection = StudyPlanSection.stub( + id = currentSectionId, + type = StudyPlanSectionType.EXTRA_TOPICS, + activities = currentSectionActivities + ) + + val nextSectionId = 1L + val nextSectionActivities = listOf(/*5L, 6L, 7L,*/ 8L, 9L) + val nextSection = StudyPlanSection.stub( + id = nextSectionId, + type = StudyPlanSectionType.EXTRA_TOPICS, + activities = nextSectionActivities + ) + + val state = StudyPlanWidgetFeature.State( + studyPlanSections = mapOf( + currentSection.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( + studyPlanSection = currentSection, + isExpanded = true, + mainPageContentStatus = ContentStatus.IDLE, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE + ), + nextSection.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( + studyPlanSection = nextSection, + isExpanded = true, + mainPageContentStatus = ContentStatus.IDLE, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE + ) + ), + activities = allActivities.associateWith { id -> LearningActivity.stub(id = id) } + ) + + assertEquals( + expected = emptyList(), + actual = state.getActivitiesBeforeCurrentActivityToBeLoaded(nextSectionId) + ) + } + + /* ktlint-disable*/ + @Test + fun `Azfter successfully fetch completed activities page status should become LOADED even if not all activities are loaded`() { + val allActivities = List(10) { it.toLong() } + val mainPageActivities = listOf(5L, 6L, 7L, 8L, 9L) + val loadedCompletedPageActivities = listOf(/*0L,*/ 1L, /*2L, 3L,*/ 4L) + + val sectionId = 0L + val section = StudyPlanSection.stub( + id = sectionId, + type = StudyPlanSectionType.EXTRA_TOPICS, + activities = allActivities + ) + + val initialState = StudyPlanWidgetFeature.State( + sectionsStatus = ContentStatus.LOADED, + studyPlanSections = mapOf( + section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( + studyPlanSection = section, + isExpanded = true, + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.LOADING + ) + ), + activities = mainPageActivities.associateWith { id -> LearningActivity.stub(id = id) } + ) + + val (state, _) = reducer.reduce( + initialState, + StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success( + sectionId = sectionId, + activities = loadedCompletedPageActivities.map { id -> LearningActivity.stub(id = id) }, + targetPage = StudyPlanWidgetFeature.SectionPage.COMPLETED + ) + ) + + assertEquals( + expected = PageContentStatus.LOADED, + actual = state.studyPlanSections[sectionId]?.completedPageContentStatus + ) + } +} \ No newline at end of file diff --git a/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanLoadMoreActivitiesTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanLoadMoreActivitiesTest.kt new file mode 100644 index 000000000..536167a87 --- /dev/null +++ b/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanLoadMoreActivitiesTest.kt @@ -0,0 +1,180 @@ +package org.hyperskill.study_plan.widget + +import kotlin.test.Test +import kotlin.test.assertEquals +import org.hyperskill.app.learning_activities.domain.model.LearningActivity +import org.hyperskill.app.study_plan.domain.model.StudyPlanSection +import org.hyperskill.app.study_plan.domain.model.StudyPlanSectionType +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.ContentStatus +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.PageContentStatus +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetReducer +import org.hyperskill.app.study_plan.widget.presentation.getNextRootTopicsActivitiesToBeLoaded +import org.hyperskill.learning_activities.domain.model.stub +import org.hyperskill.study_plan.domain.model.stub + +class StudyPlanLoadMoreActivitiesTest { + + private val reducer = StudyPlanWidgetReducer() + + @Test + fun `getNextRootTopicsActivitiesToBeLoaded should return all the section activities for ROOT_TOPICS section`() { + val sectionId = 1L + val sectionActivitiesIds = List(2) { it.toLong() } + val section = StudyPlanSection.stub( + id = sectionId, + type = StudyPlanSectionType.ROOT_TOPICS, + activities = sectionActivitiesIds + ) + val state = StudyPlanWidgetFeature.State( + studyPlanSections = mapOf( + section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( + studyPlanSection = section, + isExpanded = true, + mainPageContentStatus = ContentStatus.IDLE, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE + ) + ), + activities = emptyMap() + ) + + val activitiesToBeLoaded = state.getNextRootTopicsActivitiesToBeLoaded(sectionId) + assertEquals(section.activities, activitiesToBeLoaded) + } + + /*ktlint-disable*/ + @Test + fun `getNextRootTopicsActivitiesToBeLoaded should return all activities after last loaded activity for ROOT_TOPICS section`() { + val sectionId = 1L + val sectionActivitiesIds = List(10) { it.toLong() } + val section = StudyPlanSection.stub( + id = sectionId, + type = StudyPlanSectionType.ROOT_TOPICS, + activities = sectionActivitiesIds + ) + val loadedActivitiesIds = listOf(/*0L,*/ 1L, /*2L,*/ 3L, 4L) + val state = StudyPlanWidgetFeature.State( + studyPlanSections = mapOf( + section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( + studyPlanSection = section, + isExpanded = true, + mainPageContentStatus = ContentStatus.IDLE, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE + ) + ), + activities = loadedActivitiesIds.associateWith { id -> LearningActivity.stub(id = id) } + ) + + val expectedActivitiesToBeLoaded = listOf(5L, 6L, 7L, 8L, 9L) + val actualActivitiesToBeLoaded = state.getNextRootTopicsActivitiesToBeLoaded(sectionId) + assertEquals(expectedActivitiesToBeLoaded.toList(), actualActivitiesToBeLoaded) + } + + /*ktlint-disable*/ + @Test + fun `getNextRootTopicsActivitiesToBeLoaded should return empty list if all ROOT_TOPICS section activities are loaded`() { + val sectionId = 1L + val sectionActivitiesIds = List(10) { it.toLong() } + val section = StudyPlanSection.stub( + id = sectionId, + type = StudyPlanSectionType.ROOT_TOPICS, + activities = sectionActivitiesIds + ) + val state = StudyPlanWidgetFeature.State( + studyPlanSections = mapOf( + section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( + studyPlanSection = section, + isExpanded = true, + mainPageContentStatus = ContentStatus.IDLE, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE + ) + ), + activities = sectionActivitiesIds.associateWith { id -> LearningActivity.stub(id = id) } + ) + + val actualActivitiesToBeLoaded = state.getNextRootTopicsActivitiesToBeLoaded(sectionId) + assertEquals( + expected = emptyList(), + actual = actualActivitiesToBeLoaded + ) + } + + @Test + fun `getNextRootTopicsActivitiesToBeLoaded should return empty list for ROOT_TOPICS sections`() { + val nonRootSectionTypes = + StudyPlanSectionType.entries - StudyPlanSectionType.ROOT_TOPICS + nonRootSectionTypes.forEach { sectionType -> + val sectionId = 1L + val sectionActivitiesIds = List(2) { it.toLong() } + val section = StudyPlanSection.stub( + id = sectionId, + type = sectionType, + activities = sectionActivitiesIds + ) + val state = StudyPlanWidgetFeature.State( + studyPlanSections = mapOf( + section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( + studyPlanSection = section, + isExpanded = true, + mainPageContentStatus = ContentStatus.IDLE, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE + ) + ), + activities = emptyMap() + ) + + val activitiesToBeLoaded = state.getNextRootTopicsActivitiesToBeLoaded(sectionId) + assertEquals( + expected = emptyList(), + actual = activitiesToBeLoaded + ) + } + } + + /*ktlint-disable*/ + @Test + fun `After successfully fetch completed activities page status should become LOADED even if not all activities are loaded`() { + val allActivities = List(10) { it.toLong() } + val mainPageActivities = listOf(5L, 6L, 7L, 8L, 9L) + val loadedCompletedPageActivities = listOf(/*0L,*/ 1L, /*2L, 3L,*/ 4L) + + val sectionId = 0L + val section = StudyPlanSection.stub( + id = sectionId, + type = StudyPlanSectionType.EXTRA_TOPICS, + activities = allActivities + ) + + val initialState = StudyPlanWidgetFeature.State( + sectionsStatus = ContentStatus.LOADED, + studyPlanSections = mapOf( + section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( + studyPlanSection = section, + isExpanded = true, + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.LOADING + ) + ), + activities = mainPageActivities.associateWith { id -> LearningActivity.stub(id = id) } + ) + + val (state, _) = reducer.reduce( + initialState, + StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success( + sectionId = sectionId, + activities = loadedCompletedPageActivities.map { id -> LearningActivity.stub(id = id) }, + targetPage = StudyPlanWidgetFeature.SectionPage.NEXT + ) + ) + + assertEquals( + expected = PageContentStatus.LOADED, + actual = state.studyPlanSections[sectionId]?.nextPageContentStatus + ) + } +} \ No newline at end of file diff --git a/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanWidgetStateExtensionsTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanWidgetStateExtensionsTest.kt index 8d4e0108c..eb3c0a124 100644 --- a/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanWidgetStateExtensionsTest.kt +++ b/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanWidgetStateExtensionsTest.kt @@ -13,7 +13,6 @@ import org.hyperskill.app.profile.domain.model.Profile import org.hyperskill.app.study_plan.domain.model.StudyPlanSection import org.hyperskill.app.study_plan.domain.model.StudyPlanSectionType import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature -import org.hyperskill.app.study_plan.widget.presentation.getActivitiesToBeLoaded import org.hyperskill.app.study_plan.widget.presentation.getCurrentActivity import org.hyperskill.app.study_plan.widget.presentation.getCurrentSection import org.hyperskill.app.study_plan.widget.presentation.getLoadedSectionActivities @@ -33,14 +32,18 @@ class StudyPlanWidgetStateExtensionsTest { val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf( section1.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section1, - true, - StudyPlanWidgetFeature.SectionContentStatus.IDLE + studyPlanSection = section1, + isExpanded = true, + mainPageContentStatus = StudyPlanWidgetFeature.ContentStatus.IDLE, + nextPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE, + completedPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE ), section2.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section2, - true, - StudyPlanWidgetFeature.SectionContentStatus.IDLE + studyPlanSection = section2, + isExpanded = true, + mainPageContentStatus = StudyPlanWidgetFeature.ContentStatus.IDLE, + nextPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE, + completedPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE ) ) ) @@ -62,9 +65,11 @@ class StudyPlanWidgetStateExtensionsTest { val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf( section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section, - true, - StudyPlanWidgetFeature.SectionContentStatus.IDLE + studyPlanSection = section, + isExpanded = true, + mainPageContentStatus = StudyPlanWidgetFeature.ContentStatus.IDLE, + nextPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE, + completedPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE ) ), activities = mapOf(activity.id to activity) @@ -83,9 +88,11 @@ class StudyPlanWidgetStateExtensionsTest { val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf( section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section, + studyPlanSection = section, isExpanded = false, - sectionContentStatus = StudyPlanWidgetFeature.SectionContentStatus.IDLE + mainPageContentStatus = StudyPlanWidgetFeature.ContentStatus.IDLE, + nextPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE, + completedPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE ) ), activities = mapOf(activity1.id to activity1, activity2.id to activity2) @@ -103,9 +110,11 @@ class StudyPlanWidgetStateExtensionsTest { val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf( section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section, - true, - StudyPlanWidgetFeature.SectionContentStatus.IDLE + studyPlanSection = section, + isExpanded = true, + mainPageContentStatus = StudyPlanWidgetFeature.ContentStatus.IDLE, + nextPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE, + completedPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE ) ), activities = activities.associateBy { it.id } @@ -115,43 +124,6 @@ class StudyPlanWidgetStateExtensionsTest { assertEquals(activities, loadedActivities) } - @Test - fun `getActivitiesToBeLoaded should return activities to be loaded for ROOT_TOPICS section`() { - val sectionId = 1L - val activities = listOf(1L, 2L).map { LearningActivity.stub(id = it) } - val section = StudyPlanSection.stub( - id = sectionId, - type = StudyPlanSectionType.ROOT_TOPICS, - activities = activities.map { it.id } - ) - val state = StudyPlanWidgetFeature.State( - studyPlanSections = mapOf( - section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section, - true, - StudyPlanWidgetFeature.SectionContentStatus.IDLE - ) - ), - activities = emptyMap() - ) - - val activitiesToBeLoaded = state.getActivitiesToBeLoaded(sectionId) - assertEquals(section.activities.toSet(), activitiesToBeLoaded) - } - - @Test - fun `StudyPlanSection getActivitiesToBeLoaded should return activities to be loaded for a given section`() { - val section = StudyPlanSection.stub( - id = 1, - type = StudyPlanSectionType.ROOT_TOPICS, - activities = listOf(1L, 2L, 3L) - ) - val loadedActivities = listOf(LearningActivity.stub(id = 1)) - - val activitiesToBeLoaded = section.getActivitiesToBeLoaded(loadedActivities) - assertEquals(setOf(2L, 3L), activitiesToBeLoaded) - } - @Test fun `isActivityLocked should return true for locked activities in case of topics limit`() { val section = StudyPlanSection.stub( @@ -162,9 +134,11 @@ class StudyPlanWidgetStateExtensionsTest { val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf( section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section, + studyPlanSection = section, isExpanded = false, - sectionContentStatus = StudyPlanWidgetFeature.SectionContentStatus.IDLE + mainPageContentStatus = StudyPlanWidgetFeature.ContentStatus.IDLE, + nextPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE, + completedPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE ) ), activities = mapOf(1L to LearningActivity.stub(id = 1)), @@ -187,9 +161,11 @@ class StudyPlanWidgetStateExtensionsTest { val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf( section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section, + studyPlanSection = section, isExpanded = false, - sectionContentStatus = StudyPlanWidgetFeature.SectionContentStatus.IDLE + mainPageContentStatus = StudyPlanWidgetFeature.ContentStatus.IDLE, + nextPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE, + completedPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE ) ), profile = Profile.stub( @@ -214,9 +190,11 @@ class StudyPlanWidgetStateExtensionsTest { val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf( section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section, + studyPlanSection = section, isExpanded = false, - sectionContentStatus = StudyPlanWidgetFeature.SectionContentStatus.IDLE + mainPageContentStatus = StudyPlanWidgetFeature.ContentStatus.IDLE, + nextPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE, + completedPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE ) ), profile = Profile.stub( @@ -241,9 +219,11 @@ class StudyPlanWidgetStateExtensionsTest { val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf( section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section, + studyPlanSection = section, isExpanded = false, - sectionContentStatus = StudyPlanWidgetFeature.SectionContentStatus.IDLE + mainPageContentStatus = StudyPlanWidgetFeature.ContentStatus.IDLE, + nextPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE, + completedPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE ) ), profile = Profile.stub( @@ -268,9 +248,11 @@ class StudyPlanWidgetStateExtensionsTest { val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf( section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section, + studyPlanSection = section, isExpanded = false, - sectionContentStatus = StudyPlanWidgetFeature.SectionContentStatus.IDLE + mainPageContentStatus = StudyPlanWidgetFeature.ContentStatus.IDLE, + nextPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE, + completedPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE ) ), profile = Profile.stub( @@ -301,14 +283,18 @@ class StudyPlanWidgetStateExtensionsTest { val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf( section1.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section1, + studyPlanSection = section1, isExpanded = false, - sectionContentStatus = StudyPlanWidgetFeature.SectionContentStatus.IDLE + mainPageContentStatus = StudyPlanWidgetFeature.ContentStatus.IDLE, + nextPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE, + completedPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE ), section2.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section2, + studyPlanSection = section2, isExpanded = false, - sectionContentStatus = StudyPlanWidgetFeature.SectionContentStatus.IDLE + mainPageContentStatus = StudyPlanWidgetFeature.ContentStatus.IDLE, + nextPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE, + completedPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE ) ), profile = Profile.stub( @@ -333,9 +319,11 @@ class StudyPlanWidgetStateExtensionsTest { val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf( section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section, + studyPlanSection = section, isExpanded = false, - sectionContentStatus = StudyPlanWidgetFeature.SectionContentStatus.IDLE + mainPageContentStatus = StudyPlanWidgetFeature.ContentStatus.IDLE, + nextPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE, + completedPageContentStatus = StudyPlanWidgetFeature.PageContentStatus.IDLE ) ), profile = Profile.stub( diff --git a/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanWidgetTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanWidgetTest.kt index d185e87d6..fe45b7795 100644 --- a/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanWidgetTest.kt +++ b/shared/src/commonTest/kotlin/org/hyperskill/study_plan/widget/StudyPlanWidgetTest.kt @@ -23,11 +23,13 @@ import org.hyperskill.app.study_plan.domain.model.StudyPlanSection import org.hyperskill.app.study_plan.domain.model.StudyPlanSectionType import org.hyperskill.app.study_plan.widget.domain.mapper.LearningActivityToTopicProgressMapper import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature -import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionContentStatus -import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionStatus +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.ContentStatus +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.PageContentStatus +import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetFeature.SectionPage import org.hyperskill.app.study_plan.widget.presentation.StudyPlanWidgetReducer import org.hyperskill.app.study_plan.widget.view.mapper.StudyPlanWidgetViewStateMapper import org.hyperskill.app.study_plan.widget.view.model.StudyPlanWidgetViewState +import org.hyperskill.app.study_plan.widget.view.model.StudyPlanWidgetViewState.SectionContentPageLoadingState import org.hyperskill.app.subscriptions.domain.model.Subscription import org.hyperskill.app.subscriptions.domain.model.SubscriptionLimitType import org.hyperskill.app.subscriptions.domain.model.SubscriptionStatus @@ -52,7 +54,7 @@ class StudyPlanWidgetTest { val initialState = StudyPlanWidgetFeature.State() val (state, actions) = reducer.reduce(initialState, StudyPlanWidgetFeature.InternalMessage.Initialize()) assertContains(actions, StudyPlanWidgetFeature.InternalAction.FetchLearningActivitiesWithSections()) - assertEquals(state.sectionsStatus, SectionStatus.LOADING) + assertEquals(state.sectionsStatus, ContentStatus.LOADING) } @Test @@ -67,7 +69,7 @@ class StudyPlanWidgetTest { learnedTopicsCount = 0 ) ) - assertEquals(SectionStatus.LOADED, state.sectionsStatus) + assertEquals(ContentStatus.LOADED, state.sectionsStatus) } @Test @@ -174,7 +176,7 @@ class StudyPlanWidgetTest { fun `Study plan sections should be empty if loaded sections does not contains current section`() { val expectedState = StudyPlanWidgetFeature.State( studyPlanSections = emptyMap(), - sectionsStatus = SectionStatus.LOADED, + sectionsStatus = ContentStatus.LOADED, isRefreshing = false ) @@ -216,8 +218,8 @@ class StudyPlanWidgetTest { state = StudyPlanWidgetViewState.SectionItemState.NEXT ) ), - isLoadAllTopicsButtonShown = false, - isNextPageLoadingShowed = false + nextPageLoadingState = SectionContentPageLoadingState.HIDDEN, + completedPageLoadingState = SectionContentPageLoadingState.HIDDEN ) }, isCurrent = sectionId == expectedSectionsIds[0] @@ -275,7 +277,7 @@ class StudyPlanWidgetTest { } val actualFirstSection = state.studyPlanSections[firstSection.id] - assertEquals(SectionContentStatus.ALL_PAGES_LOADED, actualFirstSection?.sectionContentStatus) + assertEquals(ContentStatus.LOADED, actualFirstSection?.mainPageContentStatus) assertEquals(true, actualFirstSection?.isExpanded) } @@ -286,7 +288,9 @@ class StudyPlanWidgetTest { studyPlanSections = mapOf( sectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(sectionId), - sectionContentStatus = SectionContentStatus.NEXT_PAGE_LOADING, + mainPageContentStatus = ContentStatus.LOADING, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, isExpanded = true ) ) @@ -296,10 +300,14 @@ class StudyPlanWidgetTest { initialState, StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success( sectionId = sectionId, - activities = listOf(stubLearningActivity(1L)) + activities = listOf(stubLearningActivity(1L)), + targetPage = SectionPage.MAIN ) ) - assertEquals(SectionContentStatus.ALL_PAGES_LOADED, state.studyPlanSections[sectionId]?.sectionContentStatus) + assertEquals( + expected = ContentStatus.LOADED, + actual = state.studyPlanSections[sectionId]?.mainPageContentStatus + ) } @Test @@ -310,12 +318,16 @@ class StudyPlanWidgetTest { studyPlanSections = mapOf( currentSectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(currentSectionId), - sectionContentStatus = SectionContentStatus.FIRST_PAGE_LOADING, + mainPageContentStatus = ContentStatus.LOADING, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, isExpanded = true ), nextSectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(nextSectionId), - sectionContentStatus = SectionContentStatus.IDLE, + mainPageContentStatus = ContentStatus.IDLE, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, isExpanded = false ) ) @@ -323,12 +335,19 @@ class StudyPlanWidgetTest { val (state, _) = reducer.reduce( initialState, - StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success(sectionId = currentSectionId, emptyList()) + StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success( + sectionId = currentSectionId, + activities = emptyList(), + targetPage = SectionPage.MAIN + ) ) assertTrue(state.studyPlanSections.containsKey(currentSectionId).not()) val nextSection = state.studyPlanSections[nextSectionId] assertTrue(nextSection?.isExpanded == true) - assertEquals(SectionContentStatus.FIRST_PAGE_LOADING, nextSection?.sectionContentStatus) + assertEquals( + expected = ContentStatus.LOADING, + actual = nextSection?.mainPageContentStatus + ) } @Test @@ -339,12 +358,16 @@ class StudyPlanWidgetTest { studyPlanSections = mapOf( currentSectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(currentSectionId), - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED, + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, isExpanded = true ), notCurrentSectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(notCurrentSectionId), - sectionContentStatus = SectionContentStatus.FIRST_PAGE_LOADING, + mainPageContentStatus = ContentStatus.LOADING, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, isExpanded = false ) ) @@ -354,7 +377,8 @@ class StudyPlanWidgetTest { initialState, StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success( sectionId = notCurrentSectionId, - activities = emptyList() + activities = emptyList(), + targetPage = SectionPage.MAIN ) ) assertTrue(state.studyPlanSections.containsKey(notCurrentSectionId).not()) @@ -372,7 +396,11 @@ class StudyPlanWidgetTest { val (state, _) = reducer.reduce( StudyPlanWidgetFeature.State(), - StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success(sectionId, activities) + StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success( + sectionId = sectionId, + activities = activities, + targetPage = SectionPage.MAIN + ) ) val resultActivitiesIds = state.activities.keys @@ -394,7 +422,9 @@ class StudyPlanWidgetTest { sectionId, activities = activities.map { it.id } ), - sectionContentStatus = SectionContentStatus.NEXT_PAGE_LOADING, + mainPageContentStatus = ContentStatus.LOADING, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, isExpanded = true ) ) @@ -405,7 +435,11 @@ class StudyPlanWidgetTest { val (_, resultActions) = reducer.reduce( initialState, - StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success(sectionId, activities) + StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success( + sectionId = sectionId, + activities = activities, + targetPage = SectionPage.MAIN + ) ) assertContains( @@ -438,12 +472,18 @@ class StudyPlanWidgetTest { activities = oldActivities.map { it.id }, type = StudyPlanSectionType.EXTRA_TOPICS ), - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED, + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, isExpanded = true ) ) ), - StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success(sectionId, newActivities) + StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success( + sectionId = sectionId, + activities = newActivities, + targetPage = SectionPage.MAIN + ) ) val resultActivitiesIds = state.activities.keys @@ -475,12 +515,18 @@ class StudyPlanWidgetTest { activities = oldActivities.map { it.id }, type = StudyPlanSectionType.ROOT_TOPICS ), - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED, + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, isExpanded = true ) ) ), - StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success(sectionId, newActivities) + StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success( + sectionId = sectionId, + activities = newActivities, + targetPage = SectionPage.MAIN + ) ) val resultActivitiesIds = state.activities.keys @@ -495,9 +541,11 @@ class StudyPlanWidgetTest { val initialState = StudyPlanWidgetFeature.State( studyPlanSections = mapOf( 0L to StudyPlanWidgetFeature.StudyPlanSectionInfo( - section, + studyPlanSection = section, isExpanded = true, - SectionContentStatus.ERROR + mainPageContentStatus = ContentStatus.ERROR, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) ) ) @@ -510,12 +558,14 @@ class StudyPlanWidgetTest { StudyPlanWidgetFeature.InternalAction.FetchLearningActivities( sectionId = section.id, activitiesIds = activities, - sentryTransaction = HyperskillSentryTransactionBuilder.buildStudyPlanWidgetFetchLearningActivities(true) + sentryTransaction = HyperskillSentryTransactionBuilder + .buildStudyPlanWidgetFetchLearningActivities(true), + targetPage = SectionPage.MAIN ) ) assertEquals( - SectionContentStatus.FIRST_PAGE_LOADING, - state.studyPlanSections[section.id]?.sectionContentStatus + expected = ContentStatus.LOADING, + actual = state.studyPlanSections[section.id]?.mainPageContentStatus ) val analyticAction = actions.last() as StudyPlanWidgetFeature.InternalAction.LogAnalyticEvent @@ -533,20 +583,24 @@ class StudyPlanWidgetTest { val collapsedSection = StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = section, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED, + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, isExpanded = false ) val idleSection = StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = section, - sectionContentStatus = SectionContentStatus.IDLE, + mainPageContentStatus = ContentStatus.IDLE, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, isExpanded = false ) listOf(collapsedSection, idleSection).forEach { givenSection -> val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf(sectionId to givenSection), - sectionsStatus = SectionStatus.LOADED + sectionsStatus = ContentStatus.LOADED ) val viewState = studyPlanWidgetViewStateMapper.map(state) @@ -572,12 +626,14 @@ class StudyPlanWidgetTest { val activityId = 0L val section = StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(sectionId, activities = listOf(activityId)), - sectionContentStatus = SectionContentStatus.FIRST_PAGE_LOADING, + mainPageContentStatus = ContentStatus.LOADING, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, isExpanded = true ) val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf(sectionId to section), - sectionsStatus = SectionStatus.LOADED, + sectionsStatus = ContentStatus.LOADED, activities = mapOf(activityId to stubLearningActivity(activityId)) ) @@ -595,8 +651,8 @@ class StudyPlanWidgetTest { state = StudyPlanWidgetViewState.SectionItemState.NEXT ) ), - isLoadAllTopicsButtonShown = false, - isNextPageLoadingShowed = false + nextPageLoadingState = SectionContentPageLoadingState.HIDDEN, + completedPageLoadingState = SectionContentPageLoadingState.HIDDEN ), isCurrent = true ) @@ -613,12 +669,14 @@ class StudyPlanWidgetTest { val section = StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(sectionId, activities = listOf(activityId)), - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED, + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, isExpanded = true ) val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf(sectionId to section), - sectionsStatus = SectionStatus.LOADED, + sectionsStatus = ContentStatus.LOADED, activities = mapOf(activityId to stubLearningActivity(activityId)) ) @@ -636,8 +694,8 @@ class StudyPlanWidgetTest { state = StudyPlanWidgetViewState.SectionItemState.NEXT ) ), - isLoadAllTopicsButtonShown = false, - isNextPageLoadingShowed = false + nextPageLoadingState = SectionContentPageLoadingState.HIDDEN, + completedPageLoadingState = SectionContentPageLoadingState.HIDDEN ), isCurrent = true ) @@ -659,20 +717,22 @@ class StudyPlanWidgetTest { val sectionId = 0L val section = StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(sectionId), - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED, + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, isExpanded = true ) val state = StudyPlanWidgetFeature.State( studyPlanSections = mapOf(sectionId to section), - sectionsStatus = SectionStatus.LOADED + sectionsStatus = ContentStatus.LOADED ) val (newState, actions) = reducer.reduce(state, StudyPlanWidgetFeature.InternalMessage.ReloadContentInBackground) assertEquals( - state.studyPlanSections[sectionId]?.sectionContentStatus, - newState.studyPlanSections[sectionId]?.sectionContentStatus + expected = state.studyPlanSections[sectionId]?.mainPageContentStatus, + actual = newState.studyPlanSections[sectionId]?.mainPageContentStatus ) assertContains(actions, StudyPlanWidgetFeature.InternalAction.FetchLearningActivitiesWithSections()) } @@ -692,8 +752,8 @@ class StudyPlanWidgetTest { state = StudyPlanWidgetViewState.SectionItemState.NEXT ) ), - isLoadAllTopicsButtonShown = false, - isNextPageLoadingShowed = false + nextPageLoadingState = SectionContentPageLoadingState.HIDDEN, + completedPageLoadingState = SectionContentPageLoadingState.HIDDEN ), isCurrent = true ) @@ -705,13 +765,15 @@ class StudyPlanWidgetTest { 0L to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(id = 0, activities = listOf(0)), isExpanded = true, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) ), activities = mapOf( 0L to stubLearningActivity(id = 0, title = "Activity 1") ), - sectionsStatus = SectionStatus.LOADED + sectionsStatus = ContentStatus.LOADED ) val viewState = studyPlanWidgetViewStateMapper.map(state) @@ -733,8 +795,8 @@ class StudyPlanWidgetTest { state = StudyPlanWidgetViewState.SectionItemState.NEXT ) ), - isLoadAllTopicsButtonShown = false, - isNextPageLoadingShowed = false + nextPageLoadingState = SectionContentPageLoadingState.HIDDEN, + completedPageLoadingState = SectionContentPageLoadingState.HIDDEN ), isCurrent = true ) @@ -746,11 +808,13 @@ class StudyPlanWidgetTest { 0L to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(id = 0, activities = listOf(0)), isExpanded = true, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) ), activities = mapOf(0L to stubLearningActivity(id = 0, title = "")), - sectionsStatus = SectionStatus.LOADED + sectionsStatus = ContentStatus.LOADED ) val viewState = studyPlanWidgetViewStateMapper.map(state) @@ -773,8 +837,8 @@ class StudyPlanWidgetTest { state = StudyPlanWidgetViewState.SectionItemState.NEXT ) ), - isLoadAllTopicsButtonShown = false, - isNextPageLoadingShowed = false + nextPageLoadingState = SectionContentPageLoadingState.HIDDEN, + completedPageLoadingState = SectionContentPageLoadingState.HIDDEN ), isCurrent = true ) @@ -786,7 +850,9 @@ class StudyPlanWidgetTest { 0L to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(id = 0, activities = listOf(0)), isExpanded = true, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) ), activities = mapOf( @@ -796,7 +862,7 @@ class StudyPlanWidgetTest { description = "Work on project. Stage: 1/6" ) ), - sectionsStatus = SectionStatus.LOADED + sectionsStatus = ContentStatus.LOADED ) val viewState = studyPlanWidgetViewStateMapper.map(state) @@ -815,10 +881,12 @@ class StudyPlanWidgetTest { topicsCount = 10 ), isExpanded = isExpanded, - sectionContentStatus = SectionContentStatus.IDLE + mainPageContentStatus = ContentStatus.IDLE, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) ), - sectionsStatus = SectionStatus.LOADED + sectionsStatus = ContentStatus.LOADED ) val expectedViewState = StudyPlanWidgetViewState.Content( @@ -852,8 +920,8 @@ class StudyPlanWidgetTest { state = StudyPlanWidgetViewState.SectionItemState.NEXT ) ), - isLoadAllTopicsButtonShown = false, - isNextPageLoadingShowed = false + nextPageLoadingState = SectionContentPageLoadingState.HIDDEN, + completedPageLoadingState = SectionContentPageLoadingState.HIDDEN ), isCurrent = true ), @@ -866,8 +934,8 @@ class StudyPlanWidgetTest { state = StudyPlanWidgetViewState.SectionItemState.IDLE ) ), - isLoadAllTopicsButtonShown = false, - isNextPageLoadingShowed = false + nextPageLoadingState = SectionContentPageLoadingState.HIDDEN, + completedPageLoadingState = SectionContentPageLoadingState.HIDDEN ), isCurrent = false, formattedTopicsCount = "1 / 10", @@ -881,7 +949,9 @@ class StudyPlanWidgetTest { 0L to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(id = 0, activities = listOf(0)), isExpanded = true, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADING, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, ), 1L to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub( @@ -891,14 +961,16 @@ class StudyPlanWidgetTest { topicsCount = 10 ), isExpanded = true, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, ) ), activities = mapOf( 0L to stubLearningActivity(id = 0), 1L to stubLearningActivity(id = 1) ), - sectionsStatus = SectionStatus.LOADED + sectionsStatus = ContentStatus.LOADED ) val viewState = studyPlanWidgetViewStateMapper.map(state) @@ -918,7 +990,9 @@ class StudyPlanWidgetTest { sectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(id = sectionId, activities = listOf(activityId)), isExpanded = true, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, ) ), activities = mapOf( @@ -956,7 +1030,9 @@ class StudyPlanWidgetTest { sectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(id = 0, activities = listOf(activityId)), isExpanded = true, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) ), activities = mapOf( @@ -990,7 +1066,9 @@ class StudyPlanWidgetTest { sectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(id = sectionId, activities = listOf(activityId)), isExpanded = true, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) ), activities = mapOf( @@ -1038,7 +1116,9 @@ class StudyPlanWidgetTest { 0L to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(id = 0, activities = listOf(activityId)), isExpanded = true, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) ), activities = mapOf( @@ -1072,7 +1152,9 @@ class StudyPlanWidgetTest { sectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(id = sectionId, activities = listOf(activityId)), isExpanded = true, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) ), activities = mapOf( @@ -1113,7 +1195,9 @@ class StudyPlanWidgetTest { sectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(id = sectionId, activities = listOf(activityId)), isExpanded = true, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) ), activities = mapOf( @@ -1145,7 +1229,9 @@ class StudyPlanWidgetTest { sectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(id = sectionId, activities = listOf(activityId)), isExpanded = true, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) ), activities = mapOf( @@ -1176,7 +1262,9 @@ class StudyPlanWidgetTest { sectionId to StudyPlanWidgetFeature.StudyPlanSectionInfo( studyPlanSection = studyPlanSectionStub(id = sectionId), isExpanded = false, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) ) ) @@ -1203,7 +1291,9 @@ class StudyPlanWidgetTest { activities = expectedActivitiesIds ), isExpanded = false, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) assertEquals( @@ -1222,7 +1312,9 @@ class StudyPlanWidgetTest { activities = expectedActivitiesIds ), isExpanded = false, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) assertEquals( @@ -1242,7 +1334,9 @@ class StudyPlanWidgetTest { nextActivityId = 3L ), isExpanded = false, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) assertEquals( @@ -1262,7 +1356,9 @@ class StudyPlanWidgetTest { nextActivityId = 5L ), isExpanded = false, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) assertEquals( @@ -1282,7 +1378,9 @@ class StudyPlanWidgetTest { nextActivityId = 10L ), isExpanded = false, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) assertEquals( @@ -1302,7 +1400,9 @@ class StudyPlanWidgetTest { nextActivityId = 5L ), isExpanded = false, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE ) assertEquals( @@ -1325,14 +1425,16 @@ class StudyPlanWidgetTest { nextActivityId = nextActivityId ), isExpanded = true, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, ) ), activities = mapOf( notNextActivityId to stubLearningActivity(notNextActivityId), nextActivityId to stubLearningActivity(nextActivityId) ), - sectionsStatus = SectionStatus.LOADED + sectionsStatus = ContentStatus.LOADED ) val viewState = studyPlanWidgetViewStateMapper.map(state) @@ -1368,14 +1470,16 @@ class StudyPlanWidgetTest { nextActivityId = null ), isExpanded = true, - sectionContentStatus = SectionContentStatus.ALL_PAGES_LOADED + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, ) ), activities = mapOf( firstActivityId to stubLearningActivity(firstActivityId), secondActivityId to stubLearningActivity(secondActivityId) ), - sectionsStatus = SectionStatus.LOADED + sectionsStatus = ContentStatus.LOADED ) val viewState = studyPlanWidgetViewStateMapper.map(state) @@ -1410,11 +1514,28 @@ class StudyPlanWidgetTest { val (_, actions) = reducer.reduce( - StudyPlanWidgetFeature.State(), - StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success(0, activities) + StudyPlanWidgetFeature.State( + studyPlanSections = mapOf( + 0L to StudyPlanWidgetFeature.StudyPlanSectionInfo( + studyPlanSectionStub(0L), + isExpanded = true, + mainPageContentStatus = ContentStatus.LOADING, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, + ) + ) + ), + StudyPlanWidgetFeature.LearningActivitiesFetchResult.Success( + sectionId = 0, + activities = activities, + targetPage = SectionPage.MAIN + ) ) - assertContains(actions, StudyPlanWidgetFeature.InternalAction.PutTopicsProgressesToCache(expectedProgresses)) + assertContains( + actions, + StudyPlanWidgetFeature.InternalAction.PutTopicsProgressesToCache(expectedProgresses) + ) } @Test @@ -1432,7 +1553,9 @@ class StudyPlanWidgetTest { section.id to StudyPlanWidgetFeature.StudyPlanSectionInfo( section, isExpanded = false, - sectionContentStatus = SectionContentStatus.IDLE + mainPageContentStatus = ContentStatus.LOADED, + nextPageContentStatus = PageContentStatus.IDLE, + completedPageContentStatus = PageContentStatus.IDLE, ) ), profile = Profile.stub(), @@ -1444,16 +1567,20 @@ class StudyPlanWidgetTest { StudyPlanWidgetFeature.Message.LoadMoreActivitiesClicked(sectionId) ) - assertTrue { - actions.any { - it is StudyPlanWidgetFeature.InternalAction.FetchLearningActivities && - it.sectionId == sectionId && - it.activitiesIds == unloadedActivitiesIds - } - } + assertContains( + actions, + StudyPlanWidgetFeature.InternalAction.FetchLearningActivities( + sectionId = sectionId, + activitiesIds = unloadedActivitiesIds, + sentryTransaction = HyperskillSentryTransactionBuilder.buildStudyPlanWidgetFetchLearningActivities( + isCurrentSection = true + ), + targetPage = SectionPage.NEXT + ) + ) assertEquals( - SectionContentStatus.NEXT_PAGE_LOADING, - state.studyPlanSections[sectionId]?.sectionContentStatus + expected = PageContentStatus.LOADING, + actual = state.studyPlanSections[sectionId]?.nextPageContentStatus ) assertTrue { From a08aed426f422d7ad8251d6839634d9e78d37c2b Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 2 Sep 2024 04:38:36 +0000 Subject: [PATCH 16/18] Bump build number --- gradle/app.versions.toml | 2 +- .../NotificationServiceExtension/Info.plist | 2 +- .../iosHyperskillApp.xcodeproj/project.pbxproj | 16 ++++++++-------- iosHyperskillApp/iosHyperskillApp/Info.plist | 2 +- .../iosHyperskillAppTests/Info.plist | 2 +- .../iosHyperskillAppUITests/Info.plist | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/gradle/app.versions.toml b/gradle/app.versions.toml index 87e19a854..bb3de75e4 100644 --- a/gradle/app.versions.toml +++ b/gradle/app.versions.toml @@ -3,4 +3,4 @@ minSdk = '24' targetSdk = '34' compileSdk = '34' versionName = '1.70' -versionCode = '539' \ No newline at end of file +versionCode = '540' \ No newline at end of file diff --git a/iosHyperskillApp/NotificationServiceExtension/Info.plist b/iosHyperskillApp/NotificationServiceExtension/Info.plist index d78e6ac7e..f53f2b421 100644 --- a/iosHyperskillApp/NotificationServiceExtension/Info.plist +++ b/iosHyperskillApp/NotificationServiceExtension/Info.plist @@ -9,7 +9,7 @@ CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleVersion - 568 + 569 CFBundleShortVersionString 1.70 CFBundlePackageType diff --git a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj index 5a021e39b..737f556e7 100644 --- a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj +++ b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj @@ -5673,7 +5673,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 568; + CURRENT_PROJECT_VERSION = 569; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; @@ -5694,7 +5694,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 568; + CURRENT_PROJECT_VERSION = 569; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = iosHyperskillAppUITests/Info.plist; @@ -5715,7 +5715,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 568; + CURRENT_PROJECT_VERSION = 569; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; @@ -5736,7 +5736,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 568; + CURRENT_PROJECT_VERSION = 569; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5757,7 +5757,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 568; + CURRENT_PROJECT_VERSION = 569; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; @@ -5786,7 +5786,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 568; + CURRENT_PROJECT_VERSION = 569; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5932,7 +5932,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 568; + CURRENT_PROJECT_VERSION = 569; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; @@ -5968,7 +5968,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 568; + CURRENT_PROJECT_VERSION = 569; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; diff --git a/iosHyperskillApp/iosHyperskillApp/Info.plist b/iosHyperskillApp/iosHyperskillApp/Info.plist index ff2742802..157f86b7f 100644 --- a/iosHyperskillApp/iosHyperskillApp/Info.plist +++ b/iosHyperskillApp/iosHyperskillApp/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 568 + 569 FirebaseAppDelegateProxyEnabled FirebaseMessagingAutoInitEnabled diff --git a/iosHyperskillApp/iosHyperskillAppTests/Info.plist b/iosHyperskillApp/iosHyperskillAppTests/Info.plist index fe183c32e..fefcfcf37 100644 --- a/iosHyperskillApp/iosHyperskillAppTests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppTests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 568 + 569 diff --git a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist index 5b5987e21..bd4be5046 100644 --- a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 568 + 569 From 58f7c1d80a2331ce20938ec26431d10bf67ed336 Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Mon, 2 Sep 2024 14:53:42 +0900 Subject: [PATCH 17/18] iOS: Support skipped and completed activities in study plan (#1173) ^ALTAPPS-1334 --- .../project.pbxproj | 20 +++++ .../Assets.xcassets/StudyPlan/Contents.json | 6 ++ .../Contents.json | 15 ++++ .../study-plan-section-expand-completed.pdf | Bin 0 -> 1127 bytes .../Sources/Models/Constants/Strings.swift | 1 + ...ectionContentPageLoadingStateWrapper.swift | 30 +++++++ .../StudyPlan/StudyPlanViewModel.swift | 8 ++ ...SectionCompletedPageLoadingStateView.swift | 78 ++++++++++++++++++ ...yPlanSectionNextPageLoadingStateView.swift | 45 ++++++++++ .../Views/Section/StudyPlanSectionView.swift | 36 ++++---- .../StudyPlan/Views/StudyPlanView.swift | 13 +-- .../view/model/StudyPlanWidgetViewState.kt | 12 +-- 12 files changed, 226 insertions(+), 38 deletions(-) create mode 100644 iosHyperskillApp/iosHyperskillApp/Assets.xcassets/StudyPlan/Contents.json create mode 100644 iosHyperskillApp/iosHyperskillApp/Assets.xcassets/StudyPlan/study-plan-section-expand-completed.imageset/Contents.json create mode 100644 iosHyperskillApp/iosHyperskillApp/Assets.xcassets/StudyPlan/study-plan-section-expand-completed.imageset/study-plan-section-expand-completed.pdf create mode 100644 iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Model/StudyPlanWidgetViewStateSectionContentPageLoadingStateWrapper.swift create mode 100644 iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/SectionContentPageLoadingState/StudyPlanSectionCompletedPageLoadingStateView.swift create mode 100644 iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/SectionContentPageLoadingState/StudyPlanSectionNextPageLoadingStateView.swift diff --git a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj index 006cef760..3f4bc93fb 100644 --- a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj +++ b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj @@ -168,6 +168,7 @@ 2C3796122877001700C197E2 /* ProfileHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C3796112877001700C197E2 /* ProfileHeaderView.swift */; }; 2C3B84E82C637AE100FE9D5C /* StepQuizCodeBlanksVariableInstructionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C3B84E72C637AE100FE9D5C /* StepQuizCodeBlanksVariableInstructionView.swift */; }; 2C3CE3962C1073990011BECA /* StepToolbarContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C3CE3952C1073990011BECA /* StepToolbarContent.swift */; }; + 2C3D92D42C857DAA00D271B7 /* StudyPlanWidgetViewStateSectionContentPageLoadingStateWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C3D92D32C857DAA00D271B7 /* StudyPlanWidgetViewStateSectionContentPageLoadingStateWrapper.swift */; }; 2C3E656D2A12722800BC8DC0 /* TrackSelectionListHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C3E656C2A12722800BC8DC0 /* TrackSelectionListHeaderView.swift */; }; 2C3E65702A127F2300BC8DC0 /* BrandLinearGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C3E656F2A127F2300BC8DC0 /* BrandLinearGradient.swift */; }; 2C3E65732A1280A500BC8DC0 /* TrackSelectionListHeaderSkeletonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C3E65722A1280A500BC8DC0 /* TrackSelectionListHeaderSkeletonView.swift */; }; @@ -492,6 +493,8 @@ 2CC78D0C28C74EF90006EF91 /* ViewRelatedEventsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC78D0B28C74EF90006EF91 /* ViewRelatedEventsViewController.swift */; }; 2CC78D0E28C75A3D0006EF91 /* UIViewControllerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC78D0D28C75A3D0006EF91 /* UIViewControllerExtensions.swift */; }; 2CC95C0E2A4EBB970036C73E /* ProjectLevelAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC95C0D2A4EBB970036C73E /* ProjectLevelAvatarView.swift */; }; + 2CCA0DED2C857F51007E50A9 /* StudyPlanSectionNextPageLoadingStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CCA0DEC2C857F51007E50A9 /* StudyPlanSectionNextPageLoadingStateView.swift */; }; + 2CCA0DEF2C858110007E50A9 /* StudyPlanSectionCompletedPageLoadingStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CCA0DEE2C858110007E50A9 /* StudyPlanSectionCompletedPageLoadingStateView.swift */; }; 2CCAAB7F2BEA561C001A040F /* spacebot-progress-bar-wow.lottie in Resources */ = {isa = PBXBuildFile; fileRef = 2CCAAB7E2BEA561C001A040F /* spacebot-progress-bar-wow.lottie */; }; 2CCC421F2ABD810A0067C869 /* GhostButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CCC421E2ABD810A0067C869 /* GhostButtonStyle.swift */; }; 2CCCA3992862D58E00D98089 /* StepQuizStringView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CCCA3982862D58E00D98089 /* StepQuizStringView.swift */; }; @@ -962,6 +965,7 @@ 2C3796112877001700C197E2 /* ProfileHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHeaderView.swift; sourceTree = ""; }; 2C3B84E72C637AE100FE9D5C /* StepQuizCodeBlanksVariableInstructionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepQuizCodeBlanksVariableInstructionView.swift; sourceTree = ""; }; 2C3CE3952C1073990011BECA /* StepToolbarContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepToolbarContent.swift; sourceTree = ""; }; + 2C3D92D32C857DAA00D271B7 /* StudyPlanWidgetViewStateSectionContentPageLoadingStateWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyPlanWidgetViewStateSectionContentPageLoadingStateWrapper.swift; sourceTree = ""; }; 2C3E656C2A12722800BC8DC0 /* TrackSelectionListHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackSelectionListHeaderView.swift; sourceTree = ""; }; 2C3E656F2A127F2300BC8DC0 /* BrandLinearGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrandLinearGradient.swift; sourceTree = ""; }; 2C3E65722A1280A500BC8DC0 /* TrackSelectionListHeaderSkeletonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackSelectionListHeaderSkeletonView.swift; sourceTree = ""; }; @@ -1291,6 +1295,8 @@ 2CC78D0B28C74EF90006EF91 /* ViewRelatedEventsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewRelatedEventsViewController.swift; sourceTree = ""; }; 2CC78D0D28C75A3D0006EF91 /* UIViewControllerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerExtensions.swift; sourceTree = ""; }; 2CC95C0D2A4EBB970036C73E /* ProjectLevelAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectLevelAvatarView.swift; sourceTree = ""; }; + 2CCA0DEC2C857F51007E50A9 /* StudyPlanSectionNextPageLoadingStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyPlanSectionNextPageLoadingStateView.swift; sourceTree = ""; }; + 2CCA0DEE2C858110007E50A9 /* StudyPlanSectionCompletedPageLoadingStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyPlanSectionCompletedPageLoadingStateView.swift; sourceTree = ""; }; 2CCAAB7E2BEA561C001A040F /* spacebot-progress-bar-wow.lottie */ = {isa = PBXFileReference; lastKnownFileType = file; path = "spacebot-progress-bar-wow.lottie"; sourceTree = ""; }; 2CCC421E2ABD810A0067C869 /* GhostButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhostButtonStyle.swift; sourceTree = ""; }; 2CCCA3982862D58E00D98089 /* StepQuizStringView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepQuizStringView.swift; sourceTree = ""; }; @@ -3111,6 +3117,7 @@ 2C99B0FE2A14253B0018627B /* Model */ = { isa = PBXGroup; children = ( + 2C3D92D32C857DAA00D271B7 /* StudyPlanWidgetViewStateSectionContentPageLoadingStateWrapper.swift */, 2C99B0FF2A14255F0018627B /* StudyPlanWidgetViewStateSectionItemStateWrapper.swift */, ); path = Model; @@ -3131,6 +3138,7 @@ E9F0A2AD29D4390400C4A61E /* StudyPlanSectionView.swift */, 2C99B1032A1429330018627B /* Header */, 2C99B1042A1429DC0018627B /* List */, + 2CCA0DF02C85813E007E50A9 /* SectionContentPageLoadingState */, ); path = Section; sourceTree = ""; @@ -3623,6 +3631,15 @@ path = Avatars; sourceTree = ""; }; + 2CCA0DF02C85813E007E50A9 /* SectionContentPageLoadingState */ = { + isa = PBXGroup; + children = ( + 2CCA0DEE2C858110007E50A9 /* StudyPlanSectionCompletedPageLoadingStateView.swift */, + 2CCA0DEC2C857F51007E50A9 /* StudyPlanSectionNextPageLoadingStateView.swift */, + ); + path = SectionContentPageLoadingState; + sourceTree = ""; + }; 2CCCA3972862D57100D98089 /* StepQuizString */ = { isa = PBXGroup; children = ( @@ -5337,6 +5354,7 @@ 2C9D20CA2BFF512300F88A6C /* TopicCompletedModalContentView.swift in Sources */, 2CD4EDFB2B79D74B0091F0B2 /* TransparentBlurView.swift in Sources */, 2CA8E094281039EB00154088 /* RoundedRectangleButtonStyle.swift in Sources */, + 2CCA0DED2C857F51007E50A9 /* StudyPlanSectionNextPageLoadingStateView.swift in Sources */, 2CF34F9D2C340DB60054477E /* CommentsSkeletonView.swift in Sources */, E9D537D02A71056100F21828 /* ProfileBadgesGridItemView.swift in Sources */, 2CB0ADEE2B04AD6D0089D557 /* ChallengeWidgetViewModel.swift in Sources */, @@ -5394,6 +5412,7 @@ E96E80F627EF57BA00AA6683 /* AuthSocialViewModel.swift in Sources */, 2C9D20C62BFF0BE200F88A6C /* TopicCompletedModalBackgroundView.swift in Sources */, E9A22BA0295081BD001700B7 /* StreakFreezeModalViewController.swift in Sources */, + 2CCA0DEF2C858110007E50A9 /* StudyPlanSectionCompletedPageLoadingStateView.swift in Sources */, 2CBD1917291D392400F5FB0B /* UIView+Animations.swift in Sources */, 2C4F639E2A10203000D4EE39 /* ProjectSelectionListGridSectionView.swift in Sources */, 2C079683285CEF0900EE0487 /* StepQuizMatchingAssembly.swift in Sources */, @@ -5553,6 +5572,7 @@ 2CCCA39D2862E44B00D98089 /* StepQuizStringAssembly.swift in Sources */, 2CA5F8F32994CB870013B854 /* DebugStepNavigationView.swift in Sources */, 2C792324287B759500A3F61D /* StringExtensions.swift in Sources */, + 2C3D92D42C857DAA00D271B7 /* StudyPlanWidgetViewStateSectionContentPageLoadingStateWrapper.swift in Sources */, 2C5215AC291E6AD8006C2427 /* UIView+FindViewOfSpecifiedType.swift in Sources */, 2C58DE252803C185002A2774 /* AuthSocialControlsView.swift in Sources */, 2CBD1915291D2AF800F5FB0B /* ProfileDailyStudyRemindersPickerViewController.swift in Sources */, diff --git a/iosHyperskillApp/iosHyperskillApp/Assets.xcassets/StudyPlan/Contents.json b/iosHyperskillApp/iosHyperskillApp/Assets.xcassets/StudyPlan/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/iosHyperskillApp/iosHyperskillApp/Assets.xcassets/StudyPlan/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosHyperskillApp/iosHyperskillApp/Assets.xcassets/StudyPlan/study-plan-section-expand-completed.imageset/Contents.json b/iosHyperskillApp/iosHyperskillApp/Assets.xcassets/StudyPlan/study-plan-section-expand-completed.imageset/Contents.json new file mode 100644 index 000000000..c0e7608a3 --- /dev/null +++ b/iosHyperskillApp/iosHyperskillApp/Assets.xcassets/StudyPlan/study-plan-section-expand-completed.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "study-plan-section-expand-completed.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/iosHyperskillApp/iosHyperskillApp/Assets.xcassets/StudyPlan/study-plan-section-expand-completed.imageset/study-plan-section-expand-completed.pdf b/iosHyperskillApp/iosHyperskillApp/Assets.xcassets/StudyPlan/study-plan-section-expand-completed.imageset/study-plan-section-expand-completed.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0608ffca4638eefb954be14691a97f0fa8f2d418 GIT binary patch literal 1127 zcmZuxO>dkq5WUZ@n9EAZAqE2muM|aUcAKhdt8PheQ4f@LHdKIHAXVC5pRqB9-OZea z2lL*1Jh9qs@2)t@062!IfBpi*>ubEZ0oCr!BScF)_^jH)@Psjex%sLeI#o0fY}MbY zRQZQn+}tmp${zjzXQ2%Yr{v0K=kNThvH_o;CwS;)0W?iV2-yTEw`2>Ej48W`MeZLqtiyu360DnwD1j#E8NK^ ze;6x_zDcC?u1P#fZVKMDtsZ)O_(ATX2)_UOtuWXX2jze-YF`z%`UM|mkN7?o`|$Ui zm~z$Y=dM&e8vo{wZll{lwWMgwJAu2RBMgOu78W;!UE2>&`V6OLZOY6Akb)zovqS{}MO9^tV2Q&lQ1qb)le?{Hfe?rj> OcARyX!fN&Y>)k6mZumj~ literal 0 HcmV?d00001 diff --git a/iosHyperskillApp/iosHyperskillApp/Sources/Models/Constants/Strings.swift b/iosHyperskillApp/iosHyperskillApp/Sources/Models/Constants/Strings.swift index 819f9f4d4..8210c0054 100644 --- a/iosHyperskillApp/iosHyperskillApp/Sources/Models/Constants/Strings.swift +++ b/iosHyperskillApp/iosHyperskillApp/Sources/Models/Constants/Strings.swift @@ -289,6 +289,7 @@ enum Strings { static let paywallSubscribeButton = sharedStrings.study_plan_paywall_subscribe_button.localized() static let loadMoreButton = sharedStrings.study_plan_load_more_button_text.localized() + static let expandCompletedButton = sharedStrings.study_plan_expand_completed_button_text.localized() } // MARK: - Leaderboard - diff --git a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Model/StudyPlanWidgetViewStateSectionContentPageLoadingStateWrapper.swift b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Model/StudyPlanWidgetViewStateSectionContentPageLoadingStateWrapper.swift new file mode 100644 index 000000000..291cdc6c7 --- /dev/null +++ b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Model/StudyPlanWidgetViewStateSectionContentPageLoadingStateWrapper.swift @@ -0,0 +1,30 @@ +import Foundation +import shared + +enum StudyPlanWidgetViewStateSectionContentPageLoadingStateWrapper { + case hidden + case loadMore + case loading +} + +extension StudyPlanWidgetViewStateSectionContentPageLoadingStateWrapper { + init?(sharedState: StudyPlanWidgetViewStateSectionContentPageLoadingState) { + switch sharedState { + case .hidden: + self = .hidden + case .loadMore: + self = .loadMore + case .loading: + self = .loading + default: + assertionFailure("Did receive unsupported item state = \(sharedState)") + return nil + } + } +} + +extension StudyPlanWidgetViewStateSectionContentPageLoadingState { + var wrapped: StudyPlanWidgetViewStateSectionContentPageLoadingStateWrapper? { + StudyPlanWidgetViewStateSectionContentPageLoadingStateWrapper(sharedState: self) + } +} diff --git a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/StudyPlanViewModel.swift b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/StudyPlanViewModel.swift index 64f6825de..3e2b868c3 100644 --- a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/StudyPlanViewModel.swift +++ b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/StudyPlanViewModel.swift @@ -81,6 +81,14 @@ final class StudyPlanViewModel: FeatureViewModel< ) } + func doExpandCompletedActivities(sectionID: Int64) { + onNewMessage( + StudyPlanScreenFeatureMessageStudyPlanWidgetMessage( + message: StudyPlanWidgetFeatureMessageExpandCompletedActivitiesClicked(sectionId: sectionID) + ) + ) + } + func doPaywallBannerAction() { onNewMessage( StudyPlanScreenFeatureMessageStudyPlanWidgetMessage( diff --git a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/SectionContentPageLoadingState/StudyPlanSectionCompletedPageLoadingStateView.swift b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/SectionContentPageLoadingState/StudyPlanSectionCompletedPageLoadingStateView.swift new file mode 100644 index 000000000..ffba1cba7 --- /dev/null +++ b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/SectionContentPageLoadingState/StudyPlanSectionCompletedPageLoadingStateView.swift @@ -0,0 +1,78 @@ +import shared +import SwiftUI + +extension StudyPlanSectionCompletedPageLoadingStateView { + struct Appearance { + let skeletonHeight: CGFloat + + let spacing = LayoutInsets.smallInset + let iconWidthHeight: CGFloat = 24 + + let buttonStyle = OutlineButtonStyle( + borderColor: .border, + alignment: .leading, + paddingEdgeSet: [], + backgroundColor: Color(ColorPalette.surface) + ) + } +} + +struct StudyPlanSectionCompletedPageLoadingStateView: View { + let appearance: Appearance + + let completedPageLoadingState: StudyPlanWidgetViewStateSectionContentPageLoadingState + + let action: () -> Void + + var body: some View { + switch completedPageLoadingState.wrapped ?? .hidden { + case .hidden: + EmptyView() + case .loadMore: + Button( + action: action, + label: { + HStack(alignment: .center, spacing: appearance.spacing) { + Image(.studyPlanSectionExpandCompleted) + .renderingMode(.template) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(widthHeight: appearance.iconWidthHeight) + + Text(Strings.StudyPlan.expandCompletedButton) + } + .padding() + } + ) + .buttonStyle(appearance.buttonStyle) + case .loading: + SkeletonRoundedView() + .frame(height: appearance.skeletonHeight) + } + } +} + +#if DEBUG +#Preview { + VStack { + let appearance = StudyPlanSectionCompletedPageLoadingStateView.Appearance(skeletonHeight: 52) + + StudyPlanSectionCompletedPageLoadingStateView( + appearance: appearance, + completedPageLoadingState: .hidden, + action: {} + ) + StudyPlanSectionCompletedPageLoadingStateView( + appearance: appearance, + completedPageLoadingState: .loadMore, + action: {} + ) + StudyPlanSectionCompletedPageLoadingStateView( + appearance: appearance, + completedPageLoadingState: .loading, + action: {} + ) + } + .padding() +} +#endif diff --git a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/SectionContentPageLoadingState/StudyPlanSectionNextPageLoadingStateView.swift b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/SectionContentPageLoadingState/StudyPlanSectionNextPageLoadingStateView.swift new file mode 100644 index 000000000..b739386b3 --- /dev/null +++ b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/SectionContentPageLoadingState/StudyPlanSectionNextPageLoadingStateView.swift @@ -0,0 +1,45 @@ +import shared +import SwiftUI + +extension StudyPlanSectionNextPageLoadingStateView { + struct Appearance { + let skeletonHeight: CGFloat + } +} + +struct StudyPlanSectionNextPageLoadingStateView: View { + let appearance: Appearance + + let nextPageLoadingState: StudyPlanWidgetViewStateSectionContentPageLoadingState + + let action: () -> Void + + var body: some View { + switch nextPageLoadingState.wrapped ?? .hidden { + case .hidden: + EmptyView() + case .loadMore: + Button( + Strings.StudyPlan.loadMoreButton, + action: action + ) + .frame(maxWidth: .infinity, alignment: .center) + .padding(.vertical) + case .loading: + SkeletonRoundedView() + .frame(height: appearance.skeletonHeight) + } + } +} + +#if DEBUG +#Preview { + VStack { + let appearance = StudyPlanSectionNextPageLoadingStateView.Appearance(skeletonHeight: 52) + + StudyPlanSectionNextPageLoadingStateView(appearance: appearance, nextPageLoadingState: .hidden, action: {}) + StudyPlanSectionNextPageLoadingStateView(appearance: appearance, nextPageLoadingState: .loadMore, action: {}) + StudyPlanSectionNextPageLoadingStateView(appearance: appearance, nextPageLoadingState: .loading, action: {}) + } +} +#endif diff --git a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/StudyPlanSectionView.swift b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/StudyPlanSectionView.swift index e4f59580e..d6c9d75dd 100644 --- a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/StudyPlanSectionView.swift +++ b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/Section/StudyPlanSectionView.swift @@ -16,6 +16,7 @@ struct StudyPlanSectionView: View { let onActivityTap: (Int64, Int64) -> Void let onRetryActivitiesLoadingTap: (Int64) -> Void let onLoadMoreActivitiesTap: (Int64) -> Void + let onExpandCompletedActivitiesTap: (Int64) -> Void var body: some View { VStack(alignment: .leading, spacing: LayoutInsets.smallInset) { @@ -35,6 +36,12 @@ struct StudyPlanSectionView: View { SkeletonRoundedView() .frame(height: appearance.skeletonHeight) case .content(let content): + StudyPlanSectionCompletedPageLoadingStateView( + appearance: .init(skeletonHeight: appearance.skeletonHeight), + completedPageLoadingState: content.completedPageLoadingState, + action: { onExpandCompletedActivitiesTap(section.id) } + ) + StudyPlanSectionActivitiesList( sectionItems: content.sectionItems, onTap: { activityID in @@ -42,18 +49,11 @@ struct StudyPlanSectionView: View { } ) - if content.isLoadAllTopicsButtonShown { - Button( - Strings.StudyPlan.loadMoreButton, - action: { onLoadMoreActivitiesTap(section.id) } - ) - .frame(maxWidth: .infinity, alignment: .center) - .padding(.vertical) - } - if content.isNextPageLoadingShowed { - SkeletonRoundedView() - .frame(height: appearance.skeletonHeight) - } + StudyPlanSectionNextPageLoadingStateView( + appearance: .init(skeletonHeight: appearance.skeletonHeight), + nextPageLoadingState: content.nextPageLoadingState, + action: { onLoadMoreActivitiesTap(section.id) } + ) } } } @@ -66,7 +66,8 @@ struct StudyPlanSectionView: View { onSectionTap: { _ in }, onActivityTap: { _, _ in }, onRetryActivitiesLoadingTap: { _ in }, - onLoadMoreActivitiesTap: { _ in } + onLoadMoreActivitiesTap: { _ in }, + onExpandCompletedActivitiesTap: { _ in } ) .padding() .background(Color(ColorPalette.background)) @@ -80,7 +81,8 @@ struct StudyPlanSectionView: View { onSectionTap: { _ in }, onActivityTap: { _, _ in }, onRetryActivitiesLoadingTap: { _ in }, - onLoadMoreActivitiesTap: { _ in } + onLoadMoreActivitiesTap: { _ in }, + onExpandCompletedActivitiesTap: { _ in } ) .padding() .background(Color(ColorPalette.background)) @@ -94,7 +96,8 @@ struct StudyPlanSectionView: View { onSectionTap: { _ in }, onActivityTap: { _, _ in }, onRetryActivitiesLoadingTap: { _ in }, - onLoadMoreActivitiesTap: { _ in } + onLoadMoreActivitiesTap: { _ in }, + onExpandCompletedActivitiesTap: { _ in } ) .padding() .background(Color(ColorPalette.background)) @@ -102,7 +105,8 @@ struct StudyPlanSectionView: View { extension StudyPlanWidgetViewStateSection { static func makePlaceholder( - nextPageLoadingState: StudyPlanWidgetViewStateSectionContentPageLoadingState = StudyPlanWidgetViewStateSectionContentPageLoadingState.hidden, + nextPageLoadingState: StudyPlanWidgetViewStateSectionContentPageLoadingState = + StudyPlanWidgetViewStateSectionContentPageLoadingState.hidden, completedPageLoadingState: StudyPlanWidgetViewStateSectionContentPageLoadingState = StudyPlanWidgetViewStateSectionContentPageLoadingState.hidden ) -> StudyPlanWidgetViewStateSection { diff --git a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/StudyPlanView.swift b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/StudyPlanView.swift index 2ea25a8b1..448525e9f 100644 --- a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/StudyPlanView.swift +++ b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StudyPlan/Views/StudyPlanView.swift @@ -110,7 +110,8 @@ struct StudyPlanView: View { onSectionTap: viewModel.doSectionToggle(sectionId:), onActivityTap: viewModel.doActivityPresentation(activityID:sectionID:), onRetryActivitiesLoadingTap: viewModel.doRetryActivitiesLoading(sectionId:), - onLoadMoreActivitiesTap: viewModel.doLoadMoreActivities(sectionID:) + onLoadMoreActivitiesTap: viewModel.doLoadMoreActivities(sectionID:), + onExpandCompletedActivitiesTap: viewModel.doExpandCompletedActivities(sectionID:) ) } } @@ -236,13 +237,3 @@ private extension StudyPlanView { } } } - -// MARK: - StudyPlanView_Previews: PreviewProvider - - -struct StudyPlanView_Previews: PreviewProvider { - static var previews: some View { - UIKitViewControllerPreview { - StudyPlanAssembly().makeModule() - } - } -} diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/model/StudyPlanWidgetViewState.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/model/StudyPlanWidgetViewState.kt index 00e416742..1ad2db1c0 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/model/StudyPlanWidgetViewState.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/study_plan/widget/view/model/StudyPlanWidgetViewState.kt @@ -36,17 +36,7 @@ sealed interface StudyPlanWidgetViewState { val sectionItems: List, val nextPageLoadingState: SectionContentPageLoadingState, val completedPageLoadingState: SectionContentPageLoadingState - ) : SectionContent { - // TODO: ALTAPPS-1334 remove this property and use nextPageLoadingState directly - @Deprecated("Is used only for iOS compatibility") - val isLoadAllTopicsButtonShown: Boolean - get() = nextPageLoadingState == SectionContentPageLoadingState.LOAD_MORE - - // TODO: ALTAPPS-1334 remove this property and use completedPageLoadingState directly - @Deprecated("Is used only for iOS compatibility") - val isNextPageLoadingShowed: Boolean - get() = nextPageLoadingState == SectionContentPageLoadingState.LOADING - } + ) : SectionContent } enum class SectionContentPageLoadingState { From d4d9269b2fc73f3c42a86c1a8527e6271a88a834 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 2 Sep 2024 05:54:23 +0000 Subject: [PATCH 18/18] Bump build number --- gradle/app.versions.toml | 2 +- .../NotificationServiceExtension/Info.plist | 2 +- .../iosHyperskillApp.xcodeproj/project.pbxproj | 16 ++++++++-------- iosHyperskillApp/iosHyperskillApp/Info.plist | 2 +- .../iosHyperskillAppTests/Info.plist | 2 +- .../iosHyperskillAppUITests/Info.plist | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/gradle/app.versions.toml b/gradle/app.versions.toml index bb3de75e4..83757f011 100644 --- a/gradle/app.versions.toml +++ b/gradle/app.versions.toml @@ -3,4 +3,4 @@ minSdk = '24' targetSdk = '34' compileSdk = '34' versionName = '1.70' -versionCode = '540' \ No newline at end of file +versionCode = '541' \ No newline at end of file diff --git a/iosHyperskillApp/NotificationServiceExtension/Info.plist b/iosHyperskillApp/NotificationServiceExtension/Info.plist index f53f2b421..5a6418042 100644 --- a/iosHyperskillApp/NotificationServiceExtension/Info.plist +++ b/iosHyperskillApp/NotificationServiceExtension/Info.plist @@ -9,7 +9,7 @@ CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleVersion - 569 + 570 CFBundleShortVersionString 1.70 CFBundlePackageType diff --git a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj index a2ae5383d..79c449b63 100644 --- a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj +++ b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj @@ -5693,7 +5693,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 569; + CURRENT_PROJECT_VERSION = 570; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; @@ -5714,7 +5714,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 569; + CURRENT_PROJECT_VERSION = 570; DEVELOPMENT_TEAM = UJ4KC2QN7B; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = iosHyperskillAppUITests/Info.plist; @@ -5735,7 +5735,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 569; + CURRENT_PROJECT_VERSION = 570; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; @@ -5756,7 +5756,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 569; + CURRENT_PROJECT_VERSION = 570; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5777,7 +5777,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 569; + CURRENT_PROJECT_VERSION = 570; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; @@ -5806,7 +5806,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 569; + CURRENT_PROJECT_VERSION = 570; DEVELOPMENT_TEAM = UJ4KC2QN7B; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -5952,7 +5952,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 569; + CURRENT_PROJECT_VERSION = 570; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; @@ -5988,7 +5988,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 569; + CURRENT_PROJECT_VERSION = 570; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = UJ4KC2QN7B; ENABLE_PREVIEWS = YES; diff --git a/iosHyperskillApp/iosHyperskillApp/Info.plist b/iosHyperskillApp/iosHyperskillApp/Info.plist index 157f86b7f..827ca6db1 100644 --- a/iosHyperskillApp/iosHyperskillApp/Info.plist +++ b/iosHyperskillApp/iosHyperskillApp/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 569 + 570 FirebaseAppDelegateProxyEnabled FirebaseMessagingAutoInitEnabled diff --git a/iosHyperskillApp/iosHyperskillAppTests/Info.plist b/iosHyperskillApp/iosHyperskillAppTests/Info.plist index fefcfcf37..202cd0442 100644 --- a/iosHyperskillApp/iosHyperskillAppTests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppTests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 569 + 570 diff --git a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist index bd4be5046..82b15ddbb 100644 --- a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist @@ -15,6 +15,6 @@ CFBundleShortVersionString 1.70 CFBundleVersion - 569 + 570