Mobile Strict Mode and Types (#578)

* better compiler options

* fix types

* fix more types
This commit is contained in:
Utku 2023-02-24 11:11:48 +03:00 committed by GitHub
parent 32ffd5f820
commit 7b739d0b33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 259 additions and 231 deletions

View file

@ -10,7 +10,7 @@ interface Props {
} }
export default function DocsSidebar(props: Props) { export default function DocsSidebar(props: Props) {
const activeSection = props.activePath?.split('/')[0] || props.navigation[0].slug; const activeSection = props.activePath?.split('/')[0] || props.navigation[0]?.slug;
const activeSectionData = props.navigation.find((section) => section.slug === activeSection); const activeSectionData = props.navigation.find((section) => section.slug === activeSection);
@ -27,7 +27,7 @@ export default function DocsSidebar(props: Props) {
const Icon = config.sections.find((s) => s.slug === section.slug)?.icon; const Icon = config.sections.find((s) => s.slug === section.slug)?.icon;
return ( return (
<a <a
href={`/docs/${section.section[0].category[0].url}`} href={`/docs/${section.section[0]?.category[0]?.url}`}
key={section.slug} key={section.slug}
className={clsx( className={clsx(
`doc-sidebar-button flex items-center py-1.5 text-[14px] font-semibold`, `doc-sidebar-button flex items-center py-1.5 text-[14px] font-semibold`,

View file

@ -2,7 +2,7 @@ import { PageContextBuiltIn } from 'vite-plugin-ssr';
import { getPost } from './blog'; import { getPost } from './blog';
export async function onBeforeRender(pageContext: PageContextBuiltIn) { export async function onBeforeRender(pageContext: PageContextBuiltIn) {
const post = await getPost(pageContext.routeParams['slug']); const post = await getPost(pageContext.routeParams['slug']!);
return { return {
pageContext: { pageContext: {

View file

@ -47,13 +47,13 @@ export function getDocs(config: DocsConfig): Record<string, Doc> {
const url = parsePath(path); const url = parsePath(path);
if (!url) return null; if (!url) return null;
const { render, metadata } = parseMarkdown(config.docs[path]); const { render, metadata } = parseMarkdown(config.docs[path]!);
parsedDocs[url] = { parsedDocs[url] = {
title: metadata?.name ?? toTitleCase(url.split('/')[2]), title: metadata?.name ?? toTitleCase(url.split('/')[2]!),
slug: url.split('/')[2], slug: url.split('/')[2]!,
url, url,
categoryName: toTitleCase(url.split('/')[1]), categoryName: toTitleCase(url.split('/')[1]!),
sortByIndex: metadata?.index ?? DEFAULT_INDEX, sortByIndex: metadata?.index ?? DEFAULT_INDEX,
html: render html: render
}; };
@ -77,15 +77,15 @@ export function getDocsNavigation(config: DocsConfig, docs?: Record<string, Doc>
delete clonedDoc.html; delete clonedDoc.html;
const category = url.split('/')[1], const category = url.split('/')[1],
title = toTitleCase(category), title = toTitleCase(category!),
existingCategory = categories.findIndex((i) => i.slug === category); existingCategory = categories.findIndex((i) => i.slug === category);
if (existingCategory != -1) { if (existingCategory != -1) {
categories[existingCategory].category.push(clonedDoc); categories[existingCategory]?.category.push(clonedDoc);
} else { } else {
categories.push({ categories.push({
title, title,
slug: category, slug: category!,
index: DEFAULT_INDEX, index: DEFAULT_INDEX,
category: [clonedDoc] category: [clonedDoc]
}); });
@ -98,7 +98,7 @@ export function getDocsNavigation(config: DocsConfig, docs?: Record<string, Doc>
return cat; return cat;
}) })
// sort categories smallest first doc's index // sort categories smallest first doc's index
.sort((a, b) => a.category[0].sortByIndex - b.category[0].sortByIndex); .sort((a, b) => a.category[0]!.sortByIndex - b.category[0]!.sortByIndex);
navigation.push({ navigation.push({
title: section.title, title: section.title,
@ -134,8 +134,8 @@ export function getDoc(url: string, config: DocsConfig): SingleDocResponse {
} }
function parsePath(path: string): string | null { function parsePath(path: string): string | null {
const url = path.split('docs/')[1].split('.md')[0]; const url = path.split('docs/')[1]!.split('.md')[0];
if (!url.includes('/')) return null; if (!url?.includes('/')) return null;
return url; return url;
} }
@ -153,7 +153,7 @@ type DocUrls = { url: string; title: string }[];
export function getNextDoc( export function getNextDoc(
navigation: DocsNavigation, navigation: DocsNavigation,
docUrl: string docUrl: string
): { url: string; title: string } { ): { url: string; title: string } | undefined {
const docUrls: DocUrls = []; const docUrls: DocUrls = [];
// flatten the navigation // flatten the navigation
for (const section of navigation) { for (const section of navigation) {

View file

@ -7,7 +7,7 @@ export const passToClient = ['pageProps'];
export async function onBeforeRender(pageContext: PageContextBuiltIn) { export async function onBeforeRender(pageContext: PageContextBuiltIn) {
return { return {
pageContext: { pageContext: {
pageProps: getDoc(pageContext.routeParams['*'], config) pageProps: getDoc(pageContext.routeParams['*']!, config)
} }
}; };
} }

View file

@ -11,7 +11,7 @@ const teamMembers: Array<TeamMemberProps> = [
{ {
name: 'Jamie Pine', name: 'Jamie Pine',
role: 'Founder, Engineer & Designer', role: 'Founder, Engineer & Designer',
image: teamImages['jamie.jpg'], image: teamImages['jamie.jpg']!,
socials: { socials: {
twitter: 'https://twitter.com/jamiepine', twitter: 'https://twitter.com/jamiepine',
twitch: 'https://twitch.tv/jamiepinelive', twitch: 'https://twitch.tv/jamiepinelive',
@ -21,7 +21,7 @@ const teamMembers: Array<TeamMemberProps> = [
{ {
name: 'Brendan Allan', name: 'Brendan Allan',
role: 'Rust Engineer', role: 'Rust Engineer',
image: teamImages['brendan.jpg'], image: teamImages['brendan.jpg']!,
socials: { socials: {
twitter: 'https://twitter.com/brendonovichdev', twitter: 'https://twitter.com/brendonovichdev',
twitch: 'https://twitch.tv/brendonovich', twitch: 'https://twitch.tv/brendonovich',
@ -31,7 +31,7 @@ const teamMembers: Array<TeamMemberProps> = [
{ {
name: 'Oscar Beaumont', name: 'Oscar Beaumont',
role: 'Rust Engineer', role: 'Rust Engineer',
image: teamImages['oscar.jpg'], image: teamImages['oscar.jpg']!,
socials: { socials: {
twitter: 'https://twitter.com/oscartbeaumont', twitter: 'https://twitter.com/oscartbeaumont',
twitch: 'https://twitch.tv/oscartbeaumont', twitch: 'https://twitch.tv/oscartbeaumont',
@ -41,16 +41,16 @@ const teamMembers: Array<TeamMemberProps> = [
{ {
name: 'Ericson Soares', name: 'Ericson Soares',
role: 'Rust Engineer', role: 'Rust Engineer',
image: teamImages['ericson.jpg'], image: teamImages['ericson.jpg']!,
socials: { socials: {
twitter: 'https://twitter.com/fogodev', twitter: 'https://twitter.com/fogodev',
github: 'https://github.com/fogodev' github: 'https://github.com/fogodev'
} }
}, },
{ {
name: 'Utku Bakir', name: 'Utku Bakır',
role: 'React Native Engineer', role: 'React Native Engineer',
image: teamImages['utku.jpg'], image: teamImages['utku.jpg']!,
socials: { socials: {
github: 'https://github.com/utkubakir' github: 'https://github.com/utkubakir'
} }
@ -58,7 +58,7 @@ const teamMembers: Array<TeamMemberProps> = [
{ {
name: 'Haden Fletcher', name: 'Haden Fletcher',
role: 'Engineer & Designer', role: 'Engineer & Designer',
image: teamImages['haden.jpg'], image: teamImages['haden.jpg']!,
socials: { socials: {
twitter: 'https://twitter.com/heymaxichrome', twitter: 'https://twitter.com/heymaxichrome',
twitch: 'https://twitch.tv/maxichrome', twitch: 'https://twitch.tv/maxichrome',
@ -68,7 +68,7 @@ const teamMembers: Array<TeamMemberProps> = [
{ {
name: 'Jake Robinson', name: 'Jake Robinson',
role: 'Rust Engineer', role: 'Rust Engineer',
image: teamImages['jake.jpg'], image: teamImages['jake.jpg']!,
socials: { socials: {
github: 'https://github.com/brxken128' github: 'https://github.com/brxken128'
} }
@ -76,7 +76,7 @@ const teamMembers: Array<TeamMemberProps> = [
{ {
name: 'Mihail Dounaev', name: 'Mihail Dounaev',
role: 'Graphic Designer', role: 'Graphic Designer',
image: teamImages['mihail.jpg'], image: teamImages['mihail.jpg']!,
socials: { socials: {
twitter: 'https://twitter.com/mmmintdesign', twitter: 'https://twitter.com/mmmintdesign',
dribbble: 'https://dribbble.com/mmmint' dribbble: 'https://dribbble.com/mmmint'
@ -89,109 +89,109 @@ const investors: Array<TeamMemberProps> = [
name: 'Joseph Jacks', name: 'Joseph Jacks',
role: 'Founder, OSSC', role: 'Founder, OSSC',
investmentRound: 'Lead Seed', investmentRound: 'Lead Seed',
image: investorImages['josephjacks.jpg'] image: investorImages['josephjacks.jpg']!
}, },
{ {
name: 'Guillermo Rauch', name: 'Guillermo Rauch',
role: 'CEO, Vercel', role: 'CEO, Vercel',
investmentRound: 'Co-Lead Seed', investmentRound: 'Co-Lead Seed',
image: investorImages['guillermo.jpg'] image: investorImages['guillermo.jpg']!
}, },
{ {
name: 'Naval Ravikant', name: 'Naval Ravikant',
role: 'Founder, AngelList', role: 'Founder, AngelList',
investmentRound: 'Co-Lead Seed', investmentRound: 'Co-Lead Seed',
image: investorImages['naval.jpg'] image: investorImages['naval.jpg']!
}, },
{ {
name: 'Neha Narkhede', name: 'Neha Narkhede',
role: 'Confluent, Apache Kafka', role: 'Confluent, Apache Kafka',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['neha.jpg'] image: investorImages['neha.jpg']!
}, },
{ {
name: 'Austen Allred', name: 'Austen Allred',
role: 'CEO, Bloom Institute of Technology', role: 'CEO, Bloom Institute of Technology',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['austen.jpg'] image: investorImages['austen.jpg']!
}, },
{ {
name: 'Tom Preston-Werner', name: 'Tom Preston-Werner',
role: 'Founder, GitHub', role: 'Founder, GitHub',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['TOM.jpg'] image: investorImages['TOM.jpg']!
}, },
{ {
name: 'Tobias Lütke', name: 'Tobias Lütke',
role: 'CEO, Shopify', role: 'CEO, Shopify',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['tobiaslutke.jpg'] image: investorImages['tobiaslutke.jpg']!
}, },
{ {
name: 'Justin Hoffman', name: 'Justin Hoffman',
role: 'Former VP Sales, Elasticsearch', role: 'Former VP Sales, Elasticsearch',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['justinhoffman.jpg'] image: investorImages['justinhoffman.jpg']!
}, },
{ {
name: 'Ry Walker', name: 'Ry Walker',
role: 'Founder, Astronomer', role: 'Founder, Astronomer',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['rywalker.jpg'] image: investorImages['rywalker.jpg']!
}, },
{ {
name: 'Zachary Smith', name: 'Zachary Smith',
role: 'Head of Edge Infrastructure, Equinix', role: 'Head of Edge Infrastructure, Equinix',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['zacharysmith.jpg'] image: investorImages['zacharysmith.jpg']!
}, },
{ {
name: 'Sanjay Poonen', name: 'Sanjay Poonen',
role: 'Former COO, VMware', role: 'Former COO, VMware',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['sanjay.jpg'] image: investorImages['sanjay.jpg']!
}, },
{ {
name: 'David Mytton', name: 'David Mytton',
role: 'CEO, console.dev', role: 'CEO, console.dev',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['davidmytton.jpg'] image: investorImages['davidmytton.jpg']!
}, },
{ {
name: 'Peer Richelsen', name: 'Peer Richelsen',
role: 'CEO, Cal.com', role: 'CEO, Cal.com',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['peer.jpg'] image: investorImages['peer.jpg']!
}, },
{ {
name: 'Lester Lee', name: 'Lester Lee',
role: 'Founder, Slapdash', role: 'Founder, Slapdash',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['lesterlee.jpg'] image: investorImages['lesterlee.jpg']!
}, },
{ {
name: 'Haoyuan Li', name: 'Haoyuan Li',
role: 'Founder, Alluxio', role: 'Founder, Alluxio',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['haoyuan.jpg'] image: investorImages['haoyuan.jpg']!
}, },
{ {
name: 'Augusto Marietti', name: 'Augusto Marietti',
role: 'CEO, Kong', role: 'CEO, Kong',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['augusto.jpg'] image: investorImages['augusto.jpg']!
}, },
{ {
name: 'Vijay Sharma', name: 'Vijay Sharma',
role: 'CEO, Belong', role: 'CEO, Belong',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['vijay.jpg'] image: investorImages['vijay.jpg']!
}, },
{ {
name: 'Naveen R', name: 'Naveen R',
role: 'Founder, NocoDB', role: 'Founder, NocoDB',
investmentRound: 'Seed', investmentRound: 'Seed',
image: investorImages['naveen.jpg'] image: investorImages['naveen.jpg']!
} }
]; ];

View file

@ -41,10 +41,10 @@ export function parseMarkdown(markdownRaw: string): MarkdownParsed {
return text; return text;
} else { } else {
const rawText = rawSplit[index], const rawText = rawSplit[index],
meta = rawText.split(/\r?\n/)[0].trim(), meta = rawText!.split(/\r?\n/)[0]!.trim(),
kind = meta.split(' ')[0], kind = meta.split(' ')[0],
name = meta.split(' ')[1], name = meta.split(' ')[1],
extra = meta.substring(kind.length + name.length + 2), extra = meta.substring(kind!.length + name!.length + 2),
content = text.substring(meta.length + 1, text.length).trim(); content = text.substring(meta.length + 1, text.length).trim();
// console.log({ kind, name, extra, content }); // console.log({ kind, name, extra, content });

View file

@ -1,4 +1,4 @@
// @ts-check // @ts-ignore
let fs = require('fs-extra'); let fs = require('fs-extra');
let path = require('path'); let path = require('path');

View file

@ -1,7 +1,7 @@
import { useState } from 'react'; import { useState } from 'react';
import { queryClient, useBridgeMutation } from '@sd/client'; import { queryClient, useBridgeMutation } from '@sd/client';
import { Input } from '~/components/form/Input';
import Dialog from '~/components/layout/Dialog'; import Dialog from '~/components/layout/Dialog';
import { Input } from '~/components/primitive/Input';
import { currentLibraryStore } from '~/utils/nav'; import { currentLibraryStore } from '~/utils/nav';
type Props = { type Props = {

View file

@ -35,7 +35,7 @@ const DrawerLibraryManager = () => {
: 'border-b-app-box border-sidebar-line bg-sidebar-button rounded-t-md' : 'border-b-app-box border-sidebar-line bg-sidebar-button rounded-t-md'
)} )}
> >
<Text style={tw`text-ink text-sm font-semibold`}>{currentLibrary.config.name}</Text> <Text style={tw`text-ink text-sm font-semibold`}>{currentLibrary?.config.name}</Text>
<MotiView <MotiView
animate={{ animate={{
rotate: dropdownClosed ? '0deg' : '180deg', rotate: dropdownClosed ? '0deg' : '180deg',
@ -57,13 +57,13 @@ const DrawerLibraryManager = () => {
<View <View
style={twStyle( style={twStyle(
'mt-1 p-2', 'mt-1 p-2',
currentLibrary.uuid === library.uuid && 'bg-accent rounded' currentLibrary?.uuid === library.uuid && 'bg-accent rounded'
)} )}
> >
<Text <Text
style={twStyle( style={twStyle(
'text-ink text-sm font-semibold', 'text-ink text-sm font-semibold',
currentLibrary.uuid === library.uuid && 'text-white' currentLibrary?.uuid === library.uuid && 'text-white'
)} )}
> >
{library.config.name} {library.config.name}

View file

@ -36,7 +36,7 @@ type DrawerLocationsProp = {
const DrawerLocations = ({ stackName }: DrawerLocationsProp) => { const DrawerLocations = ({ stackName }: DrawerLocationsProp) => {
const navigation = useNavigation<DrawerNavigationHelpers>(); const navigation = useNavigation<DrawerNavigationHelpers>();
const importModalRef = useRef<ModalRef>(); const importModalRef = useRef<ModalRef>(null);
const { data: locations } = useLibraryQuery(['locations.list'], { keepPreviousData: true }); const { data: locations } = useLibraryQuery(['locations.list'], { keepPreviousData: true });
@ -62,7 +62,7 @@ const DrawerLocations = ({ stackName }: DrawerLocationsProp) => {
))} ))}
</View> </View>
{/* Add Location */} {/* Add Location */}
<Pressable onPress={() => importModalRef.current.present()}> <Pressable onPress={() => importModalRef.current?.present()}>
<View style={tw`border-app-line/80 mt-1 rounded border border-dashed`}> <View style={tw`border-app-line/80 mt-1 rounded border border-dashed`}>
<Text style={tw`p-2 text-center text-xs font-bold text-gray-400`}>Add Location</Text> <Text style={tw`p-2 text-center text-xs font-bold text-gray-400`}>Add Location</Text>
</View> </View>

View file

@ -37,7 +37,7 @@ const DrawerTags = ({ stackName }: DrawerTagsProp) => {
const { data: tags } = useLibraryQuery(['tags.list'], { keepPreviousData: true }); const { data: tags } = useLibraryQuery(['tags.list'], { keepPreviousData: true });
const createTagModalRef = useRef<ModalRef>(); const createTagModalRef = useRef<ModalRef>(null);
return ( return (
<CollapsibleView <CollapsibleView
@ -49,7 +49,7 @@ const DrawerTags = ({ stackName }: DrawerTagsProp) => {
{tags?.map((tag) => ( {tags?.map((tag) => (
<DrawerTagItem <DrawerTagItem
key={tag.id} key={tag.id}
tagName={tag.name} tagName={tag.name!}
onPress={() => onPress={() =>
navigation.navigate(stackName, { navigation.navigate(stackName, {
screen: 'Tag', screen: 'Tag',
@ -61,7 +61,7 @@ const DrawerTags = ({ stackName }: DrawerTagsProp) => {
))} ))}
</View> </View>
{/* Add Tag */} {/* Add Tag */}
<Pressable onPress={() => createTagModalRef.current.present()}> <Pressable onPress={() => createTagModalRef.current?.present()}>
<View style={tw`border-app-line/80 mt-1 rounded border border-dashed`}> <View style={tw`border-app-line/80 mt-1 rounded border border-dashed`}>
<Text style={tw`p-2 text-center text-xs font-bold text-gray-400`}>Add Tag</Text> <Text style={tw`p-2 text-center text-xs font-bold text-gray-400`}>Add Tag</Text>
</View> </View>

View file

@ -14,7 +14,7 @@ import FileItem from './FileItem';
import FileRow from './FileRow'; import FileRow from './FileRow';
type ExplorerProps = { type ExplorerProps = {
data: ExplorerData; data: ExplorerData | undefined;
}; };
const Explorer = ({ data }: ExplorerProps) => { const Explorer = ({ data }: ExplorerProps) => {
@ -22,7 +22,7 @@ const Explorer = ({ data }: ExplorerProps) => {
const [layoutMode, setLayoutMode] = useState<'grid' | 'list'>(getExplorerStore().layoutMode); const [layoutMode, setLayoutMode] = useState<'grid' | 'list'>(getExplorerStore().layoutMode);
function changeLayoutMode(kind) { function changeLayoutMode(kind: 'grid' | 'list') {
// We need to keep layoutMode as a state to make sure flash-list re-renders. // We need to keep layoutMode as a state to make sure flash-list re-renders.
setLayoutMode(kind); setLayoutMode(kind);
getExplorerStore().layoutMode = kind; getExplorerStore().layoutMode = kind;
@ -35,7 +35,7 @@ const Explorer = ({ data }: ExplorerProps) => {
navigation.push('Location', { id: data.item.location_id, path: data.item.materialized_path }); navigation.push('Location', { id: data.item.location_id, path: data.item.materialized_path });
} else { } else {
setData(data); setData(data);
modalRef.current.present(); modalRef.current?.present();
} }
} }

View file

@ -1,3 +1,4 @@
import { PropsWithChildren } from 'react';
import { Image, View } from 'react-native'; import { Image, View } from 'react-native';
import { DocumentDirectoryPath } from 'react-native-fs'; import { DocumentDirectoryPath } from 'react-native-fs';
import { ExplorerItem, isObject, isPath } from '@sd/client'; import { ExplorerItem, isObject, isPath } from '@sd/client';
@ -18,7 +19,7 @@ type FileThumbProps = {
export const getThumbnailUrlById = (casId: string) => export const getThumbnailUrlById = (casId: string) =>
`${DocumentDirectoryPath}/thumbnails/${encodeURIComponent(casId)}.webp`; `${DocumentDirectoryPath}/thumbnails/${encodeURIComponent(casId)}.webp`;
const FileThumbWrapper = ({ children, size = 1 }) => ( const FileThumbWrapper = ({ children, size = 1 }: PropsWithChildren<{ size: number }>) => (
<View style={[tw`items-center justify-center`, { width: 80 * size, height: 80 * size }]}> <View style={[tw`items-center justify-center`, { width: 80 * size, height: 80 * size }]}>
{children} {children}
</View> </View>
@ -38,7 +39,7 @@ export default function FileThumb({ data, size = 1, kind }: FileThumbProps) {
); );
const casId = isObject(data) ? data.item.file_paths[0]?.cas_id : data.item.cas_id; const casId = isObject(data) ? data.item.file_paths[0]?.cas_id : data.item.cas_id;
if (!casId) return undefined; if (!casId) return null;
// Icon // Icon
let icon = undefined; let icon = undefined;

View file

@ -12,7 +12,7 @@ type Props = {
const InfoTagPills = ({ data, style }: Props) => { const InfoTagPills = ({ data, style }: Props) => {
const objectData = data ? (isObject(data) ? data.item : data.item.object) : null; const objectData = data ? (isObject(data) ? data.item : data.item.object) : null;
const tagsQuery = useLibraryQuery(['tags.getForObject', objectData?.id], { const tagsQuery = useLibraryQuery(['tags.getForObject', objectData?.id ?? -1], {
enabled: Boolean(objectData) enabled: Boolean(objectData)
}); });
@ -25,7 +25,7 @@ const InfoTagPills = ({ data, style }: Props) => {
{/* Kind */} {/* Kind */}
<InfoPill <InfoPill
containerStyle={tw`mr-1`} containerStyle={tw`mr-1`}
text={isDir ? 'Folder' : ObjectKind[objectData?.kind || 0]} text={isDir ? 'Folder' : ObjectKind[objectData?.kind || 0]!}
/> />
{/* Extension */} {/* Extension */}
{item.extension && <InfoPill text={item.extension} containerStyle={tw`mr-1`} />} {item.extension && <InfoPill text={item.extension} containerStyle={tw`mr-1`} />}
@ -33,7 +33,7 @@ const InfoTagPills = ({ data, style }: Props) => {
{tagsQuery.data?.map((tag) => ( {tagsQuery.data?.map((tag) => (
<InfoPill <InfoPill
key={tag.id} key={tag.id}
text={tag.name} text={tag.name ?? 'Unnamed Tag'}
containerStyle={twStyle('mr-1', { backgroundColor: tag.color + 'CC' })} containerStyle={twStyle('mr-1', { backgroundColor: tag.color + 'CC' })}
textStyle={tw`text-white`} textStyle={tw`text-white`}
/> />

View file

@ -0,0 +1,38 @@
import React from 'react';
import WheelColorPicker from 'react-native-wheel-color-picker';
import { tw } from '~/lib/tailwind';
type ColorPickerProps = {
color: string;
onColorChangeComplete: (color: string) => void;
};
const defaultPalette = [
tw.color('blue-500'),
tw.color('red-500'),
tw.color('green-500'),
tw.color('yellow-500'),
tw.color('purple-500'),
tw.color('pink-500'),
tw.color('gray-500'),
tw.color('black'),
tw.color('white')
];
const ColorPicker = ({ color, onColorChangeComplete }: ColorPickerProps) => {
return (
<WheelColorPicker
autoResetSlider
gapSize={0}
thumbSize={40}
sliderSize={24}
shadeSliderThumb
color={color}
onColorChangeComplete={onColorChangeComplete}
swatchesLast={false}
palette={defaultPalette as string[]}
/>
);
};
export default ColorPicker;

View file

@ -31,7 +31,7 @@ const ModalHandle = (props: ModalHandle) => (
> >
{props.showCloseButton && ( {props.showCloseButton && (
<Pressable <Pressable
onPress={() => props.modalRef.current.close()} onPress={() => props.modalRef.current?.close()}
style={tw`bg-app-button absolute top-5 right-4 h-7 w-7 items-center justify-center rounded-full`} style={tw`bg-app-button absolute top-5 right-4 h-7 w-7 items-center justify-center rounded-full`}
> >
<X size={16} color="white" weight="bold" /> <X size={16} color="white" weight="bold" />
@ -101,7 +101,7 @@ export const ConfirmModal = forwardRef<ModalRef, ConfirmModalProps>((props, ref)
return ( return (
<> <>
{props.trigger && ( {props.trigger && (
<Pressable onPress={() => modalRef.current.present()}>{props.trigger}</Pressable> <Pressable onPress={() => modalRef.current?.present()}>{props.trigger}</Pressable>
)} )}
<BottomSheetModal <BottomSheetModal
ref={modalRef} ref={modalRef}
@ -126,7 +126,7 @@ export const ConfirmModal = forwardRef<ModalRef, ConfirmModalProps>((props, ref)
style={tw`flex-1`} style={tw`flex-1`}
size="lg" size="lg"
disabled={props.loading} // Disables Close button if loading disabled={props.loading} // Disables Close button if loading
onPress={() => modalRef.current.close()} onPress={() => modalRef.current?.close()}
> >
<Text style={tw`text-ink text-sm font-medium`}>Close</Text> <Text style={tw`text-ink text-sm font-medium`}>Close</Text>
</Button> </Button>

View file

@ -1,6 +1,7 @@
import { PropsWithChildren } from 'react';
import { FlatList } from 'react-native'; import { FlatList } from 'react-native';
export default function VirtualizedListWrapper({ children }) { export default function VirtualizedListWrapper({ children }: PropsWithChildren) {
return ( return (
<FlatList <FlatList
data={[]} data={[]}

View file

@ -13,11 +13,13 @@ const sortOptions = {
date_last_opened: 'Date Last Opened' date_last_opened: 'Date Last Opened'
}; };
type SortByType = keyof typeof sortOptions;
const ArrowUpIcon = () => <ArrowUp weight="bold" size={16} color={tw.color('ink')} />; const ArrowUpIcon = () => <ArrowUp weight="bold" size={16} color={tw.color('ink')} />;
const ArrowDownIcon = () => <ArrowDown weight="bold" size={16} color={tw.color('ink')} />; const ArrowDownIcon = () => <ArrowDown weight="bold" size={16} color={tw.color('ink')} />;
const SortByMenu = () => { const SortByMenu = () => {
const [sortBy, setSortBy] = useState('name'); const [sortBy, setSortBy] = useState<SortByType>('name');
const [sortDirection, setSortDirection] = useState('asc' as 'asc' | 'desc'); const [sortDirection, setSortDirection] = useState('asc' as 'asc' | 'desc');
return ( return (
@ -32,7 +34,9 @@ const SortByMenu = () => {
{Object.entries(sortOptions).map(([value, text]) => ( {Object.entries(sortOptions).map(([value, text]) => (
<MenuItem <MenuItem
key={value} key={value}
icon={value === sortBy && (sortDirection === 'asc' ? ArrowUpIcon : ArrowDownIcon)} icon={
value === sortBy ? (sortDirection === 'asc' ? ArrowUpIcon : ArrowDownIcon) : undefined
}
text={text} text={text}
value={value} value={value}
onSelect={() => { onSelect={() => {
@ -42,7 +46,7 @@ const SortByMenu = () => {
} }
// Reset sort direction to descending // Reset sort direction to descending
sortDirection === 'asc' && setSortDirection('desc'); sortDirection === 'asc' && setSortDirection('desc');
setSortBy(value); setSortBy(value as SortByType);
}} }}
/> />
))} ))}

View file

@ -1,14 +1,15 @@
import * as ML from 'expo-media-library';
import { forwardRef, useCallback } from 'react'; import { forwardRef, useCallback } from 'react';
import { Alert, Platform, Text, View } from 'react-native'; import { Alert, Text, View } from 'react-native';
import DocumentPicker from 'react-native-document-picker'; import DocumentPicker from 'react-native-document-picker';
import { useLibraryMutation } from '@sd/client'; import { useLibraryMutation } from '@sd/client';
// import RFS from 'react-native-fs';
import { Modal, ModalRef } from '~/components/layout/Modal'; import { Modal, ModalRef } from '~/components/layout/Modal';
import { Button } from '~/components/primitive/Button'; import { Button } from '~/components/primitive/Button';
import useForwardedRef from '~/hooks/useForwardedRef'; import useForwardedRef from '~/hooks/useForwardedRef';
import { tw } from '~/lib/tailwind'; import { tw } from '~/lib/tailwind';
// import RFS from 'react-native-fs';
// import * as ML from 'expo-media-library';
// WIP component // WIP component
const ImportModal = forwardRef<ModalRef, unknown>((_, ref) => { const ImportModal = forwardRef<ModalRef, unknown>((_, ref) => {
const modalRef = useForwardedRef(ref); const modalRef = useForwardedRef(ref);
@ -29,6 +30,8 @@ const ImportModal = forwardRef<ModalRef, unknown>((_, ref) => {
presentationStyle: 'pageSheet' presentationStyle: 'pageSheet'
}); });
if (!response) return;
createLocation({ createLocation({
path: decodeURIComponent(response.uri.replace('file://', '')), path: decodeURIComponent(response.uri.replace('file://', '')),
indexer_rules_ids: [] indexer_rules_ids: []
@ -43,70 +46,70 @@ const ImportModal = forwardRef<ModalRef, unknown>((_, ref) => {
Alert.alert('TODO'); Alert.alert('TODO');
return; return;
// Check if we have full access to the photos library // // Check if we have full access to the photos library
let permission = await ML.getPermissionsAsync(); // let permission = await ML.getPermissionsAsync();
// {"accessPrivileges": "none", "canAskAgain": true, "expires": "never", "granted": false, "status": "undetermined"} // // {"accessPrivileges": "none", "canAskAgain": true, "expires": "never", "granted": false, "status": "undetermined"}
if ( // if (
permission.status === ML.PermissionStatus.UNDETERMINED || // permission.status === ML.PermissionStatus.UNDETERMINED ||
(permission.status === ML.PermissionStatus.DENIED && permission.canAskAgain) // (permission.status === ML.PermissionStatus.DENIED && permission.canAskAgain)
) { // ) {
permission = await ML.requestPermissionsAsync(); // permission = await ML.requestPermissionsAsync();
} // }
// Permission Denied // // Permission Denied
if (permission.status === ML.PermissionStatus.DENIED) { // if (permission.status === ML.PermissionStatus.DENIED) {
Alert.alert( // Alert.alert(
'Permission required', // 'Permission required',
'You need to grant access to your photos library to import your photos/videos.' // 'You need to grant access to your photos library to import your photos/videos.'
); // );
return; // return;
} // }
// Limited Permission (Can't access path) // // Limited Permission (Can't access path)
if (permission.accessPrivileges === 'limited') { // if (permission.accessPrivileges === 'limited') {
Alert.alert( // Alert.alert(
'Limited access', // 'Limited access',
'You need to grant full access to your photos library to import your photos/videos.' // 'You need to grant full access to your photos library to import your photos/videos.'
); // );
return; // return;
} // }
// If android return error for now... // // If android return error for now...
if (Platform.OS !== 'ios') { // if (Platform.OS !== 'ios') {
Alert.alert('Not supported', 'Not supported for now...'); // Alert.alert('Not supported', 'Not supported for now...');
return; // return;
} // }
// And for IOS we are assuming every asset is under the same path (which is not the case) // // And for IOS we are assuming every asset is under the same path (which is not the case)
// file:///Users/xxxx/Library/Developer/CoreSimulator/Devices/F99C471F-C9F9-458D-8B87-BCC4B46C644C/data/Media/DCIM/100APPLE/IMG_0004.JPG // // file:///Users/xxxx/Library/Developer/CoreSimulator/Devices/F99C471F-C9F9-458D-8B87-BCC4B46C644C/data/Media/DCIM/100APPLE/IMG_0004.JPG
// file:///var/mobile/Media/DCIM/108APPLE/IMG_8332.JPG // // file:///var/mobile/Media/DCIM/108APPLE/IMG_8332.JPG
const firstAsset = (await ML.getAssetsAsync({ first: 1 })).assets[0]; // const firstAsset = (await ML.getAssetsAsync({ first: 1 })).assets[0];
if (!firstAsset) return; // if (!firstAsset) return;
// Gets asset uri: ph://CC95F08C-88C3-4012-9D6D-64A413D254B3 // // Gets asset uri: ph://CC95F08C-88C3-4012-9D6D-64A413D254B3
const assetId = firstAsset.id; // const assetId = firstAsset?.id;
// Gets Actual Path // // Gets Actual Path
const path = (await ML.getAssetInfoAsync(assetId)).localUri; // const path = (await ML.getAssetInfoAsync(assetId)).localUri;
const libraryPath = Platform.select({ // const libraryPath = Platform.select({
android: '', // android: '',
ios: path.replace('file://', '').split('Media/DCIM/')[0] + 'Media/DCIM/' // ios: path.replace('file://', '').split('Media/DCIM/')[0] + 'Media/DCIM/'
}); // });
createLocation({ // createLocation({
path: libraryPath, // path: libraryPath,
indexer_rules_ids: [] // indexer_rules_ids: []
}); // });
// const assets = await ML.getAssetsAsync({ mediaType: ML.MediaType.photo }); // const assets = await ML.getAssetsAsync({ mediaType: ML.MediaType.photo });
// assets.assets.map(async (i) => { // assets.assets.map(async (i) => {
// console.log((await ML.getAssetInfoAsync(i)).localUri); // console.log((await ML.getAssetInfoAsync(i)).localUri);
// }); // });
}, [createLocation]); }, []);
// const testFN = useCallback(async () => { // const testFN = useCallback(async () => {
// console.log(RFS.PicturesDirectoryPath); // console.log(RFS.PicturesDirectoryPath);

View file

@ -9,7 +9,7 @@ type Props = {
}; };
const DeleteLibraryModal = ({ trigger, onSubmit, libraryUuid }: Props) => { const DeleteLibraryModal = ({ trigger, onSubmit, libraryUuid }: Props) => {
const modalRef = useRef<ModalRef>(); const modalRef = useRef<ModalRef>(null);
const { mutate: deleteLibrary, isLoading: deleteLibLoading } = useBridgeMutation( const { mutate: deleteLibrary, isLoading: deleteLibLoading } = useBridgeMutation(
'library.delete', 'library.delete',
@ -19,12 +19,13 @@ const DeleteLibraryModal = ({ trigger, onSubmit, libraryUuid }: Props) => {
onSubmit?.(); onSubmit?.();
}, },
onSettled: () => { onSettled: () => {
modalRef.current.close(); modalRef.current?.close();
} }
} }
); );
return ( return (
<ConfirmModal <ConfirmModal
ref={modalRef}
title="Delete Library" title="Delete Library"
description="Deleting a library will permanently the database, the files themselves will not be deleted." description="Deleting a library will permanently the database, the files themselves will not be deleted."
ctaLabel="Delete" ctaLabel="Delete"

View file

@ -9,7 +9,7 @@ type Props = {
}; };
const DeleteLocationModal = ({ trigger, onSubmit, locationId }: Props) => { const DeleteLocationModal = ({ trigger, onSubmit, locationId }: Props) => {
const modalRef = useRef<ModalRef>(); const modalRef = useRef<ModalRef>(null);
const { mutate: deleteLoc, isLoading: deleteLocLoading } = useLibraryMutation( const { mutate: deleteLoc, isLoading: deleteLocLoading } = useLibraryMutation(
'locations.delete', 'locations.delete',
@ -18,7 +18,7 @@ const DeleteLocationModal = ({ trigger, onSubmit, locationId }: Props) => {
onSubmit?.(); onSubmit?.();
}, },
onSettled: () => { onSettled: () => {
modalRef.current.close(); modalRef.current?.close();
} }
} }
); );

View file

@ -9,19 +9,20 @@ type Props = {
}; };
const DeleteTagModal = ({ trigger, onSubmit, tagId }: Props) => { const DeleteTagModal = ({ trigger, onSubmit, tagId }: Props) => {
const modalRef = useRef<ModalRef>(); const modalRef = useRef<ModalRef>(null);
const { mutate: deleteTag, isLoading: deleteTagLoading } = useLibraryMutation('tags.delete', { const { mutate: deleteTag, isLoading: deleteTagLoading } = useLibraryMutation('tags.delete', {
onSuccess: () => { onSuccess: () => {
onSubmit?.(); onSubmit?.();
}, },
onSettled: () => { onSettled: () => {
modalRef.current.close(); modalRef.current?.close();
} }
}); });
return ( return (
<ConfirmModal <ConfirmModal
ref={modalRef}
title="Delete Tag" title="Delete Tag"
description="Are you sure you want to delete this tag? This cannot be undone and tagged files will be unlinked." description="Are you sure you want to delete this tag? This cannot be undone and tagged files will be unlinked."
ctaLabel="Delete" ctaLabel="Delete"

View file

@ -71,14 +71,14 @@ export const ActionsModal = () => {
<View style={tw`flex-1 px-4`}> <View style={tw`flex-1 px-4`}>
<View style={tw`flex flex-row items-center`}> <View style={tw`flex flex-row items-center`}>
{/* Thumbnail/Icon */} {/* Thumbnail/Icon */}
<Pressable onPress={() => fileInfoRef.current.present()}> <Pressable onPress={() => fileInfoRef.current?.present()}>
<FileThumb data={data} size={1} /> <FileThumb data={data} size={1} />
</Pressable> </Pressable>
<View style={tw`ml-2 flex-1`}> <View style={tw`ml-2 flex-1`}>
{/* Name + Extension */} {/* Name + Extension */}
<Text style={tw`text-base font-bold text-gray-200`} numberOfLines={1}> <Text style={tw`text-base font-bold text-gray-200`} numberOfLines={1}>
{item.name} {item?.name}
{item.extension && `.${item.extension}`} {item?.extension && `.${item?.extension}`}
</Text> </Text>
<View style={tw`flex flex-row`}> <View style={tw`flex flex-row`}>
<Text style={tw`text-ink-faint text-xs`}> <Text style={tw`text-ink-faint text-xs`}>
@ -86,12 +86,12 @@ export const ActionsModal = () => {
</Text> </Text>
<Text style={tw`text-ink-faint text-xs`}> <Text style={tw`text-ink-faint text-xs`}>
{' '} {' '}
{dayjs(item.date_created).format('MMM Do YYYY')} {dayjs(item?.date_created).format('MMM Do YYYY')}
</Text> </Text>
</View> </View>
<InfoTagPills data={data} /> <InfoTagPills data={data} />
</View> </View>
<FavoriteButton style={tw`mr-4`} data={objectData} /> {objectData && <FavoriteButton style={tw`mr-4`} data={objectData} />}
</View> </View>
<View style={tw`my-3`} /> <View style={tw`my-3`} />
{/* Actions */} {/* Actions */}
@ -99,7 +99,7 @@ export const ActionsModal = () => {
<ActionsItem <ActionsItem
icon={Info} icon={Info}
title="Show Info" title="Show Info"
onPress={() => fileInfoRef.current.present()} onPress={() => fileInfoRef.current?.present()}
/> />
</ActionsContainer> </ActionsContainer>
<ActionsContainer style={tw`mt-2`}> <ActionsContainer style={tw`mt-2`}>

View file

@ -31,7 +31,7 @@ function MetaItem({ title, value, icon }: MetaItemProps) {
<> <>
<View style={tw`flex flex-row items-center`}> <View style={tw`flex flex-row items-center`}>
<View style={tw`w-30 flex flex-row items-center`}> <View style={tw`w-30 flex flex-row items-center`}>
{icon && <Icon color="white" size={18} style={tw`mr-1`} />} {Icon && <Icon color="white" size={18} style={tw`mr-1`} />}
<Text style={tw`text-sm font-medium text-white`}>{title}</Text> <Text style={tw`text-sm font-medium text-white`}>{title}</Text>
</View> </View>
<Text style={tw`text-sm text-gray-400`}>{value}</Text> <Text style={tw`text-sm text-gray-400`}>{value}</Text>
@ -42,7 +42,7 @@ function MetaItem({ title, value, icon }: MetaItemProps) {
} }
type FileInfoModalProps = { type FileInfoModalProps = {
data: ExplorerItem; data: ExplorerItem | null;
}; };
const FileInfoModal = forwardRef<ModalRef, FileInfoModalProps>((props, ref) => { const FileInfoModal = forwardRef<ModalRef, FileInfoModalProps>((props, ref) => {
@ -69,13 +69,13 @@ const FileInfoModal = forwardRef<ModalRef, FileInfoModalProps>((props, ref) => {
{data && ( {data && (
<ModalScrollView style={tw`flex-1 p-4`}> <ModalScrollView style={tw`flex-1 p-4`}>
{/* Back Button */} {/* Back Button */}
<Pressable onPress={() => modalRef.current.close()} style={tw`absolute z-10 ml-4`}> <Pressable onPress={() => modalRef.current?.close()} style={tw`absolute z-10 ml-4`}>
<CaretLeft color={tw.color('accent')} size={20} weight="bold" /> <CaretLeft color={tw.color('accent')} size={20} weight="bold" />
</Pressable> </Pressable>
{/* File Icon / Name */} {/* File Icon / Name */}
<View style={tw`items-center`}> <View style={tw`items-center`}>
<FileThumb data={data} size={1.6} /> <FileThumb data={data} size={1.6} />
<Text style={tw`mt-2 text-base font-bold text-gray-200`}>{item.name}</Text> <Text style={tw`mt-2 text-base font-bold text-gray-200`}>{item?.name}</Text>
<InfoTagPills data={data} style={tw`mt-3`} /> <InfoTagPills data={data} style={tw`mt-3`} />
</View> </View>
{/* Details */} {/* Details */}
@ -99,19 +99,21 @@ const FileInfoModal = forwardRef<ModalRef, FileInfoModalProps>((props, ref) => {
<MetaItem <MetaItem
icon={Clock} icon={Clock}
title="Created" title="Created"
value={dayjs(item.date_created).format('MMM Do YYYY')} value={dayjs(item?.date_created).format('MMM Do YYYY')}
/> />
{/* Indexed */} {/* Indexed */}
<MetaItem <MetaItem
icon={Barcode} icon={Barcode}
title="Indexed" title="Indexed"
value={dayjs(item.date_indexed).format('MMM Do YYYY')} value={dayjs(item?.date_indexed).format('MMM Do YYYY')}
/> />
{filePathData && ( {filePathData && (
<> <>
{/* TODO: Note */} {/* TODO: Note */}
<MetaItem icon={Snowflake} title="Content ID" value={filePathData.cas_id} /> {filePathData.cas_id && (
<MetaItem icon={Snowflake} title="Content ID" value={filePathData.cas_id} />
)}
{/* Checksum */} {/* Checksum */}
{filePathData?.integrity_checksum && ( {filePathData?.integrity_checksum && (
<MetaItem <MetaItem

View file

@ -3,9 +3,9 @@ import { Pressable, Text, View } from 'react-native';
import ColorPicker from 'react-native-wheel-color-picker'; import ColorPicker from 'react-native-wheel-color-picker';
import { queryClient, useLibraryMutation } from '@sd/client'; import { queryClient, useLibraryMutation } from '@sd/client';
import { FadeInAnimation } from '~/components/animation/layout'; import { FadeInAnimation } from '~/components/animation/layout';
import { Input } from '~/components/form/Input';
import { Modal, ModalRef } from '~/components/layout/Modal'; import { Modal, ModalRef } from '~/components/layout/Modal';
import { Button } from '~/components/primitive/Button'; import { Button } from '~/components/primitive/Button';
import { Input } from '~/components/primitive/Input';
import useForwardedRef from '~/hooks/useForwardedRef'; import useForwardedRef from '~/hooks/useForwardedRef';
import { tw, twStyle } from '~/lib/tailwind'; import { tw, twStyle } from '~/lib/tailwind';
@ -29,12 +29,12 @@ const CreateTagModal = forwardRef<ModalRef, unknown>((_, ref) => {
}, },
onSettled: () => { onSettled: () => {
// Close modal // Close modal
modalRef.current.dismiss(); modalRef.current?.dismiss();
} }
}); });
useEffect(() => { useEffect(() => {
modalRef.current.snapToIndex(showPicker ? 1 : 0); modalRef.current?.snapToIndex(showPicker ? 1 : 0);
}, [modalRef, showPicker]); }, [modalRef, showPicker]);
return ( return (
@ -70,27 +70,7 @@ const CreateTagModal = forwardRef<ModalRef, unknown>((_, ref) => {
{showPicker && ( {showPicker && (
<FadeInAnimation> <FadeInAnimation>
<View style={tw`mt-4 h-64`}> <View style={tw`mt-4 h-64`}>
<ColorPicker <ColorPicker color={tagColor} onColorChangeComplete={(color) => setTagColor(color)} />
autoResetSlider
gapSize={0}
thumbSize={40}
sliderSize={24}
shadeSliderThumb
color={tagColor}
onColorChangeComplete={(color) => setTagColor(color)}
swatchesLast={false}
palette={[
tw.color('blue-500'),
tw.color('red-500'),
tw.color('green-500'),
tw.color('yellow-500'),
tw.color('purple-500'),
tw.color('pink-500'),
tw.color('gray-500'),
tw.color('black'),
tw.color('white')
]}
/>
</View> </View>
</FadeInAnimation> </FadeInAnimation>
)} )}

View file

@ -1,11 +1,11 @@
import { forwardRef, useEffect, useState } from 'react'; import { forwardRef, useEffect, useState } from 'react';
import { Pressable, Text, View } from 'react-native'; import { ColorValue, Pressable, Text, View } from 'react-native';
import ColorPicker from 'react-native-wheel-color-picker';
import { Tag, queryClient, useLibraryMutation } from '@sd/client'; import { Tag, queryClient, useLibraryMutation } from '@sd/client';
import { FadeInAnimation } from '~/components/animation/layout'; import { FadeInAnimation } from '~/components/animation/layout';
import ColorPicker from '~/components/form/ColorPicker';
import { Input } from '~/components/form/Input';
import { Modal, ModalRef } from '~/components/layout/Modal'; import { Modal, ModalRef } from '~/components/layout/Modal';
import { Button } from '~/components/primitive/Button'; import { Button } from '~/components/primitive/Button';
import { Input } from '~/components/primitive/Input';
import useForwardedRef from '~/hooks/useForwardedRef'; import useForwardedRef from '~/hooks/useForwardedRef';
import { tw, twStyle } from '~/lib/tailwind'; import { tw, twStyle } from '~/lib/tailwind';
@ -17,8 +17,8 @@ type Props = {
const UpdateTagModal = forwardRef<ModalRef, Props>((props, ref) => { const UpdateTagModal = forwardRef<ModalRef, Props>((props, ref) => {
const modalRef = useForwardedRef(ref); const modalRef = useForwardedRef(ref);
const [tagName, setTagName] = useState(props.tag.name); const [tagName, setTagName] = useState(props.tag.name!);
const [tagColor, setTagColor] = useState(props.tag.color); const [tagColor, setTagColor] = useState(props.tag.color!);
const [showPicker, setShowPicker] = useState(false); const [showPicker, setShowPicker] = useState(false);
const { mutate: updateTag, isLoading } = useLibraryMutation('tags.update', { const { mutate: updateTag, isLoading } = useLibraryMutation('tags.update', {
@ -31,12 +31,12 @@ const UpdateTagModal = forwardRef<ModalRef, Props>((props, ref) => {
props.onSubmit?.(); props.onSubmit?.();
}, },
onSettled: () => { onSettled: () => {
modalRef.current.dismiss(); modalRef.current?.dismiss();
} }
}); });
useEffect(() => { useEffect(() => {
modalRef.current.snapToIndex(showPicker ? 1 : 0); modalRef.current?.snapToIndex(showPicker ? 1 : 0);
}, [modalRef, showPicker]); }, [modalRef, showPicker]);
return ( return (
@ -63,32 +63,12 @@ const UpdateTagModal = forwardRef<ModalRef, Props>((props, ref) => {
style={twStyle({ backgroundColor: tagColor }, 'h-5 w-5 rounded-full')} style={twStyle({ backgroundColor: tagColor }, 'h-5 w-5 rounded-full')}
/> />
{/* TODO: Make this editable. Need to make sure color is a valid hexcode and update the color on picker etc. etc. */} {/* TODO: Make this editable. Need to make sure color is a valid hexcode and update the color on picker etc. etc. */}
<Input editable={false} value={tagColor} style={tw`ml-2 flex-1`} /> <Input editable={false} value={tagColor as string} style={tw`ml-2 flex-1`} />
</View> </View>
{showPicker && ( {showPicker && (
<FadeInAnimation> <FadeInAnimation>
<View style={tw`mt-4 h-64`}> <View style={tw`mt-4 h-64`}>
<ColorPicker <ColorPicker color={tagColor} onColorChangeComplete={(color) => setTagColor(color)} />
autoResetSlider
gapSize={0}
thumbSize={40}
sliderSize={24}
shadeSliderThumb
color={tagColor}
onColorChangeComplete={(color) => setTagColor(color)}
swatchesLast={false}
palette={[
tw.color('blue-500'),
tw.color('red-500'),
tw.color('green-500'),
tw.color('yellow-500'),
tw.color('purple-500'),
tw.color('pink-500'),
tw.color('gray-500'),
tw.color('black'),
tw.color('white')
]}
/>
</View> </View>
</FadeInAnimation> </FadeInAnimation>
)} )}

View file

@ -79,7 +79,7 @@ export function createReactNativeClient(): TRPCWebSocketClient {
body = JSON.stringify(outgoing); body = JSON.stringify(outgoing);
} }
SDCore.sd_core_msg(body).then((rawData) => { SDCore.sd_core_msg(body).then((rawData: string) => {
const data = JSON.parse(rawData); const data = JSON.parse(rawData);
if (Array.isArray(data)) { if (Array.isArray(data)) {
for (const payload of data) { for (const payload of data) {

View file

@ -58,7 +58,7 @@ if (Platform.OS === 'ios') {
const App = lazy(async () => { const App = lazy(async () => {
const keys = await AsyncStorage.getAllKeys(); const keys = await AsyncStorage.getAllKeys();
const values = await AsyncStorage.multiGet(keys); const values = await AsyncStorage.multiGet(keys);
values.forEach(([key, value]) => _localStorage.set(key, value)); values.forEach(([key, value]) => _localStorage.set(key, value!));
return await import('./App'); return await import('./App');
}); });

View file

@ -11,7 +11,7 @@ export default function LocationScreen({ navigation, route }: SharedScreenProps<
'locations.getExplorerData', 'locations.getExplorerData',
{ {
location_id: id, location_id: id,
path: path || '', path: path ?? '',
limit: 100, limit: 100,
cursor: null cursor: null
} }
@ -26,14 +26,14 @@ export default function LocationScreen({ navigation, route }: SharedScreenProps<
}); });
} else { } else {
navigation.setOptions({ navigation.setOptions({
title: data?.context.name title: data?.context.name ?? 'Location'
}); });
} }
}, [data, navigation, path]); }, [data, navigation, path]);
useEffect(() => { useEffect(() => {
getExplorerStore().locationId = id; getExplorerStore().locationId = id;
getExplorerStore().path = path; getExplorerStore().path = path ?? '';
}, [id, path]); }, [id, path]);
return <Explorer data={data} />; return <Explorer data={data} />;

View file

@ -6,7 +6,15 @@ export default function NotFoundScreen({ navigation }: RootStackScreenProps<'Not
return ( return (
<View style={tw`flex-1 items-center justify-center p-5`}> <View style={tw`flex-1 items-center justify-center p-5`}>
<Text style={tw`text-xl font-bold`}>This screen doesn&apos;t exist.</Text> <Text style={tw`text-xl font-bold`}>This screen doesn&apos;t exist.</Text>
<TouchableOpacity onPress={() => navigation.replace('Root')} style={tw`mt-4 py-4`}> <TouchableOpacity
onPress={() =>
navigation.replace('Root', {
screen: 'Home',
params: { screen: 'OverviewStack', params: { screen: 'Overview' } }
})
}
style={tw`mt-4 py-4`}
>
<Text style={tw`text-ink-dull text-sm`}>Go to home screen!</Text> <Text style={tw`text-ink-dull text-sm`}>Go to home screen!</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>

View file

@ -12,7 +12,7 @@ export default function TagScreen({ navigation, route }: SharedScreenProps<'Tag'
useEffect(() => { useEffect(() => {
// Set screen title to tag name. // Set screen title to tag name.
navigation.setOptions({ navigation.setOptions({
title: data?.context.name title: data?.context.name ?? 'Tag'
}); });
}, [data?.context.name, navigation]); }, [data?.context.name, navigation]);

View file

@ -1,9 +1,9 @@
import React from 'react'; import React from 'react';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import { useBridgeQuery } from '@sd/client'; import { useBridgeQuery } from '@sd/client';
import { Input } from '~/components/form/Input';
import Card from '~/components/layout/Card'; import Card from '~/components/layout/Card';
import Divider from '~/components/primitive/Divider'; import Divider from '~/components/primitive/Divider';
import { Input } from '~/components/primitive/Input';
import { tw } from '~/lib/tailwind'; import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator'; import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';

View file

@ -3,9 +3,9 @@ import React from 'react';
import { Controller, useForm } from 'react-hook-form'; import { Controller, useForm } from 'react-hook-form';
import { Alert, Text, View } from 'react-native'; import { Alert, Text, View } from 'react-native';
import { useBridgeMutation, useLibraryContext } from '@sd/client'; import { useBridgeMutation, useLibraryContext } from '@sd/client';
import { Input } from '~/components/form/Input';
import { Switch } from '~/components/form/Switch';
import { Button } from '~/components/primitive/Button'; import { Button } from '~/components/primitive/Button';
import { Input } from '~/components/primitive/Input';
import { Switch } from '~/components/primitive/Switch';
import { SettingsContainer } from '~/components/settings/SettingsContainer'; import { SettingsContainer } from '~/components/settings/SettingsContainer';
import { SettingsItem } from '~/components/settings/SettingsItem'; import { SettingsItem } from '~/components/settings/SettingsItem';
import { useAutoForm } from '~/hooks/useAutoForm'; import { useAutoForm } from '~/hooks/useAutoForm';

View file

@ -11,7 +11,7 @@ import { tw, twStyle } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator'; import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
function TagItem({ tag, index }: { tag: Tag; index: number }) { function TagItem({ tag, index }: { tag: Tag; index: number }) {
const updateTagModalRef = useRef<ModalRef>(); const updateTagModalRef = useRef<ModalRef>(null);
const renderRightActions = ( const renderRightActions = (
progress: Animated.AnimatedInterpolation<number>, progress: Animated.AnimatedInterpolation<number>,
@ -29,7 +29,7 @@ function TagItem({ tag, index }: { tag: Tag; index: number }) {
style={[tw`flex flex-row items-center`, { transform: [{ translateX: translate }] }]} style={[tw`flex flex-row items-center`, { transform: [{ translateX: translate }] }]}
> >
<UpdateTagModal tag={tag} ref={updateTagModalRef} onSubmit={() => swipeable.close()} /> <UpdateTagModal tag={tag} ref={updateTagModalRef} onSubmit={() => swipeable.close()} />
<AnimatedButton size="md" onPress={() => updateTagModalRef.current.present()}> <AnimatedButton size="md" onPress={() => updateTagModalRef.current?.present()}>
<Pen size={18} color="white" /> <Pen size={18} color="white" />
</AnimatedButton> </AnimatedButton>
<DeleteTagModal <DeleteTagModal
@ -55,7 +55,7 @@ function TagItem({ tag, index }: { tag: Tag; index: number }) {
> >
<View style={tw`flex flex-row items-center justify-between`}> <View style={tw`flex flex-row items-center justify-between`}>
<View style={tw`flex flex-row`}> <View style={tw`flex flex-row`}>
<View style={twStyle({ backgroundColor: tag.color }, 'h-4 w-4 rounded-full')} /> <View style={twStyle({ backgroundColor: tag.color! }, 'h-4 w-4 rounded-full')} />
<Text style={tw`text-ink ml-3`}>{tag.name}</Text> <Text style={tw`text-ink ml-3`}>{tag.name}</Text>
</View> </View>
<CaretRight color={tw.color('ink-dull')} size={18} /> <CaretRight color={tw.color('ink-dull')} size={18} />

View file

@ -1,6 +1,7 @@
import { import {
DrawerNavigationState, DrawerNavigationState,
ParamListBase, ParamListBase,
Route,
getFocusedRouteNameFromRoute getFocusedRouteNameFromRoute
} from '@react-navigation/native'; } from '@react-navigation/native';
import { valtioPersist } from '@sd/client'; import { valtioPersist } from '@sd/client';
@ -9,7 +10,9 @@ export const currentLibraryStore = valtioPersist('sdActiveLibrary', {
id: null as string | null id: null as string | null
}); });
export const getActiveRouteFromState = function (state: any) { export const getActiveRouteFromState = function (
state: any
): Partial<Route<string, object | undefined>> {
if (!state.routes || state.routes.length === 0 || state.index >= state.routes.length) { if (!state.routes || state.routes.length === 0 || state.index >= state.routes.length) {
return state; return state;
} }

View file

@ -1,14 +1,24 @@
{ {
"extends": "expo/tsconfig.base",
"compilerOptions": { "compilerOptions": {
"allowJs": true,
"esModuleInterop": true,
"jsx": "react-native",
"lib": ["DOM", "ESNext"],
"moduleResolution": "node",
"resolveJsonModule": true,
"skipLibCheck": true,
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "ESNext",
"noEmit": true, "noEmit": true,
"paths": { "paths": {
"~/*": ["./src/*"] "~/*": ["./src/*"]
}, },
"jsx": "react-native" "strict": true,
"noUncheckedIndexedAccess": true,
"forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true
}, },
"exclude": ["node_modules", "babel.config.js", "metro.config.js", "jest.config.js"],
"references": [ "references": [
{ {
"path": "../../packages/client" "path": "../../packages/client"

View file

@ -2,25 +2,21 @@
"$schema": "https://json.schemastore.org/tsconfig", "$schema": "https://json.schemastore.org/tsconfig",
"display": "Default", "display": "Default",
"compilerOptions": { "compilerOptions": {
// Actual type stuff
"strict": true, "strict": true,
"jsx": "react-jsx", "jsx": "react-jsx",
"esModuleInterop": true, "esModuleInterop": true,
// Various configs
"skipLibCheck": true, "skipLibCheck": true,
"preserveWatchOutput": true, "preserveWatchOutput": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
// Project references stuff "noUncheckedIndexedAccess": true,
"composite": true, "composite": true,
"declaration": true, "declaration": true,
"emitDeclarationOnly": true, "emitDeclarationOnly": true,
// Module stuff
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"module": "ESNext", "module": "ESNext",
"target": "ESNext", "target": "ESNext",
// Extra types
"types": ["vite-plugin-svgr/client", "vite/client"] "types": ["vite-plugin-svgr/client", "vite/client"]
} }
} }

View file

@ -14,7 +14,7 @@ function Index() {
const currentLibrary = libraries.data.find((l) => l.uuid === currentLibraryCache.id); const currentLibrary = libraries.data.find((l) => l.uuid === currentLibraryCache.id);
const libraryId = currentLibrary ? currentLibrary.uuid : libraries.data[0].uuid; const libraryId = currentLibrary ? currentLibrary.uuid : libraries.data[0]?.uuid;
return <Navigate to={`${libraryId}/overview`} />; return <Navigate to={`${libraryId}/overview`} />;
} }

View file

@ -35,7 +35,7 @@ export const EncryptFileDialog = ({ ...props }: EncryptDialogProps) => {
const keys = useLibraryQuery(['keys.list']); const keys = useLibraryQuery(['keys.list']);
const mountedUuids = useLibraryQuery(['keys.listMounted'], { const mountedUuids = useLibraryQuery(['keys.listMounted'], {
onSuccess: (data) => { onSuccess: (data) => {
UpdateKey(data[0]); UpdateKey(data[0] ?? '');
} }
}); });

View file

@ -56,9 +56,9 @@ export const EraseFileDialog = (props: EraseDialogProps) => {
min={1} min={1}
step={1} step={1}
defaultValue={[4]} defaultValue={[4]}
onValueChange={(e) => { onValueChange={(val) => {
setPasses(e); setPasses(val);
form.setValue('passes', e[0]); form.setValue('passes', val[0] ?? 1);
}} }}
/> />
</div> </div>

View file

@ -41,7 +41,7 @@ export const KeyViewerDialog = (props: KeyViewerDialogProps) => {
const keys = useLibraryQuery(['keys.list'], { const keys = useLibraryQuery(['keys.list'], {
onSuccess: (data) => { onSuccess: (data) => {
if (key === '' && data.length !== 0) { if (key === '' && data.length !== 0) {
setKey(data[0].uuid); setKey(data[0]?.uuid ?? '');
} }
} }
}); });

View file

@ -145,7 +145,7 @@ export const VirtualizedList = memo(({ data, context, onScroll }: Props) => {
kind="list" kind="list"
isSelected={getExplorerStore().selectedRowIndex === virtualRow.index} isSelected={getExplorerStore().selectedRowIndex === virtualRow.index}
index={virtualRow.index} index={virtualRow.index}
item={data[virtualRow.index]} item={data[virtualRow.index]!}
/> />
) : ( ) : (
[...Array(amountOfColumns)].map((_, i) => { [...Array(amountOfColumns)].map((_, i) => {

View file

@ -1,8 +1,8 @@
import cryptoRandomString from 'crypto-random-string'; import cryptoRandomString from 'crypto-random-string';
import { Eye, EyeSlash, Info } from 'phosphor-react'; import { Eye, EyeSlash, Info } from 'phosphor-react';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { Algorithm, useLibraryMutation, useLibraryQuery } from '@sd/client'; import { Algorithm, useLibraryMutation } from '@sd/client';
import { Button, CategoryHeading, Input, Select, SelectOption, Switch, cva, tw } from '@sd/ui'; import { Button, CategoryHeading, Input, Select, SelectOption, Switch, tw } from '@sd/ui';
import { getHashingAlgorithmSettings } from '../../screens/settings/library/KeysSetting'; import { getHashingAlgorithmSettings } from '../../screens/settings/library/KeysSetting';
import Slider from '../primitive/Slider'; import Slider from '../primitive/Slider';
import { Tooltip } from '../tooltip/Tooltip'; import { Tooltip } from '../tooltip/Tooltip';
@ -67,12 +67,12 @@ export function KeyMounter() {
min={8} min={8}
step={4} step={4}
defaultValue={[64]} defaultValue={[64]}
onValueChange={(e) => { onValueChange={(val) => {
setSliderValue(e); setSliderValue(val);
setKey(generatePassword(e[0])); setKey(generatePassword(val[0] ?? 8));
}} }}
onClick={() => { onClick={() => {
setKey(generatePassword(sliderValue[0])); setKey(generatePassword(sliderValue[0] ?? 8));
}} }}
/> />
</div> </div>

View file

@ -6,7 +6,7 @@ export const useCurrentOnboardingScreenKey = (): string | null => {
const { pathname } = useLocation(); const { pathname } = useLocation();
if (pathname.startsWith(`/${ONBOARDING_ROUTE_PREFIX_NAME}/`)) { if (pathname.startsWith(`/${ONBOARDING_ROUTE_PREFIX_NAME}/`)) {
return pathname.split('/')[2]; return pathname.split('/')[2] || null;
} }
return null; return null;

View file

@ -18,9 +18,9 @@ export function MacTrafficLights(props: TrafficLightsProps) {
return ( return (
<div data-tauri-drag-region className={clsx('group flex flex-row space-x-[7.5px]', className)}> <div data-tauri-drag-region className={clsx('group flex flex-row space-x-[7.5px]', className)}>
<TrafficLight type="close" onClick={onClose} colorful={focused} /> <TrafficLight type="close" onClick={onClose} colorful={focused ?? false} />
<TrafficLight type="minimize" onClick={onMinimize} colorful={focused} /> <TrafficLight type="minimize" onClick={onMinimize} colorful={focused ?? false} />
<TrafficLight type="fullscreen" onClick={onFullscreen} colorful={focused} /> <TrafficLight type="fullscreen" onClick={onFullscreen} colorful={focused ?? false} />
</div> </div>
); );
} }

View file

@ -312,7 +312,7 @@ const table: Record<string, HashingAlgorithm> = {
// not sure of a suitable place for this function // not sure of a suitable place for this function
export const getHashingAlgorithmSettings = (hashingAlgorithm: string): HashingAlgorithm => { export const getHashingAlgorithmSettings = (hashingAlgorithm: string): HashingAlgorithm => {
return table[hashingAlgorithm]; return table[hashingAlgorithm] || { name: 'Argon2id', params: 'Standard' };
}; };
// not sure of a suitable place for this function // not sure of a suitable place for this function