Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/configure units #15630

Merged
merged 5 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)}`;
}
Loading