Live video on the AT Protocol

add hovering to button or whatever

+42 -12
+2 -1
js/app/components/follow-button.tsx
··· 123 123 disabled={isFollowing === null} 124 124 loading={isFollowing === null} 125 125 leftIcon={!isFollowing && <Icon icon={Plus} size="sm" />} 126 + hoverStyle={isFollowing ? { backgroundColor: "#dc2626" } : undefined} 126 127 > 127 128 {isFollowing === null 128 129 ? "Loading..." 129 130 : isFollowing 130 - ? "Unfollow" 131 + ? "Following" 131 132 : "Follow"} 132 133 </Button> 133 134 {error && <Text style={[{ color: "#c00" }, zero.ml[2]]}>{error}</Text>}
+3
js/components/src/components/ui/button.tsx
··· 41 41 loading?: boolean; 42 42 loadingText?: string; 43 43 width?: "full" | "min" | number; 44 + hoverStyle?: ButtonPrimitiveProps["hoverStyle"]; 44 45 } 45 46 46 47 export const Button = forwardRef<any, ButtonProps>( ··· 56 57 disabled, 57 58 style, 58 59 width = "full", 60 + hoverStyle, 59 61 ...props 60 62 }, 61 63 ref, ··· 222 224 ref={ref} 223 225 disabled={disabled || loading} 224 226 style={[buttonStyle, sizeStyles.button, widthStyle, style]} 227 + hoverStyle={hoverStyle} 225 228 {...props} 226 229 > 227 230 <ButtonPrimitive.Content style={sizeStyles.inner}>
+37 -11
js/components/src/components/ui/primitives/button.tsx
··· 1 - import React, { forwardRef } from "react"; 1 + import React, { forwardRef, useState } from "react"; 2 2 import { 3 3 AccessibilityRole, 4 4 GestureResponderEvent, 5 + Platform, 6 + Pressable, 7 + PressableProps, 8 + StyleProp, 5 9 StyleSheet, 6 10 Text, 7 11 TextProps, 8 - TouchableOpacity, 9 - TouchableOpacityProps, 10 12 View, 11 13 ViewProps, 14 + ViewStyle, 12 15 } from "react-native"; 13 16 14 17 // Base button primitive interface 15 - export interface ButtonPrimitiveProps 16 - extends Omit<TouchableOpacityProps, "onPress"> { 18 + export interface ButtonPrimitiveProps extends Omit<PressableProps, "onPress"> { 17 19 onPress?: (event: GestureResponderEvent) => void; 18 20 disabled?: boolean; 19 21 loading?: boolean; ··· 21 23 accessibilityLabel?: string; 22 24 accessibilityHint?: string; 23 25 testID?: string; 26 + hoverStyle?: StyleProp<ViewStyle>; 24 27 } 25 28 26 29 // Button root primitive - handles all touch interactions 27 30 export const ButtonRoot = forwardRef< 28 - React.ComponentRef<typeof TouchableOpacity>, 31 + React.ComponentRef<typeof Pressable>, 29 32 ButtonPrimitiveProps 30 33 >( 31 34 ( ··· 43 46 accessibilityState, 44 47 testID, 45 48 style, 46 - activeOpacity = 0.7, 49 + hoverStyle, 47 50 ...props 48 51 }, 49 52 ref, 50 53 ) => { 54 + const [isHovered, setIsHovered] = useState(false); 55 + 51 56 const handlePress = React.useCallback( 52 57 (event: GestureResponderEvent) => { 53 58 if (!disabled && !loading && onPress) { ··· 84 89 [disabled, loading, onLongPress], 85 90 ); 86 91 92 + const handleHoverIn = React.useCallback(() => { 93 + if (!disabled && !loading) { 94 + setIsHovered(true); 95 + } 96 + }, [disabled, loading]); 97 + 98 + const handleHoverOut = React.useCallback(() => { 99 + setIsHovered(false); 100 + }, []); 101 + 87 102 return ( 88 - <TouchableOpacity 103 + <Pressable 89 104 ref={ref} 90 105 onPress={handlePress} 91 106 onPressIn={handlePressIn} 92 107 onPressOut={handlePressOut} 93 108 onLongPress={handleLongPress} 109 + onHoverIn={handleHoverIn} 110 + onHoverOut={handleHoverOut} 94 111 disabled={disabled || loading} 95 - activeOpacity={disabled || loading ? 1 : activeOpacity} 96 112 accessibilityRole={accessibilityRole} 97 113 accessibilityLabel={accessibilityLabel} 98 114 accessibilityHint={accessibilityHint} ··· 104 120 testID={testID} 105 121 style={[ 106 122 primitiveStyles.button, 123 + primitiveStyles.transition, 107 124 (disabled || loading) && primitiveStyles.disabled, 108 - style, 125 + style as any, 126 + isHovered && hoverStyle, 109 127 ]} 110 128 {...props} 111 129 > 112 130 {children} 113 - </TouchableOpacity> 131 + </Pressable> 114 132 ); 115 133 }, 116 134 ); ··· 245 263 alignItems: "center", 246 264 justifyContent: "center", 247 265 }, 266 + transition: 267 + Platform.OS === "web" 268 + ? // probably fine if web-only 269 + ({ 270 + transitionDuration: "150ms", 271 + transitionProperty: "background-color, border-color, color", 272 + } as any) 273 + : undefined, 248 274 disabled: { 249 275 opacity: 0.5, 250 276 },