@@ -46,24 +46,39 @@ import { IconName } from '../../../../../../component-library/components/Icons/I
4646import Routes from '../../../../../../constants/navigation/Routes' ;
4747import { formatPriceWithSubscriptNotation } from '../../../../Predict/utils/format' ;
4848import {
49+ Alert ,
4950 ManagePriceAlertsTestIds ,
50- PriceAlert ,
5151 PriceAlertRouteParams ,
5252 PriceAlertAnalytics ,
5353} from '../../constants' ;
5454import {
55+ deleteAlertByType ,
5556 fetchAlerts ,
56- deleteAlert ,
57- updateAlert ,
57+ normalizeAlerts ,
5858 priceAlertsQueryKey ,
59+ updateAlertByType ,
5960} from '../../api' ;
61+ import {
62+ formatPercentAlertSubtitle ,
63+ formatPercentAlertTitle ,
64+ } from '../../utils' ;
6065import { useAnalytics } from '../../../../../hooks/useAnalytics/useAnalytics' ;
6166import { MetaMetricsEvents } from '../../../../../../core/Analytics' ;
6267
6368const styles = StyleSheet . create ( {
6469 switchDisabled : { opacity : 0.5 } ,
6570} ) ;
6671
72+ /** Analytics `alert_type` + `period`/`direction` properties for a given alert. */
73+ const analyticsPropsForAlert = ( alert : Alert ) =>
74+ alert . type === 'percent_change'
75+ ? {
76+ alert_type : PriceAlertAnalytics . TYPE . PERCENT ,
77+ period : alert . period ,
78+ direction : alert . direction ,
79+ }
80+ : { alert_type : PriceAlertAnalytics . TYPE . THRESHOLD } ;
81+
6782const ManagePriceAlertsView : React . FC = ( ) => {
6883 const tw = useTailwind ( ) ;
6984 const { colors, brandColors } = useTheme ( ) ;
@@ -98,10 +113,11 @@ const ManagePriceAlertsView: React.FC = () => {
98113 isError,
99114 } = useQuery ( {
100115 queryKey : priceAlertsQueryKey ( assetId ) ,
101- queryFn : async ( ) : Promise < PriceAlert [ ] > => {
116+ queryFn : async ( ) : Promise < Alert [ ] > => {
102117 const response = await fetchAlerts ( assetId ) ;
103118 if ( ! response . ok ) throw new Error ( `HTTP ${ response . status } ` ) ;
104- return response . json ( ) ;
119+ const body : unknown [ ] = await response . json ( ) ;
120+ return normalizeAlerts ( body ) ;
105121 } ,
106122 retry : false ,
107123 staleTime : 0 ,
@@ -150,16 +166,22 @@ const ManagePriceAlertsView: React.FC = () => {
150166 } , [ navigation ] ) ;
151167
152168 const handleNavigateToCreate = useCallback (
153- ( editingAlert ?: PriceAlert ) => {
169+ ( editingAlert ?: Alert ) => {
154170 navigation . navigate ( Routes . CREATE_PRICE_ALERT , {
155171 symbol,
156172 ticker,
157173 currentPrice,
158174 currentCurrency,
159175 assetId,
160176 fromManage : true ,
161- existingThresholds : alerts . map ( ( a ) => a . threshold ) ,
177+ existingThresholds : alerts
178+ . filter ( ( a ) => a . type === 'absolute_price' )
179+ . map ( ( a ) => a . threshold ) ,
180+ existingPercentAlerts : alerts . filter (
181+ ( a ) => a . type === 'percent_change' ,
182+ ) ,
162183 editingAlert,
184+ initialType : editingAlert ?. type ,
163185 } ) ;
164186 } ,
165187 [
@@ -180,30 +202,27 @@ const ManagePriceAlertsView: React.FC = () => {
180202 setDeletingIds ( ( prev ) => new Set ( prev ) . add ( id ) ) ;
181203
182204 const queryKey = priceAlertsQueryKey ( assetId ) ;
183- const previous = queryClient . getQueryData < PriceAlert [ ] > ( queryKey ) ?? [ ] ;
205+ const previous = queryClient . getQueryData < Alert [ ] > ( queryKey ) ?? [ ] ;
206+ const target = previous . find ( ( a ) => a . id === id ) ;
184207
185208 try {
186- const response = await deleteAlert ( id ) ;
209+ if ( ! target ) throw new Error ( 'Alert not found' ) ;
210+ const response = await deleteAlertByType ( target ) ;
187211 if ( ! response . ok ) throw new Error ( `HTTP ${ response . status } ` ) ;
188212
189- const deleted = previous . find ( ( a ) => a . id === id ) ;
190- if ( deleted ) {
191- trackEvent (
192- createEventBuilder (
193- MetaMetricsEvents . PRICE_ALERT_CREATION_INTERACTION ,
194- )
195- . addProperties ( {
196- interaction_type : PriceAlertAnalytics . INTERACTION_TYPE . DELETED ,
197- asset_id : assetId ,
198- token_symbol : displayTicker ,
199- alert_type : PriceAlertAnalytics . TYPE . THRESHOLD ,
200- alert_value : deleted . threshold ,
201- alert_recurring : deleted . recurring ,
202- alert_active : deleted . active ,
203- } )
204- . build ( ) ,
205- ) ;
206- }
213+ trackEvent (
214+ createEventBuilder ( MetaMetricsEvents . PRICE_ALERT_CREATION_INTERACTION )
215+ . addProperties ( {
216+ interaction_type : PriceAlertAnalytics . INTERACTION_TYPE . DELETED ,
217+ asset_id : assetId ,
218+ token_symbol : displayTicker ,
219+ ...analyticsPropsForAlert ( target ) ,
220+ alert_value : target . threshold ,
221+ alert_recurring : target . recurring ,
222+ alert_active : target . active ,
223+ } )
224+ . build ( ) ,
225+ ) ;
207226
208227 const next = previous . filter ( ( a ) => a . id !== id ) ;
209228 queryClient . setQueryData ( queryKey , next ) ;
@@ -227,8 +246,8 @@ const ManagePriceAlertsView: React.FC = () => {
227246 } ) ;
228247 const response = await fetchAlerts ( assetId ) . catch ( ( ) => null ) ;
229248 if ( response ?. ok ) {
230- const data : PriceAlert [ ] = await response . json ( ) . catch ( ( ) => [ ] ) ;
231- queryClient . setQueryData ( queryKey , data ) ;
249+ const body : unknown [ ] = await response . json ( ) . catch ( ( ) => [ ] ) ;
250+ queryClient . setQueryData ( queryKey , normalizeAlerts ( body ) ) ;
232251 } else {
233252 queryClient . setQueryData ( queryKey , previous ) ;
234253 }
@@ -260,37 +279,36 @@ const ManagePriceAlertsView: React.FC = () => {
260279 setTogglingIds ( ( prev ) => new Set ( prev ) . add ( id ) ) ;
261280
262281 const queryKey = priceAlertsQueryKey ( assetId ) ;
263- const previous = queryClient . getQueryData < PriceAlert [ ] > ( queryKey ) ?? [ ] ;
282+ const previous = queryClient . getQueryData < Alert [ ] > ( queryKey ) ?? [ ] ;
264283 const toggled = previous . find ( ( a ) => a . id === id ) ;
265284 queryClient . setQueryData (
266285 queryKey ,
267286 previous . map ( ( a ) => ( a . id === id ? { ...a , active : newValue } : a ) ) ,
268287 ) ;
269288
270289 try {
271- const response = await updateAlert ( id , { active : newValue } ) ;
290+ if ( ! toggled ) throw new Error ( 'Alert not found' ) ;
291+ const response = await updateAlertByType ( toggled , {
292+ active : newValue ,
293+ } ) ;
272294 if ( ! response . ok ) throw new Error ( `HTTP ${ response . status } ` ) ;
273295
274- if ( toggled ) {
275- trackEvent (
276- createEventBuilder (
277- MetaMetricsEvents . PRICE_ALERT_CREATION_INTERACTION ,
278- )
279- . addProperties ( {
280- interaction_type : PriceAlertAnalytics . INTERACTION_TYPE . UPDATED ,
281- asset_id : assetId ,
282- token_symbol : displayTicker ,
283- alert_type : PriceAlertAnalytics . TYPE . THRESHOLD ,
284- alert_value : toggled . threshold ,
285- alert_recurring : toggled . recurring ,
286- alert_active : newValue ,
287- prev_alert_value : toggled . threshold ,
288- prev_alert_recurring : toggled . recurring ,
289- prev_alert_active : toggled . active ,
290- } )
291- . build ( ) ,
292- ) ;
293- }
296+ trackEvent (
297+ createEventBuilder ( MetaMetricsEvents . PRICE_ALERT_CREATION_INTERACTION )
298+ . addProperties ( {
299+ interaction_type : PriceAlertAnalytics . INTERACTION_TYPE . UPDATED ,
300+ asset_id : assetId ,
301+ token_symbol : displayTicker ,
302+ ...analyticsPropsForAlert ( toggled ) ,
303+ alert_value : toggled . threshold ,
304+ alert_recurring : toggled . recurring ,
305+ alert_active : newValue ,
306+ prev_alert_value : toggled . threshold ,
307+ prev_alert_recurring : toggled . recurring ,
308+ prev_alert_active : toggled . active ,
309+ } )
310+ . build ( ) ,
311+ ) ;
294312 } catch {
295313 toastRef ?. current ?. showToast ( {
296314 variant : ToastVariants . Icon ,
@@ -323,10 +341,28 @@ const ManagePriceAlertsView: React.FC = () => {
323341 ] ,
324342 ) ;
325343
326- const renderItem = ( { item } : { item : PriceAlert } ) => {
344+ const renderItem = ( { item } : { item : Alert } ) => {
327345 const isDeleting = deletingIds . has ( item . id ) ;
328346 const isToggling = togglingIds . has ( item . id ) ;
329347
348+ const title =
349+ item . type === 'percent_change'
350+ ? formatPercentAlertTitle ( item )
351+ : strings ( 'price_alerts.reaches_threshold' , {
352+ threshold : formatPriceWithSubscriptNotation (
353+ item . threshold ,
354+ currentCurrency ,
355+ { maximumFractionDigits : 15 } ,
356+ ) ,
357+ } ) ;
358+
359+ const subtitle =
360+ item . type === 'percent_change'
361+ ? formatPercentAlertSubtitle ( item )
362+ : item . recurring
363+ ? strings ( 'price_alerts.recurring' )
364+ : strings ( 'price_alerts.once_label' ) ;
365+
330366 return (
331367 < Box
332368 flexDirection = { BoxFlexDirection . Row }
@@ -342,18 +378,10 @@ const ManagePriceAlertsView: React.FC = () => {
342378 testID = { `${ ManagePriceAlertsTestIds . ALERT_EDIT_PREFIX } -${ item . id } ` }
343379 >
344380 < Text variant = { TextVariant . BodyMd } color = { TextColor . TextDefault } >
345- { strings ( 'price_alerts.reaches_threshold' , {
346- threshold : formatPriceWithSubscriptNotation (
347- item . threshold ,
348- currentCurrency ,
349- { maximumFractionDigits : 15 } ,
350- ) ,
351- } ) }
381+ { title }
352382 </ Text >
353383 < Text variant = { TextVariant . BodySm } color = { TextColor . TextAlternative } >
354- { item . recurring
355- ? strings ( 'price_alerts.recurring' )
356- : strings ( 'price_alerts.once_label' ) }
384+ { subtitle }
357385 </ Text >
358386 </ TouchableOpacity >
359387
0 commit comments