| 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 | 32 | ?.resolve("t3-showcase-ready") |
| 33 | 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 | 87 | let readyPath = NSHomeDirectory() + "/Library/Caches/T3ShowcaseReadyScene" |
| 88 | 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 | } |