diff --git a/asset-manifest.json b/asset-manifest.json index e173d23..cb354fc 100644 --- a/asset-manifest.json +++ b/asset-manifest.json @@ -1,7 +1,7 @@ { "files": { - "app.js": "/bob-the-algorithm/static/js/app.5219cb71.chunk.js", - "app.js.map": "/bob-the-algorithm/static/js/app.5219cb71.chunk.js.map", + "app.js": "/bob-the-algorithm/static/js/app.7da76fef.chunk.js", + "app.js.map": "/bob-the-algorithm/static/js/app.7da76fef.chunk.js.map", "runtime~app.js": "/bob-the-algorithm/static/js/runtime~app.508ab609.js", "runtime~app.js.map": "/bob-the-algorithm/static/js/runtime~app.508ab609.js.map", "static/js/2.86ddfd84.chunk.js": "/bob-the-algorithm/static/js/2.86ddfd84.chunk.js", @@ -29,6 +29,6 @@ "entrypoints": [ "static/js/runtime~app.508ab609.js", "static/js/2.86ddfd84.chunk.js", - "static/js/app.5219cb71.chunk.js" + "static/js/app.7da76fef.chunk.js" ] } \ No newline at end of file diff --git a/index.html b/index.html index ac1af61..3756863 100644 --- a/index.html +++ b/index.html @@ -1 +1 @@ -Bob
\ No newline at end of file +Bob
\ No newline at end of file diff --git a/static/js/app.5219cb71.chunk.js b/static/js/app.5219cb71.chunk.js deleted file mode 100644 index 36fcc2e..0000000 --- a/static/js/app.5219cb71.chunk.js +++ /dev/null @@ -1,2 +0,0 @@ -(this.webpackJsonp=this.webpackJsonp||[]).push([[0],{425:function(e,t,n){"use strict";n(434);var r,a=n(5),o=n.n(a),c=n(427),s=n(430),i=n(0),u=n(8),l=n.n(u),b=n(14),j=n.n(b),f=function(e,t){var n=Object(i.useState)(),r=l()(n,2),a=r[0],c=r[1],s=Object(i.useState)(),u=l()(s,2),b=u[0],f=u[1],d=Object(i.useState)(!1),p=l()(d,2),O=p[0],m=p[1],h=Object(i.useState)(),y=l()(h,2),x=y[0],v=y[1],g=Object(i.useCallback)(e,t),w=Object(i.useCallback)((function(){var e,t,n,r,a=arguments;return o.a.async((function(s){for(;;)switch(s.prev=s.next){case 0:for(m(!0),v(!1),e=a.length,t=new Array(e),n=0;nR(t)},R=function(e){return 60*e.hour+e.minute},H={timeToString:function(e){return e.hour+":"+e.minute},stringToTime:function(e){var t=e.split(":").map((function(e){return e.trim()})).filter(Boolean),n=l()(t,2),r=n[0],a=n[1],o=parseInt(r),c=parseInt(a||"0");if(Number.isInteger(o)&&Number.isInteger(c)&&!Number.isNaN(o)&&!Number.isNaN(c))return{hour:o,minute:c}},equal:function(e,t){return e.hour==t.hour&&e.minute===t.minute},largerThan:K,timeToMinutes:R,max:function(e,t){return K(e,t)?e:t},add:function(e,t){var n,r="number"===typeof t?t:60*t.hour+t.minute,a=60*e.hour+e.minute+r;return n=a,{hour:Math.floor(n/60),minute:n%60}}};function J(e,t){var n="undefined"!==typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(n)return(n=n.call(e)).next.bind(n);if(Array.isArray(e)||(n=function(e,t){if(!e)return;if("string"===typeof e)return U(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return U(e,t)}(e))||t&&e&&"number"===typeof e.length){n&&(e=n);var r=0;return function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function U(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function X(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n0)){M.next=28;break}return M.abrupt("return",P(z));case 28:if(z.length>0&&g.push.apply(g,j()(z)),s!==Z.firstComplet){M.next=33;break}if(!(I=z.find((function(e){return 0===e.impossibeTasks.length})))){M.next=33;break}return M.abrupt("return",P([I]));case 33:s!==Z.all&&w.push.apply(w,j()(E)),M.next=9;break;case 36:return M.abrupt("return",P(g));case 37:case"end":return M.stop()}}),null,null,null,Promise)},se=S({createDefault:function(){return{startTime:{hour:7,minute:0},strategy:Z.firstComplet}}}),ie=se.Context,ue=se.Provider,le=n(12),be=n.n(le),je=n(669),fe=S({createDefault:function(){return{}}}),de=fe.Context,pe=fe.Provider;function Oe(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function me(e){for(var t=1;t=e.length?{done:!0}:{done:!1,value:e[r++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function Se(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n Promise;\n}\n\ntype AppointmentsContextApproved = {\n status: AppointmentsStatus.approved;\n getDay: (day: Day) => Promise\n}\n\ntype AppointmentsContextValue = AppointmentsContextUnavailable\n | AppointmentsContextUnapprovedValue\n | AppointmentsContextApproved;\n\nconst AppointmentsContext = createContext(undefined as any);\n\nexport type {\n AppointmentsContextValue,\n};\nexport { AppointmentsContext, AppointmentsStatus };\n","import { useCallback, useEffect, useMemo, useState } from \"react\"\n\ntype AsyncCallbackOutput = [\n (...args: TArgs) => Promise,\n {\n loading: boolean;\n error?: any;\n result?: TResult;\n args?: TArgs;\n }\n];\n\ntype AsyncOutput = [\n TResult | undefined,\n {\n loading: boolean;\n error?: any;\n rerun: () => Promise;\n }\n]\n\nconst useAsyncCallback = <\n TArgs extends any[],\n TResult,\n>(fn: (...args: TArgs) => Promise, deps: any[]): AsyncCallbackOutput => {\n const [result, setResult] = useState();\n const [prevArgs, setPrevArgs] = useState();\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState();\n\n const action = useCallback(fn, deps);\n \n const invoke = useCallback(\n async (...args: TArgs) => {\n setLoading(true);\n setError(false);\n setPrevArgs(args);\n try {\n const output = await action(...args);\n setResult(output);\n return output;\n } catch (err) {\n setResult(undefined);\n setError(err);\n throw err;\n } finally {\n setLoading(false);\n }\n },\n [setLoading, setError, setResult, action, ...deps],\n );\n\n const options = useMemo(\n () => {\n const output: AsyncCallbackOutput = [\n invoke,\n {\n result,\n loading,\n error,\n args: prevArgs,\n }\n ];\n return output;\n },\n [invoke, result, loading, error, prevArgs, ...deps],\n );\n\n return options;\n};\n\nconst useAsync = (fn: () => Promise, deps: any[]): AsyncOutput => {\n const [invoke, options] = useAsyncCallback(fn, deps);\n useEffect(\n () => {\n invoke();\n },\n [invoke],\n );\n\n const localOptions = useMemo(\n () => ({\n loading: options.loading,\n error: options.error,\n rerun: invoke,\n }),\n [invoke, options.loading, options.error],\n );\n\n return [\n options.result,\n localOptions,\n ]\n};\n\nexport type { AsyncCallbackOutput };\nexport { useAsync, useAsyncCallback };\n","import { useAsync } from \"#/features/async\";\nimport { ReactNode } from \"react\"\nimport { Platform } from \"react-native\";\nimport { AppointmentsContext, AppointmentsContextValue, AppointmentsStatus } from './context';\n\ntype AppointmentsProviderProps = {\n children: ReactNode;\n};\n\nconst AppointmentsProvider: React.FC = ({\n children,\n}) => {\n const [value] = useAsync(\n async () => {\n if (Platform.OS !== 'ios') {\n return { status: AppointmentsStatus.unavailable };\n }\n return { status: AppointmentsStatus.unavailable };\n },\n [],\n );\n\n if (!value) {\n return <>\n }\n\n return (\n \n {children}\n \n );\n}\n\nexport type { AppointmentsProviderProps };\nexport { AppointmentsProvider };\n","import { createContext } from \"react\";\nimport { Day } from \".\";\n\ntype DateContextValue = {\n date: Day;\n setDate: (date: Day) => void;\n}\n\nconst DateContext = createContext(undefined as any);\n\nexport type { DateContextValue };\nexport { DateContext }\n","import { Day } from \"./day\";\n\nconst today = () => {\n return dateToDay(new Date());\n}\n\nconst dayToDate = (day: Day) => {\n return new Date(day.year, day.month - 1, day.date, 0, 0, 0, 0);\n}\n\nconst dateToDay = (input: Date) => {\n const year = input.getFullYear();\n const month = input.getMonth() + 1;\n const date = input.getDate();\n const day: Day = { year, month, date };\n return day;\n}\n\nconst toId = (day: Day) => {\n return `${day.year.toString().padStart(4, '0')}-${day.month.toString().padStart(2, '0')}-${day.date.toString().padStart(2, '0')}`;\n}\n\nconst dayUtils = {\n today,\n dateToDay,\n dayToDate,\n toId,\n};\n\nexport { dayUtils };\n","import { ReactNode, useState } from \"react\";\nimport { DateContext } from \"./context\";\nimport { dayUtils } from \"./utils\";\n\ntype DateProviderProps = {\n children: ReactNode;\n};\n\nconst DateProvider: React.FC = ({ children }) => {\n const [date, setDate] = useState(dayUtils.today());\n\n return (\n \n {children}\n \n );\n}\n\nexport type { DateProviderProps };\nexport { DateProvider };\n","import { useContext } from \"react\"\nimport { DateContext } from \"./context\"\n\nexport const useDate = () => {\n const { date } = useContext(DateContext);\n return date;\n}\n\nexport const useSetDate = () => {\n const { setDate } = useContext(DateContext);\n return setDate;\n}\n","import { useAsync, useAsyncCallback } from \"#/features/async\";\nimport AsyncStorageLib from \"@react-native-async-storage/async-storage\";\nimport { createContext, ReactNode, useState } from \"react\"\n\ntype DataContextOptions = {\n createDefault: () => T;\n deserialize?: (item: T) => T;\n};\n\ntype DataContextProviderProps = {\n storageKey: string;\n children: ReactNode;\n};\n\nfunction createDataContext({\n createDefault,\n deserialize = a => a,\n}: DataContextOptions) {\n const Context = createContext<{\n data: T;\n setData: (data: T | ((current: T) => T)) => Promise;\n }>(undefined as any);\n\n const Provider: React.FC = ({\n storageKey: key,\n children,\n }) => {\n const [current, setCurrent] = useState();\n\n const [setData] = useAsyncCallback(\n async (input: T | ((current: T) => T)) => {\n let next = typeof input === 'function'\n ? input(current!)\n : input;\n setCurrent(next);\n await AsyncStorageLib.setItem(key, JSON.stringify(next));\n },\n [key, current, setCurrent],\n );\n\n useAsync(\n async () => {\n const raw = await AsyncStorageLib.getItem(key);\n const next = raw ? deserialize(JSON.parse(raw)) : createDefault();\n setCurrent(next);\n },\n [key, setCurrent],\n )\n\n if (!current) {\n return <>\n }\n\n return (\n \n {children}\n \n )\n };\n\n return { Context, Provider };\n}\n\nexport { createDataContext };\n","import { createDataContext } from \"#/utils/data-context\";\nimport { Goal } from \"../data\";\n\nconst {\n Context: GoalsContext,\n Provider: GoalsProvider,\n}= createDataContext<{[id: string]: Goal}>({\n createDefault: () => ({}),\n})\n\nexport { GoalsContext, GoalsProvider };\n","import { Time, UserLocation } from \"../data\";\nimport { createContext } from \"react\"\n\ntype Transition = {\n time: number;\n usableTime: number;\n to: UserLocation;\n from: UserLocation;\n};\n\ntype GetTransition = (\n from: UserLocation,\n to: UserLocation,\n time: Time,\n) => Promise;\n\ntype LocationContextValue = {\n locations: {\n [id: string]: UserLocation;\n };\n set: (location: UserLocation) => any;\n remove: (id: string) => any;\n lookup?: (address: string) => UserLocation[];\n getTransition: GetTransition;\n}\n\nconst LocationContext = createContext(undefined as any);\n\nexport type { LocationContextValue, GetTransition, Transition };\nexport { LocationContext };\n","import { useAsync, useAsyncCallback } from \"#/features/async\";\nimport { GetTransition } from \"./context\";\nimport AsyncStorage from \"@react-native-async-storage/async-storage\";\nimport { ReactNode, useState } from \"react\";\nimport { LocationContext } from \"./context\";\nimport { UserLocation } from \"../data\";\n\ntype LocationProviderProps = {\n children: ReactNode;\n lookup: (address: string) => UserLocation[];\n getTransition: GetTransition;\n}\n\nconst LOCATION_STORAGE_KEY = 'locations';\n\nconst LocationProvider: React.FC = ({\n children,\n lookup,\n getTransition,\n}) => {\n const [locations, setLocations] = useState<{[id: string]: UserLocation}>({});\n\n useAsync(\n async () => {\n const raw = await AsyncStorage.getItem(LOCATION_STORAGE_KEY);\n if (raw) {\n setLocations(JSON.parse(raw));\n }\n },\n [],\n );\n\n const [set] = useAsyncCallback(\n async (location: UserLocation) => {\n const index = {\n ...locations,\n [location.id]: location,\n }\n setLocations(index);\n await AsyncStorage.setItem(LOCATION_STORAGE_KEY, JSON.stringify(index));\n },\n [setLocations, locations],\n )\n\n const [remove] = useAsyncCallback(\n async (id: string) => {\n const index = {\n ...locations,\n }\n delete index[id];\n setLocations(index);\n await AsyncStorage.setItem(LOCATION_STORAGE_KEY, JSON.stringify(index));\n },\n [setLocations, locations],\n );\n\n return (\n \n {children}\n \n )\n}\n\nexport type { LocationProviderProps };\nexport { LocationProvider };\n","import { useAsync } from \"#/features/async\";\nimport { useContext, useMemo } from \"react\"\nimport { requestForegroundPermissionsAsync, getCurrentPositionAsync } from 'expo-location';\nimport { LocationContext } from \"./context\"\nimport { UserLocation } from \"../data\";\nimport { getDistanceFromLatLonInKm } from \"./utils\";\n\nexport const useLocations = () => {\n const { locations } = useContext(LocationContext);\n const result = useMemo(() => Object.values(locations), [locations]);\n return result;\n}\n\nexport const useSetLocation = () => {\n const { set } = useContext(LocationContext);\n return set;\n}\n\nexport const useRemoveLocation = () => {\n const { remove } = useContext(LocationContext);\n return remove;\n}\n\nexport const useGetTransition = () => {\n const { getTransition } = useContext(LocationContext);\n return getTransition;\n}\n\nexport const useLookup = () => {\n const { lookup } = useContext(LocationContext);\n return lookup;\n}\n\nexport const useCurrentLocation = (proximity: number = 0.5) => {\n const { locations } = useContext(LocationContext);\n const result = useAsync(\n async () => {\n let { status } = await requestForegroundPermissionsAsync();\n if (status !== 'granted') {\n return undefined;\n }\n let position = await getCurrentPositionAsync({});\n const withDistance = Object.values(locations).map((location) => {\n if (!location.position) {\n return;\n }\n const distance = getDistanceFromLatLonInKm(\n position.coords.latitude,\n position.coords.longitude,\n location.position.latitude,\n location.position.longitute,\n )\n return {\n distance,\n location,\n }\n }).filter(Boolean).sort((a, b) => a!.distance - b!.distance)\n const current = withDistance.find(d => d!.distance < proximity);\n if (!current) {\n return {\n id: `${position.coords.longitude} ${position.coords.latitude}`,\n title: 'Unknown',\n position: {\n latitude: position.coords.latitude,\n longitute: position.coords.longitude,\n },\n };\n }\n return current.location;\n },\n [],\n );\n return result;\n}\n","import { createContext, SetStateAction } from \"react\";\nimport { Time, UserLocation } from \"../data\";\nimport { Day } from \"../day\";\n\ntype Override = {\n locations?: UserLocation[] | null;\n startMin?: Time;\n startMax?: Time;\n duration?: number;\n required?: boolean;\n priority?: number;\n enabled?: boolean;\n}\n\ntype OverrideIndex = {\n startTime?: Time;\n tasks: {\n [id: string]: Override;\n };\n};\n\ntype OverrideContextValue = {\n overrides: OverrideIndex;\n get: (date: Day) => Promise;\n set: React.Dispatch>;\n}\nconst OverrideContext = createContext(undefined as any);\n\nexport type { Override, OverrideIndex, OverrideContextValue };\nexport { OverrideContext };\n","import AsyncStorageLib from \"@react-native-async-storage/async-storage\";\nimport React, { ReactNode, SetStateAction, useCallback, useState } from \"react\";\nimport { useAsync } from \"../async\";\nimport { Day, useDate, dayUtils } from \"../day\";\nimport { Override, OverrideContext, OverrideIndex } from \"./context\";\n\ntype OverrideProviderProps = {\n children: ReactNode;\n}\n\nconst StorageKey = 'overrides';\n\nconst OverrideProvider: React.FC = ({ children }) => {\n const currentDate = useDate();\n const [overrides, setOverrides] = useState();\n\n const get = useCallback(\n async (date: Day): Promise => {\n const raw = await AsyncStorageLib.getItem(`${StorageKey}_${dayUtils.toId(date)}`);\n if (!raw) {\n return { tasks: {} };\n }\n return JSON.parse(raw);\n },\n [],\n );\n\n const set = useCallback(\n async (override: SetStateAction) => {\n const next = typeof override === 'function' ? override(overrides!) : overrides;\n setOverrides(next);\n await AsyncStorageLib.setItem(\n `${StorageKey}_${dayUtils.toId(currentDate)}`,\n JSON.stringify(next),\n );\n },\n [currentDate, overrides],\n );\n\n useAsync(\n async () => {\n setOverrides(await get(currentDate));\n },\n [currentDate, setOverrides],\n );\n\n if (!overrides) {\n return <>\n }\n\n return (\n \n {children}\n \n );\n}\n\nexport type { OverrideProviderProps };\nexport { OverrideProvider };\n","import { useContext } from \"react\"\nimport { useAsyncCallback } from \"../async\";\nimport { Time } from \"../data\";\nimport { Override, OverrideContext } from \"./context\"\n\nexport const useOverrides = () => {\n const { overrides } = useContext(OverrideContext);\n return overrides;\n}\n\nexport const useSetOverride = () => {\n const { set } = useContext(OverrideContext);\n return set;\n}\n\nexport const useGetOverride = () => {\n const { get } = useContext(OverrideContext);\n return get;\n}\n\nexport const useSetTaskOverride = () => {\n const { set } = useContext(OverrideContext);\n const setTaskOverride = useAsyncCallback(\n async (id: string, overrides: Override) => {\n set(current => ({\n ...current,\n tasks: {\n ...current.tasks,\n [id]: overrides,\n },\n }));\n },\n [set],\n );\n return setTaskOverride;\n}\n\nexport const useClearTaskOverride = () => {\n const { set } = useContext(OverrideContext);\n const clearTaskOverride = useAsyncCallback(\n async (id: string) => {\n set(current => {\n const tasks = {...current.tasks};\n delete tasks[id]\n return {\n ...current,\n tasks,\n };\n });\n },\n [set],\n );\n return clearTaskOverride;\n}\n\nexport const useStartTimeOverride = () => {\n const { overrides } = useContext(OverrideContext);\n return overrides.startTime; \n};\n\nexport const useSetStartTimeOverride = () => {\n const { set } = useContext(OverrideContext);\n const setStartTime = useAsyncCallback(\n async (startTime?: Time) => {\n set(current => ({\n ...current,\n startTime,\n }));\n },\n [set],\n );\n return setStartTime;\n};\n","import { Day } from \"../day\"\n\nexport enum TaskType {\n appointment = 'appointment',\n goal = 'goal',\n routine = 'routine',\n}\n\nexport type Time = {\n hour: number;\n minute: number;\n}\n\nexport type UserLocation = {\n id: string;\n title: string;\n position: {\n longitute: number;\n latitude: number;\n };\n}\n\nexport type TaskBase = {\n type: TaskType;\n id: string;\n title: string;\n locations?: UserLocation[];\n required: boolean;\n priority?: number;\n startTime: {\n min: Time;\n max: Time;\n };\n duration: number;\n}\n\nexport type Appointment = TaskBase & {\n type: TaskType.appointment;\n calendarId: string;\n}\n\nexport type Goal = TaskBase & {\n type: TaskType.goal;\n completed: boolean;\n deadline?: Day;\n startDate?: Day;\n days: boolean[];\n}\n\nexport type Routine = TaskBase & {\n type: TaskType.routine;\n days: boolean[];\n}\n\nexport type Task = Appointment | Goal | Routine;\n","import { Time } from \"./types\";\n\nconst equal = (a: Time, b: Time) => {\n return a.hour == b.hour && a.minute === b.minute;\n}\n\nconst stringToTime = (input: string) => {\n const [hourPart, minutePart] = input.split(':').map(a => a.trim()).filter(Boolean);\n const hour = parseInt(hourPart);\n const minute = parseInt(minutePart || '0');\n\n if (\n !Number.isInteger(hour)\n || !Number.isInteger(minute)\n || Number.isNaN(hour)\n || Number.isNaN(minute)\n ) {\n return undefined;\n }\n\n const result: Time = {\n hour,\n minute,\n };\n\n return result;\n};\n\nconst largerThan = (a: Time, b: Time) => {\n return timeToMinutes(a) > timeToMinutes(b);\n}\n\nconst max = (a: Time, b: Time) => largerThan(a, b) ? a : b;\nconst min = (a: Time, b: Time) => largerThan(a, b) ? b : a;\n\nconst timeToString = (input: Time) => `${input.hour}:${input.minute}`;\n\nconst timeToMinutes = (time: Time) => time.hour * 60 + time.minute;\n\nconst minutesToTime = (minutes: number): Time => {\n const hour = Math.floor(minutes / 60);\n const minute = minutes % 60;\n return { hour, minute };\n}\n\nconst add = (a: Time, b: Time | number) => {\n const toAdd = typeof b === 'number' ? b : b.hour * 60 + b.minute\n const current = a.hour * 60 + a.minute + toAdd;\n return minutesToTime(current);\n}\n\nconst timeUtils = {\n timeToString,\n stringToTime,\n equal,\n largerThan,\n timeToMinutes,\n max,\n add,\n};\n\nexport { timeUtils };\n","import { UserLocation } from \"#/types/location\";\nimport { Task } from \"#/types/task\";\n\nexport const locationEqual = (a: UserLocation, b: UserLocation) => {\n if (a === b) {\n return true;\n }\n // if (a.location === b.location) {\n // return true;\n // }\n // if (a.location && b.location && a.location.latitude === b.location.latitude && a.location.longitute === b.location.longitute) {\n // return true;\n // }\n if (a.title === b.title) {\n return true;\n }\n return false;\n}\n\nexport const listContainLocation = (list: UserLocation[], target: UserLocation) => {\n return !!list.find(l => locationEqual(l, target));\n}\n\nexport const getRemainingLocations = (tasks: Task[], current: UserLocation) => {\n const result: UserLocation[] = [];\n tasks.forEach((task) => {\n if (!task.locations) {\n return;\n }\n for (let location of task.locations) {\n if (!listContainLocation(result, location) && !locationEqual(current, location)) {\n result.push(location)\n }\n }\n })\n return result;\n};\n\n","import { Task, Time, timeUtils } from '#/features/data';\nimport { Transition } from '#/features/location';\nimport { Context, GraphNode } from '../types';\nimport { getRemainingLocations, listContainLocation } from './utils';\n\nconst DEFAULT_PRIORITY = 50;\n\nconst isDead = (impossible: Task[]) => {\n const missingRequered = impossible.find(t => t.required);\n return !!missingRequered;\n}\n\ntype GetImpossibleResult = {\n remaining: Task[];\n impossible: Task[];\n}\n\nexport const getImpossible = (\n tasks: Task[],\n time: Time,\n) => {\n const result: GetImpossibleResult = {\n remaining: [],\n impossible: [],\n }\n\n for (let task of tasks) {\n if (timeUtils.largerThan(time, task.startTime.max)) {\n result.impossible.push(task);\n } else {\n result.remaining.push(task);\n }\n };\n\n return result;\n}\n\ntype CalculateScoreOptions = {\n tasks?: Task[];\n transition?: Transition;\n impossible: Task[];\n}\n\nconst calculateScore = ({\n tasks,\n transition,\n impossible,\n}: CalculateScoreOptions) => {\n let score = 0;\n\n tasks?.forEach((task) => {\n score += (task.priority || DEFAULT_PRIORITY) * 10;\n impossible.forEach((task) => {\n if (task.required) {\n score -= 10000 + (1 * (task.priority || DEFAULT_PRIORITY));\n } else {\n score -= 100 + (1 * (task.priority || DEFAULT_PRIORITY));\n }\n });\n });\n if (transition) {\n const minutes = transition.time;\n score -= 10 + (1 * minutes);\n }\n return score;\n}\nconst getNext = async (\n currentNode: GraphNode,\n context: Context,\n): Promise => {\n const nextNodes: GraphNode[] = [];\n if (!currentNode.transition) {\n const remainingLocations = getRemainingLocations(currentNode.remainingTasks, currentNode.location);\n await Promise.all(remainingLocations.map(async(location) => {\n const transition = await context.getTransition(currentNode.location, location, currentNode.time.end);\n const endTime = timeUtils.add(currentNode.time.end, transition.time);\n const { remaining, impossible } = getImpossible(currentNode.remainingTasks, endTime);\n const score = calculateScore({\n transition, \n impossible,\n });\n nextNodes.push({\n parent: currentNode,\n location: transition.to,\n remainingTasks: remaining,\n transition,\n impossibeTasks: [\n ...impossible,\n ...currentNode.impossibeTasks,\n ],\n score: currentNode.score + score,\n status: {\n completed: false,\n dead: false, // TODO: fix isDead(impossible),\n },\n time: {\n start: currentNode.time.end,\n end: endTime,\n },\n })\n }));\n }\n const possibleTasks = currentNode.remainingTasks.filter(task => !task.locations || listContainLocation(task.locations, currentNode.location))\n await Promise.all(possibleTasks.map(async (orgTask) => {\n const task = {...orgTask};\n let startTime = \n timeUtils.max(\n currentNode.time.end,\n task.startTime.min,\n );\n const parentRemainging = currentNode.remainingTasks.filter(t => t !== orgTask);\n let endTime = timeUtils.add(startTime, task.duration);\n const { remaining, impossible } = getImpossible(parentRemainging, endTime);\n const score = calculateScore({\n tasks: [task], \n impossible,\n });\n nextNodes.push({\n parent: currentNode,\n location: currentNode.location,\n task,\n remainingTasks: remaining,\n impossibeTasks: [\n ...impossible,\n ...currentNode.impossibeTasks,\n ],\n score: currentNode.score + score,\n status: {\n completed: remaining.length === 0,\n dead: isDead(impossible),\n },\n time: {\n start: startTime,\n end: endTime,\n },\n })\n }));\n return nextNodes;\n};\n\nexport { getNext };\n","import { Task, Time, UserLocation } from \"#/features/data\";\nimport { Context, GraphNode } from \"../types\";\nimport { getImpossible, getNext } from \"./get-next\";\n\nenum Strategies {\n all = 'all',\n allValid = 'all-valid',\n firstValid = 'first-valid',\n firstComplet = 'first-complete',\n}\ntype RunningStatus = {\n current: 'running';\n nodes: number;\n start: Date;\n strategy: Strategies,\n cancel: () => void;\n}\n\ntype CompletedStatus = {\n current: 'completed';\n start: Date;\n end: Date;\n nodes: number;\n strategy: Strategies,\n}\n\ntype Status = RunningStatus | CompletedStatus;\n\ntype BuildGraphOptions = {\n location: UserLocation;\n time: Time;\n tasks: Task[];\n context: Context;\n strategy?: Strategies;\n batchSize?: number;\n sleepTime?: number;\n callback?: (status: Status) => void;\n};\n\nconst sleep = (time: number) => new Promise(resolve => setTimeout(resolve, time));\n\nconst fil = (\n fn: ((item: T) => boolean)[],\n input: T[],\n): T[][] => {\n const output: T[][] = new Array(fn.length).fill(undefined).map(() => []);\n for (let i = 0; i < input.length; i++) {\n for (let b = 0; b < fn.length; b++) {\n if (fn[b](input[i])) {\n output[b].push(input[i]);\n break;\n }\n }\n }\n return output;\n};\n\nconst buildGraph = async ({\n location,\n time,\n tasks,\n context,\n strategy = Strategies.allValid,\n callback,\n batchSize = 1000,\n sleepTime = 10,\n}: BuildGraphOptions) => {\n const start = new Date();\n let nodeCount = 0;\n let running = true;\n const { remaining, impossible } = getImpossible(tasks, time);\n let leafList: GraphNode[] = [{\n location,\n time: {\n end: time,\n start: time,\n },\n score: 0,\n remainingTasks: remaining,\n impossibeTasks: impossible,\n status: {\n dead: false,\n completed: false,\n },\n }];\n const completedList: GraphNode[] = [];\n const deadList: GraphNode[] = [];\n\n const complete = (nodes: GraphNode[]) => {\n if (callback) {\n callback({\n current: 'completed',\n nodes: nodeCount,\n start,\n end: new Date(),\n strategy,\n });\n }\n return nodes.sort((a, b) => b.score - a.score);\n }\n\n while (true) {\n nodeCount++;\n if (!running) {\n return [];\n }\n if (\n leafList.length === 0\n && completedList.length === 0\n && strategy !== Strategies.all\n ) {\n strategy = Strategies.all;\n leafList.push(...deadList);\n }\n const node = leafList.pop();\n if (!node) {\n break;\n }\n if (nodeCount % batchSize === 0) {\n if (callback) {\n callback({\n current: 'running',\n nodes: nodeCount,\n strategy,\n start,\n cancel: () => {\n running = false;\n }\n })\n }\n await sleep(sleepTime);\n }\n const next = await getNext(node, context); \n const [alive, completed, dead] = fil([\n n => (strategy === Strategies.all || !n.status.dead) && !n.status.completed,\n n => !!n.status.completed && (strategy === Strategies.all || !n.status.dead),\n n => n.status.dead,\n ], next);\n leafList.push(...alive);\n if (strategy === Strategies.firstValid && completed.length > 0) {\n return complete(completed);\n }\n if (completed.length > 0) {\n completedList.push(...completed)\n }\n if (strategy === Strategies.firstComplet) {\n const fullComplete = completed.find(c => c.impossibeTasks.length === 0);\n if (fullComplete) {\n return complete([fullComplete]);\n }\n }\n if (strategy !== Strategies.all) {\n deadList.push(...dead);\n }\n }\n\n return complete(completedList);\n}\n\nexport type { Status, BuildGraphOptions };\nexport { buildGraph, Strategies };\n","import { createDataContext } from '#/utils/data-context';\nimport { Time } from '../data';\nimport { Strategies } from \"./algorithm/build-graph\";\n\ntype PlannerOptions = {\n strategy: Strategies;\n startTime: Time;\n}\n\nconst {\n Context: PlannerContext,\n Provider: PlannerProvider,\n} = createDataContext({\n createDefault: () => ({\n startTime: { hour: 7, minute: 0 },\n strategy: Strategies.firstComplet,\n }),\n});\n\nexport type { PlannerOptions };\nexport { PlannerContext, PlannerProvider };\n","import { createDataContext } from \"#/utils/data-context\";\nimport { Routine } from \"../data\";\n\nconst {\n Context: RoutinesContext,\n Provider: RoutinesProvider,\n}= createDataContext<{[id: string]: Routine}>({\n createDefault: () => ({}),\n})\n\nexport { RoutinesContext, RoutinesProvider };\n","import { useCallback, useContext, useMemo } from \"react\"\nimport { Routine } from \"../data\";\nimport { RoutinesContext } from \"./context\"\n\nexport const useRoutines = () => {\n const { data } = useContext(RoutinesContext);\n const current = useMemo(\n () => Object.values(data),\n [data],\n )\n return current;\n};\n\nexport const useSetRoutine = () => {\n const { setData } = useContext(RoutinesContext);\n const set = useCallback(\n (routine: Routine) => setData(current => ({\n ...current,\n [routine.id]: routine, \n })),\n [setData],\n );\n\n return set;\n}\n\nexport const useRemoveRoutine = () => {\n const { setData } = useContext(RoutinesContext);\n const removeRoutine = useCallback(\n (id: string) => {\n setData(current => {\n const next = {...current};\n delete next[id];\n return next;\n })\n },\n [setData],\n );\n\n return removeRoutine;\n}\n","import { useCallback, useContext, useMemo } from \"react\"\nimport { Goal } from \"../data\";\nimport { GoalsContext } from \"./context\"\n\nexport const useGoals = () => {\n const { data } = useContext(GoalsContext);\n const current = useMemo(\n () => Object.values(data),\n [data],\n )\n return current;\n};\n\nexport const useSetGoals = () => {\n const { setData } = useContext(GoalsContext);\n const set = useCallback(\n (goal: Goal) => setData(current => ({\n ...current,\n [goal.id]: goal, \n })),\n [setData],\n );\n\n return set;\n}\n\nexport const useRemoveGoal = () => {\n const { setData } = useContext(GoalsContext);\n const removeRoutine = useCallback(\n (id: string) => {\n setData(current => {\n const next = {...current};\n delete next[id];\n return next;\n })\n },\n [setData],\n );\n\n return removeRoutine;\n}\n","import { timeUtils } from \"#/features/data\";\nimport { GraphNode, PlannedEntry } from \"../types\";\n\nconst constructDay = (node: GraphNode) => {\n let current: GraphNode | undefined = node;\n const plans: PlannedEntry[] = [];\n\n while(current) {\n if (current.task) {\n plans.push({\n type: 'task',\n name: current.task?.title || 'start',\n start: timeUtils.add(current.time.start, (current.transition?.time || 0)),\n end: current.time.end,\n score: current.score,\n })\n }\n if (current.transition) {\n plans.push({\n type: 'transition',\n start: current.time.start,\n end: timeUtils.add(current.time.start, current.transition.time),\n from: current.transition.from,\n to: current.transition.to,\n })\n }\n current = current.parent;\n }\n\n return plans.reverse();\n}\n\nexport { constructDay };\n","import { buildGraph, Status, Strategies } from \"./algorithm/build-graph\";\nimport { useContext } from \"react\";\nimport { add } from 'date-fns';\nimport { PlannerContext } from \"./context\";\nimport { Task, Time, UserLocation } from \"../data\";\nimport { useRoutines } from \"../routines\";\nimport { useGoals } from \"../goals/hooks\";\nimport { useAsyncCallback } from \"../async\";\nimport { Day, dayUtils } from \"../day\";\nimport { useGetOverride } from \"../overrides\";\nimport { useGetAppointments } from \"../appointments\";\nimport { useGetTransition } from \"../location\";\nimport { PlannedEntry } from \"./types\";\nimport { constructDay } from \"./algorithm/construct-day\";\n\nexport type PreparePlanOptions = {\n start: Day;\n end: Day;\n}\n\nexport type PlanOptions = PreparePlanOptions & {\n location: UserLocation;\n}\n\nexport type PlanResultDay = {\n day: Day;\n start: Time;\n} & ({\n status: 'waiting',\n} | {\n status: 'running',\n nodes: number;\n strategy: Strategies;\n} | {\n status: 'done';\n nodes: number;\n strategy: Strategies;\n plan: PlannedEntry[];\n impossible: Task[];\n});\n\nexport type PlanResult = {\n impossible: Task[];\n days: {\n [day: string]: PlanResultDay;\n }\n}\n\nconst getDays = (start: Day, end: Day): Day[] => {\n const result: Day[] = [];\n let currentDate = dayUtils.dayToDate(start);\n const stopDate = dayUtils.dayToDate(end);\n while (currentDate <= stopDate) {\n result.push(dayUtils.dateToDay(currentDate));\n currentDate = add(currentDate, {\n days: 1,\n });\n }\n return result;\n}\n\nconst firstValue = (...args: (T | undefined)[]): T => {\n for (let arg of args) {\n if (typeof arg !== 'undefined') {\n return arg;\n }\n }\n return undefined as unknown as T;\n}\n\nexport const useOptions = () => {\n const { data } = useContext(PlannerContext);\n return data;\n}\n\nexport const useSetOptions = () => {\n const { setData } = useContext(PlannerContext);\n return setData;\n}\n\nconst usePreparePlan = () => {\n const routines = useRoutines();\n const goals = useGoals();\n const getOverrides = useGetOverride();\n const [getAppontments] = useGetAppointments();\n\n const preparePlan = useAsyncCallback(\n async ({ start, end }: PreparePlanOptions) => {\n const days = await Promise.all(getDays(start, end).map(async (day) => {\n const overrides = await getOverrides(day);\n const start: Time = firstValue(overrides.startTime, { hour: 7, minute: 0 });\n const appointments = await getAppontments(day);\n const tasks = [...routines, ...appointments].map((task) => {\n const override = overrides.tasks[task.id];\n if (override?.enabled === false) {\n return undefined;\n }\n const result: Task = {\n ...task,\n startTime: {\n min: firstValue(override?.startMin, task.startTime.min),\n max: firstValue(override?.startMax, task.startTime.max),\n },\n duration: firstValue(override?.duration, task.duration),\n required: firstValue(override?.required, task.required),\n }\n return result;\n }).filter(Boolean).map(a => a as Exclude);\n\n return {\n day,\n start,\n tasks,\n }\n\n }));\n return {\n goals: [...goals],\n days,\n }\n },\n [routines, goals, getOverrides, getAppontments],\n );\n\n return preparePlan;\n}\n\nexport const usePlanOptions = () => {\n const { data } = useContext(PlannerContext);\n return data;\n}\n\nexport const useSetPlanOptions = () => {\n const { setData } = useContext(PlannerContext);\n return setData;\n}\n\nexport const usePlan = () => {\n const [preparePlan] = usePreparePlan();\n const getTransition = useGetTransition();\n const options = usePlanOptions();\n const createPlan = useAsyncCallback(\n async ({ location, ...prepareOptions}: PlanOptions) => {\n const prepared = await preparePlan(prepareOptions);\n let result: PlanResult = {\n impossible: [],\n days: prepared.days.reduce((output, current) => ({\n ...output,\n [dayUtils.toId(current.day)]: {\n day: current.day,\n start: current.start,\n status: 'waiting',\n },\n }), {} as {[name: string]: PlanResultDay})\n }\n const update = (next: PlanResult) => {\n result = next;\n }\n for (let day of prepared.days) {\n const id = dayUtils.toId(day.day);\n const dayGoal = prepared.goals;\n const graph = await buildGraph({\n location,\n time: day.start,\n tasks: [...day.tasks, ...dayGoal],\n strategy: options.strategy,\n context: {\n getTransition,\n },\n callback: (status) => {\n update({\n ...result,\n days: {\n ...result.days,\n [id]: {\n day: day.day,\n start: day.start,\n status: 'running',\n nodes: status.nodes,\n strategy: status.strategy,\n }\n }\n });\n }\n });\n const [winner] = graph;\n if (!winner) {\n continue;\n }\n const plan = constructDay(winner);\n update({\n ...result,\n days: {\n ...result.days,\n [id]: {\n ...result.days[id],\n impossible: winner.impossibeTasks,\n status: 'done',\n plan,\n }\n }\n })\n prepared.goals = prepared.goals.filter((goal) => {\n if (!dayGoal.find(d => d.id === goal.id)) {\n return true;\n }\n if (!winner.impossibeTasks.find(d => d.id === goal.id)) {\n return false;\n }\n return true;\n })\n }\n\n return {\n ...result,\n impossible: prepared.goals,\n };\n },\n [preparePlan, getTransition, options],\n );\n return createPlan;\n}\n","import { useAsync, useAsyncCallback } from \"#/features/async\";\nimport { useContext } from \"react\"\nimport { Day, useDate } from \"../day\";\nimport { AppointmentsContext, AppointmentsStatus } from \"./context\"\n\nexport const useAppointmentStatus = () => {\n const { status } = useContext(AppointmentsContext);\n return status;\n};\n\nexport const useAppointments = () => {\n const date = useDate();\n const context = useContext(AppointmentsContext);\n const result = useAsync(\n async () => {\n if (context.status !== AppointmentsStatus.approved) {\n return [];\n }\n const appointments = await context.getDay(date);\n return appointments;\n },\n [\n context.status === AppointmentsStatus.approved && context.getDay,\n date,\n ],\n );\n return result;\n}\n\nexport const useGetAppointments = () => {\n const context = useContext(AppointmentsContext);\n const result = useAsyncCallback(\n async (date: Day) => {\n if (context.status !== AppointmentsStatus.approved) {\n return [];\n }\n const appointments = await context.getDay(date);\n return appointments;\n },\n [\n context.status === AppointmentsStatus.approved && context.getDay,\n ],\n );\n return result;\n}\n","import { ReactNode } from \"react\"\nimport { AppointmentsProvider } from \"./appointments\"\nimport { DateProvider } from \"./day\"\nimport { GoalsProvider } from \"./goals/context\"\nimport { GetTransition, LocationProvider } from \"./location\"\nimport { OverrideProvider } from \"./overrides\"\nimport { PlannerProvider } from \"./planner\"\nimport { RoutinesProvider } from \"./routines\"\n\ntype SetupProps = {\n children: ReactNode;\n getTransit: GetTransition;\n}\n\nconst Setup: React.FC = ({\n children,\n getTransit,\n}) => {\n return (\n \n \n []}>\n \n \n \n \n {children}\n \n \n \n \n \n \n \n );\n};\n\nexport type { SetupProps };\nexport { Setup };\n","import React from 'react';\nimport { Feather, } from '@expo/vector-icons';\nimport { useTheme } from 'styled-components/native';\nimport { Theme } from '#/ui/theme';\n\ntype IconNames = keyof typeof Feather.glyphMap;\ntype Props = {\n size?: number;\n color?: keyof Theme['colors'];\n name: IconNames;\n}\n\nfunction Icon({\n size,\n color,\n name,\n}: Props) {\n const theme = useTheme();\n return (\n \n ) \n};\n\nexport type { IconNames };\nexport { Icon };\n","import ReactDOM from 'react-dom';\nimport React, { useMemo, useEffect, ReactNode } from 'react';\n\ninterface Props {\n visible: boolean;\n children: ReactNode;\n}\n\nconst Modal: React.FC = ({ visible, children }) => {\n const elm = useMemo(() => {\n const newElm = document.createElement('div');\n newElm.style.position = 'fixed';\n newElm.style.display = 'flex';\n newElm.style.flexDirection = 'column';\n newElm.style.left = '0px';\n newElm.style.top = '0px';\n newElm.style.width = '100%';\n newElm.style.height = '100%';\n newElm.style.transition = 'transform 0.3s';\n newElm.style.transform = 'translateY(100%)';\n return newElm;\n }, []);\n useEffect(() => {\n document.body.appendChild(elm);\n return () => {\n document.body.removeChild(elm);\n };\n }, [elm]);\n useEffect(() => {\n if (visible) {\n elm.style.transform = 'translateY(0)';\n } else {\n elm.style.transform = 'translateY(100%)';\n }\n }, [elm, visible]);\n\n return ReactDOM.createPortal(\n <>{children},\n elm,\n );\n};\n\nexport default Modal;\n","import React, { ReactNode } from 'react';\nimport { TouchableOpacity } from 'react-native';\nimport styled from 'styled-components/native';\nimport { Theme } from '#/ui/theme';\n\ntype CellProps = {\n accessibilityRole?: TouchableOpacity['props']['accessibilityRole'];\n accessibilityLabel?: string;\n accessibilityHint?: string;\n children?: ReactNode;\n onPress?: () => any;\n background?: string;\n flex?: string | number;\n direction?: 'row' | 'column';\n align?: 'flex-start' | 'flex-end' | 'center' | 'stretch';\n opacity?: number;\n}\n\nconst Wrapper = styled.View<{\n background?: string;\n flex?: string | number;\n direction?: 'row' | 'column';\n theme: Theme;\n align?: 'flex-start' | 'flex-end' | 'center' | 'stretch';\n opacity?: number;\n}>`\n padding: ${({ theme }) => theme.margins.medium / 2}px;\n ${({ background }) => (background ? `background: ${background};` : '')}\n ${({ flex }) => (flex ? `flex: ${flex};` : '')}\n flex-direction: ${({ direction }) => (direction ? direction : 'row')};\n align-items: ${({ align }) => (align ? align : 'center')};\n ${({ opacity }) => (opacity? `opacity: ${opacity};` : '')}\n`;\n\nconst Touch = styled.TouchableOpacity`\n`;\n\nconst Cell: React.FC = ({ children, onPress, ...props}) => {\n const {\n accessibilityLabel,\n accessibilityRole,\n accessibilityHint,\n ...others\n } = props;\n const node = (\n \n {children}\n \n );\n if (onPress) {\n return (\n \n {node}\n \n );\n }\n return node;\n};\n\nexport type { CellProps };\nexport { Cell };\n","import styled from 'styled-components/native';\nimport { Theme } from 'theme';\n\ninterface TextProps {\n color?: keyof Theme['colors'];\n bold?: boolean;\n theme: Theme;\n}\n\nconst BaseText = styled.Text`\n color: ${({ color, theme }) =>\n color ? theme.colors[color] : theme.colors.text};\n font-weight: ${({ bold }) => (bold ? 'bold' : 'normal')};\n font-size: ${({ theme }) => theme.font.baseSize}px;\n`;\n\nconst Jumbo = styled(BaseText)`\n font-size: ${({ theme }) => theme.font.baseSize * 2.8}px;\n font-weight: bold;\n`;\n\nconst Title2 = styled(BaseText)`\n font-size: ${({ theme }) => theme.font.baseSize * 1.3}px;\n font-weight: bold;\n`;\n\nconst Title1 = styled(BaseText)`\n font-weight: bold;\n`;\n\nconst Body1 = styled(BaseText)``;\n\nconst Overline = styled(BaseText)`\n font-size: ${({ theme }) => theme.font.baseSize * 0.6}px;\n text-transform: uppercase;\n`;\n\nconst Caption = styled(BaseText)`\n font-size: ${({ theme }) => theme.font.baseSize * 0.8}px;\n`;\n\nconst Link = styled(BaseText)`\n text-transform: uppercase;\n`;\n\nexport type { TextProps };\nexport { Jumbo, Title2, Title1, Body1, Overline, Caption, Link };\n","import React, { ReactNode } from 'react';\nimport styled from 'styled-components/native';\nimport { Title1, Body1, Overline } from '#/ui/typography';\nimport { Cell, CellProps } from './cell';\n\ntype RowProps = CellProps & {\n top?: ReactNode;\n left?: ReactNode;\n right?: ReactNode;\n title?: ReactNode;\n overline?: ReactNode;\n description?: ReactNode;\n children?: ReactNode;\n}\n\nconst Children = styled.View``;\n\nconst componentOrString = (\n input: ReactNode,\n Component: React.FC<{ children: ReactNode }>\n) => {\n if (!input) {\n return null;\n }\n if (typeof input === 'string') {\n return {input};\n }\n return input;\n};\n\nconst Row: React.FC = ({\n top,\n left,\n right,\n title,\n overline,\n description,\n children,\n ...cellProps\n}) => (\n \n {left}\n \n {!!top}\n {componentOrString(overline, Overline)}\n {componentOrString(title, Title1)}\n {componentOrString(description, Body1)}\n {!!children && {children}}\n \n {right}\n \n);\n\nexport type { RowProps };\nexport { Row };\n","import React from 'react';\nimport styled from 'styled-components/native';\nimport { Cell } from './cell';\n\ninterface Props {\n color?: string;\n size?: number;\n onPress?: () => void;\n}\n\nconst Icon = styled.View<{ size: number; color: string }>`\n background: ${({ color }) => color};\n width: ${({ size }) => size}px;\n height: ${({ size }) => size}px;\n border-radius: ${({ size }) => size / 4}px;\n`;\n\nconst PlaceholderIcon: React.FC = ({\n color = 'red',\n size = 24,\n onPress,\n}) => (\n \n \n \n);\n\nexport { PlaceholderIcon };\n","import React, { useState, useEffect } from 'react';\nimport styled from 'styled-components/native';\nimport { Keyboard, Platform } from 'react-native';\n\nconst KeyboardAvoiding = styled.KeyboardAvoidingView`\n flex: 1;\n`;\n\nconst Pressable = styled.Pressable`\n flex: 1;\n`\n// background-color: ${({ theme }) => theme.colors.background};\n\nconst Page: React.FC = ({ children }) => {\n const [keyboardShown, setKeyboardShown] = useState(false);\n useEffect(() => {\n const keyboardDidShow = () => setKeyboardShown(true);\n const keyboardDidHide = () => setKeyboardShown(false);\n Keyboard.addListener('keyboardDidShow', keyboardDidShow);\n Keyboard.addListener('keyboardDidHide', keyboardDidHide);\n\n return () => {\n Keyboard.removeListener('keyboardDidShow', keyboardDidShow);\n Keyboard.removeListener('keyboardDidHide', keyboardDidHide);\n };\n }, []);\n return (\n Keyboard.dismiss()}\n >\n \n {children}\n \n \n );\n};\n\nexport { Page };\n","import React, { ReactNode, useCallback, useRef } from 'react';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\nimport styled from 'styled-components/native';\nimport { Icon } from '../icon';\nimport { Row, Cell, RowProps } from '../row';\nimport { Page } from '../page';\nimport { ScrollView } from 'react-native';\n\ntype Props = RowProps & {\n onClose?: () => void;\n children: ReactNode;\n}\n\nconst Top = styled.Pressable`\n flex: 1;\n`;\n\nconst Wrapper = styled.View`\n background: ${({ theme }) => theme.colors.background};\n width: 100%;\n max-width: 500px;\n shadow-color: ${({ theme }) => theme.colors.shadow};\n shadow-offset: 0 0;\n shadow-opacity: 1;\n shadow-radius: 200px;\n border-radius: 12px;\n margin-bottom: -12px;\n max-height: 80%;\n`;\n\nconst Outer = styled.View`\n flex: 1;\n align-items: center;\n`;\n\nconst Content = styled.ScrollView`\n`;\n\nconst Popup: React.FC = ({ children, onClose, right, ...rowProps }) => {\n const insets = useSafeAreaInsets();\n\n return (\n \n \n \n \n \n {right}\n \n \n \n \n }\n />\n \n {children}\n \n \n \n \n );\n};\n\nexport { Popup };\n","import { ReactNode } from 'react';\nimport Wrapper from './react-modal';\nimport { Popup } from '../popup';\ntype ModalProps = {\n visible: boolean;\n onClose: () => void;\n children: ReactNode;\n}\n\nconst Modal: React.FC = ({ visible, onClose, children }) => (\n \n \n {children}\n \n \n);\n\nexport { Modal };\n","import React, { ReactNode } from 'react';\nimport styled from 'styled-components/native';\nimport { TouchableOpacity } from 'react-native';\nimport { IconNames, Icon } from '../icon';\nimport { Theme } from '#/ui/theme';\nimport { Link } from '#/ui/typography';\n\ninterface Props {\n title?: string;\n icon?: IconNames;\n onPress?: () => any;\n accessibilityRole?: TouchableOpacity['props']['accessibilityRole'];\n accessibilityLabel?: string;\n accessibilityHint?: string;\n type?: 'primary' | 'secondary' | 'destructive';\n}\n\nconst Touch = styled.TouchableOpacity``;\n\nconst Wrapper = styled.View<{ theme: Theme }>`\n color: ${({ theme }) => theme.colors.primary};\n padding: ${({ theme }) => theme.margins.small}px;\n border-radius: ${({ theme }) => theme.sizes.corners}px;\n align-items: center;\n`;\n\nconst Button: React.FC = ({\n title,\n icon,\n type,\n onPress,\n accessibilityHint,\n accessibilityRole,\n accessibilityLabel,\n}) => (\n \n \n {title && {title}}\n {icon && }\n \n \n);\n\nexport { Button };\n","import React, { ReactNode } from 'react';\nimport { Icon } from '../icon';\nimport { Row, Cell } from '../row';\n\ninterface Props {\n title: string;\n add?: () => void;\n onPress?: () => void;\n left?: ReactNode;\n}\n\nfunction Header({ title, add, onPress, left }: Props) {\n return (\n \n \n \n )\n }\n />\n );\n}\n\nexport { Header };\n","import React, { Fragment, ReactNode, useState } from 'react';\nimport styled from 'styled-components/native';\nimport Collapsible from 'react-native-collapsible';\nimport { Body1 } from '#/ui/typography';\nimport { Icon } from '../icon';\nimport { Row, Cell } from '../row';\nimport { Header } from './header';\n\ninterface ListProps {\n title: string;\n items: T[];\n startHidden?: boolean;\n getKey: (item: T) => any;\n render: (item: T) => ReactNode;\n add?: () => void;\n}\n\ninterface ChildProps {\n title: string;\n startHidden?: boolean;\n add?: () => void;\n children?: ReactNode;\n}\n\nconst Wrapper = styled.View`\n border-radius: 7px;\n background: ${({ theme }) => theme.colors.background};\n shadow-offset: 0 0;\n shadow-opacity: 0.1;\n shadow-color: ${({ theme }) => theme.colors.shadow};\n shadow-radius: 5px;\n`;\n\nfunction Group(props: ListProps | ChildProps) {\n const [visible, setVisible] = useState(!props.startHidden);\n const { title, items, getKey, render, add, children } =\n props as ListProps & ChildProps;\n return (\n \n \n <>\n \n }\n title={title}\n add={add}\n onPress={() => setVisible(!visible)}\n />\n \n {items && items.map((item, i) => (\n {render(item)}\n ))}\n {children}\n {!children && (!items || items.length === 0) && (\n \n \n \n }\n >\n \n Empty\n \n \n )}\n \n \n \n \n );\n}\n\nexport { Group };\n","import { FlatList } from \"react-native\";\nimport { Button } from \"../button\";\nimport { Icon } from \"../icon\";\nimport { Cell, Row, RowProps } from \"../row\";\n\ntype ListProps = {\n add?: () => void;\n remove?: (item: T) => any;\n getKey: (item: T) => string;\n items: T[];\n render: (item: T) => RowProps;\n}\n\nfunction List({\n add,\n remove,\n getKey,\n items,\n render,\n}: ListProps) {\n return (\n <>\n {!!add &&