web version

This commit is contained in:
Morten Olsen
2022-05-07 12:35:14 +02:00
parent 7057e11e96
commit 0b2f23ecb2
10 changed files with 144 additions and 15 deletions

25
.github/workflows/publish-web.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Build and Deploy web
on:
workflow_dispatch:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v2.3.1
with:
persist-credentials: false
- name: Install and Build 🔧
run: |
yarn install
NODE_ENV=production yarn expo build:web
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@4.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: gh-pages
folder: web-build

View File

@@ -2,6 +2,7 @@
"name": "bob",
"version": "1.0.0",
"main": "node_modules/expo/AppEntry.js",
"homepage": "/bob-the-algorithm",
"scripts": {
"start": "expo start",
"android": "expo start --android",
@@ -50,6 +51,7 @@
"@babel/core": "^7.12.9",
"@types/chroma-js": "^2.1.3",
"@types/react": "~17.0.21",
"@types/react-dom": "^18.0.3",
"@types/react-native": "~0.64.12",
"@types/styled-components-react-native": "^5.1.3",
"babel-plugin-module-resolver": "^4.1.0",

View File

@@ -1,7 +1,20 @@
import { Calendar } from "expo-calendar";
import { createContext } from "react";
type CalendarContextValue = {
type RejectedCalendarContextValue = {
status: 'rejected';
date: Date;
setDate: (date: Date) => void;
}
type UnavailableCalendarContextValue = {
status: 'unavailable';
date: Date;
setDate: (date: Date) => void;
}
type AcceptedCalendarContextValue = {
status: 'ready';
date: Date;
setDate: (date: Date) => void;
calendars: Calendar[];
@@ -11,6 +24,10 @@ type CalendarContextValue = {
error?: any;
}
type CalendarContextValue = RejectedCalendarContextValue
| UnavailableCalendarContextValue
| AcceptedCalendarContextValue
const CalendarContext = createContext<CalendarContextValue>(undefined as any);
export type { CalendarContextValue };

View File

@@ -5,24 +5,39 @@ import { useAsync, useAsyncCallback } from "#/hooks/async";
import { createEventAsync, deleteEventAsync, getEventsAsync } from "expo-calendar";
import { PlanItem } from "#/types/plans";
const emptyArray: never[] = [];
const emptyFn = () => undefined;
export const useCalendar = () => {
const { calendar } = useContext(CalendarContext);
return calendar;
const context = useContext(CalendarContext);
if (context.status !== 'ready') {
return undefined;
}
return context.calendar;
}
export const useCalendars = () => {
const { calendars } = useContext(CalendarContext);
return calendars;
const context = useContext(CalendarContext);
if (context.status !== 'ready') {
return emptyArray;
}
return context.calendars;
}
export const useSelectedCalendars = () => {
const { selected } = useContext(CalendarContext);
return selected;
const context = useContext(CalendarContext);
if (context.status !== 'ready') {
return emptyArray;
}
return context.selected;
}
export const useSetSelectedCalendars = () => {
const { setSelected } = useContext(CalendarContext);
return setSelected;
const context = useContext(CalendarContext);
if (context.status !== 'ready') {
return emptyFn;
}
return context.setSelected;
}
export const useDate = () => {
@@ -40,6 +55,9 @@ export const useCommit = () => {
const calendar = useCalendar();
const result = useAsyncCallback(
async (plan: PlanItem[]) => {
if (!calendar) {
return;
}
const end = set(date, {
hours: 24,
minutes: 0,
@@ -64,7 +82,7 @@ export const useCommit = () => {
})
}
},
[date],
[date, calendar],
);
return result;

View File

@@ -3,6 +3,7 @@ import React, { ReactNode, useCallback, useMemo, useState } from "react";
import { useAsync } from "#/hooks/async";
import { CalendarContext } from "./context";
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Platform } from "react-native";
const SELECTED_STORAGE_KEY = 'selected_calendars';
@@ -15,6 +16,8 @@ type CalendarProviderProps = {
type SetupResponse = {
status: 'rejected';
} | {
status: 'unavailable';
} | {
status: 'ready';
calendar: Calendar;
@@ -31,6 +34,9 @@ const CalendarProvider: React.FC<CalendarProviderProps> = ({
const [value] = useAsync<SetupResponse>(
async () => {
const { status } = await requestCalendarPermissionsAsync();
if (Platform.OS !== 'ios') {
return { status: 'unavailable' };
}
if (status !== 'granted') {
return { status: 'rejected' };
}
@@ -81,13 +87,22 @@ const CalendarProvider: React.FC<CalendarProviderProps> = ({
[value, selectedIds],
);
if (!value || value.status !== 'ready') {
if (!value) {
return <></>
}
if (value.status !== 'ready') {
return (
<CalendarContext.Provider value={{ status: value.status, date, setDate }}>
{children}
</CalendarContext.Provider>
);
}
return (
<CalendarContext.Provider
value={{
status: 'ready',
setDate,
date,
selected,

View File

@@ -1,7 +1,7 @@
import { UserLocation } from "#/types/location";
import { createContext } from "react"
type Routine = {
export type Routine = {
id: string;
title: string;
required: boolean;
@@ -15,7 +15,7 @@ type Routine = {
days?: boolean[];
}
type RoutinesContextValue = {
export type RoutinesContextValue = {
routines: Routine[];
remove: (id: string) => any;
set: (routine: Routine) => any;
@@ -23,5 +23,4 @@ type RoutinesContextValue = {
const RoutinesContext = createContext<RoutinesContextValue>(undefined as any);
export type { Routine, RoutinesContextValue };
export { RoutinesContext };

View File

@@ -1,5 +1,5 @@
import { ReactNode } from 'react';
import { Modal as Wrapper } from 'react-native';
import Wrapper from './react-modal';
import { Popup } from '../popup';
type ModalProps = {
visible: boolean;

View File

@@ -0,0 +1,43 @@
import ReactDOM from 'react-dom';
import React, { useMemo, useEffect, ReactNode } from 'react';
interface Props {
visible: boolean;
children: ReactNode;
}
const Modal: React.FC<Props> = ({ visible, children }) => {
const elm = useMemo(() => {
const newElm = document.createElement('div');
newElm.style.position = 'fixed';
newElm.style.display = 'flex';
newElm.style.flexDirection = 'column';
newElm.style.left = '0px';
newElm.style.top = '0px';
newElm.style.width = '100%';
newElm.style.height = '100%';
newElm.style.transition = 'transform 0.3s';
newElm.style.transform = 'translateY(100%)';
return newElm;
}, []);
useEffect(() => {
document.body.appendChild(elm);
return () => {
document.body.removeChild(elm);
};
}, [elm]);
useEffect(() => {
if (visible) {
elm.style.transform = 'translateY(0)';
} else {
elm.style.transform = 'translateY(100%)';
}
}, [elm, visible]);
return ReactDOM.createPortal(
<>{children}</>,
elm,
);
};
export default Modal;

View File

@@ -0,0 +1,3 @@
import { Modal } from 'react-native';
export default Modal;

View File

@@ -2269,6 +2269,13 @@
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df"
integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==
"@types/react-dom@^18.0.3":
version "18.0.3"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.3.tgz#a022ea08c75a476fe5e96b675c3e673363853831"
integrity sha512-1RRW9kst+67gveJRYPxGmVy8eVJ05O43hg77G2j5m76/RFJtMbcfAs2viQ2UNsvvDg8F7OfQZx8qQcl6ymygaQ==
dependencies:
"@types/react" "*"
"@types/react-native@^0.65":
version "0.65.22"
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.65.22.tgz#58eaf2f64eb37fcb5fa9725547a8e043c4ecbc07"