@@ -12,6 +12,7 @@ import XPCShared
1212final class RealtimeSuggestionIndicatorController {
1313 class IndicatorContentViewModel : ObservableObject {
1414 @Published var isPrefetching = false
15+ @Published var progress : Double = 1
1516 private var prefetchTask : Task < Void , Error > ?
1617
1718 @MainActor
@@ -21,33 +22,55 @@ final class RealtimeSuggestionIndicatorController {
2122 isPrefetching = true
2223 }
2324 prefetchTask = Task {
24- try await Task . sleep ( nanoseconds: 2 * 1_000_000_000 )
25- withAnimation ( . easeOut ( duration : 0.2 ) ) {
26- isPrefetching = false
25+ try await Task . sleep ( nanoseconds: 5 * 1_000_000_000 )
26+ if isPrefetching {
27+ endPrefetch ( )
2728 }
2829 }
2930 }
31+
32+ @MainActor
33+ func endPrefetch( ) {
34+ withAnimation ( . easeOut( duration: 0.2 ) ) {
35+ isPrefetching = false
36+ progress = 1
37+ }
38+ }
3039 }
3140
3241 struct IndicatorContentView : View {
3342 @ObservedObject var viewModel : IndicatorContentViewModel
34- @State var progress : CGFloat = 1
35- var opacityA : CGFloat { min ( progress, 0.7 ) }
36- var opacityB : CGFloat { 1 - progress }
37- var scaleA : CGFloat { progress / 2 + 0.5 }
38- var scaleB : CGFloat { max ( 1 - progress, 0.01 ) }
43+ var opacityA : CGFloat { min ( viewModel. progress, 0.7 ) }
44+ var opacityB : CGFloat { 1 - viewModel. progress }
45+ var scaleA : CGFloat { viewModel. progress / 2 + 0.5 }
46+ var scaleB : CGFloat { max ( 1 - viewModel. progress, 0.01 ) }
3947
4048 var body : some View {
4149 Circle ( )
4250 . fill ( Color . accentColor. opacity ( opacityA) )
4351 . scaleEffect ( . init( width: scaleA, height: scaleA) )
4452 . frame ( width: 8 , height: 8 )
45- . background (
46- Circle ( )
47- . fill ( Color . white. opacity ( viewModel. isPrefetching ? opacityB : 0 ) )
48- . scaleEffect ( . init( width: scaleB, height: scaleB) )
49- . frame ( width: 8 , height: 8 )
50- )
53+ . overlay {
54+ if viewModel. isPrefetching {
55+ Circle ( )
56+ . fill ( Color . white. opacity ( opacityB) )
57+ . scaleEffect ( . init( width: scaleB, height: scaleB) )
58+ . frame ( width: 8 , height: 8 )
59+ . onAppear {
60+ Task {
61+ await Task . yield ( )
62+ withAnimation (
63+ . easeInOut( duration: 0.4 )
64+ . repeatForever (
65+ autoreverses: true
66+ )
67+ ) {
68+ viewModel. progress = 0
69+ }
70+ }
71+ }
72+ }
73+ }
5174 }
5275 }
5376
@@ -208,4 +231,8 @@ final class RealtimeSuggestionIndicatorController {
208231 func triggerPrefetchAnimation( ) {
209232 viewModel. prefetch ( )
210233 }
234+
235+ func endPrefetchAnimation( ) {
236+ viewModel. endPrefetch ( )
237+ }
211238}
0 commit comments