Skip to content

Commit

Permalink
feat(widgets): add weather widget
Browse files Browse the repository at this point in the history
  • Loading branch information
pwltr committed Jan 21, 2025
1 parent 47d4c62 commit 7330baf
Show file tree
Hide file tree
Showing 17 changed files with 946 additions and 247 deletions.
3 changes: 3 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"noBannedTypes": "off",
"noForEach": "off"
},
"nursery": {
"useExplicitType": "off"
},
"performance": {
"noAccumulatingSpread": "off"
},
Expand Down
8 changes: 8 additions & 0 deletions src/assets/icons/widgets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,11 @@ export const calculatorIcon = (): string =>
<path fill-rule="evenodd" clip-rule="evenodd" d="M36.1161 18.6161C36.6043 18.128 37.3957 18.128 37.8839 18.6161L45.3839 26.1161C45.872 26.6043 45.872 27.3957 45.3839 27.8839C44.8957 28.372 44.1043 28.372 43.6161 27.8839L36.1161 20.3839C35.628 19.8957 35.628 19.1043 36.1161 18.6161Z" fill="white"/>
</svg>
`;

export const weatherIcon = (): string =>
`<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.776611" width="32" height="32" rx="6.4" fill="#FF4400"/>
<path opacity="0.2" d="M13.0265 16C13.0265 14.7639 13.3931 13.5555 14.0798 12.5277C14.7666 11.4999 15.7427 10.6988 16.8847 10.2258C18.0268 9.75271 19.2834 9.62894 20.4958 9.87009C21.7082 10.1113 22.8218 10.7065 23.6959 11.5806C24.57 12.4547 25.1653 13.5683 25.4064 14.7807C25.6476 15.9931 25.5238 17.2497 25.0508 18.3918C24.5777 19.5338 23.7766 20.5099 22.7488 21.1967C21.721 21.8834 20.5126 22.25 19.2765 22.25H12.4015C11.7811 22.2494 11.1679 22.1168 10.6026 21.8611C10.0373 21.6054 9.53284 21.2324 9.12274 20.7668C8.71264 20.3012 8.40626 19.7537 8.22394 19.1607C8.04161 18.5676 7.9875 17.9426 8.06521 17.327C8.14292 16.7115 8.35067 16.1195 8.67467 15.5904C8.99866 15.0612 9.43149 14.6071 9.94443 14.258C10.4574 13.909 11.0387 13.673 11.6498 13.5658C12.2609 13.4586 12.8878 13.4826 13.4889 13.6362" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.3739 10.4831C19.2827 10.266 18.1517 10.3774 17.1239 10.8032C16.0961 11.2289 15.2176 11.9499 14.5995 12.8749C13.9814 13.7999 13.6515 14.8875 13.6515 16C13.6515 16.3452 13.3717 16.625 13.0265 16.625C12.6813 16.625 12.4015 16.3452 12.4015 16C12.4015 15.3655 12.4893 14.7377 12.6597 14.1336C12.3591 14.1132 12.0562 14.1291 11.7578 14.1814C11.234 14.2733 10.7357 14.4756 10.2961 14.7747C9.85639 15.0739 9.48539 15.4632 9.20768 15.9167C8.92997 16.3703 8.7519 16.8777 8.68529 17.4053C8.61868 17.9329 8.66506 18.4687 8.82134 18.977C8.97762 19.4853 9.24023 19.9546 9.59174 20.3537C9.94326 20.7527 10.3756 21.0725 10.8602 21.2917C11.3447 21.5109 11.8703 21.6245 12.4021 21.625H19.2765C20.389 21.625 21.4766 21.2951 22.4016 20.677C23.3266 20.0589 24.0476 19.1804 24.4733 18.1526C24.8991 17.1248 25.0105 15.9938 24.7934 14.9026C24.5764 13.8115 24.0407 12.8092 23.254 12.0225C22.4673 11.2359 21.465 10.7001 20.3739 10.4831ZM13.1264 12.9272C12.6015 12.8508 12.0665 12.8582 11.5418 12.9502C10.8434 13.0727 10.179 13.3424 9.59281 13.7413C9.0066 14.1402 8.51193 14.6593 8.14165 15.264C7.77137 15.8687 7.53395 16.5453 7.44514 17.2488C7.35632 17.9522 7.41816 18.6666 7.62653 19.3443C7.83491 20.0221 8.18505 20.6478 8.65374 21.1799C9.12242 21.712 9.69892 22.1383 10.345 22.4306C10.991 22.7228 11.6918 22.8743 12.4009 22.875H19.2765C20.6363 22.875 21.9655 22.4718 23.0961 21.7164C24.2266 20.9609 25.1078 19.8872 25.6282 18.631C26.1485 17.3747 26.2847 15.9924 26.0194 14.6588C25.7541 13.3251 25.0993 12.1001 24.1379 11.1386C23.1764 10.1772 21.9514 9.52238 20.6178 9.2571C19.2841 8.99183 17.9018 9.12798 16.6456 9.64833C15.3893 10.1687 14.3156 11.0499 13.5602 12.1805C13.3995 12.4209 13.2547 12.6704 13.1264 12.9272Z" fill="white"/>
</svg>
`;
74 changes: 41 additions & 33 deletions src/components/Widgets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
widgetsSelector,
} from '../store/reselect/widgets';
import { setWidgetsSortOrder } from '../store/slices/widgets';
import { TFeedWidget } from '../store/types/widgets';
import { TFeedWidget, TWeatherWidgetOptions } from '../store/types/widgets';
import { TouchableOpacity, View } from '../styles/components';
import { Checkmark, PlusIcon, SortAscendingIcon } from '../styles/icons';
import { Caption13Up } from '../styles/text';
Expand All @@ -34,6 +34,7 @@ import LuganoFeedWidget from './LuganoFeedWidget';
import PriceWidget from './PriceWidget';
import Button from './buttons/Button';
import CalculatorWidget from './widgets/CalculatorWidget';
import WeatherWidget from './widgets/WeatherWidget';

const Widgets = (): ReactElement => {
const { t } = useTranslation('slashtags');
Expand Down Expand Up @@ -82,33 +83,40 @@ const Widgets = (): ReactElement => {
}
};

let testID: string;
let Component:
| typeof PriceWidget
| typeof HeadlinesWidget
| typeof BlocksWidget
| typeof FactsWidget
| typeof FeedWidget
| typeof CalculatorWidget;

if (id === 'calculator') {
Component = CalculatorWidget;
testID = 'CalculatorWidget';
return (
<CalculatorWidget
style={styles.widget}
isEditing={editing}
testID="CalculatorWidget"
onLongPress={initiateDrag}
onPressIn={initiateDrag}
/>
);
}

if (id === 'weather') {
const options = widgets[id] as TWeatherWidgetOptions;
return (
<ScaleDecorator>
<Component
style={styles.widget}
isEditing={editing}
testID={testID}
onLongPress={initiateDrag}
onPressIn={initiateDrag}
/>
</ScaleDecorator>
<WeatherWidget
style={styles.widget}
options={options}
isEditing={editing}
testID="WeatherWidget"
onLongPress={initiateDrag}
onPressIn={initiateDrag}
/>
);
}

const feedWidget = widgets[id] as TFeedWidget;
let testID: string;
let Component:
| typeof PriceWidget
| typeof HeadlinesWidget
| typeof BlocksWidget
| typeof FactsWidget
| typeof FeedWidget;

switch (feedWidget.type) {
case SUPPORTED_FEED_TYPES.PRICE_FEED:
Expand Down Expand Up @@ -137,17 +145,15 @@ const Widgets = (): ReactElement => {
}

return (
<ScaleDecorator>
<Component
style={styles.widget}
url={id}
widget={feedWidget}
isEditing={editing}
testID={testID}
onLongPress={initiateDrag}
onPressIn={initiateDrag}
/>
</ScaleDecorator>
<Component
style={styles.widget}
url={id}
widget={feedWidget}
isEditing={editing}
testID={testID}
onLongPress={initiateDrag}
onPressIn={initiateDrag}
/>
);
},
[editing, widgets, sortedWidgets.length],
Expand All @@ -174,7 +180,9 @@ const Widgets = (): ReactElement => {
<DraggableFlatList
data={sortedWidgets}
keyExtractor={(id): string => id}
renderItem={renderItem}
renderItem={(params): ReactElement => (
<ScaleDecorator>{renderItem(params)}</ScaleDecorator>
)}
scrollEnabled={false}
activationDistance={editing ? 0 : 100}
onDragEnd={onDragEnd}
Expand Down
12 changes: 12 additions & 0 deletions src/components/widgets/BaseWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import { ListIcon, SettingsIcon, TrashIcon } from '../../styles/icons';
import { BodyMSB } from '../../styles/text';
import { truncate } from '../../utils/helpers';
import Dialog from '../Dialog';
// import LoadingView from '../LoadingView';
import SvgImage from '../SvgImage';

const BaseWidget = ({
id,
children,
isLoading,
isEditing,
style,
testID,
Expand All @@ -27,6 +29,7 @@ const BaseWidget = ({
}: {
id: string;
children: ReactElement;
isLoading?: boolean;
isEditing?: boolean;
style?: StyleProp<ViewStyle>;
testID?: string;
Expand Down Expand Up @@ -110,6 +113,15 @@ const BaseWidget = ({
{showTitle && !isEditing && <View style={styles.spacer} />}

{!isEditing && children}

{/* {!isEditing && (
<LoadingView
style={styles.content}
loading={!!isLoading}
delay={1000}>
{children}
</LoadingView>
)} */}
</TouchableOpacity>

<Dialog
Expand Down
156 changes: 156 additions & 0 deletions src/components/widgets/WeatherWidget.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import React, { ReactElement } from 'react';
import { Platform, StyleProp, StyleSheet, View, ViewStyle } from 'react-native';

import { useTranslation } from 'react-i18next';
import { useDisplayValues } from '../../hooks/displayValues';
import useWeatherWidget from '../../hooks/weatherWidget';
import { BodyM, BodyMSB, BodySSB, Title } from '../../styles/text';
import BaseWidget from './BaseWidget';

type TWeatherWidgetOptions = {
showStatus: boolean;
showText: boolean;
showMedian: boolean;
showNextBlockFee: boolean;
};

const WeatherWidget = ({
options,
isEditing = false,
style,
testID,
onPressIn,
onLongPress,
}: {
options: TWeatherWidgetOptions;
isEditing?: boolean;
style?: StyleProp<ViewStyle>;
testID?: string;
onPressIn?: () => void;
onLongPress?: () => void;
}): ReactElement => {
const { t } = useTranslation('widgets');
const { data, status } = useWeatherWidget();
const { condition, currentFee, nextBlockFee } = data;
const currentFeeFiat = useDisplayValues(currentFee);

return (
<BaseWidget
id="weather"
isLoading={status === 'loading'}
isEditing={isEditing}
style={style}
testID={testID}
onPressIn={onPressIn}
onLongPress={onLongPress}>
<View style={styles.container}>
{options.showStatus && (
<View style={styles.condition}>
<Title style={styles.conditionText}>
{t(`weather.condition.${condition}.title`)}
</Title>

<Title style={styles.conditionIcon}>
{condition === 'good' && '☀️'}
{condition === 'average' && '⛅'}
{condition === 'poor' && '⛈️'}
</Title>
</View>
)}

{options.showText && (
<BodyM>{t(`weather.condition.${condition}.description`)}</BodyM>
)}

{(options.showMedian || options.showNextBlockFee) && (
<View style={styles.rows}>
{options.showMedian && (
<View style={styles.row}>
<View style={styles.columnLeft}>
<BodySSB color="secondary" numberOfLines={1}>
{t('weather.current_fee')}
</BodySSB>
</View>
<View style={styles.columnRight}>
<BodyMSB numberOfLines={1}>
{currentFeeFiat.fiatSymbol} {currentFeeFiat.fiatFormatted}
</BodyMSB>
</View>
</View>
)}

{options.showNextBlockFee && (
<View style={styles.row}>
<View style={styles.columnLeft}>
<BodySSB color="secondary" numberOfLines={1}>
{t('weather.next_block')}
</BodySSB>
</View>
<View style={styles.columnRight}>
<BodyMSB numberOfLines={1}>{nextBlockFee} bitcoin/vB</BodyMSB>
</View>
</View>
)}
</View>
)}
</View>
</BaseWidget>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
gap: 16,
},
condition: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
conditionText: {
fontSize: 34,
fontWeight: 'bold',
lineHeight: 34,
letterSpacing: 0,
maxWidth: '70%',
},
conditionIcon: {
height: 100,
marginTop: 0,
paddingTop: 16,
paddingBottom: -16,
...Platform.select({
ios: {
fontSize: 100,
lineHeight: 100,
},
android: {
fontSize: 85,
lineHeight: 85,
},
}),
},
rows: {
gap: 8,
},
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
minHeight: 20,
},
columnLeft: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
},
columnRight: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-end',
},
});

export default WeatherWidget;
3 changes: 2 additions & 1 deletion src/constants/widgets.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { calculatorIcon } from '../assets/icons/widgets';
import { calculatorIcon, weatherIcon } from '../assets/icons/widgets';

export const priceFeedUrl =
'slashfeed:9ckhj7ea31ugskdewy9eiod5trhtbgcu9juza8aypjyugsp5f4oo/Bitcoin Price';
Expand All @@ -11,4 +11,5 @@ export const bitcoinFactsUrl =

export const widgets = {
calculator: { id: 'calculator', icon: calculatorIcon() },
weather: { id: 'weather', icon: weatherIcon() },
};
Loading

0 comments on commit 7330baf

Please sign in to comment.