Skip to content

Commit 549746c

Browse files
authored
Merge branch 'master' into release-2.2.1
2 parents c893ddc + 18313b0 commit 549746c

118 files changed

Lines changed: 9435 additions & 302 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/commands/pr.md

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,10 @@ gh pr create --base $base --title "..." --body "..." [--draft]
145145
- Add `--draft` flag if draft mode selected
146146
- If actual PR number differs from predicted, rename the saved file
147147

148-
### 8b. Backfill Changelog PR Number
148+
### 8b. Changelog Fragments
149149

150-
If the PR was created (not dry run) and `CHANGELOG.md` was modified in the branch (`git diff $base...HEAD --name-only | grep CHANGELOG.md`):
151-
- Run `git diff $base...HEAD -- CHANGELOG.md` to identify only lines added in this branch (lines prefixed with `+`, excluding the `+++` header line)
152-
- From those added lines, find entries under `## [Unreleased]` that are missing a `#NUMBER` suffix
153-
- Append the actual PR number to only those entries, e.g. `- Add foo` becomes `- Add foo #123`
154-
- If any entries were updated, create a new commit with message `chore: backfill changelog pr number` and push
150+
If the PR is user-facing, verify the branch adds exactly one changelog fragment under `changelog.d/next/` or `changelog.d/hotfix/`.
151+
Do not edit `CHANGELOG.md` in normal PRs and do not backfill PR numbers into fragments.
155152

156153
### 9. Output Summary
157154

