| author | |
| committer | |
| log | bad1143b02f7b585d1fe1335b3d9a97983ce8d8b |
| tree | d5e9ff66930547f244253f20a28babfc29ad3852 |
| parent | 92d4a2e996b19239d95e32a3feb05208e0a52306 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>6 files changed, 0 insertions(+), 223 deletions(-)
apps/mobile/modules/t3-native-controls/android/src/main/java/expo/modules/t3nativecontrols/T3HeaderButtonView.kt deleted-97| ... | @@ -1,97 +0,0 @@ | ||
| 1 | package expo.modules.t3nativecontrols | ||
| 2 | |||
| 3 | import android.content.Context | ||
| 4 | import android.graphics.Canvas | ||
| 5 | import android.graphics.Color | ||
| 6 | import android.graphics.Paint | ||
| 7 | import android.view.View | ||
| 8 | import expo.modules.kotlin.AppContext | ||
| 9 | import expo.modules.kotlin.viewevent.EventDispatcher | ||
| 10 | import expo.modules.kotlin.views.ExpoView | ||
| 11 | |||
| 12 | class T3HeaderButtonView(context: Context, appContext: AppContext) : ExpoView(context, appContext) { | ||
| 13 | private val iconView = HeaderIconView(context) | ||
| 14 | private val onTriggered by EventDispatcher() | ||
| 15 | |||
| 16 | init { | ||
| 17 | isClickable = true | ||
| 18 | isFocusable = true | ||
| 19 | setOnClickListener { | ||
| 20 | onTriggered(emptyMap<String, Any>()) | ||
| 21 | } | ||
| 22 | addView(iconView, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)) | ||
| 23 | } | ||
| 24 | |||
| 25 | fun setLabel(label: String) { | ||
| 26 | contentDescription = label | ||
| 27 | } | ||
| 28 | |||
| 29 | fun setSystemImage(systemImage: String) { | ||
| 30 | iconView.systemImage = systemImage | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | private class HeaderIconView(context: Context) : View(context) { | ||
| 35 | private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { | ||
| 36 | color = Color.parseColor("#6B7280") | ||
| 37 | strokeCap = Paint.Cap.ROUND | ||
| 38 | strokeJoin = Paint.Join.ROUND | ||
| 39 | strokeWidth = 3f * resources.displayMetrics.density | ||
| 40 | style = Paint.Style.STROKE | ||
| 41 | } | ||
| 42 | |||
| 43 | var systemImage: String = "gearshape" | ||
| 44 | set(value) { | ||
| 45 | field = value | ||
| 46 | invalidate() | ||
| 47 | } | ||
| 48 | |||
| 49 | override fun onDraw(canvas: Canvas) { | ||
| 50 | super.onDraw(canvas) | ||
| 51 | val cx = width / 2f | ||
| 52 | val cy = height / 2f | ||
| 53 | val size = minOf(width, height).toFloat() | ||
| 54 | if (systemImage == "square.and.pencil") { | ||
| 55 | drawNewTask(canvas, cx, cy, size) | ||
| 56 | } else { | ||
| 57 | drawSettings(canvas, cx, cy, size) | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | private fun drawSettings(canvas: Canvas, cx: Float, cy: Float, size: Float) { | ||
| 62 | val radius = size * 0.12f | ||
| 63 | canvas.drawCircle(cx, cy, radius, paint) | ||
| 64 | for (index in 0 until 8) { | ||
| 65 | val angle = Math.PI * index / 4.0 | ||
| 66 | val inner = size * 0.19f | ||
| 67 | val outer = size * 0.27f | ||
| 68 | val sx = cx + kotlin.math.cos(angle).toFloat() * inner | ||
| 69 | val sy = cy + kotlin.math.sin(angle).toFloat() * inner | ||
| 70 | val ex = cx + kotlin.math.cos(angle).toFloat() * outer | ||
| 71 | val ey = cy + kotlin.math.sin(angle).toFloat() * outer | ||
| 72 | canvas.drawLine(sx, sy, ex, ey, paint) | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | private fun drawNewTask(canvas: Canvas, cx: Float, cy: Float, size: Float) { | ||
| 77 | val left = cx - size * 0.2f | ||
| 78 | val top = cy - size * 0.16f | ||
| 79 | val right = cx + size * 0.14f | ||
| 80 | val bottom = cy + size * 0.2f | ||
| 81 | canvas.drawRoundRect(left, top, right, bottom, size * 0.04f, size * 0.04f, paint) | ||
| 82 | canvas.drawLine( | ||
| 83 | cx - size * 0.02f, | ||
| 84 | cy + size * 0.13f, | ||
| 85 | cx + size * 0.24f, | ||
| 86 | cy - size * 0.13f, | ||
| 87 | paint | ||
| 88 | ) | ||
| 89 | canvas.drawLine( | ||
| 90 | cx + size * 0.17f, | ||
| 91 | cy - size * 0.2f, | ||
| 92 | cx + size * 0.24f, | ||
| 93 | cy - size * 0.13f, | ||
| 94 | paint | ||
| 95 | ) | ||
| 96 | } | ||
| 97 | } | ||
apps/mobile/modules/t3-native-controls/android/src/main/java/expo/modules/t3nativecontrols/T3NativeControlsModule.kt-11| ... | @@ -32,16 +32,5 @@ class T3NativeControlsModule : Module() { | ... | @@ -32,16 +32,5 @@ class T3NativeControlsModule : Module() { |
| 32 | ?.resolve("t3-showcase-ready") | 32 | ?.resolve("t3-showcase-ready") |
| 33 | ?.writeText(scene) | 33 | ?.writeText(scene) |
| 34 | } | 34 | } |
| 35 | |||
| 36 | View(T3HeaderButtonView::class) { | ||
| 37 | Prop("label") { view: T3HeaderButtonView, label: String -> | ||
| 38 | view.setLabel(label) | ||
| 39 | } | ||
| 40 | Prop("systemImage") { view: T3HeaderButtonView, systemImage: String -> | ||
| 41 | view.setSystemImage(systemImage) | ||
| 42 | } | ||
| 43 | |||
| 44 | Events("onTriggered") | ||
| 45 | } | ||
| 46 | } | 35 | } |
| 47 | } | 36 | } |
apps/mobile/modules/t3-native-controls/ios/T3HeaderButtonView.swift deleted-62| ... | @@ -1,62 +0,0 @@ | ||
| 1 | import ExpoModulesCore | ||
| 2 | import UIKit | ||
| 3 | |||
| 4 | public final class T3HeaderButtonView: ExpoView { | ||
| 5 | private static let size: CGFloat = 44 | ||
| 6 | private static let symbolSize: CGFloat = 18 | ||
| 7 | |||
| 8 | private let button = UIButton(type: .system) | ||
| 9 | private var systemImage = "circle" | ||
| 10 | |||
| 11 | let onTriggered = EventDispatcher() | ||
| 12 | |||
| 13 | public required init(appContext: AppContext? = nil) { | ||
| 14 | super.init(appContext: appContext) | ||
| 15 | |||
| 16 | isAccessibilityElement = false | ||
| 17 | button.frame = bounds | ||
| 18 | button.autoresizingMask = [.flexibleWidth, .flexibleHeight] | ||
| 19 | button.addTarget(self, action: #selector(handlePress), for: .primaryActionTriggered) | ||
| 20 | addSubview(button) | ||
| 21 | applyConfiguration() | ||
| 22 | } | ||
| 23 | |||
| 24 | public override var intrinsicContentSize: CGSize { | ||
| 25 | CGSize(width: Self.size, height: Self.size) | ||
| 26 | } | ||
| 27 | |||
| 28 | public func setLabel(_ label: String) { | ||
| 29 | button.accessibilityLabel = label | ||
| 30 | } | ||
| 31 | |||
| 32 | public func setSystemImage(_ systemImage: String) { | ||
| 33 | guard self.systemImage != systemImage else { | ||
| 34 | return | ||
| 35 | } | ||
| 36 | self.systemImage = systemImage | ||
| 37 | applyConfiguration() | ||
| 38 | } | ||
| 39 | |||
| 40 | private func applyConfiguration() { | ||
| 41 | var configuration: UIButton.Configuration | ||
| 42 | if #available(iOS 26.0, *) { | ||
| 43 | configuration = .glass() | ||
| 44 | configuration.cornerStyle = .capsule | ||
| 45 | } else { | ||
| 46 | configuration = .plain() | ||
| 47 | } | ||
| 48 | |||
| 49 | configuration.baseForegroundColor = .label | ||
| 50 | configuration.contentInsets = .zero | ||
| 51 | configuration.image = UIImage(systemName: systemImage) | ||
| 52 | configuration.preferredSymbolConfigurationForImage = UIImage.SymbolConfiguration( | ||
| 53 | pointSize: Self.symbolSize, | ||
| 54 | weight: .regular | ||
| 55 | ) | ||
| 56 | button.configuration = configuration | ||
| 57 | } | ||
| 58 | |||
| 59 | @objc private func handlePress() { | ||
| 60 | onTriggered() | ||
| 61 | } | ||
| 62 | } | ||
apps/mobile/modules/t3-native-controls/ios/T3NativeControlsModule.swift-11| ... | @@ -87,16 +87,5 @@ public final class T3NativeControlsModule: Module { | ... | @@ -87,16 +87,5 @@ public final class T3NativeControlsModule: Module { |
| 87 | let readyPath = NSHomeDirectory() + "/Library/Caches/T3ShowcaseReadyScene" | 87 | let readyPath = NSHomeDirectory() + "/Library/Caches/T3ShowcaseReadyScene" |
| 88 | try? scene.write(toFile: readyPath, atomically: true, encoding: .utf8) | 88 | try? scene.write(toFile: readyPath, atomically: true, encoding: .utf8) |
| 89 | } | 89 | } |
| 90 | |||
| 91 | View(T3HeaderButtonView.self) { | ||
| 92 | Prop("label") { (view: T3HeaderButtonView, label: String) in | ||
| 93 | view.setLabel(label) | ||
| 94 | } | ||
| 95 | Prop("systemImage") { (view: T3HeaderButtonView, systemImage: String) in | ||
| 96 | view.setSystemImage(systemImage) | ||
| 97 | } | ||
| 98 | |||
| 99 | Events("onTriggered") | ||
| 100 | } | ||
| 101 | } | 90 | } |
| 102 | } | 91 | } |
apps/mobile/src/features/threads/sidebar-header-actions.android.tsx deleted-16| ... | @@ -1,16 +0,0 @@ | ||
| 1 | import { View } from "react-native"; | ||
| 2 | |||
| 3 | import { T3HeaderButton } from "../../native/T3HeaderButton.android"; | ||
| 4 | import type { SidebarHeaderActionsProps } from "./sidebar-header-actions"; | ||
| 5 | |||
| 6 | export function SidebarHeaderActions(props: SidebarHeaderActionsProps) { | ||
| 7 | return ( | ||
| 8 | <View className="h-11 flex-row gap-1"> | ||
| 9 | <T3HeaderButton | ||
| 10 | accessibilityLabel="Open settings" | ||
| 11 | icon="gearshape" | ||
| 12 | onPress={props.onOpenSettings} | ||
| 13 | /> | ||
| 14 | </View> | ||
| 15 | ); | ||
| 16 | } | ||
apps/mobile/src/native/T3HeaderButton.android.tsx deleted-26| ... | @@ -1,26 +0,0 @@ | ||
| 1 | import { requireNativeView } from "expo"; | ||
| 2 | import type { NativeSyntheticEvent, StyleProp, ViewProps, ViewStyle } from "react-native"; | ||
| 3 | |||
| 4 | interface NativeHeaderButtonProps extends ViewProps { | ||
| 5 | readonly label: string; | ||
| 6 | readonly systemImage: "gearshape" | "square.and.pencil"; | ||
| 7 | readonly onTriggered: (event: NativeSyntheticEvent<Record<string, never>>) => void; | ||
| 8 | } | ||
| 9 | |||
| 10 | const NativeHeaderButton = requireNativeView<NativeHeaderButtonProps>("T3NativeControls"); | ||
| 11 | |||
| 12 | export function T3HeaderButton(props: { | ||
| 13 | readonly accessibilityLabel: string; | ||
| 14 | readonly icon: NativeHeaderButtonProps["systemImage"]; | ||
| 15 | readonly onPress: () => void; | ||
| 16 | readonly style?: StyleProp<ViewStyle>; | ||
| 17 | }) { | ||
| 18 | return ( | ||
| 19 | <NativeHeaderButton | ||
| 20 | label={props.accessibilityLabel} | ||
| 21 | onTriggered={props.onPress} | ||
| 22 | style={[{ width: 44, height: 44 }, props.style]} | ||
| 23 | systemImage={props.icon} | ||
| 24 | /> | ||
| 25 | ); | ||
| 26 | } | ||