Files
Felitendo ff0d3108fe feat: merge the web dashboard into the monorepo as ftstore admin
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.
2026-08-03 10:25:03 +02:00

113 lines
2.2 KiB
Swift

//
// NBSheetButton.swift
// NimbleKit
//
// Created by samara on 8.05.2025.
//
import SwiftUI
public struct NBSheetButton: View {
public enum NSSheetButtonStyle {
case prominent
case standard
}
@Environment(\.isEnabled) private var isEnabled
private var _title: String
private var _systemImage: String?
private var _style: NSSheetButtonStyle
public init(
title: String,
systemImage: String? = nil,
style: NSSheetButtonStyle = .standard
) {
self._title = title
self._systemImage = systemImage
self._style = style
}
public var body: some View {
ZStack {
if isEnabled {
Label {
Text(_title)
} icon: {
if let image = _systemImage {
Image(systemName: image)
}
}
.labelStyle(.titleAndIcon)
} else {
ProgressView()
.progressViewStyle(.circular)
.opacity(1)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background {
adaptiveGlassBackground(
tintColor: isEnabled ? backgroundColor : Color(uiColor: .quaternarySystemFill),
fallback: isEnabled ? backgroundColor : Color(uiColor: .quaternarySystemFill)
)
}
.foregroundColor(foregroundColor)
.clipShape(RoundedRectangle(cornerRadius: _cornerRadius, style: .continuous))
.contentShape(RoundedRectangle(cornerRadius: _cornerRadius, style: .continuous))
.fontWeight(.semibold)
.frame(height: 50)
}
@ViewBuilder
private var content: some View {
Label {
Text(_title)
} icon: {
if let image = _systemImage {
Image(systemName: image)
}
}
.labelStyle(.titleAndIcon)
}
private var backgroundColor: Color {
switch _style {
case .prominent: .accentColor
case .standard: Color(uiColor: .quaternarySystemFill)
}
}
private var foregroundColor: Color {
switch _style {
case .prominent: .white
case .standard: .accentColor
}
}
private var _cornerRadius: CGFloat {
if #available(iOS 26.0, *) {
return 28.0
} else {
return 12.0
}
}
}
private extension View {
@ViewBuilder
func adaptiveGlassBackground(
tintColor: Color,
fallback: Color
) -> some View {
if #available(iOS 26.0, *) {
Color.clear
.glassEffect(.regular.tint(tintColor), in: .rect(cornerRadius: 28, style: .continuous))
} else {
fallback
}
}
}