Skip to content

Commit

Permalink
Feature/configure units (#15630)
Browse files Browse the repository at this point in the history
* adding configurable measurement units

* updating changelog, adding config screenshot

* running prettier 3.3.3

* Update CHANGELOG.md and optimise images

---------

Co-authored-by: raycastbot <[email protected]>
  • Loading branch information
johnalxndr and raycastbot authored Dec 2, 2024
1 parent 8e898da commit ae09e00
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 27 deletions.
4 changes: 4 additions & 0 deletions extensions/surf-check/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Surf Check Changelog

## [Update] - 2024-12-01

- Added unit configuration optionsfor wave height, swell height, tide, wind, and temperature.

## [Update] - 2024-11-28

- Updated the extension description to better clarify its functionality
Expand Down
21 changes: 17 additions & 4 deletions extensions/surf-check/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@ Surf Check is a Raycast extension that provides quick access to surf conditions
- Detailed view for each spot with comprehensive surf data
- Access to forecaster reports and swell information
- Direct links and quick actions to open Surfline reports for each spot
- Customizable measurement units:
- Surf height (ft/m)
- Swell height (ft/m)
- Tide height (ft/m)
- Wind speed (kts/km/h/m/s)
- Temperature (°F/°C)

## Screenshots

![Configuration](./metadata/surf-check-3.png)
![List View](./metadata/surf-check-1.png)
![Detailed View](./metadata/surf-check-2.png)

Expand All @@ -24,16 +30,22 @@ Surf Check is a Raycast extension that provides quick access to surf conditions
1. Install the Surf Check extension in Raycast
2. Set your preferred spot ID in the extension preferences
3. example: https://www.surfline.com/surf-report/lower-trestles/${spotId} | spotId is the numbers at the end of the url
4. Configure your preferred measurement units in the extension preferences:
- Choose between feet (ft) or meters (m) for surf heights
- Choose between feet (ft) or meters (m) for swell heights
- Choose between feet (ft) or meters (m) for tide heights
- Choose between knots (kts), km/h, or m/s for wind speed
- Choose between Fahrenheit (F) or Celsius (C) for temperature

### Checking Surf Conditions

1. Open Raycast and type "Surf Check"
2. You'll see a list of nearby surf spots with basic information:
- Spot name
- Current conditions (e.g., Fair, Good)
- Wave height range
- Wind speed and description
- Water temperature
- Wave height range (in your preferred unit)
- Wind speed and description (in your preferred unit)
- Water temperature (in your preferred unit)

### Viewing Detailed Spot Information

Expand All @@ -43,6 +55,7 @@ Surf Check is a Raycast extension that provides quick access to surf conditions
- Tide information
- Swell details
- Forecaster report (if available)
All measurements are displayed in your chosen units.

### Additional Actions

Expand Down
Binary file added extensions/surf-check/metadata/surf-check-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
94 changes: 94 additions & 0 deletions extensions/surf-check/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,100 @@
"required": true,
"title": "Spot ID",
"description": "Enter the Spot ID for your preferred surf spot."
},
{
"name": "surfHeightUnit",
"type": "dropdown",
"required": true,
"title": "Surf Height Unit",
"description": "Choose unit for wave heights",
"default": "ft",
"data": [
{
"title": "Feet (ft)",
"value": "ft"
},
{
"title": "Meters (m)",
"value": "m"
}
]
},
{
"name": "swellHeightUnit",
"type": "dropdown",
"required": true,
"title": "Swell Height Unit",
"description": "Choose unit for swell measurements",
"default": "ft",
"data": [
{
"title": "Feet (ft)",
"value": "ft"
},
{
"title": "Meters (m)",
"value": "m"
}
]
},
{
"name": "tideHeightUnit",
"type": "dropdown",
"required": true,
"title": "Tide Height Unit",
"description": "Choose unit for tide measurements",
"default": "ft",
"data": [
{
"title": "Feet (ft)",
"value": "ft"
},
{
"title": "Meters (m)",
"value": "m"
}
]
},
{
"name": "windSpeedUnit",
"type": "dropdown",
"required": true,
"title": "Wind Speed Unit",
"description": "Choose your preferred wind speed unit",
"default": "kts",
"data": [
{
"title": "Knots (kts)",
"value": "kts"
},
{
"title": "Kilometers per hour (km/h)",
"value": "kph"
},
{
"title": "Meters per second (m/s)",
"value": "ms"
}
]
},
{
"name": "temperatureUnit",
"type": "dropdown",
"required": true,
"title": "Temperature Unit",
"description": "Choose temperature unit",
"default": "F",
"data": [
{
"title": "Fahrenheit (°F)",
"value": "F"
},
{
"title": "Celsius (°C)",
"value": "C"
}
]
}
]
}
Expand Down
73 changes: 50 additions & 23 deletions extensions/surf-check/src/surf-check.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@ import { ActionPanel, Detail, List, Action, Icon, Color, useNavigation } from "@
import { useState, useEffect } from "react";
import fetch from "node-fetch";
import { getPreferenceValues } from "@raycast/api";
import {
HeightUnit,
WindSpeedUnit,
TemperatureUnit,
UnitPreferences,
formatHeight,
formatTemp,
formatWindSpeed,
} from "./utils";

