Skip to content

Commit 0480412

Browse files
committed
add signup with apple
1 parent 51aaee5 commit 0480412

10 files changed

Lines changed: 153 additions & 26 deletions

File tree

android/app/capacitor.build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ android {
99

1010
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
1111
dependencies {
12-
12+
implementation project(':capacitor-community-apple-sign-in')
1313

1414
}
1515

android/capacitor.settings.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
22
include ':capacitor-android'
33
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
4+
5+
include ':capacitor-community-apple-sign-in'
6+
project(':capacitor-community-apple-sign-in').projectDir = new File('../node_modules/@capacitor-community/apple-sign-in/android')

index.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3270,8 +3270,12 @@ <h1>Artists</h1>
32703270
>Login with Discord</a>
32713271
<a
32723272
href="https://synergism.cc/login?with=patreon"
3273-
style="display:inline-block;border: 2px solid #ff5900; height: 25px; width: 20%;"
3273+
style="display:inline-block;border: 2px solid #ff5900; height: 25px; width: 20%; margin-bottom:5px;"
32743274
>Login with Patreon</a>
3275+
<button
3276+
id="apple-sign-in"
3277+
style="display:none;border: 2px solid #000; background: #000; color: #fff; height: 29px; width: 20%;"
3278+
>Sign in with Apple</button>
32753279
</div>
32763280
<div id="right" class="scrollbarX">
32773281
<div id="buttons">

ios/App/App.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
/* Begin PBXFileReference section */
2121
2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = "<group>"; };
22+
34E3232D2F01ACBB00C38784 /* Synergism.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Synergism.entitlements; sourceTree = "<group>"; };
2223
50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; };
2324
504EC3041FED79650016851F /* Synergism.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Synergism.app; sourceTree = BUILT_PRODUCTS_DIR; };
2425
504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -57,6 +58,7 @@
5758
504EC2FB1FED79650016851F = {
5859
isa = PBXGroup;
5960
children = (
61+
34E3232D2F01ACBB00C38784 /* Synergism.entitlements */,
6062
504EC3061FED79650016851F /* App */,
6163
504EC3051FED79650016851F /* Products */,
6264
7F8756D8B27F46E3366F6CEA /* Pods */,
@@ -349,6 +351,7 @@
349351
baseConfigurationReference = A2AB2C8774981C159407C1F0 /* Pods-Synergism.debug.xcconfig */;
350352
buildSettings = {
351353
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
354+
CODE_SIGN_ENTITLEMENTS = Synergism.entitlements;
352355
CODE_SIGN_STYLE = Automatic;
353356
CURRENT_PROJECT_VERSION = 1;
354357
INFOPLIST_FILE = App/Info.plist;
@@ -369,6 +372,7 @@
369372
baseConfigurationReference = A7AEF4E3248133B6475B949F /* Pods-Synergism.release.xcconfig */;
370373
buildSettings = {
371374
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
375+
CODE_SIGN_ENTITLEMENTS = Synergism.entitlements;
372376
CODE_SIGN_STYLE = Automatic;
373377
CURRENT_PROJECT_VERSION = 1;
374378
INFOPLIST_FILE = App/Info.plist;

ios/App/Podfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ install! 'cocoapods', :disable_input_output_paths => true
1111
def capacitor_pods
1212
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
1313
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
14-
14+
pod 'CapacitorCommunityAppleSignIn', :path => '../../node_modules/@capacitor-community/apple-sign-in'
1515
end
1616

1717
target 'Synergism' do

ios/App/Podfile.lock

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
11
PODS:
22
- Capacitor (7.4.4):
33
- CapacitorCordova
4+
- CapacitorCommunityAppleSignIn (7.1.0):
5+
- Capacitor
46
- CapacitorCordova (7.4.4)
57

68
DEPENDENCIES:
79
- "Capacitor (from `../../node_modules/@capacitor/ios`)"
10+
- "CapacitorCommunityAppleSignIn (from `../../node_modules/@capacitor-community/apple-sign-in`)"
811
- "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
912

1013
EXTERNAL SOURCES:
1114
Capacitor:
1215
:path: "../../node_modules/@capacitor/ios"
16+
CapacitorCommunityAppleSignIn:
17+
:path: "../../node_modules/@capacitor-community/apple-sign-in"
1318
CapacitorCordova:
1419
:path: "../../node_modules/@capacitor/ios"
1520

1621
SPEC CHECKSUMS:
1722
Capacitor: 358dd1c3fdd71d969547b17e159fd8a7736cb45f
23+
CapacitorCommunityAppleSignIn: 271cb2860576fd2961508bbfa7a82117d93eadca
1824
CapacitorCordova: bf648a636f3c153f652d312ae145fb508b6ffced
1925

20-
PODFILE CHECKSUM: a3507cd540a0724903277289cfc3b8ee3cf198c3
26+
PODFILE CHECKSUM: a1c8e640b6e98e08dbf5d5dff3099232f7e41f81
2127

2228
COCOAPODS: 1.16.2

ios/App/Synergism.entitlements

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>com.apple.developer.applesignin</key>
6+
<array>
7+
<string>Default</string>
8+
</array>
9+
</dict>
10+
</plist>

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"node": ">=24.0.0"
88
},
99
"dependencies": {
10+
"@capacitor-community/apple-sign-in": "^7.1.0",
1011
"@capacitor/android": "^7.4.4",
1112
"@capacitor/core": "^7.4.4",
1213
"@capacitor/ios": "^7.4.4",
@@ -59,7 +60,7 @@
5960
"msw:init": "npx -y msw init . --save",
6061
"check-circular": "madge --circular src/",
6162
"cap:copy": "mkdir -p dist-cap && cp -r Pictures translations dist Synergism.css index.html dist-cap/",
62-
"cap:build": "npm run build:esbuild && npm run cap:copy && npx cap sync",
63+
"cap:build": "npm run build:esbuild -- --define:DEVICE=\\\"'mobile'\\\" && npm run cap:copy && npx cap sync",
6364
"cap:generate:assets": "npx @capacitor/assets generate --ios --android",
6465
"cap:open:ios": "npx cap open ios"
6566
},

src/Config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
declare const PROD: boolean | undefined
22
declare const DEV: boolean | undefined
3+
declare const DEVICE: 'mobile' | undefined
34

45
export const version = '4.1.1 December 17, 2025: The Ants Update'
56

@@ -11,3 +12,5 @@ export const lastUpdated = new Date('##LAST_UPDATED##')
1112

1213
export const prod = typeof PROD === 'undefined' ? false : PROD
1314
export const dev = typeof DEV === 'undefined' ? false : DEV
15+
16+
export const device = typeof DEVICE === 'undefined' ? 'browser' : DEVICE

src/Login.ts

Lines changed: 117 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import DOMPurify from 'dompurify'
44
import i18next from 'i18next'
55
import { z } from 'zod'
66
import { DOMCacheGetOrSet } from './Cache/DOM'
7+
import { dev, device } from './Config'
78
import { calculateAmbrosiaGenerationSpeed, calculateOffline, calculateRedAmbrosiaGenerationSpeed } from './Calculate'
89
import { updateGlobalsIsEvent } from './Event'
910
import { addTimers, automaticTools } from './Helper'
@@ -285,7 +286,19 @@ export async function handleLogin () {
285286
document.getElementById('accountSubTab')?.appendChild(logoutElement)
286287
}
287288

288-
const response = await fetch('https://synergism.cc/api/v1/users/me', { credentials: 'same-origin' }).catch(
289+
// Build headers - include token for mobile auth
290+
const headers: HeadersInit = {}
291+
if (device === 'mobile') {
292+
const token = localStorage.getItem('synergism_token')
293+
if (token) {
294+
headers.Authorization = `Bearer ${token}`
295+
}
296+
}
297+
298+
const response = await fetch('https://synergism.cc/api/v1/users/me', {
299+
credentials: device === 'browser' ? 'same-origin' : undefined,
300+
headers
301+
}).catch(
289302
() =>
290303
new Response(
291304
JSON.stringify(
@@ -313,7 +326,7 @@ export async function handleLogin () {
313326

314327
// biome-ignore lint/suspicious/noConfusingLabels: it's not confusing or suspicious
315328
generateSubtab: {
316-
if (location.hostname !== 'synergism.cc') {
329+
if (location.hostname !== 'synergism.cc' && device === 'browser') {
317330
subtabElement.innerHTML =
318331
'Login is not available here, go to <a href="https://synergism.cc">https://synergism.cc</a> instead!'
319332
} else if (hasAccount(account)) {
@@ -485,27 +498,45 @@ export async function handleLogin () {
485498
eventBonusesChevron.style.transform = isCollapsed ? 'rotate(0deg)' : 'rotate(-90deg)'
486499
})
487500
} else if (!hasAccount(account)) {
488-
// User is not logged in
489-
subtabElement.querySelector('#open-register')?.addEventListener('click', () => {
490-
subtabElement.querySelector<HTMLElement>('#register')?.style.setProperty('display', 'flex')
491-
subtabElement.querySelector<HTMLElement>('#login')?.style.setProperty('display', 'none')
492-
subtabElement.querySelector<HTMLElement>('#forgotpassword')?.style.setProperty('display', 'none')
493-
renderCaptcha()
494-
})
501+
if (device !== 'mobile') {
502+
// User is not logged in
503+
subtabElement.querySelector('#open-register')?.addEventListener('click', () => {
504+
subtabElement.querySelector<HTMLElement>('#register')?.style.setProperty('display', 'flex')
505+
subtabElement.querySelector<HTMLElement>('#login')?.style.setProperty('display', 'none')
506+
subtabElement.querySelector<HTMLElement>('#forgotpassword')?.style.setProperty('display', 'none')
507+
renderCaptcha()
508+
})
495509

496-
subtabElement.querySelector('#open-signin')?.addEventListener('click', () => {
497-
subtabElement.querySelector<HTMLElement>('#register')?.style.setProperty('display', 'none')
498-
subtabElement.querySelector<HTMLElement>('#login')?.style.setProperty('display', 'flex')
499-
subtabElement.querySelector<HTMLElement>('#forgotpassword')?.style.setProperty('display', 'none')
500-
renderCaptcha()
501-
})
510+
subtabElement.querySelector('#open-signin')?.addEventListener('click', () => {
511+
subtabElement.querySelector<HTMLElement>('#register')?.style.setProperty('display', 'none')
512+
subtabElement.querySelector<HTMLElement>('#login')?.style.setProperty('display', 'flex')
513+
subtabElement.querySelector<HTMLElement>('#forgotpassword')?.style.setProperty('display', 'none')
514+
renderCaptcha()
515+
})
502516

503-
subtabElement.querySelector('#open-forgotpassword')?.addEventListener('click', () => {
504-
subtabElement.querySelector<HTMLElement>('#register')?.style.setProperty('display', 'none')
505-
subtabElement.querySelector<HTMLElement>('#login')?.style.setProperty('display', 'none')
506-
subtabElement.querySelector<HTMLElement>('#forgotpassword')?.style.setProperty('display', 'flex')
507-
renderCaptcha()
508-
})
517+
subtabElement.querySelector('#open-forgotpassword')?.addEventListener('click', () => {
518+
subtabElement.querySelector<HTMLElement>('#register')?.style.setProperty('display', 'none')
519+
subtabElement.querySelector<HTMLElement>('#login')?.style.setProperty('display', 'none')
520+
subtabElement.querySelector<HTMLElement>('#forgotpassword')?.style.setProperty('display', 'flex')
521+
renderCaptcha()
522+
})
523+
} else {
524+
// Mobile: hide browser login forms and show Sign in with Apple
525+
subtabElement.querySelector('#button-holder')?.classList.add('none')
526+
subtabElement.querySelector('#register')?.classList.add('none')
527+
subtabElement.querySelector('#login')?.classList.add('none')
528+
subtabElement.querySelector('#forgotpassword')?.classList.add('none')
529+
// Hide Discord/Patreon login links
530+
subtabElement.querySelectorAll<HTMLElement>('a[href*="login?with="]').forEach((el) => {
531+
el.classList.add('none')
532+
})
533+
534+
const appleButton = subtabElement.querySelector<HTMLButtonElement>('#apple-sign-in')
535+
if (appleButton) {
536+
appleButton.style.display = 'inline-block'
537+
appleButton.addEventListener('click', signInWithApple)
538+
}
539+
}
509540
} else {
510541
assert(false, `unknown account type ${account.accountType}`)
511542
}
@@ -718,6 +749,71 @@ export function renderCaptcha () {
718749
}
719750
}
720751

752+
/**
753+
* Sign in with Apple
754+
*/
755+
export async function signInWithApple (): Promise<void> {
756+
if (device !== 'mobile') {
757+
return
758+
}
759+
760+
try {
761+
const { SignInWithApple } = await import('@capacitor-community/apple-sign-in')
762+
763+
const result = await SignInWithApple.authorize({
764+
clientId: 'com.pseudocorp.synergism',
765+
redirectURI: 'https://synergism.cc',
766+
scopes: 'email name',
767+
nonce: crypto.randomUUID()
768+
})
769+
770+
// Send the authorization to the backend to register/sign in
771+
let response: Response
772+
try {
773+
response = await fetch('https://synergism.cc/login/apple/register', {
774+
method: 'POST',
775+
headers: {
776+
'Content-Type': 'application/json'
777+
},
778+
body: JSON.stringify({
779+
identityToken: result.response.identityToken,
780+
authorizationCode: result.response.authorizationCode,
781+
email: result.response.email,
782+
givenName: result.response.givenName,
783+
familyName: result.response.familyName
784+
})
785+
})
786+
} catch (e) {
787+
console.error('Apple Sign-In fetch error:', e)
788+
Notification(`Network error: ${(e as Error).message}`)
789+
return
790+
}
791+
792+
console.log('got response', response)
793+
794+
if (response.ok) {
795+
const data = await response.json() as { success: boolean; token: string }
796+
console.log('data', JSON.stringify(data))
797+
// Store token for authenticated requests
798+
localStorage.setItem('synergism_token', data.token)
799+
// Reload to trigger handleLogin with the new session
800+
location.reload()
801+
} else {
802+
const text = await response.text()
803+
console.error('Apple Sign-In error:', response.status, text)
804+
try {
805+
const data = JSON.parse(text) as { error: string }
806+
Notification(data.error || 'Apple Sign-In failed. Please try again.')
807+
} catch {
808+
Notification(`Apple Sign-In failed: ${text || response.status}`)
809+
}
810+
}
811+
} catch (error) {
812+
console.error('Apple Sign-In error:', error)
813+
Notification('Apple Sign-In was cancelled or failed.')
814+
}
815+
}
816+
721817
const createFastForward = (name: PseudoCoinTimeskipNames, minutes: number) => {
722818
const seconds = minutes * 60
723819
// Only display relevant fast forward stats based on which one was purchased.

0 commit comments

Comments
 (0)