3fda5649c3
Build Docker images with Sentry / Build release image with Sentry (push) Failing after 1m59s
Release / Release (android, ubuntu-latest) (push) Failing after 38m31s
Release / Release (ios, macos-14) (push) Has been cancelled
Integration tests / Run integration tests for mobile apps (push) Failing after 15s
Build dev binaries / Build app (ios, macos-14) (push) Waiting to run
Build dev binaries / Build app (android, ubuntu-latest) (push) Failing after 34m13s
Tier 1+2 - web/UI user-facing:
- Replace all visible "Twake Mail" / "Linagora" with "Workavia Mail"
- Swap 3 logo SVGs (icon kept, wordmark replaced via SVG <text>)
- Splash: replace icon_twp.png with HTML <div>Workavia</div>
- Remove power_by_linagora.svg, replace with AGPL SourceLinkWidget
- Patch web/index.html, manifest.json, login/logout-callback HTMLs
- Patch all 16 ARB locales + scribe ARB + app_localizations.dart
- Patch 4 web/i18n/*.json (FR/EN/DE/VI smart banner titles)
- App grid reduced to "Mail" + "Calendar"
- User-Agent: Twake-Mail -> Workavia-Mail
Tier 3+4 - mobile native:
- Bundle ID: com.linagora.{android,ios}.teammail -> com.workavia.mail
- URL schemes consolidated: teammail.mobile/twakemail.mobile -> workaviamail.mobile
- Android: applicationId, namespace, AndroidManifest, strings.xml
- Android: move Kotlin sources com.linagora.android.tmail -> com.workavia.mail
- Android: empty branding.xml + android12branding.xml (vector text not patchable)
- iOS: Info.plist (CFBundleDisplayName/Name, schemes), Runner.entitlements
- iOS: TwakeMailNSE + TeamMailShareExtension entitlements + Info.plist
- iOS: Runner.xcodeproj (12x PRODUCT_BUNDLE_IDENTIFIER, 9x APP_GROUP_ID)
- iOS: TokenRefreshManager.swift redirect URLs
- iOS: fastlane Appfile + Matchfile
- Dart: oidc_constant, app_config keychain, notification MethodChannel
Known follow-ups (require user action):
- Apple Team ID KUT463DS29 (still Linagora) -> needs Workavia team for App Store
- Android branding.xml emptied: need PNG/path-converted wordmark for splash
- iOS sub-bundle paths (TwakeMailNSE/, TeamMailShareExtension/) on disk unchanged
- iOS App Store URL emptied; Play Store ID points to com.workavia.mail
82 lines
2.7 KiB
Swift
82 lines
2.7 KiB
Swift
import Foundation
|
|
import Alamofire
|
|
|
|
class TokenRefreshManager {
|
|
private let MOBIE_CLIENT_ID = "teammail-mobile"
|
|
private let MOBIE_REDIRECT_URL = "workaviamail.mobile://oauthredirect"
|
|
private let OIDC_SCOPES = ["openid", "profile", "email", "offline_access"]
|
|
private let TWP_MOBIE_REDIRECT_URL = "workaviamail.mobile://redirect"
|
|
|
|
private let GRANT_TYPE = "grant_type"
|
|
private let REFRESH_TOKEN = "refresh_token"
|
|
private let CLIENT_ID = "client_id"
|
|
private let REDIRECT_URI = "redirect_uri"
|
|
private let SCOPES = "scopes"
|
|
|
|
let refreshToken: String
|
|
let tokenEndpoint: String
|
|
let scopes: [String]?
|
|
let isTWP: Bool?
|
|
|
|
init(refreshToken: String, tokenEndpoint: String, scopes: [String]?, isTWP: Bool?) {
|
|
self.refreshToken = refreshToken
|
|
self.tokenEndpoint = tokenEndpoint
|
|
self.scopes = scopes
|
|
self.isTWP = isTWP
|
|
}
|
|
|
|
private func getScopes() -> String {
|
|
if let scopes, !scopes.isEmpty {
|
|
return scopes.joined(separator: " ")
|
|
}
|
|
return OIDC_SCOPES.joined(separator: " ")
|
|
}
|
|
|
|
private func getRedirectUrl() -> String {
|
|
if (isTWP == true) {
|
|
return TWP_MOBIE_REDIRECT_URL
|
|
} else {
|
|
return MOBIE_REDIRECT_URL
|
|
}
|
|
}
|
|
|
|
func handleRefreshAccessToken(
|
|
onSuccess: @escaping (TokenResponse) -> Void,
|
|
onFailure: @escaping (Error) -> Void
|
|
) {
|
|
guard let tokenEndpointUrl = URL(string: tokenEndpoint),
|
|
var request = try? URLRequest(url: tokenEndpointUrl, method: .post) else {
|
|
return onFailure(NetworkExceptions.requestUrlInvalid)
|
|
}
|
|
|
|
let params = [
|
|
CLIENT_ID: MOBIE_CLIENT_ID,
|
|
GRANT_TYPE: REFRESH_TOKEN,
|
|
REDIRECT_URI: getRedirectUrl(),
|
|
REFRESH_TOKEN: refreshToken,
|
|
SCOPES: getScopes(),
|
|
]
|
|
|
|
let data = params
|
|
.map { "\($0)=\($1)" }
|
|
.joined(separator: "&")
|
|
.data(using: .utf8)
|
|
|
|
request.httpBody = data
|
|
|
|
AF.request(request)
|
|
.responseDecodable(of: TokenResponse.self, queue: .global(qos: .default)) { response in
|
|
if (response.response?.statusCode != 200) {
|
|
onFailure(NetworkExceptions(value: "Failed to get token: \(response.error?.localizedDescription ?? "Unknown Error")"))
|
|
} else {
|
|
switch(response.result) {
|
|
case .success(let data):
|
|
onSuccess(data)
|
|
case .failure(let error):
|
|
onFailure(NetworkExceptions(value: "Failed to get token \(error.localizedDescription)"))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|