Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
b73c0c3
fix: resolve issue 581
Demian-Yushyn Jul 30, 2025
cc0e655
fix: update adjustments
Demian-Yushyn Jul 30, 2025
84a2156
fix: add timeout constant
Demian-Yushyn Jul 30, 2025
cd349b3
fix: adjust naming
Demian-Yushyn Jul 30, 2025
5aa8e55
fix: update connectivity
Demian-Yushyn Jul 30, 2025
64c59a1
fix: remove unused functions and hardcoded variables
Demian-Yushyn Jul 31, 2025
b4fcf31
fix: Removed unused functions and hardcoded variables
Demian-Yushyn Jul 31, 2025
253341d
Merge remote-tracking branch 'origin/fix/issue-581' into fix/issue-581
Demian-Yushyn Jul 31, 2025
6121c2a
fix: issue 581
Demian-Yushyn Jul 31, 2025
b26c3d7
fix: progress on issue 581
Demian-Yushyn Jul 31, 2025
f21a460
fix: apply updates
Demian-Yushyn Jul 31, 2025
14441a6
fix: update wi DI Config, thread fixes
Demian-Yushyn Jul 31, 2025
327bb30
fix: connectivity logic update
Demian-Yushyn Jul 31, 2025
a9074d0
fix: cacheValidity change
Demian-Yushyn Jul 31, 2025
dabc744
fix: unit tests update
Demian-Yushyn Aug 6, 2025
48d55bc
fix: removed team and coma
Demian-Yushyn Aug 6, 2025
786fa30
fix: removed extra comas
Demian-Yushyn Aug 6, 2025
a7475cd
Merge branch 'develop' into fix/issue-581
IvanStepanok Sep 16, 2025
0fce30b
fix: unit tests
Demian-Yushyn Sep 16, 2025
635c716
Merge branch 'develop' into fix/issue-581
Demian-Yushyn Oct 29, 2025
ad3f260
fix: removed state object warning
Demian-Yushyn Oct 29, 2025
39ace76
fix: update config
Demian-Yushyn Oct 29, 2025
8465734
Merge pull request #2 from raccoongang/fix/issue-581
IvanStepanok Nov 10, 2025
7196747
Merge branch 'openedx:develop' into develop
Demian-Yushyn Jan 28, 2026
b852394
Merge branch 'openedx:develop' into develop
Demian-Yushyn Feb 4, 2026
4f1992b
feat: add theme and fix connectivity
IvanStepanok Mar 16, 2026
d702e00
Merge branch 'openedx:develop' into develop
Demian-Yushyn Mar 19, 2026
37d04f8
feat: sandbox app setup
IvanStepanok Apr 27, 2026
0e0a008
feat: update swiftlint config
IvanStepanok Apr 27, 2026
00472da
feat: update design
IvanStepanok Apr 27, 2026
692665e
feat: update design
IvanStepanok May 5, 2026
50e1bcb
fix: empty fields is nil
IvanStepanok Jul 6, 2026
071b981
Merge branch 'openedx:develop' into develop
IvanStepanok Jul 8, 2026
d02f2d2
feat: update app version
IvanStepanok Jul 31, 2026
a17fd9b
Merge branch 'develop' into feat/sandbox-setup
IvanStepanok Jul 31, 2026
624f12e
Merge branch 'develop' into feat/sandbox-setup
IvanStepanok Jul 31, 2026
d9bad74
Merge branch 'feat/sandbox-setup' of https://github.com/openedx/opene…
IvanStepanok Jul 31, 2026
f8cf1e4
Merge branch 'develop' into feat/sandbox-setup
IvanStepanok Jul 31, 2026
0200d60
Merge branch 'feat/sandbox-setup' of https://github.com/openedx/opene…
IvanStepanok Jul 31, 2026
83428ae
feat: update deployment target to iOS 17
IvanStepanok Jul 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ opt_in_rules: # some rules are only opt-in
excluded: # paths to ignore during linting. Takes precedence over `included`.
- Carthage
- DerivedData
- build
- Pods
- DerivedData
- Core/CoreTests
Expand Down
4 changes: 4 additions & 0 deletions Authorization/Authorization.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
DE843D6BB1B9DDA398494890 /* Pods_App_Authorization.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 47BCFB7C19382EECF15131B6 /* Pods_App_Authorization.framework */; };
E03261642AE64676002CA7EB /* StartupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E03261632AE64676002CA7EB /* StartupViewModel.swift */; };
E03261662AE64AF4002CA7EB /* StartupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E03261652AE64AF4002CA7EB /* StartupView.swift */; };
E03261682AE64AF4002CA7EB /* SandboxSplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E03261672AE64AF4002CA7EB /* SandboxSplashView.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -119,6 +120,7 @@
CEB25A002CC13A36007FC792 /* SocialAuthResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialAuthResponse.swift; sourceTree = "<group>"; };
E03261632AE64676002CA7EB /* StartupViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupViewModel.swift; sourceTree = "<group>"; };
E03261652AE64AF4002CA7EB /* StartupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupView.swift; sourceTree = "<group>"; };
E03261672AE64AF4002CA7EB /* SandboxSplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SandboxSplashView.swift; sourceTree = "<group>"; };
E78971D8E6ED2116BBF9FD66 /* Pods-App-Authorization.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Authorization.release.xcconfig"; path = "Target Support Files/Pods-App-Authorization/Pods-App-Authorization.release.xcconfig"; sourceTree = "<group>"; };
F52826C68AEA1CF4769389EA /* Pods-App-Authorization.releasestage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Authorization.releasestage.xcconfig"; path = "Target Support Files/Pods-App-Authorization/Pods-App-Authorization.releasestage.xcconfig"; sourceTree = "<group>"; };
F5802BBA113276950ABCD9B3 /* Pods-App-Authorization.releaseprod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Authorization.releaseprod.xcconfig"; path = "Target Support Files/Pods-App-Authorization/Pods-App-Authorization.releaseprod.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -364,6 +366,7 @@
children = (
E03261632AE64676002CA7EB /* StartupViewModel.swift */,
E03261652AE64AF4002CA7EB /* StartupView.swift */,
E03261672AE64AF4002CA7EB /* SandboxSplashView.swift */,
);
path = Startup;
sourceTree = "<group>";
Expand Down Expand Up @@ -580,6 +583,7 @@
02F3BFE5292533720051930C /* AuthorizationRouter.swift in Sources */,
99C1654F2C0C4F5900DC384D /* SSOWebView.swift in Sources */,
E03261662AE64AF4002CA7EB /* StartupView.swift in Sources */,
E03261682AE64AF4002CA7EB /* SandboxSplashView.swift in Sources */,
071009C728D1DA4F00344290 /* SignInViewModel.swift in Sources */,
BA8B3A322AD5487300D25EF5 /* SocialAuthView.swift in Sources */,
99C1654D2C0C4F2F00DC384D /* SSOHelper.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public struct SignInView: View {
ThemeAssets.appLogo.swiftUIImage
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 189, maxHeight: 89)
.frame(maxWidth: 263, maxHeight: 76)
.padding(.top, isHorizontal ? 20 : 40)
.padding(.bottom, isHorizontal ? 10 : 40)
.accessibilityIdentifier("logo_image")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,11 @@ import MSAL

private func configureFields() -> [String: String] {
var validateFields: [String: String] = [:]
fields.forEach { validateFields[$0.field.name] = $0.text }
fields.forEach { field in
if field.field.required || !field.text.isEmpty {
validateFields[field.field.name] = field.text
}
}
validateFields["honor_code"] = "true"
validateFields["terms_of_service"] = "true"
if let externalToken = externalToken, let backend = backend {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// SandboxSplashView.swift
// Authorization
//

import SwiftUI
import Core
import Theme

public struct SandboxSplashView: View {

@Environment(\.isHorizontal) private var isHorizontal

private let router: AuthorizationRouter

public init(router: AuthorizationRouter) {
self.router = router
}

public var body: some View {
ZStack(alignment: .top) {
VStack {
ThemeAssets.headerBackground.swiftUIImage
.resizable()
.edgesIgnoringSafeArea(.top)
.accessibilityIdentifier("sandbox_splash_bg_image")
}
.frame(maxWidth: .infinity, maxHeight: 200)

VStack(alignment: .center) {
ThemeAssets.appLogo.swiftUIImage
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 263, maxHeight: 76)
.padding(.top, isHorizontal ? 20 : 40)
.padding(.bottom, isHorizontal ? 10 : 40)
.accessibilityIdentifier("sandbox_splash_logo")

GeometryReader { proxy in
ScrollView {
VStack(alignment: .center, spacing: 16) {
Text("Welcome to the Open edX\u{00AE} Mobile App")
.font(Theme.Fonts.displaySmall)
.foregroundColor(Theme.Colors.textPrimary)
.multilineTextAlignment(.center)
.padding(.top, 8)
.accessibilityIdentifier("sandbox_splash_title")

Text(
"This app was built for you to explore the Open edX\u{00AE} Mobile App. "
+ "Courses are for demonstration purposes only."
)
.font(Theme.Fonts.titleSmall)
.foregroundColor(Theme.Colors.textPrimary)
.multilineTextAlignment(.center)
.accessibilityIdentifier("sandbox_splash_subtitle")

StyledButton(
"Start Exploring",
action: {
router.showLoginScreen(sourceScreen: .default)
},
iconImage: CoreAssets.arrowRight16.swiftUIImage,
iconPosition: .right
)
.frame(maxWidth: .infinity)
.padding(.top, 24)
.accessibilityIdentifier("sandbox_splash_start_button")

Spacer()
}
.padding(.horizontal, 24)
.padding(.top, 50)
.frameLimit(width: proxy.size.width)
}
.roundedBackground(Theme.Colors.loginBackground)
}
}
}
.navigationBarHidden(true)
.ignoresSafeArea(.all, edges: .horizontal)
.background(Theme.Colors.background.ignoresSafeArea(.all))
}
}

#if DEBUG
struct SandboxSplashView_Previews: PreviewProvider {
static var previews: some View {
SandboxSplashView(router: AuthorizationRouterMock())
.preferredColorScheme(.light)
.previewDisplayName("SandboxSplashView Light")
.loadFonts()

SandboxSplashView(router: AuthorizationRouterMock())
.preferredColorScheme(.dark)
.previewDisplayName("SandboxSplashView Dark")
.loadFonts()
}
}
#endif
2 changes: 2 additions & 0 deletions Core/Core/Configuration/Connectivity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public class Connectivity: ConnectivityProtocol {
// MARK: - Observable property (new way)
public private(set) var internetState: InternetState? {
didSet {
// Don't re-emit an unchanged state — repeated .notReachable sends re-trigger the offline snackbar
guard oldValue != internetState else { return }
// Keep backward compatibility - update Combine subject
internetReachableSubject.send(internetState)
}
Expand Down
155 changes: 155 additions & 0 deletions Open edX Sandbox.ssgen/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
{
"appInfo" : {
"description" : "Open edx mobile application in a test environment.",
"name" : "Open edX Sandbox"
},
"backgroundGroups" : [
{
"createdAt" : "2026-03-16T13:51:48Z",
"id" : "9E42C8F6-B4A0-4BD3-9A55-EBED3B1F5D4D",
"panoramicImageFilename" : "panoramic_9E42C8F6-B4A0-4BD3-9A55-EBED3B1F5D4D.png",
"prompt" : "Generate a BACKGROUND image for App Store marketing.\n\nUSE EXACTLY THESE COLORS (mandatory):\n- #3E5EC2\n- #082B4D\n- #CC3165\n\nThe background MUST use these exact colors or darker\/more saturated versions of them.\nCreate smooth gradients and large soft shapes using ONLY these colors.\n\nDESIGN REQUIREMENTS:\n- Minimalist flat design\n- Large soft rounded geometric shapes\n- Smooth color transitions between the 3 colors\n- Premium, professional, advertising-ready aesthetic\n\nSTRICTLY FORBIDDEN:\n- ANY white, off-white, cream, or light colors\n- ANY shadows or drop shadows\n- ANY lighting effects or glows\n- Colors NOT in the palette above\n- Text, icons, UI elements, people, objects, devices\n\nComposition: Asymmetric balanced layout with 2-3 large rounded forms on dark background from palette\n\nWhite text must be easily readable on the background.",
"style" : "abstract"
},
{
"createdAt" : "2026-03-16T13:51:42Z",
"id" : "6FB20265-02CB-4061-ADC3-CE55D3FEECA1",
"panoramicImageFilename" : "panoramic_6FB20265-02CB-4061-ADC3-CE55D3FEECA1.png",
"prompt" : "Generate a BACKGROUND image for App Store marketing.\n\nUSE EXACTLY THESE COLORS (mandatory):\n- #3E5EC2\n- #082B4D\n- #CC3165\n\nThe background MUST use these exact colors or darker\/more saturated versions of them.\nCreate smooth gradients and large soft shapes using ONLY these colors.\n\nDESIGN REQUIREMENTS:\n- Minimalist flat design\n- Large soft rounded geometric shapes\n- Smooth color transitions between the 3 colors\n- Premium, professional, advertising-ready aesthetic\n\nSTRICTLY FORBIDDEN:\n- ANY white, off-white, cream, or light colors\n- ANY shadows or drop shadows\n- ANY lighting effects or glows\n- Colors NOT in the palette above\n- Text, icons, UI elements, people, objects, devices\n\nComposition: Centered radial composition with soft expanding circles\n\nWhite text must be easily readable on the background.",
"style" : "abstract"
},
{
"createdAt" : "2026-03-16T13:51:34Z",
"id" : "FC40496F-BDC1-470F-A9C1-17484E613DA4",
"panoramicImageFilename" : "panoramic_FC40496F-BDC1-470F-A9C1-17484E613DA4.png",
"prompt" : "Generate a BACKGROUND image for App Store marketing.\n\nUSE EXACTLY THESE COLORS (mandatory):\n- #3E5EC2\n- #082B4D\n- #CC3165\n\nThe background MUST use these exact colors or darker\/more saturated versions of them.\nCreate smooth gradients and large soft shapes using ONLY these colors.\n\nDESIGN REQUIREMENTS:\n- Minimalist flat design\n- Large soft rounded geometric shapes\n- Smooth color transitions between the 3 colors\n- Premium, professional, advertising-ready aesthetic\n\nSTRICTLY FORBIDDEN:\n- ANY white, off-white, cream, or light colors\n- ANY shadows or drop shadows\n- ANY lighting effects or glows\n- Colors NOT in the palette above\n- Text, icons, UI elements, people, objects, devices\n\nComposition: Large diagonal soft shapes flowing from bottom-left to top-right\n\nWhite text must be easily readable on the background.",
"style" : "abstract"
}
],
"colorPalette" : {
"accentColor" : "#CC3266",
"allColors" : [
"#19212F",
"#E6E7E8",
"#092B4E",
"#97A5BB",
"#C2C3BD",
"#18191A",
"#3250A8",
"#C3C8C4"
],
"backgroundColor" : "#E6E7E8",
"primaryColor" : "#19212F",
"secondaryColor" : "#092B4E",
"suggestedGradients" : [
[
"#19212F",
"#41567B"
],
[
"#19212F",
"#092B4E"
],
[
"#41567B",
"#000000"
],
[
"#CC3266",
"#FF3E7F"
]
]
},
"customGradients" : [

],
"designVariants" : [
{
"createdAt" : "2026-03-16T13:43:34Z",
"globalBackgroundColors" : [

],
"globalBackgroundStyle" : "ai_generated:panoramic",
"globalDeviceFrame" : "iphone-deep-blue",
"globalIPadDeviceFrame" : "ipad-stroke-black",
"id" : "EF313D21-6805-4B4D-AA0C-6BD2BE742208",
"name" : "Variant A",
"screenshots" : [
{
"backgroundColors" : [

],
"backgroundStyle" : "ai_generated:panoramic",
"deviceFrame" : "iphone-deep-blue",
"id" : "6B4BA7EF-1CBE-4D17-B35D-8D36622DFD26",
"iphoneImageFilename" : "6B4BA7EF-1CBE-4D17-B35D-8D36622DFD26_iphone.png",
"layout" : "device_top",
"overlayElements" : [

],
"subtitle" : "Track progress and continue your learning journey.",
"title" : "Your Personalized Learning Hub"
},
{
"backgroundColors" : [

],
"backgroundStyle" : "ai_generated:panoramic",
"deviceFrame" : "iphone-deep-blue",
"id" : "38C0D365-07CE-4A4F-941D-F0D0EA7F0ED6",
"iphoneImageFilename" : "38C0D365-07CE-4A4F-941D-F0D0EA7F0ED6_iphone.png",
"layout" : "device_bottom",
"overlayElements" : [

],
"subtitle" : "Understand content, objectives, and enroll instantly.",
"title" : "Dive Into Course Details"
},
{
"backgroundColors" : [

],
"backgroundStyle" : "ai_generated:panoramic",
"deviceFrame" : "iphone-deep-blue",
"id" : "F9FCE3B6-BE0C-4828-AC79-60BD86B1AE4D",
"iphoneImageFilename" : "F9FCE3B6-BE0C-4828-AC79-60BD86B1AE4D_iphone.png",
"layout" : "device_top",
"overlayElements" : [

],
"subtitle" : "Search and explore courses effortlessly.",
"title" : "Discover New Courses"
},
{
"backgroundColors" : [

],
"backgroundStyle" : "ai_generated:panoramic",
"deviceFrame" : "iphone-deep-blue",
"id" : "53B18FC8-BEC1-4A9B-B28B-9515C840502C",
"iphoneImageFilename" : "53B18FC8-BEC1-4A9B-B28B-9515C840502C_iphone.png",
"layout" : "device_bottom",
"overlayElements" : [

],
"subtitle" : "Navigate lessons, track progress, and learn efficiently.",
"title" : "Master Your Coursework"
}
],
"selectedBackgroundGroupId" : "9E42C8F6-B4A0-4BD3-9A55-EBED3B1F5D4D"
}
],
"deviceType" : "iPhone",
"isPrimaryColorAutoDetected" : true,
"localization" : {
"selectedLanguageIds" : [

],
"sourceLanguageId" : "en-US"
},
"primaryColorHex" : "#18202E",
"selectedGroupId" : "9E42C8F6-B4A0-4BD3-9A55-EBED3B1F5D4D",
"selectedScreenshotIndex" : 0,
"selectedVariantIndex" : 0,
"version" : "1.0"
}
Loading
Loading