Skip to content

Commit

Permalink
Weather: Add weather 0.3.0 (raycast#74)
Browse files Browse the repository at this point in the history
* add multi language support for en, de and fr

* add better error message

* add support for switching the unit-system

* add default query

* remove multi language support

* add icon for daily forecast items
  • Loading branch information
tonka3000 authored and FezVrasta committed Nov 24, 2021
1 parent 84bf735 commit 2bbe128
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 17 deletions.
25 changes: 24 additions & 1 deletion extensions/weather/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "weather",
"title": "Weather",
"version": "0.2.0",
"version": "0.3.0",
"description": "Weather forecast via wttr.in",
"icon": "weather.png",
"author": "tonka3000",
Expand All @@ -14,6 +14,29 @@
"mode": "view"
}
],
"preferences" : [
{
"name" : "unitsystem",
"type" : "dropdown",
"title" : "Unit System",
"required" : false,
"default" : "metric",
"description" : "Unit System",
"data" : [
{ "title" : "Metric System", "value" : "metric" },
{ "title" : "Imperial Units", "value" : "imperial" }
]
},
{
"name" : "defaultquery",
"type" : "textfield",
"title" : "Default query",
"placeholder" : "Empty or location (e.g. London)",
"required" : false,
"default" : "",
"description" : "Default query when search bar is empty. Same content as in the search bar."
}
],
"dependencies": {
"@raycast/api": "^1.24.5",
"moment": "^2.29.1",
Expand Down
36 changes: 33 additions & 3 deletions extensions/weather/src/components/day.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { List } from "@raycast/api";
import { getIcon, getWindDirectionIcon } from "../icons";
import { WeatherData } from "../wttr";
import { getWindUnit, getWttrTemperaturePostfix, getWttrWindPostfix } from "../unit";
import { Hourly, WeatherData } from "../wttr";

function getTime(time: string): string {
const h = parseInt(time) / 100.0;
Expand All @@ -10,16 +11,45 @@ function getTime(time: string): string {

export function DayList(props: { day: WeatherData; title: string }) {
const day = props.day;

const getWeatherDesc = (hour: Hourly): string => {
try {
return hour.weatherDesc[0].value;
} catch (e: any) {
return "?";
}
};

const getWind = (hour: Hourly): string => {
const data = hour as Record<string, any>;
const key = `windspeed${getWttrWindPostfix()}`;
let val = "?";
if (data[key]) {
val = data[key] || "?";
}
return `${val} ${getWindUnit()}`;
};

const getTemp = (hour: Hourly): string => {
const data = hour as Record<string, any>;
const key = `temp${getWttrTemperaturePostfix()}`;
let val = "?";
if (data[key]) {
val = data[key] || "?";
}
return `${val} ${getWindUnit()}`;
};

return (
<List>
<List.Section title={props.title}>
{day.hourly.map((data, _) => (
<List.Item
key={data.time.toString()}
title={`${getTime(data.time)}`}
subtitle={`${data.tempC} °C , ${data.weatherDesc[0].value}`}
subtitle={`${getTemp(data)} , ${getWeatherDesc(data)}`}
icon={getIcon(data.weatherCode)}
accessoryTitle={`humidity: ${data.humidity}% | wind: ${data.windspeedKmph} km/h ${getWindDirectionIcon(
accessoryTitle={`humidity: ${data.humidity}% | wind: ${getWind(data)} ${getWindDirectionIcon(
data.winddirDegree
)}`}
/>
Expand Down
62 changes: 54 additions & 8 deletions extensions/weather/src/components/weather.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
import { ActionPanel, List, PushAction, showToast, ToastStyle } from "@raycast/api";
import { ActionPanel, Color, getPreferenceValues, Icon, List, PushAction, showToast, ToastStyle } from "@raycast/api";
import moment from "moment";
import { useEffect, useState } from "react";
import { getIcon, getWindDirectionIcon } from "../icons";
import { getTemperatureUnit, getWindUnit, getWttrTemperaturePostfix, getWttrWindPostfix } from "../unit";
import { Weather, WeatherData, wttr } from "../wttr";
import { DayList } from "./day";

export function DayListItem(props: { day: WeatherData; title: string }) {
const data = props.day;
const wd = getWeekday(data.date);
const getTemp = (prefix: string) => {
const unit = getWttrTemperaturePostfix();
const key = `${prefix}temp${unit}`;
const rec = data as Record<string, any>;
let val = "?";
if (rec[key]) {
val = `${rec[key]}`;
}
return `${val} ${getTemperatureUnit()}`;
};
return (
<List.Item
key={data.date}
title={wd}
subtitle={`max: ${data.maxtempC} °C, min: ${data.mintempC} °C`}
subtitle={`max: ${getTemp("max")}, min: ${getTemp("min")}`}
icon={{ source: Icon.Calendar, tintColor: Color.PrimaryText }}
actions={
<ActionPanel>
<PushAction title="Show Details" target={<DayList day={data} title={`${props.title} - ${wd}`} />} />
Expand All @@ -24,10 +36,10 @@ export function DayListItem(props: { day: WeatherData; title: string }) {

function getWeekday(date: string): string {
const d = moment(date);
return d.format("dddd");
return d.locale("en").format("dddd");
}

export function WeatherList(props: {}) {
export function WeatherList() {
const [query, setQuery] = useState<string>("");
const { data, error, isLoading } = useSearch(query);
if (error) {
Expand All @@ -39,23 +51,45 @@ export function WeatherList(props: {}) {

const area = data.nearest_area[0];
const curcon = data.current_condition[0];
const curcon_data = curcon as any;

const title = `${area.areaName[0].value}, ${area.region[0].value}, ${area.country[0].value}`;

const weatherDesc = curcon.weatherDesc[0].value;

const getWind = (): string => {
const data = curcon as Record<string, any>;
const key = `windspeed${getWttrWindPostfix()}`;
let val = "?";
if (data[key]) {
val = data[key] || "?";
}
return `${val} ${getWindUnit()}`;
};

const getTemp = (): string => {
const key = `temp_${getWttrTemperaturePostfix()}`;
const f = curcon as Record<string, any>;
let val = "?";
if (f[key]) {
val = f[key];
}
return `${val} ${getTemperatureUnit()}`;
};
return (
<List
isLoading={isLoading}
searchBarPlaceholder="Search for other location (e.g. London)"
onSearchTextChange={setQuery}
throttle={true}
>
<List.Section title={`Current (${title})`}>
<List.Section title={`Weather report (${title})`}>
<List.Item
key="_"
title={`${curcon.temp_C}°C`}
subtitle={curcon.weatherDesc[0].value}
title={getTemp()}
subtitle={weatherDesc}
icon={getIcon(curcon.weatherCode)}
accessoryTitle={`humidity: ${curcon.humidity}% | wind: ${curcon.windspeedKmph} km/h ${getWindDirectionIcon(
accessoryTitle={`humidity: ${curcon.humidity}% | wind ${getWind()} ${getWindDirectionIcon(
curcon.winddirDegree
)}`}
/>
Expand All @@ -69,6 +103,12 @@ export function WeatherList(props: {}) {
);
}

function getDefaultQuery(): string | undefined {
const pref = getPreferenceValues();
const q = (pref.defaultquery as string) || undefined;
return q;
}

export function useSearch(query: string | undefined): {
data: Weather | undefined;
error?: string;
Expand All @@ -82,6 +122,12 @@ export function useSearch(query: string | undefined): {

useEffect(() => {
async function fetchData() {
if (!query) {
const dq = getDefaultQuery();
if (dq && dq.length > 0) {
query = dq;
}
}
if (query === null || cancel) {
return;
}
Expand Down
48 changes: 48 additions & 0 deletions extensions/weather/src/unit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { getPreferenceValues } from "@raycast/api";

export function getUnitSystem(): string {
const pref = getPreferenceValues();
let result = UnitSystem.Metric;
let us = (pref.unitsystem as string) || undefined;
if (us && (us === UnitSystem.Metric || us === UnitSystem.Imperial)) {
result = us;
}
return result;
}

export function getWttrTemperaturePostfix(): string {
if (getUnitSystem() === UnitSystem.Imperial) {
return "F";
} else {
return "C";
}
}

export function getTemperatureUnit(): string {
if (getUnitSystem() === UnitSystem.Imperial) {
return "°F";
} else {
return "°C";
}
}

export function getWttrWindPostfix(): string {
if (getUnitSystem() === UnitSystem.Imperial) {
return "Miles";
} else {
return "Kmph";
}
}

export function getWindUnit(): string {
if (getUnitSystem() === UnitSystem.Imperial) {
return "mph";
} else {
return "km/h";
}
}

export enum UnitSystem {
Metric = "metric",
Imperial = "imperial"
}
31 changes: 26 additions & 5 deletions extensions/weather/src/wttr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,22 @@ function paramString(params: { [key: string]: string }): string {

async function toJsonOrError(response: Response): Promise<any> {
const s = response.status;
const getJson = async (): Promise<any> => {
try {
return await response.json();
}
catch (e: any) {
throw Error(`Server-side issue at wttr.in (${s} - invalid json). Please try again later`);
}
};
console.log(`status code: ${s}`);
if (s >= 200 && s < 300) {
const json = await response.json();
const json = await getJson();
return json;
} else if (s == 401) {
throw Error("Unauthorized");
} else if (s == 403) {
const json = await response.json();
const json = await getJson();
let msg = "Forbidden";
if (json.error && json.error == "insufficient_scope") {
msg = "Insufficient API token scope";
Expand All @@ -134,10 +142,13 @@ async function toJsonOrError(response: Response): Promise<any> {
} else if (s == 404) {
throw Error("Not found");
} else if (s >= 400 && s < 500) {
const json = await response.json();
const json = await getJson();
console.log(json);
const msg = json.message;
throw Error(msg);
} else if (s >= 500 && s < 600) {
throw Error(`Server-side issue at wttr.in (${s}). Please try again later`);

} else {
console.log("unknown error");
throw Error(`http status ${s}`);
Expand Down Expand Up @@ -168,10 +179,20 @@ export class Wttr {
}
}

public async getWeather(city?: string): Promise<Weather> {
public async getWeather(city?: string, language?: string | undefined): Promise<Weather> {
// setting lang: "en" manipulate the e.g. kmph values
return await this.fetch(city, { format: "j1" }) as Weather;
let params: Record<string, string> = {
format: "j1"
};
if (language && supportedLanguages.includes(language)) {
params.lang = language;
}
return await this.fetch(city, params) as Weather;
}
}

export const supportedLanguages: string[] = [
"en", "de", "fr"
];

export const wttr = new Wttr();

0 comments on commit 2bbe128

Please sign in to comment.