.claude/commands/release.md

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,9 @@ If "Previous tag": ask `"Which tag?"` with a text input (default: `v{oldVersionN
5353

5454
If "master" or if the release is minor/major: `{baseRef} = master`.
5555

56-
### 2c. Check Changelog
57-
58-
Read `CHANGELOG.md` and check whether `## [Unreleased]` has any entries beneath it. Remember the result for Step 3.
59-
60-
**If no entries:** Print `⚠ CHANGELOG.md has no unreleased entries — continuing without changelog update.` and proceed.
56+
Set `{changelogTarget}`:
57+
- If `{baseRef}` is `master`: `next`
58+
- Otherwise: `hotfix`
6159

6260
### 3. Create Release Branch & Bump Version
6361

@@ -72,20 +70,30 @@ If `{baseRef}` is `master`, pull latest: `git pull origin master`. Skip pull if
7270
git checkout -b release-{newVersionName}
7371
```
7472

75-
**Finalize Changelog (if Step 2c found entries):**
76-
1. Replace `## [Unreleased]` with `## [{newVersionName}] - {YYYY-MM-DD}` (today's date)
77-
2. Insert a fresh empty `## [Unreleased]` section above the new version heading
78-
3. Update the compare link references at the bottom of the file:
79-
- Change `[Unreleased]` link to compare from `v{newVersionName}...HEAD`
80-
- Add a new `[{newVersionName}]` link comparing `v{oldVersionName}...v{newVersionName}`
81-
8273
If the base is a tag (not master), print:
8374
```
8475
Release branch created from {baseRef}.
8576
Cherry-pick the commits you need onto this branch now, then continue.
8677
```
8778
Wait for the user to confirm they are done cherry-picking before proceeding.
8879

80+
Finalize changelog after the release branch contains all release commits:
81+
82+
```bash
83+
scripts/collect-changelog.sh --target {changelogTarget}
84+
```
85+
86+
Read `CHANGELOG.md` and check whether `## [Unreleased]` has any entries beneath it after collecting fragments.
87+
88+
**If entries exist:**
89+
1. Replace `## [Unreleased]` with `## [{newVersionName}] - {YYYY-MM-DD}` (today's date)
90+
2. Insert a fresh empty `## [Unreleased]` section above the new version heading
91+
3. Update the compare link references at the bottom of the file:
92+
- Change `[Unreleased]` link to compare from `v{newVersionName}...HEAD`
93+
- Add a new `[{newVersionName}]` link comparing `v{oldVersionName}...v{newVersionName}`
94+
95+
**If no entries:** Print `⚠ CHANGELOG.md has no unreleased entries — continuing without changelog update.` and proceed.
96+
8997
Edit `Bitkit.xcodeproj/project.pbxproj` using sed to replace **all** occurrences of the old values (test targets use `= 1` / `= 1.0` so they won't match):
9098

9199
```bash
@@ -97,12 +105,12 @@ Verify the edit updated exactly 4 occurrences of each (Bitkit Debug/Release + Bi
97105

98106
```bash
99107
git add Bitkit.xcodeproj/project.pbxproj
100-
# Only stage CHANGELOG.md if it was finalized above (i.e. unreleased entries were found in step 2c)
101-
git add CHANGELOG.md
102108
git commit -m "chore: version {newVersionName}"
103109
git push -u origin release-{newVersionName}
104110
```
105111

112+
If changelog collection updated `CHANGELOG.md` or deleted consumed fragments, run `git add CHANGELOG.md changelog.d` before the commit.
113+
106114
### 4. Create Version Bump PR
107115

108116
Read `.github/pull_request_template.md` for structure. Create PR:

.github/pull_request_template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- Changelog: Add an entry under ## [Unreleased] in CHANGELOG.md for user-facing changes (skip for chores/CI/refactors). -->
1+
<!-- Changelog: For user-facing changes, add one fragment in changelog.d/next/ or changelog.d/hotfix/. Do not edit CHANGELOG.md in normal PRs. -->
22

33
### Description
44

AGENTS.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ This project follows **modern SwiftUI patterns** and explicitly **AVOIDS traditi
116116
- Async operations should delegate to `@Observable` business logic objects
117117

118118
4. **Component Design**
119+
- **Always reuse existing components** before creating new ones — check `Components/` for buttons, text styles, layouts, and other shared UI. If identical or near-identical UI exists elsewhere in the codebase, extract it into a shared component rather than duplicating it.
120+
- Use the project's text components (`DisplayText`, `HeadlineText`, `TitleText`, `SubtitleText`, `BodyMText`, `BodyMSBText`, `BodySSBText`, `BodySText`, `CaptionMText`, `CaptionText`) instead of raw `Text().font().foregroundColor()` chains.
119121
- Decompose views into small, focused, single-purpose components
120122
- Use descriptive names (e.g., `UserProfileCard` not `Card`)
121123
- Prefer composition over deep view hierarchies
@@ -276,13 +278,14 @@ Ensure accessibility modifiers and labels are added to custom components.
276278

277279
### Changelog
278280

279-
- ALWAYS add exactly ONE entry per PR under `## [Unreleased]` in `CHANGELOG.md` for `feat:` and `fix:` PRs; skip for `chore:`, `ci:`, `refactor:`, `test:`, `docs:` unless the change is user-facing
280-
- NEVER add multiple changelog lines for the same PR — summarize all changes in a single concise entry
281-
- USE standard Keep a Changelog categories: `### Added`, `### Changed`, `### Deprecated`, `### Removed`, `### Fixed`, `### Security`
282-
- ALWAYS append `#PR_NUMBER` at the end of each changelog entry when the PR number is known
283-
- ALWAYS place new entries at the top of their category section (newest first)
284-
- NEVER modify released version sections — only edit `## [Unreleased]`
285-
- ALWAYS create category headings on demand (don't add empty stubs)
281+
- NEVER edit `CHANGELOG.md` in normal feature/fix PRs; release automation collects changelog fragments into it
282+
- ALWAYS add exactly ONE changelog fragment for user-facing `feat:` and `fix:` PRs; skip for `chore:`, `ci:`, `refactor:`, `test:`, `docs:` unless the change is user-facing
283+
- PUT normal release fragments in `changelog.d/next/` and hotfix fragments in `changelog.d/hotfix/`
284+
- NAME fragments `<issue-or-pr>.<category>.md`, where category is one of `added`, `changed`, `deprecated`, `removed`, `fixed`, or `security`
285+
- WRITE the fragment as one polished user-facing sentence without a leading bullet and without a PR number
286+
- NEVER add multiple changelog fragments for the same PR — summarize all changes in one concise fragment
287+
- Release commits consume fragments with `scripts/collect-changelog.sh --target next|hotfix`, update `CHANGELOG.md`, and delete consumed fragment files
288+
- NEVER modify released version sections manually
286289

287290
## Common Workflows
288291

Bitkit.xcodeproj/project.pbxproj

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
182817C12F59A7F10055A441 /* Paykit in Frameworks */ = {isa = PBXBuildFile; productRef = 182817C02F59A7F10055A441 /* Paykit */; };
1011
18D65E002EB964B500252335 /* VssRustClientFfi in Frameworks */ = {isa = PBXBuildFile; productRef = 18D65DFF2EB964B500252335 /* VssRustClientFfi */; };
1112
18D65E022EB964BD00252335 /* VssRustClientFfi in Frameworks */ = {isa = PBXBuildFile; productRef = 18D65E012EB964BD00252335 /* VssRustClientFfi */; };
1213
3D76260F4C9C4A53B1E4A001 /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D76260E4C9C4A53B1E4A001 /* CoreBluetooth.framework */; };
1314
3D7626104C9C4A53B1E4A001 /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D76260E4C9C4A53B1E4A001 /* CoreBluetooth.framework */; };
1415
4AAB08CA2E1FE77600BA63DF /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 4AAB08C92E1FE77600BA63DF /* Lottie */; };
15-
4AFCA3702E05933800205CAE /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = 4AFCA36F2E05933800205CAE /* Zip */; };
16-
4AFCA3722E0596D900205CAE /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = 4AFCA3712E0596D900205CAE /* Zip */; };
1716
961058E32C355B5500E1F1D8 /* BitkitNotification.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 961058DC2C355B5500E1F1D8 /* BitkitNotification.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
1817
968FDF162DFAFE230053CD7F /* LDKNode in Frameworks */ = {isa = PBXBuildFile; productRef = 9613018B2C5022D700878183 /* LDKNode */; };
1918
968FE1402DFB016B0053CD7F /* LDKNode in Frameworks */ = {isa = PBXBuildFile; productRef = 968FE13F2DFB016B0053CD7F /* LDKNode */; };
@@ -157,7 +156,6 @@
157156
3D7626104C9C4A53B1E4A001 /* CoreBluetooth.framework in Frameworks */,
158157
968FE1402DFB016B0053CD7F /* LDKNode in Frameworks */,
159158
96DEA03C2DE8BBAB009932BF /* BitkitCore in Frameworks */,
160-
4AFCA3722E0596D900205CAE /* Zip in Frameworks */,
161159
96E493A82C943184000E8BC2 /* secp256k1 in Frameworks */,
162160
18D65E022EB964BD00252335 /* VssRustClientFfi in Frameworks */,
163161
);
@@ -167,8 +165,8 @@
167165
isa = PBXFrameworksBuildPhase;
168166
buildActionMask = 2147483647;
169167
files = (
168+
182817C12F59A7F10055A441 /* Paykit in Frameworks */,
170169
3D76260F4C9C4A53B1E4A001 /* CoreBluetooth.framework in Frameworks */,
171-
4AFCA3702E05933800205CAE /* Zip in Frameworks */,
172170
968FDF162DFAFE230053CD7F /* LDKNode in Frameworks */,
173171
18D65E002EB964B500252335 /* VssRustClientFfi in Frameworks */,
174172
96E493A42C942FD1000E8BC2 /* secp256k1 in Frameworks */,
@@ -246,7 +244,6 @@
246244
packageProductDependencies = (
247245
96E493A72C943184000E8BC2 /* secp256k1 */,
248246
96DEA03B2DE8BBAB009932BF /* BitkitCore */,
249-
4AFCA3712E0596D900205CAE /* Zip */,
250247
968FE13F2DFB016B0053CD7F /* LDKNode */,
251248
18D65E012EB964BD00252335 /* VssRustClientFfi */,
252249
);
@@ -278,9 +275,9 @@
278275
96E493A32C942FD1000E8BC2 /* secp256k1 */,
279276
96E20CD32CB6D91A00C24149 /* CodeScanner */,
280277
96DEA0392DE8BBA1009932BF /* BitkitCore */,
281-
4AFCA36F2E05933800205CAE /* Zip */,
282278
4AAB08C92E1FE77600BA63DF /* Lottie */,
283279
18D65DFF2EB964B500252335 /* VssRustClientFfi */,
280+
182817C02F59A7F10055A441 /* Paykit */,
284281
);
285282
productName = Bitkit;
286283
productReference = 96FE1F612C2DE6AA006D0C8B /* Bitkit.app */;
@@ -384,10 +381,10 @@
384381
96E493A22C942FD1000E8BC2 /* XCRemoteSwiftPackageReference "swift-secp256k1" */,
385382
96E20CD22CB6D91A00C24149 /* XCRemoteSwiftPackageReference "CodeScanner" */,
386383
96DEA0382DE8BBA1009932BF /* XCRemoteSwiftPackageReference "bitkit-core" */,
387-
4AFCA36E2E05933800205CAE /* XCRemoteSwiftPackageReference "Zip" */,
388384
968FE13E2DFB016B0053CD7F /* XCRemoteSwiftPackageReference "ldk-node" */,
389385
4AAB08C82E1FE77600BA63DF /* XCRemoteSwiftPackageReference "lottie-ios" */,
390386
18D65DFE2EB9649F00252335 /* XCRemoteSwiftPackageReference "vss-rust-client-ffi" */,
387+
182817BF2F59A7F10055A441 /* XCRemoteSwiftPackageReference "paykit-rs" */,
391388
);
392389
productRefGroup = 96FE1F622C2DE6AA006D0C8B /* Products */;
393390
projectDirPath = "";
@@ -903,6 +900,14 @@
903900
/* End XCConfigurationList section */
904901

