diff --git a/.github/workflows/dependabot.yml b/.github/dependabot.yml
similarity index 100%
rename from .github/workflows/dependabot.yml
rename to .github/dependabot.yml
diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml
index 4869cd9..0c30cf5 100644
--- a/.github/workflows/dependabot-automerge.yml
+++ b/.github/workflows/dependabot-automerge.yml
@@ -11,7 +11,7 @@ jobs:
steps:
- name: Dependabot metadata
id: metadata
- uses: dependabot/fetch-metadata@v1.1.1
+ uses: dependabot/fetch-metadata@v2.1.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Enable auto-merge for Dependabot PRs
diff --git a/gatsby-node.ts b/gatsby-node.ts
index 834286a..ef0f6b0 100644
--- a/gatsby-node.ts
+++ b/gatsby-node.ts
@@ -1,7 +1,10 @@
import TidalData from "./data/tides.json";
import path from "path";
-import { TidesJson_PDFObject } from "./src/types";
-import { CreatePagesArgs } from "gatsby";
+import { TidesJson_PDFObject, TidesJson_ScheduleObject } from "./src/types";
+import { BuildArgs, CreatePagesArgs } from "gatsby";
+import fs from "fs";
+import ical, { ICalEventBusyStatus, ICalEventClass } from "ical-generator";
+import { DateTime } from "luxon";
export const createPages = async function ({
actions,
@@ -9,10 +12,70 @@ export const createPages = async function ({
}: CreatePagesArgs) {
TidalData.pdfs.forEach((pdf: TidesJson_PDFObject) => {
actions.createPage({
- path: "tide-tables/" + pdf.url,
+ path: "/tide-tables/" + pdf.url,
component: path.resolve(`./src/components/templates/TideTablePage.tsx`),
context: { pdf },
defer: false,
});
+ actions.createRedirect({
+ fromPath: "/tide-tables/" + pdf.url.replace("/", "-"),
+ toPath: "/tide-tables/" + pdf.url,
+ });
+ actions.createRedirect({
+ fromPath: "/tide-tables/" + pdf.url.replace("/", "-") + ".pdf",
+ toPath: "/tide-tables/" + pdf.url + ".pdf",
+ });
+ });
+
+ // Legacy page
+ actions.createRedirect({
+ fromPath: "/historical-tables",
+ toPath: "/tide-tables",
});
};
+
+export const onPostBootstrap = function ({ reporter }: BuildArgs) {
+ reporter.info(`Generating iCal file for tide times`);
+ const cal = ical();
+ cal.timezone("Europe/London");
+ cal.name("Porthmadog Tide Times");
+ cal.description(
+ "Tide times for Porthmadog, Borth-y-gest, Morfa Bychan and Black Rock Sands from Port-Tides.com"
+ );
+ const today = new Date();
+ today.setHours(0, 0, 0, 0);
+ const nextYear = new Date(today);
+ nextYear.setDate(today.getDate() + 365);
+ TidalData.schedule
+ .filter((tideDay: TidesJson_ScheduleObject) => {
+ let date = new Date(tideDay.date);
+ return date >= today && date <= nextYear;
+ })
+ .forEach((day: TidesJson_ScheduleObject) =>
+ day.groups.forEach((tide) => {
+ cal.createEvent({
+ start: DateTime.fromSQL(day.date + " " + tide.time).toJSDate(),
+ end: DateTime.fromSQL(day.date + " " + tide.time)
+ .plus({ minutes: 30 })
+ .toJSDate(),
+ summary: `High Tide Porthmadog - ${tide.height}m`,
+ description: {
+ plain: "Powered by port-tides.com",
+ html: `More details at port-tides.com`,
+ },
+ // Commented out to reduce file size
+ /*location: {
+ title: "Porthmadog",
+ address: "Harbwr Porthmadog, LL49 9AY, UK",
+ },
+ busystatus: ICalEventBusyStatus.FREE,
+ class: ICalEventClass.PUBLIC,
+ url: "https://port-tides.com/tide-tables",*/
+ });
+ })
+ );
+ fs.writeFileSync("public/porthmadog-tides.ical", cal.toString());
+ reporter.success(
+ `Generated iCal file for tide times at public/porthmadog-tides.ical`
+ );
+};
diff --git a/package-lock.json b/package-lock.json
index bfed7ad..e0bd6b4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,6 +17,7 @@
"gatsby-plugin-sitemap": "^6.13.1",
"gatsby-source-build-date": "^1.0.1",
"gatsby-source-filesystem": "^5.13.1",
+ "ical-generator": "^7.1.0",
"luxon": "^3.4.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -4370,7 +4371,7 @@
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz",
"integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==",
- "dev": true
+ "devOptional": true
},
"node_modules/@types/minimatch": {
"version": "5.1.2",
@@ -10886,6 +10887,56 @@
"node": ">=10.17.0"
}
},
+ "node_modules/ical-generator": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/ical-generator/-/ical-generator-7.1.0.tgz",
+ "integrity": "sha512-mv7wW35+YHbaFDQGgFPSnn+SOiN805UvQL8VaMye6F6AgTQCDlT8dc0XIndCHgp22KvOdJ0po795QHoPIAq+kA==",
+ "dependencies": {
+ "uuid-random": "^1.3.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@touch4it/ical-timezones": ">=1.6.0",
+ "@types/luxon": ">= 1.26.0",
+ "@types/mocha": ">= 8.2.1",
+ "dayjs": ">= 1.10.0",
+ "luxon": ">= 1.26.0",
+ "moment": ">= 2.29.0",
+ "moment-timezone": ">= 0.5.33",
+ "rrule": ">= 2.6.8"
+ },
+ "peerDependenciesMeta": {
+ "@touch4it/ical-timezones": {
+ "optional": true
+ },
+ "@types/luxon": {
+ "optional": true
+ },
+ "@types/mocha": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "dayjs": {
+ "optional": true
+ },
+ "luxon": {
+ "optional": true
+ },
+ "moment": {
+ "optional": true
+ },
+ "moment-timezone": {
+ "optional": true
+ },
+ "rrule": {
+ "optional": true
+ }
+ }
+ },
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -17054,6 +17105,11 @@
"uuid": "dist/bin/uuid"
}
},
+ "node_modules/uuid-random": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/uuid-random/-/uuid-random-1.3.2.tgz",
+ "integrity": "sha512-UOzej0Le/UgkbWEO8flm+0y+G+ljUon1QWTEZOq1rnMAsxo2+SckbiZdKzAHHlVh6gJqI1TjC/xwgR50MuCrBQ=="
+ },
"node_modules/v8-compile-cache": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz",
diff --git a/package.json b/package.json
index 815eecd..458f653 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"gatsby-plugin-sitemap": "^6.13.1",
"gatsby-source-build-date": "^1.0.1",
"gatsby-source-filesystem": "^5.13.1",
+ "ical-generator": "^7.1.0",
"luxon": "^3.4.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
diff --git a/src/pages/ical.tsx b/src/pages/ical.tsx
new file mode 100644
index 0000000..95cba32
--- /dev/null
+++ b/src/pages/ical.tsx
@@ -0,0 +1,171 @@
+import { Accordion, Button, Code, CopyButton, Text, rem } from "@mantine/core";
+import { IconCopy, IconCopyCheck, IconHome } from "@tabler/icons-react";
+import type { HeadFC, PageProps } from "gatsby";
+import { Link } from "gatsby";
+import * as React from "react";
+import TidalData from "../../data/tides.json";
+import { SEO } from "../components/SEO";
+import Layout from "../components/navigation/Layout";
+import { TideTablesMonthList } from "../components/tideTables/TideTablesMonthList";
+import { TidesJson_PDFObject } from "../types";
+import { useLocation } from "@reach/router";
+
+const Page: React.FC = () => {
+ const { origin } = useLocation();
+ const iCalUrl = origin + "/porthmadog-tides.ical";
+ return (
+
+ } variant="light">
+ Homepage
+
+
+ }
+ >
+
+
+
+
+ Google Calendar
+
+
+
+
+
+ -
+ Copy the URL {iCalUrl}
+
+ {({ copied, copy }) => (
+
+ )}
+
+
+ -
+
+ Open Google Calendar
+
+ , click the + above My calendars and choose{" "}
+ From URL.
+
+ -
+ Paste the URL into the field named URL of calendar{" "}
+ that appears and click
+ Add calendar.
+
+ -
+ After a few seconds, it should appear in your calendar. If it
+ does not, try reloading Google Calendar.
+
+
+
+
+
+
+
+
+ Apple Calendar
+
+
+
+
+
+ -
+ Copy the URL {iCalUrl}
+
+ {({ copied, copy }) => (
+
+ )}
+
+
+ -
+ In Apple Calendar, click the File menu and choose{" "}
+ New Calendar Subscription….
+
+ -
+ Paste the URL into the dialog that appears and click{" "}
+ Subscribe.
+
+ -
+ A window with settings of the calendar subscription will
+ appear. Set Auto-refresh to Every hour to
+ keep your calendar up to date and click OK.
+
+
+
+
+
+
+
+
+ Outlook
+
+
+
+
+
+ -
+ Copy the URL {iCalUrl}
+
+ {({ copied, copy }) => (
+
+ )}
+
+
+ -
+ In Outlook, click Open Calendar and choose{" "}
+ From Internet….
+
+ -
+ Paste the URL into the Outlook dialog that appears and click{" "}
+ OK.
+
+ -
+ After a few seconds, Outlook will ask if the internet calendar
+ should be added. Click Yes.
+
+
+
+
+
+
+
+ );
+};
+export default Page;
+
+export const Head: HeadFC = () => ;
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 4dfe86f..a353514 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -7,9 +7,9 @@ import {
Stack,
Text,
Title,
- useMatches
+ useMatches,
} from "@mantine/core";
-import { IconArrowRight, IconTable } from "@tabler/icons-react";
+import { IconArrowRight, IconCalendar, IconTable } from "@tabler/icons-react";
import { Link, type HeadFC, type PageProps } from "gatsby";
import { DateTime } from "luxon";
import * as React from "react";
@@ -123,11 +123,18 @@ const Page: React.FC = () => {
Monthly Tide Tables
-
- } variant="light">
- View All
-
-
+
+
+ } variant="light">
+ Add to Calendar
+
+
+
+ } variant="light">
+ View All
+
+
+