@@ -147,7 +147,6 @@ private fun SentryBuddyOverlayContent(
147147 var state by remember { mutableStateOf(controller.state) }
148148 var liveFeed by remember { mutableStateOf(controller.liveFeed) }
149149 var nowMs by remember { mutableStateOf(System .currentTimeMillis()) }
150- var isRecordingHelpOpen by remember { mutableStateOf(false ) }
151150 var transientRecordingEvent by remember { mutableStateOf<TransientRecordingEvent ?>(null ) }
152151 val transientRecordingEventScope = rememberCoroutineScope()
153152 val analysisScope = rememberCoroutineScope()
@@ -238,20 +237,7 @@ private fun SentryBuddyOverlayContent(
238237 else -> dispatch { close() }
239238 }
240239 },
241- onLongClick = {
242- if (state is SentryBuddySessionState .Recording ) {
243- isRecordingHelpOpen = true
244- }
245- },
246- onStopAndAnalyze = {
247- isRecordingHelpOpen = false
248- dispatch {
249- stopRecording()
250- briefRecording()
251- }
252- },
253- onDismissRecordingHelp = { isRecordingHelpOpen = false },
254- isRecordingHelpOpen = isRecordingHelpOpen,
240+ onLongClick = {},
255241 )
256242 BuddySheet (
257243 state = state,
@@ -276,9 +262,6 @@ private fun BoxScope.BuddyBubble(
276262 transientEvent : TransientRecordingEvent ? ,
277263 onClick : () -> Unit ,
278264 onLongClick : () -> Unit ,
279- onStopAndAnalyze : () -> Unit ,
280- onDismissRecordingHelp : () -> Unit ,
281- isRecordingHelpOpen : Boolean ,
282265) {
283266 val density = LocalDensity .current
284267 val bubbleSizePx = with (density) { BuddyBubbleSize .toPx() }
@@ -289,6 +272,29 @@ private fun BoxScope.BuddyBubble(
289272 val attentionItem = liveFeed.latestUnviewedAdverseItem
290273 val attentionColor = attentionItem?.let { severityColor(it.severity) }
291274 val pulseScale = remember { Animatable (1f ) }
275+ val stopTransition = rememberInfiniteTransition(label = " buddy-floating-stop-button" )
276+ val stopHaloScale by
277+ stopTransition.animateFloat(
278+ initialValue = 1.0f ,
279+ targetValue = 1.14f ,
280+ animationSpec =
281+ infiniteRepeatable(
282+ animation = tween(durationMillis = 1400 , easing = FastOutSlowInEasing ),
283+ repeatMode = RepeatMode .Reverse ,
284+ ),
285+ label = " buddy-floating-stop-button-halo-scale" ,
286+ )
287+ val stopHaloAlpha by
288+ stopTransition.animateFloat(
289+ initialValue = 0.12f ,
290+ targetValue = 0.28f ,
291+ animationSpec =
292+ infiniteRepeatable(
293+ animation = tween(durationMillis = 1400 , easing = FastOutSlowInEasing ),
294+ repeatMode = RepeatMode .Reverse ,
295+ ),
296+ label = " buddy-floating-stop-button-halo-alpha" ,
297+ )
292298 var bubbleOffset by remember { mutableStateOf<Offset ?>(null ) }
293299
294300 LaunchedEffect (attentionItem?.id) {
@@ -342,20 +348,38 @@ private fun BoxScope.BuddyBubble(
342348 verticalArrangement = Arrangement .spacedBy(8 .dp),
343349 ) {
344350 Box (
345- modifier =
346- Modifier .size(64 .dp)
347- .shadow(10 .dp, CircleShape )
348- .background(bubbleColor, CircleShape )
349- .border(2 .dp, Color .White .copy(alpha = 0.55f ), CircleShape )
350- .pointerInput(maxWidthPx, maxHeightPx) {
351- detectDragGestures { change, dragAmount ->
352- change.consume()
353- bubbleOffset = ((bubbleOffset ? : resolvedOffset) + dragAmount).constrain()
354- }
355- }
356- .combinedClickable(onClick = onClick, onLongClick = onLongClick),
351+ modifier = Modifier .size(64 .dp),
357352 contentAlignment = Alignment .Center ,
358353 ) {
354+ if (isRecording) {
355+ Box (
356+ modifier =
357+ Modifier .size(BuddyBubbleSize * stopHaloScale)
358+ .graphicsLayer { alpha = stopHaloAlpha }
359+ .background(BuddyRed , CircleShape )
360+ )
361+ }
362+ Box (
363+ modifier =
364+ Modifier .size(64 .dp)
365+ .shadow(10 .dp, CircleShape )
366+ .background(bubbleColor, CircleShape )
367+ .border(2 .dp, Color .White .copy(alpha = 0.55f ), CircleShape )
368+ .pointerInput(maxWidthPx, maxHeightPx) {
369+ detectDragGestures { change, dragAmount ->
370+ change.consume()
371+ bubbleOffset = ((bubbleOffset ? : resolvedOffset) + dragAmount).constrain()
372+ }
373+ }
374+ .combinedClickable(onClick = onClick, onLongClick = onLongClick),
375+ contentAlignment = Alignment .Center ,
376+ ) {
377+ if (isRecording) {
378+ StopIcon (tint = Color .White , modifier = Modifier .size(22 .dp))
379+ } else {
380+ SentryBuddyGlyph (tint = Color .White , modifier = Modifier .size(30 .dp))
381+ }
382+ }
359383 if (attentionColor != null && ! isRecording) {
360384 Box (
361385 modifier =
@@ -367,11 +391,6 @@ private fun BoxScope.BuddyBubble(
367391 Modifier .size(BuddyBubbleSize + 10 .dp).border(3 .dp, attentionColor, CircleShape )
368392 )
369393 }
370- if (isRecording) {
371- StopIcon (tint = Color .White , modifier = Modifier .size(22 .dp))
372- } else {
373- SentryBuddyGlyph (tint = Color .White , modifier = Modifier .size(30 .dp))
374- }
375394 if (liveFeed.unviewedAdverseCount > 0 && ! isRecording) {
376395 Text (
377396 text =
@@ -390,14 +409,6 @@ private fun BoxScope.BuddyBubble(
390409 )
391410 }
392411 }
393- if (isRecording && isRecordingHelpOpen && state is SentryBuddySessionState .Recording ) {
394- RecordingTooltip (
395- state = state,
396- nowMs = nowMs,
397- onStopAndAnalyze = onStopAndAnalyze,
398- onDismiss = onDismissRecordingHelp,
399- )
400- }
401412 elapsed?.let {
402413 Text (
403414 text = it,
@@ -759,103 +770,6 @@ private fun SentryBuddyGlyph(tint: Color, modifier: Modifier = Modifier) {
759770 )
760771}
761772
762- @Composable
763- private fun RecordingTooltip (
764- state : SentryBuddySessionState .Recording ,
765- nowMs : Long ,
766- onStopAndAnalyze : () -> Unit ,
767- onDismiss : () -> Unit ,
768- ) {
769- val durationMs = nowMs - state.startedAtMs
770- Card (
771- modifier = Modifier .width(300 .dp).shadow(12 .dp, RoundedCornerShape (20 .dp)),
772- colors = CardDefaults .cardColors(containerColor = Color .White ),
773- border = CardDefaults .outlinedCardBorder(),
774- shape = RoundedCornerShape (20 .dp),
775- ) {
776- Column (modifier = Modifier .padding(16 .dp), verticalArrangement = Arrangement .spacedBy(12 .dp)) {
777- Row (
778- modifier = Modifier .fillMaxWidth(),
779- horizontalArrangement = Arrangement .SpaceBetween ,
780- verticalAlignment = Alignment .CenterVertically ,
781- ) {
782- Column {
783- Text (" Recording Flow" , color = BuddyInk , fontWeight = FontWeight .Bold )
784- Text (
785- " Everything stays on device" ,
786- color = BuddyMuted ,
787- style = MaterialTheme .typography.bodySmall,
788- )
789- }
790- TextButton (onClick = onDismiss) { Text (" Close" ) }
791- }
792- ActiveTimelinePreview (state.intent, durationMs)
793- BreathingStopButton (onClick = onStopAndAnalyze)
794- }
795- }
796- }
797-
798- @Composable
799- private fun BreathingStopButton (onClick : () -> Unit ) {
800- val transition = rememberInfiniteTransition(label = " buddy-stop-button" )
801- val haloScale by
802- transition.animateFloat(
803- initialValue = 1.0f ,
804- targetValue = 1.04f ,
805- animationSpec =
806- infiniteRepeatable(
807- animation = tween(durationMillis = 1400 , easing = FastOutSlowInEasing ),
808- repeatMode = RepeatMode .Reverse ,
809- ),
810- label = " buddy-stop-button-halo-scale" ,
811- )
812- val haloAlpha by
813- transition.animateFloat(
814- initialValue = 0.12f ,
815- targetValue = 0.24f ,
816- animationSpec =
817- infiniteRepeatable(
818- animation = tween(durationMillis = 1400 , easing = FastOutSlowInEasing ),
819- repeatMode = RepeatMode .Reverse ,
820- ),
821- label = " buddy-stop-button-halo-alpha" ,
822- )
823- val shape = RoundedCornerShape (24 .dp)
824-
825- Box (modifier = Modifier .fillMaxWidth().height(48 .dp)) {
826- Box (
827- modifier =
828- Modifier .fillMaxSize()
829- .graphicsLayer {
830- scaleX = haloScale
831- scaleY = haloScale
832- alpha = haloAlpha
833- }
834- .background(BuddyRed , shape)
835- )
836- Button (
837- modifier = Modifier .fillMaxSize(),
838- colors = ButtonDefaults .buttonColors(containerColor = BuddyRed ),
839- shape = shape,
840- onClick = onClick,
841- ) {
842- BuddyButtonText (" Stop and Analyze" )
843- }
844- }
845- }
846-
847- @Composable
848- private fun ActiveTimelinePreview (intent : BuddyFlowIntent , durationMs : Long ) {
849- Text (" Live Trace" , style = MaterialTheme .typography.labelLarge, color = BuddyMuted )
850- TimelineRows (
851- items =
852- listOf (
853- TimelinePreviewItem (0 , " recording_started ${intent.name} " , BuddyPurple ),
854- TimelinePreviewItem (durationMs, " recording_in_progress" , BuddyRed ),
855- )
856- )
857- }
858-
859773private data class TimelinePreviewItem (val elapsedMs : Long , val label : String , val color : Color )
860774
861775@Composable
0 commit comments