905902
/* Begin XCRemoteSwiftPackageReference section */
903+
182817BF2F59A7F10055A441 /* XCRemoteSwiftPackageReference "paykit-rs" */ = {
904+
isa = XCRemoteSwiftPackageReference;
905+
repositoryURL = "https://github.com/pubky/paykit-rs";
906+
requirement = {
907+
kind = revision;
908+
revision = cd1253291b1582759d569372d5942b8871527ea1;
909+
};
910+
};
906911
18D65DFE2EB9649F00252335 /* XCRemoteSwiftPackageReference "vss-rust-client-ffi" */ = {
907912
isa = XCRemoteSwiftPackageReference;
908913
repositoryURL = "https://github.com/synonymdev/vss-rust-client-ffi";
@@ -919,20 +924,12 @@
919924
minimumVersion = 4.5.2;
920925
};
921926
};
922-
4AFCA36E2E05933800205CAE /* XCRemoteSwiftPackageReference "Zip" */ = {
923-
isa = XCRemoteSwiftPackageReference;
924-
repositoryURL = "https://github.com/marmelroy/Zip.git";
925-
requirement = {
926-
kind = upToNextMinorVersion;
927-
minimumVersion = 2.1.2;
928-
};
929-
};
930927
968FE13E2DFB016B0053CD7F /* XCRemoteSwiftPackageReference "ldk-node" */ = {
931928
isa = XCRemoteSwiftPackageReference;
932929
repositoryURL = "https://github.com/synonymdev/ldk-node";
933930
requirement = {
934931
kind = revision;
935-
revision = ae38eadab70fceb5dbe242bc02bf895581cb7c3f;
932+
revision = 52f73c4402cfb06a020ab8fa9594b5ecb94e3cd6;
936933
};
937934
};
938935
96DEA0382DE8BBA1009932BF /* XCRemoteSwiftPackageReference "bitkit-core" */ = {
@@ -962,6 +959,11 @@
962959
/* End XCRemoteSwiftPackageReference section */
963960

964961
/* Begin XCSwiftPackageProductDependency section */
962+
182817C02F59A7F10055A441 /* Paykit */ = {
963+
isa = XCSwiftPackageProductDependency;
964+
package = 182817BF2F59A7F10055A441 /* XCRemoteSwiftPackageReference "paykit-rs" */;
965+
productName = Paykit;
966+
};
965967
18D65DFF2EB964B500252335 /* VssRustClientFfi */ = {
966968
isa = XCSwiftPackageProductDependency;
967969
package = 18D65DFE2EB9649F00252335 /* XCRemoteSwiftPackageReference "vss-rust-client-ffi" */;
@@ -977,16 +979,6 @@
977979
package = 4AAB08C82E1FE77600BA63DF /* XCRemoteSwiftPackageReference "lottie-ios" */;
978980
productName = Lottie;
979981
};
980-
4AFCA36F2E05933800205CAE /* Zip */ = {
981-
isa = XCSwiftPackageProductDependency;
982-
package = 4AFCA36E2E05933800205CAE /* XCRemoteSwiftPackageReference "Zip" */;
983-
productName = Zip;
984-
};
985-
4AFCA3712E0596D900205CAE /* Zip */ = {
986-
isa = XCSwiftPackageProductDependency;
987-
package = 4AFCA36E2E05933800205CAE /* XCRemoteSwiftPackageReference "Zip" */;
988-
productName = Zip;
989-
};
990982
9613018B2C5022D700878183 /* LDKNode */ = {
991983
isa = XCSwiftPackageProductDependency;
992984
package = 968FE13E2DFB016B0053CD7F /* XCRemoteSwiftPackageReference "ldk-node" */;

Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 10 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Bitkit.xcodeproj/xcshareddata/xcschemes/Bitkit.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
</Testables>
6969
</TestAction>
7070
<LaunchAction
71-
buildConfiguration = "Debug"
71+
buildConfiguration = "Release"
7272
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
7373
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
7474
launchStyle = "0"

Bitkit/AppScene.swift

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ struct AppScene: View {
2727
@StateObject private var transferTracking: TransferTrackingManager
2828
@StateObject private var channelDetails = ChannelDetailsViewModel.shared
2929
@StateObject private var migrations = MigrationsService.shared
30+
@StateObject private var pubkyProfile = PubkyProfileManager()
31+
@StateObject private var contactsManager = ContactsManager()
3032
@State private var keyboardManager = KeyboardManager()
3133

3234
@State private var hideSplash = false
@@ -135,7 +137,29 @@ struct AppScene: View {
135137
.environmentObject(tagManager)
136138
.environmentObject(transferTracking)
137139
.environmentObject(channelDetails)
140+
.environmentObject(pubkyProfile)
141+
.environmentObject(contactsManager)
138142
.environment(keyboardManager)
143+
.onChange(of: pubkyProfile.authState, initial: true) { _, authState in
144+
if authState == .authenticated, let pk = pubkyProfile.publicKey {
145+
Task { try? await contactsManager.loadContacts(for: pk) }
146+
} else if authState == .idle {
147+
contactsManager.reset()
148+
}
149+
}
150+
.onChange(of: navigation.currentRoute) { oldRoute, newRoute in
151+
guard shouldDiscardPendingImport(currentRoute: oldRoute, destination: newRoute) else {
152+
return
153+
}
154+
155+
contactsManager.clearPendingImport()
156+
}
157+
.onChange(of: pubkyProfile.sessionRestorationFailed) { _, failed in
158+
if failed {
159+
pubkyProfile.sessionRestorationFailed = false
160+
app.toast(type: .error, title: t("profile__session_expired_title"), description: t("profile__session_expired_description"))
161+
}
162+
}
139163
.onAppear {
140164
if !settings.pinEnabled {
141165
isPinVerified = true
@@ -240,9 +264,12 @@ struct AppScene: View {
240264
WalletRestoreSuccess()
241265
} else {
242266
if !isPinVerified && settings.pinEnabled {
243-
AuthCheck {
244-
isPinVerified = true
245-
}
267+
AuthCheck(
268+
onCancel: nil,
269+
onPinVerified: {
270+
isPinVerified = true
271+
}
272+
)
246273
} else {
247274
MainNavView()
248275
}
@@ -307,19 +334,25 @@ struct AppScene: View {
307334
app?.handleLdkNodeEvent(lightningEvent)
308335
}
309336

310-
if wallet.isRestoringWallet {
311-
Task {
337+
Task {
338+
if wallet.isRestoringWallet {
312339
await restoreFromMostRecentBackup()
313340

314341
await MainActor.run {
315342
widgets.loadSavedWidgets()
316343
widgets.objectWillChange.send()
317344
}
318-
345+
await pubkyProfile.initialize()
319346
await startWallet()
347+
return
320348
}
321-
} else {
322-
Task { await startWallet() }
349+
350+
let initializePubkyTask = Task {
351+
await pubkyProfile.initialize()
352+
}
353+
354+
await startWallet()
355+
await initializePubkyTask.value
323356
}
324357
}
325358

3.4 KB
Loading

0 commit comments

Comments
 (0)