interface Preferences {
interface Preferences extends UnitPreferences {
spotId: string;
}

Expand Down Expand Up @@ -53,8 +62,8 @@ function formatString(str: string): string {
.replace(/\b\w/g, (c) => c.toUpperCase());
}

function formatSurfHeight(min: number, max: number): string {
return `${Math.round(min)}-${Math.round(max)}ft`;
function formatSurfHeight(min: number, max: number, unit: HeightUnit): string {
return `${formatHeight(min, unit)}-${formatHeight(max, unit)}`;
}

function getRatingIcon(rating: string): { icon: Icon; tintColor: Color } {
Expand Down Expand Up @@ -95,7 +104,7 @@ function formatUrlName(name: string): string {
export default function Command() {
const [spots, setSpots] = useState<Spot[]>([]);
const [isLoading, setIsLoading] = useState(true);
const { spotId } = getPreferenceValues<Preferences>();
const { spotId, surfHeightUnit, windSpeedUnit, temperatureUnit } = getPreferenceValues<Preferences>();

useEffect(() => {
fetchSurfData();
Expand Down Expand Up @@ -123,15 +132,21 @@ export default function Command() {
key={spot._id}
icon={{ source: icon, tintColor }}
title={spot.name}
subtitle={`${formatString(spot.rating.key)} (${formatSurfHeight(spot.waveHeight.min, spot.waveHeight.max)})`}
subtitle={`${formatString(spot.rating.key)} (${formatSurfHeight(spot.waveHeight.min, spot.waveHeight.max, surfHeightUnit)})`}
accessories={[
{ text: formatString(spot.waveHeight.humanRelation) },
{ icon: Icon.Wind, text: `${getWindDescription(spot.wind.speed)} ${spot.wind.speed}kts` },
{ icon: Icon.Temperature, text: `${spot.waterTemp.max}°F` },
{
icon: Icon.Wind,
text: `${getWindDescription(spot.wind.speed)} ${formatWindSpeed(spot.wind.speed, windSpeedUnit)}`,
},
{ icon: Icon.Temperature, text: formatTemp(spot.waterTemp.max, temperatureUnit) },
]}
actions={
<ActionPanel>
<Action.Push title="Show Details" target={<SpotDetails spot={spot} />} />
<Action.Push
title="Show Details"
target={<SpotDetails spot={spot} preferences={{ surfHeightUnit, windSpeedUnit, temperatureUnit }} />}
/>
<Action.OpenInBrowser title="Open Surfline Report" url={surflineUrl} />
</ActionPanel>
}
Expand All @@ -142,7 +157,17 @@ export default function Command() {
);
}

function SpotDetails({ spot }: { spot: Spot }) {
interface SpotDetailsProps {
spot: Spot;
preferences: {
surfHeightUnit: HeightUnit;
windSpeedUnit: WindSpeedUnit;
temperatureUnit: TemperatureUnit;
};
}

function SpotDetails({ spot, preferences }: SpotDetailsProps) {
const { surfHeightUnit, windSpeedUnit, temperatureUnit } = preferences;
const { pop } = useNavigation();
const [detailedSpot, setDetailedSpot] = useState<DetailedSpot | null>(null);
const [isLoading, setIsLoading] = useState(true);
Expand Down Expand Up @@ -191,26 +216,25 @@ function SpotDetails({ spot }: { spot: Spot }) {
const detailsMarkdown = `
# ${detailedSpot.spot.name}
<img src="${icon}" width="32" height="32" style="vertical-align: middle; margin-right: 8px;" /> ${formatString(detailedSpot.forecast.conditions.value)} (${formatSurfHeight(detailedSpot.forecast.waveHeight.min, detailedSpot.forecast.waveHeight.max)})
<img src="${icon}" width="32" height="32" style="vertical-align: middle; margin-right: 8px;" /> ${formatString(detailedSpot.forecast.conditions.value)} (${formatSurfHeight(detailedSpot.forecast.waveHeight.min, detailedSpot.forecast.waveHeight.max, surfHeightUnit)})
## Surf Conditions
- Wave Height: ${detailedSpot.forecast.waveHeight.humanRelation}
- Wind: ${getWindDescription(detailedSpot.forecast.wind.speed)} ${detailedSpot.forecast.wind.speed}kts ${detailedSpot.forecast.wind.directionType} (${detailedSpot.forecast.wind.direction}°)
- Water Temperature: ${detailedSpot.forecast.waterTemp.min}°F - ${detailedSpot.forecast.waterTemp.max}°F
- Wind: ${getWindDescription(detailedSpot.forecast.wind.speed)} ${formatWindSpeed(detailedSpot.forecast.wind.speed, windSpeedUnit)} ${detailedSpot.forecast.wind.directionType} (${detailedSpot.forecast.wind.direction}°)
- Water Temperature: ${formatTemp(detailedSpot.forecast.waterTemp.min, temperatureUnit)} - ${formatTemp(detailedSpot.forecast.waterTemp.max, temperatureUnit)}
## Tide Details
| Type | Height | Time |
|------|--------|------|
| Previous | ${detailedSpot.forecast.tide.previous.type} ${detailedSpot.forecast.tide.previous.height}ft | ${formatTime(detailedSpot.forecast.tide.previous.timestamp)} |
| Current | ${detailedSpot.forecast.tide.current.type} ${detailedSpot.forecast.tide.current.height}ft | ${formatTime(detailedSpot.forecast.tide.current.timestamp)} |
| Next | ${detailedSpot.forecast.tide.next.type} ${detailedSpot.forecast.tide.next.height}ft | ${formatTime(detailedSpot.forecast.tide.next.timestamp)} |
| Previous | ${detailedSpot.forecast.tide.previous.type} ${formatHeight(detailedSpot.forecast.tide.previous.height, surfHeightUnit)} | ${formatTime(detailedSpot.forecast.tide.previous.timestamp)} |
| Current | ${detailedSpot.forecast.tide.current.type} ${formatHeight(detailedSpot.forecast.tide.current.height, surfHeightUnit)} | ${formatTime(detailedSpot.forecast.tide.current.timestamp)} |
| Next | ${detailedSpot.forecast.tide.next.type} ${formatHeight(detailedSpot.forecast.tide.next.height, surfHeightUnit)} | ${formatTime(detailedSpot.forecast.tide.next.timestamp)} |
## Swells
${detailedSpot.forecast.swells
.map(
(swell, index) =>
`${index + 1}. Height: ${swell.height}ft, Period: ${swell.period}s, Direction: ${swell.direction}°`,
`${index + 1}. Height: ${formatHeight(swell.height, surfHeightUnit)}, Period: ${swell.period}s, Direction: ${swell.direction}°`,
)
.join("\n")}
Expand Down Expand Up @@ -242,24 +266,27 @@ _Data provided by Surfline_
/>
<Detail.Metadata.Label
title="Wave Height"
text={formatSurfHeight(detailedSpot.forecast.waveHeight.min, detailedSpot.forecast.waveHeight.max)}
// Removed the waveform icon here
text={formatSurfHeight(
detailedSpot.forecast.waveHeight.min,
detailedSpot.forecast.waveHeight.max,
surfHeightUnit,
)}
/>
<Detail.Metadata.Label
title="Wind"
text={`${getWindDescription(detailedSpot.forecast.wind.speed)} ${detailedSpot.forecast.wind.speed}kts`}
text={`${getWindDescription(detailedSpot.forecast.wind.speed)} ${formatWindSpeed(detailedSpot.forecast.wind.speed, windSpeedUnit)}`}
icon={Icon.Wind}
/>
<Detail.Metadata.Label
title="Water Temp"
text={`${detailedSpot.forecast.waterTemp.min}°-${detailedSpot.forecast.waterTemp.max}°F`}
title="Water Temperature"
text={`${formatTemp(detailedSpot.forecast.waterTemp.min, temperatureUnit)} - ${formatTemp(detailedSpot.forecast.waterTemp.max, temperatureUnit)}`}
icon={Icon.Temperature}
/>
</Detail.Metadata>
}
actions={
<ActionPanel>
<Action title="Go Back" icon={Icon.ArrowLeft} onAction={pop} />
<Action title="Go Back" onAction={pop} />
<Action.OpenInBrowser title="Open Surfline Report" url={surflineUrl} />
</ActionPanel>
}
Expand Down
75 changes: 75 additions & 0 deletions extensions/surf-check/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
export type HeightUnit = "ft" | "m";
export type WindSpeedUnit = "kts" | "kph" | "ms";
export type TemperatureUnit = "F" | "C";

export interface UnitPreferences {
windSpeedUnit: WindSpeedUnit;
surfHeightUnit: HeightUnit;
swellHeightUnit: HeightUnit;
tideHeightUnit: HeightUnit;
temperatureUnit: TemperatureUnit;
}

export function convertHeight(value: number, from: HeightUnit, to: HeightUnit): number {
if (from === to) return value;
return from === "ft" ? Math.round(value * 0.3048 * 10) / 10 : Math.round((value / 0.3048) * 10) / 10;
}

export function convertTemp(value: number, from: TemperatureUnit, to: TemperatureUnit): number {
if (from === to) return value;
return from === "F" ? Math.round(((value - 32) * 5) / 9) : Math.round((value * 9) / 5 + 32);
}

export function convertWindSpeed(value: number, from: WindSpeedUnit, to: WindSpeedUnit): number {
if (from === to) return value;
// Convert to knots first as base unit
let kts = value;
if (from === "kph") kts = value / 1.852;
if (from === "ms") kts = value / 0.514444;

// Convert from knots to target unit
switch (to) {
case "kts":
return Math.round(kts);
case "kph":
return Math.round(kts * 1.852);
case "ms":
return Math.round(kts * 0.514444 * 10) / 10;
}
}

export function getUnitLabel(unit: HeightUnit | WindSpeedUnit | TemperatureUnit): string {
switch (unit) {
case "ft":
return "ft";
case "m":
return "m";
case "kts":
return "kts";
case "kph":
return "km/h";
case "ms":
return "m/s";
case "F":
return "°F";
case "C":
return "°C";
default:
return unit;
}
}

export function formatHeight(value: number, unit: HeightUnit, fromUnit: HeightUnit = "ft"): string {
const converted = fromUnit === unit ? value : convertHeight(value, fromUnit, unit);
return `${converted}${getUnitLabel(unit)}`;
}

export function formatTemp(value: number, unit: TemperatureUnit, fromUnit: TemperatureUnit = "F"): string {
const converted = fromUnit === unit ? value : convertTemp(value, fromUnit, unit);
return `${converted}${getUnitLabel(unit)}`;
}

export function formatWindSpeed(value: number, unit: WindSpeedUnit, fromUnit: WindSpeedUnit = "kts"): string {
const converted = fromUnit === unit ? value : convertWindSpeed(value, fromUnit, unit);
return `${converted}${getUnitLabel(unit)}`;
}

0 comments on commit ae09e00

Please sign in to comment.