Clarify Apple verification with a focused code entry card
This commit is contained in:
1 parent
fa8e1d471b
commit
bf77e3967e
4 files changed
+82
-14
No files matched your search
@@ -12,6 +12,16 @@ enum EasyLanguage: String, CaseIterable {
|
||||
|
||||
struct EasyStrings {
|
||||
static let english: [String:String] = [
|
||||
"Verifica tu cuenta":"Verify your account",
|
||||
"Introduce los 6 dígitos que Apple muestra en tu dispositivo de confianza o envía por SMS.":"Enter the 6 digits Apple shows on your trusted device or sends by text message.",
|
||||
"Puedes pegar el código completo.":"You can paste the entire code.",
|
||||
"Verificar y continuar":"Verify and continue",
|
||||
"¿No recibiste el código?":"Did not receive a code?",
|
||||
"Elige dónde quieres recibir uno nuevo.":"Choose where to receive a new one.",
|
||||
"Mostrar en mis dispositivos Apple":"Show on my Apple devices",
|
||||
"Enviar SMS al número terminado en":"Text the number ending in",
|
||||
"Solicitar otro código":"Request another code",
|
||||
"El código se envía a Apple y no se guarda.":"The code is sent to Apple and is not saved.",
|
||||
"Prepara la IPA y te guiamos para instalarla.":"Prepare the IPA, then follow the guided installation.",
|
||||
"Una app completa. Preparar y jugar en este Mac.":"One complete app. Build and play on this Mac.",
|
||||
"Conviene recompilar":"Rebuild recommended",
|
||||
|
||||
@@ -458,7 +458,6 @@ struct SideloadView:View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var installStep=0
|
||||
@State private var password=""
|
||||
@State private var code=""
|
||||
@State private var remember=true
|
||||
@State private var autoRefresh=true
|
||||
@State private var loginItem=SMAppService.mainApp.status == .enabled
|
||||
@@ -487,7 +486,7 @@ struct SideloadView:View {
|
||||
}
|
||||
if accountOnly || installStep==1 {
|
||||
Text(text("Una cuenta para todos tus dispositivos en este Mac. Sólo se necesita para firmar, instalar y renovar apps.")).foregroundStyle(.secondary)
|
||||
if !model.authenticated {
|
||||
if !model.authenticated && !model.codeRequired {
|
||||
LabeledContent(text("Cuenta Apple personal")){TextField("name@example.com",text:$model.email).textFieldStyle(.roundedBorder).disabled(model.busy)}
|
||||
LabeledContent(text("Contraseña de Apple")){SecureField(text("Contraseña de Apple"),text:$password).textFieldStyle(.roundedBorder).disabled(model.busy)}
|
||||
Toggle(text("Recordar sesión en el Llavero; no guardar la contraseña"),isOn:$remember).disabled(model.busy).onChange(of:remember){_,value in if !value {autoRefresh=false}}
|
||||
@@ -497,7 +496,7 @@ struct SideloadView:View {
|
||||
}
|
||||
if model.hasRememberedSession {Button(text("Usar sesión del Llavero")){model.restoreSession()}.disabled(model.busy)}
|
||||
Button(text("Iniciar sesión")){model.signIn(password:password,remember:remember);password=""}.buttonStyle(.borderedProminent).disabled(model.busy || model.email.isEmpty || password.isEmpty)
|
||||
} else {
|
||||
} else if model.authenticated {
|
||||
Label(text("Sesión iniciada"),systemImage:"person.crop.circle.badge.checkmark")
|
||||
Text(model.email).font(.caption).textSelection(.enabled)
|
||||
Picker(text("Tu equipo personal"),selection:$model.team) {Text(text("Selecciona tu equipo")).tag("");ForEach(model.teams){Text($0.name+" · "+$0.id).tag($0.id)}}.disabled(model.busy)
|
||||
@@ -510,13 +509,8 @@ struct SideloadView:View {
|
||||
Text(text("Apple no informa los cupos restantes en esta consulta. Mostramos los dispositivos registrados, sin inventar disponibilidad ni eliminar registros.")).font(.caption).foregroundStyle(.secondary)
|
||||
Button(text("Cerrar sesión")){model.signOut()}.disabled(model.busy)
|
||||
}
|
||||
if model.codeRequired {
|
||||
TextField(text("Código de verificación"),text:$code)
|
||||
HStack {Button(text("Confirmar código")){model.submitCode(code);code=""};Button(text("Reenviar código")){model.resend()}}
|
||||
Button(text("Enviar a mis dispositivos")){model.sendToDevices()}
|
||||
ForEach(model.smsNumbers) {number in Button("SMS · …"+number.digits){model.sendSMS(number.id)}}
|
||||
}
|
||||
if !accountOnly {Button(text("Continuar")){installStep=2}.buttonStyle(.borderedProminent).disabled(model.busy || !model.authenticated || model.team.isEmpty)}
|
||||
if model.codeRequired {AppleVerificationView(model:model,text:text)}
|
||||
if !accountOnly && !model.codeRequired {Button(text("Continuar")){installStep=2}.buttonStyle(.borderedProminent).disabled(model.busy || !model.authenticated || model.team.isEmpty)}
|
||||
}
|
||||
if !accountOnly && installStep==2 {
|
||||
Label(text("Revisa antes de instalar"),systemImage:"checklist").font(.title2.bold())
|
||||
@@ -537,8 +531,8 @@ struct SideloadView:View {
|
||||
}
|
||||
}
|
||||
if !accountOnly && installStep==3 && !model.busy {Button(text("Revisar e intentar de nuevo")){installStep=2}}
|
||||
if !accountOnly {Text(text("Guarda y cierra el juego antes de instalar. Firma, copia y renovación nativas aún requieren validación completa con una cuenta y dispositivo reales.")).font(.caption).foregroundStyle(.secondary)}
|
||||
InstallActivityView(model:model,text:text)
|
||||
if !accountOnly && !model.codeRequired {Text(text("Guarda y cierra el juego antes de instalar. Firma, copia y renovación nativas aún requieren validación completa con una cuenta y dispositivo reales.")).font(.caption).foregroundStyle(.secondary)}
|
||||
if !model.codeRequired {InstallActivityView(model:model,text:text)}
|
||||
if !model.busy {Text(text(model.phase)).font(.callout)}
|
||||
}.padding(24)
|
||||
}
|
||||
@@ -548,6 +542,58 @@ struct SideloadView:View {
|
||||
}
|
||||
|
||||
|
||||
struct AppleVerificationView:View {
|
||||
@ObservedObject var model:SideloadModel
|
||||
let text:(String)->String
|
||||
@State private var code=""
|
||||
@FocusState private var focused:Bool
|
||||
private var valid:Bool {code.count==6 && code.utf8.allSatisfy {48...57 ~= $0}}
|
||||
private func confirm() {guard valid else {return};let submitted=code;code="";model.submitCode(submitted)}
|
||||
var body:some View {
|
||||
VStack(alignment:.leading,spacing:22) {
|
||||
HStack(alignment:.top,spacing:14) {
|
||||
Image(systemName:"lock.shield").font(.system(size:28,weight:.light)).foregroundStyle(Color(red:0.79,green:0.95,blue:0.37)).frame(width:48,height:48).background(.white.opacity(0.04),in:RoundedRectangle(cornerRadius:12))
|
||||
VStack(alignment:.leading,spacing:6) {
|
||||
Text(text("Verifica tu cuenta")).font(.title2.bold())
|
||||
if !model.email.isEmpty {Text(model.email).font(.caption).foregroundStyle(.secondary)}
|
||||
Text(text("Introduce los 6 dígitos que Apple muestra en tu dispositivo de confianza o envía por SMS.")).font(.callout).foregroundStyle(.secondary).fixedSize(horizontal:false,vertical:true)
|
||||
}
|
||||
}
|
||||
VStack(alignment:.leading,spacing:10) {
|
||||
Text(text("Código de verificación")).font(.callout.weight(.semibold))
|
||||
TextField("······",text:$code).textFieldStyle(.plain).font(.system(size:30,weight:.medium,design:.monospaced)).tracking(7)
|
||||
.padding(.horizontal,18).padding(.vertical,15).frame(width:250)
|
||||
.background(.black.opacity(0.18),in:RoundedRectangle(cornerRadius:11))
|
||||
.overlay(RoundedRectangle(cornerRadius:11).strokeBorder(focused ? Color(red:0.79,green:0.95,blue:0.37).opacity(0.65) : .white.opacity(0.12)))
|
||||
.focused($focused).accessibilityLabel(text("Código de verificación"))
|
||||
.onChange(of:code){_,value in code=String(value.utf8.filter {48...57 ~= $0}.prefix(6).map {Character(UnicodeScalar($0))})}
|
||||
.onSubmit {confirm()}
|
||||
Text(text("Puedes pegar el código completo.")).font(.caption).foregroundStyle(.secondary)
|
||||
}
|
||||
Button(action:confirm){Label(text("Verificar y continuar"),systemImage:"arrow.right").font(.callout.bold()).padding(.horizontal,18).frame(minHeight:44)
|
||||
.foregroundStyle(valid ? Color.black : Color.secondary)
|
||||
.background(valid ? Color(red:0.79,green:0.95,blue:0.37) : .white.opacity(0.06),in:RoundedRectangle(cornerRadius:10))}
|
||||
.buttonStyle(.plain).disabled(!valid)
|
||||
Divider().overlay(.white.opacity(0.06))
|
||||
DisclosureGroup(text("¿No recibiste el código?")) {
|
||||
VStack(alignment:.leading,spacing:12) {
|
||||
Text(text("Elige dónde quieres recibir uno nuevo.")).font(.caption).foregroundStyle(.secondary)
|
||||
Button {code="";model.sendToDevices()} label:{Label(text("Mostrar en mis dispositivos Apple"),systemImage:"ipad.and.iphone")}
|
||||
ForEach(model.smsNumbers) {number in
|
||||
Button {code="";model.sendSMS(number.id)} label:{Label(text("Enviar SMS al número terminado en")+" "+number.digits,systemImage:"message")}
|
||||
}
|
||||
Button(text("Solicitar otro código")){code="";model.resend()}.buttonStyle(.link)
|
||||
}.padding(.top,12)
|
||||
}.font(.callout)
|
||||
Text(text("El código se envía a Apple y no se guarda.")).font(.caption).foregroundStyle(.secondary)
|
||||
}.padding(24).frame(maxWidth:.infinity,alignment:.leading)
|
||||
.background(.white.opacity(0.035),in:RoundedRectangle(cornerRadius:18))
|
||||
.overlay(RoundedRectangle(cornerRadius:18).strokeBorder(.white.opacity(0.06)))
|
||||
.onAppear {focused=true}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct SideloadLibraryView:View {
|
||||
@ObservedObject var model:SideloadModel
|
||||
let text:(String)->String
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.13.0",
|
||||
"build": 20,
|
||||
"version": "1.13.1",
|
||||
"build": 21,
|
||||
"minimumMacOSMajor": 26
|
||||
}
|
||||
@@ -307,3 +307,15 @@ app. Support for the new game version still depends on the engine/profile.
|
||||
A lightweight IPA still needs the complete RomFS on the phone; it makes app
|
||||
signing/reinstallation smaller, not the game data optional. Full-IPA mode embeds
|
||||
those data and retransfers the larger app on renewal.
|
||||
|
||||
## Apple verification presentation (1.13.1)
|
||||
|
||||
An active verification challenge replaces the password form with one card:
|
||||
account context, six-digit ASCII code input, paste/Enter support and one primary
|
||||
Verify and continue action. It remains disabled until six digits are entered.
|
||||
Alternative trusted-device/SMS delivery and resend live under Did not receive a
|
||||
code, rather than competing with verification. The code is cleared on submission
|
||||
or requesting another delivery and is never persisted or logged. No password,
|
||||
provider/session, request transport or delivery behavior changed. Generic busy
|
||||
activity and the installer Continue button do not compete with a pending code.
|
||||
The update does not restart a running launcher or interrupt its in-memory login.
|
||||
Reference in new issue
Block a user