FTStore now holds both halves of the product:
ios/ the swift app — the fork of claration/Feather, unchanged apart
from its new home
web/ FTStore Admin, formerly its own repository. serves the
dashboard at /admin, the ftstore.json feed and IPA downloads
app-repo.json and Images/ stay at the repo root on purpose: the former is
a published AltStore source whose raw.githubusercontent URL is what
installed copies resolve updates through, so moving it would break every
subscriber.
The admin server is renamed throughout: package ftstore-admin, images
ghcr.io/<owner>/ftstore-admin-{app,worker}, postgres role/db ftstore,
session cookie ftstore_session, temp dir /tmp/ftstore, backup type
ftstore-settings, outbound user-agent FTStoreAdmin. backups written under the old
name still import, and compose is pinned to name: ftstore so the
volume prefix does not follow the directory name.
apply-rebrand.sh gains a relocation step: upstream's tree is rooted at
the repo root while ours lives in ios/, so a merge drops Feather's files
next to ios/ and the script folds them in before renaming. it never
touches web/. still idempotent.
release tags are namespaced — v* for the app, web-v* for the server —
so a server release no longer triggers a macOS build or rewrites
app-repo.json. workflows are prefixed ios-/web- and path-filtered.
97 lines
2.3 KiB
Swift
97 lines
2.3 KiB
Swift
//
|
|
// SectionProminentHeaderWrapper.swift
|
|
// Feather
|
|
//
|
|
// Created by samara on 11.04.2025.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
public struct NBSection<Content, Footer>: View
|
|
where Content: View,
|
|
Footer: View
|
|
{
|
|
private var _headerText: String
|
|
private var _headerTextSecondary: String?
|
|
private var _systemName: String?
|
|
private var _content: Content
|
|
private var _footer: Footer
|
|
|
|
public init(_ headerText: String,
|
|
secondary headerTextSecondary: String? = nil,
|
|
systemName: String? = nil,
|
|
@ViewBuilder content: () -> Content,
|
|
@ViewBuilder footer: () -> Footer
|
|
) {
|
|
self._headerText = headerText
|
|
self._headerTextSecondary = headerTextSecondary
|
|
self._systemName = systemName
|
|
self._content = content()
|
|
self._footer = footer()
|
|
}
|
|
|
|
public init(_ headerText: String,
|
|
secondary headerTextSecondary: String? = nil,
|
|
systemName: String? = nil,
|
|
@ViewBuilder content: () -> Content
|
|
) where Footer == EmptyView {
|
|
self._headerText = headerText
|
|
self._headerTextSecondary = headerTextSecondary
|
|
self._systemName = systemName
|
|
self._content = content()
|
|
self._footer = EmptyView()
|
|
}
|
|
|
|
public var body: some View {
|
|
Section(
|
|
header:
|
|
HStack(alignment: .center, spacing: 4) {
|
|
if let _systemName {
|
|
Image(systemName: _systemName)
|
|
.font(.system(size: 12))
|
|
.offset(y: -2)
|
|
}
|
|
|
|
Text(_headerText)
|
|
.fontWeight(.bold)
|
|
.font(.title2)
|
|
.foregroundStyle(.primary)
|
|
|
|
|
|
Spacer()
|
|
|
|
if let _headerTextSecondary {
|
|
if #available(iOS 26.0, *) {
|
|
Text(_headerTextSecondary)
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
.contentTransition(.numericText())
|
|
.padding(.horizontal, 8)
|
|
.padding(.vertical, 4.4)
|
|
.background(Color(uiColor: .quaternarySystemFill))
|
|
.clipShape(Capsule())
|
|
.glassEffect()
|
|
} else {
|
|
Text(_headerTextSecondary)
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
.contentTransition(.numericText())
|
|
.padding(.horizontal, 8)
|
|
.padding(.vertical, 4.4)
|
|
.background(Color(uiColor: .quaternarySystemFill))
|
|
.clipShape(Capsule())
|
|
}
|
|
}
|
|
}
|
|
.offset(y: 2)
|
|
,
|
|
footer: _footer
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
) {
|
|
_content
|
|
}
|
|
.headerProminence(.increased)
|
|
}
|
|
}
|