Add default metadata & make replaceFile function
Before Width: | Height: | Size: 23 KiB |
BIN
public/icons/android/icon-144x144.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
public/icons/android/icon-192x192.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
public/icons/android/icon-36x36.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
public/icons/android/icon-48x48.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
public/icons/android/icon-72x72.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
public/icons/android/icon-96x96.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
public/icons/apple/icon-114x114.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
public/icons/apple/icon-120x120.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
public/icons/apple/icon-144x144.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
public/icons/apple/icon-152x152.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
public/icons/apple/icon-180x180.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
public/icons/apple/icon-57x57.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
public/icons/apple/icon-60x60.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
public/icons/apple/icon-72x72.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
public/icons/apple/icon-76x76.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
public/icons/apple/icon-precomposed.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
public/icons/apple/icon.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
public/icons/favicon-16x16.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
public/icons/favicon-32x32.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
public/icons/favicon-96x96.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
public/icons/favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
public/icons/microsoft/icon-144x144.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
public/icons/microsoft/icon-150x150.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
public/icons/microsoft/icon-310x310.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
public/icons/microsoft/icon-70x70.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
@ -2,7 +2,6 @@ import Link from 'next/link';
|
|||||||
import { forgotPassword } from '@/lib/actions';
|
import { forgotPassword } from '@/lib/actions';
|
||||||
import { FormMessage, type Message, SubmitButton } from '@/components/default';
|
import { FormMessage, type Message, SubmitButton } from '@/components/default';
|
||||||
import { Input, Label } from '@/components/ui';
|
import { Input, Label } from '@/components/ui';
|
||||||
import { SmtpMessage } from '@/app/(auth-pages)/smtp-message';
|
|
||||||
|
|
||||||
const ForgotPassword = async (props: { searchParams: Promise<Message> }) => {
|
const ForgotPassword = async (props: { searchParams: Promise<Message> }) => {
|
||||||
const searchParams = await props.searchParams;
|
const searchParams = await props.searchParams;
|
||||||
@ -30,7 +29,6 @@ const ForgotPassword = async (props: { searchParams: Promise<Message> }) => {
|
|||||||
<FormMessage message={searchParams} />
|
<FormMessage message={searchParams} />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<SmtpMessage />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
import { ArrowUpRight, InfoIcon } from 'lucide-react';
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
export const SmtpMessage = () => {
|
|
||||||
return (
|
|
||||||
<div className='bg-muted/50 px-5 py-3 border rounded-md flex gap-4'>
|
|
||||||
<InfoIcon size={16} className='mt-0.5' />
|
|
||||||
<div className='flex flex-col gap-1'>
|
|
||||||
<small className='text-sm text-secondary-foreground'>
|
|
||||||
<strong> Note:</strong> Emails are rate limited. Enable Custom SMTP to
|
|
||||||
increase the rate limit.
|
|
||||||
</small>
|
|
||||||
<div>
|
|
||||||
<Link
|
|
||||||
href='https://supabase.com/docs/guides/auth/auth-smtp'
|
|
||||||
target='_blank'
|
|
||||||
className='text-primary/50 hover:text-primary flex items-center text-sm gap-1'
|
|
||||||
>
|
|
||||||
Learn more <ArrowUpRight size={14} />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -9,24 +9,169 @@ import Footer from '@/components/default/footer';
|
|||||||
import { Toaster } from '@/components/ui';
|
import { Toaster } from '@/components/ui';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'T3 Template with Supabase',
|
title: {
|
||||||
description: 'Generated by create-t3-app',
|
template: '%s | T3 Template',
|
||||||
icons: [
|
default: 'T3 Template with Supabase',
|
||||||
{
|
},
|
||||||
rel: 'icon',
|
description: 'Created by Gib with T3!',
|
||||||
url: '/images/favicon.ico',
|
applicationName: 'T3 Template',
|
||||||
|
keywords: 'T3 Template, Next.js, Supabase, Tailwind, TypeScript, React, T3, Gib, Theo',
|
||||||
|
authors: [{name: 'Gib', url: 'https://gbrown.org'}],
|
||||||
|
creator: 'Gib Brown',
|
||||||
|
publisher: 'Gib Brown',
|
||||||
|
formatDetection: {
|
||||||
|
email: false,
|
||||||
|
address: false,
|
||||||
|
telephone: false,
|
||||||
|
},
|
||||||
|
robots: {
|
||||||
|
index: true,
|
||||||
|
follow: true,
|
||||||
|
nocache: false,
|
||||||
|
googleBot: {
|
||||||
|
index: true,
|
||||||
|
follow: true,
|
||||||
|
noimageindex: false,
|
||||||
|
'max-video-preview': -1,
|
||||||
|
'max-image-preview': 'large',
|
||||||
|
'max-snippet': -1,
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
rel: 'icon',
|
icons: {
|
||||||
type: 'image/png',
|
icon: [
|
||||||
sizes: '32x32',
|
{ url: '/icons/favicon.ico', type: 'image/x-icon', sizes: 'any' },
|
||||||
url: '/images/favicon.png',
|
{ url: '/icons/favicon-16x16.png', type: 'image/png', sizes: '16x16'},
|
||||||
|
{ url: '/icons/favicon-32x32.png', type: 'image/png', sizes: '32x32'},
|
||||||
|
{ url: '/icons/favicon-96x96.png', type: 'image/png', sizes: '96x96'},
|
||||||
|
{ url: '/icons/favicon.ico', type: 'image/x-icon', sizes: 'any', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/favicon-16x16.png', type: 'image/png', sizes: '16x16', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/favicon-32x32.png', type: 'image/png', sizes: '32x32', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/favicon-96x96.png', type: 'image/png', sizes: '96x96', media: '(prefers-color-scheme: dark)' },
|
||||||
|
|
||||||
|
{ url: '/icons/android/icon-36x36', type: 'image/png', sizes: '36x36'},
|
||||||
|
{ url: '/icons/android/icon-48x48', type: 'image/png', sizes: '48x48'},
|
||||||
|
{ url: '/icons/android/icon-72x72', type: 'image/png', sizes: '72x72'},
|
||||||
|
{ url: '/icons/android/icon-96x96', type: 'image/png', sizes: '96x96'},
|
||||||
|
{ url: '/icons/android/icon-144x144', type: 'image/png', sizes: '144x144'},
|
||||||
|
{ url: '/icons/android/icon-192x192', type: 'image/png', sizes: '192x192'},
|
||||||
|
{ url: '/icons/android/icon-36x36', type: 'image/png', sizes: '36x36', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/android/icon-48x48', type: 'image/png', sizes: '48x48', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/android/icon-72x72', type: 'image/png', sizes: '72x72', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/android/icon-96x96', type: 'image/png', sizes: '96x96', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/android/icon-144x144', type: 'image/png', sizes: '144x144', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/android/icon-192x192', type: 'image/png', sizes: '192x192', media: '(prefers-color-scheme: dark)' },
|
||||||
|
],
|
||||||
|
shortcut: [
|
||||||
|
{ url: '/icons/android/icon-36x36', type: 'image/png', sizes: '36x36'},
|
||||||
|
{ url: '/icons/android/icon-48x48', type: 'image/png', sizes: '48x48'},
|
||||||
|
{ url: '/icons/android/icon-72x72', type: 'image/png', sizes: '72x72'},
|
||||||
|
{ url: '/icons/android/icon-96x96', type: 'image/png', sizes: '96x96'},
|
||||||
|
{ url: '/icons/android/icon-144x144', type: 'image/png', sizes: '144x144'},
|
||||||
|
{ url: '/icons/android/icon-192x192', type: 'image/png', sizes: '192x192'},
|
||||||
|
{ url: '/icons/android/icon-36x36', type: 'image/png', sizes: '36x36', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/android/icon-48x48', type: 'image/png', sizes: '48x48', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/android/icon-72x72', type: 'image/png', sizes: '72x72', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/android/icon-96x96', type: 'image/png', sizes: '96x96', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/android/icon-144x144', type: 'image/png', sizes: '144x144', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/android/icon-192x192', type: 'image/png', sizes: '192x192', media: '(prefers-color-scheme: dark)' },
|
||||||
|
],
|
||||||
|
apple: [
|
||||||
|
{ url: '/icons/apple/icon.png', type: 'image/png', sizes: '192x192' },
|
||||||
|
{ url: '/icons/apple/icon-57x57.png', type: 'image/png', sizes: '57x57' },
|
||||||
|
{ url: '/icons/apple/icon-60x60.png', type: 'image/png', sizes: '60x60' },
|
||||||
|
{ url: '/icons/apple/icon-72x72.png', type: 'image/png', sizes: '72x72' },
|
||||||
|
{ url: '/icons/apple/icon-76x76.png', type: 'image/png', sizes: '76x76' },
|
||||||
|
{ url: '/icons/apple/icon-114x114.png', type: 'image/png', sizes: '114x114' },
|
||||||
|
{ url: '/icons/apple/icon-120x120.png', type: 'image/png', sizes: '120x120' },
|
||||||
|
{ url: '/icons/apple/icon-144x144.png', type: 'image/png', sizes: '144x144' },
|
||||||
|
{ url: '/icons/apple/icon-152x152.png', type: 'image/png', sizes: '152x152' },
|
||||||
|
{ url: '/icons/apple/icon-180x180.png', type: 'image/png', sizes: '180x180' },
|
||||||
|
{ url: '/icons/apple/icon.png', type: 'image/png', sizes: '192x192', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/apple/icon-57x57.png', type: 'image/png', sizes: '57x57', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/apple/icon-60x60.png', type: 'image/png', sizes: '60x60', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/apple/icon-72x72.png', type: 'image/png', sizes: '72x72', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/apple/icon-76x76.png', type: 'image/png', sizes: '76x76', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/apple/icon-114x114.png', type: 'image/png', sizes: '114x114', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/apple/icon-120x120.png', type: 'image/png', sizes: '120x120', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/apple/icon-144x144.png', type: 'image/png', sizes: '144x144', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/apple/icon-152x152.png', type: 'image/png', sizes: '152x152', media: '(prefers-color-scheme: dark)' },
|
||||||
|
{ url: '/icons/apple/icon-180x180.png', type: 'image/png', sizes: '180x180', media: '(prefers-color-scheme: dark)' },
|
||||||
|
],
|
||||||
|
other: [
|
||||||
|
{
|
||||||
|
rel: 'apple-touch-icon-precomposed',
|
||||||
|
url: '/icons/apple/icon-precomposed.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '180x180'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
card: 'app',
|
||||||
|
title: 'T3 Template',
|
||||||
|
description: 'Created by Gib with T3!',
|
||||||
|
siteId: '',
|
||||||
|
creator: '@cs_gib',
|
||||||
|
creatorId: '',
|
||||||
|
images: {
|
||||||
|
url: 'https://git.gbrown.org/gib/T3-Template/raw/main/public/icons/apple/icon.png',
|
||||||
|
alt: 'T3 Template',
|
||||||
},
|
},
|
||||||
{
|
app: {
|
||||||
rel: 'apple-touch-icon',
|
name: 'T3 Template',
|
||||||
url: '/images/appicon.png',
|
id: {
|
||||||
|
iphone: '',
|
||||||
|
ipad: '',
|
||||||
|
googleplay: '',
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
iphone: '',
|
||||||
|
ipad: '',
|
||||||
|
googleplay: '',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
|
verification: {
|
||||||
|
google: 'google',
|
||||||
|
yandex: 'yandex',
|
||||||
|
yahoo: 'yahoo',
|
||||||
|
},
|
||||||
|
itunes: {
|
||||||
|
appId: '',
|
||||||
|
appArgument: '',
|
||||||
|
},
|
||||||
|
appleWebApp: {
|
||||||
|
title: 'T3 Template',
|
||||||
|
statusBarStyle: 'black-translucent',
|
||||||
|
startupImage: [
|
||||||
|
'/icons/apple/splash-768x1004.png',
|
||||||
|
{
|
||||||
|
url: '/icons/apple/splash-1536x2008.png',
|
||||||
|
media: '(device-width: 768px) and (device-height: 1024px)',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
appLinks: {
|
||||||
|
ios: {
|
||||||
|
url: 'https://t3-template.gbrown.org/ios',
|
||||||
|
app_store_id: 't3_template',
|
||||||
|
},
|
||||||
|
android: {
|
||||||
|
package: 'org.gbrown.android/t3-template',
|
||||||
|
app_name: 'app_t3_template',
|
||||||
|
},
|
||||||
|
web: {
|
||||||
|
url: 'https://t3-template.gbrown.org/web',
|
||||||
|
should_fallback: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
facebook: {
|
||||||
|
appId: '',
|
||||||
|
},
|
||||||
|
pinterest: {
|
||||||
|
richPin: true,
|
||||||
|
},
|
||||||
|
category: 'technology',
|
||||||
};
|
};
|
||||||
|
|
||||||
const geist = Geist({
|
const geist = Geist({
|
||||||
|
@ -4,6 +4,7 @@ import Link from 'next/link';
|
|||||||
import { Button } from '@/components/ui';
|
import { Button } from '@/components/ui';
|
||||||
import NavigationAuth from './auth';
|
import NavigationAuth from './auth';
|
||||||
import { ThemeToggle } from '@/components/context/theme';
|
import { ThemeToggle } from '@/components/context/theme';
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
const Navigation = () => {
|
const Navigation = () => {
|
||||||
return (
|
return (
|
||||||
@ -16,7 +17,10 @@ const Navigation = () => {
|
|||||||
items-center p-3 px-5 text-sm'
|
items-center p-3 px-5 text-sm'
|
||||||
>
|
>
|
||||||
<div className='flex gap-5 items-center font-semibold'>
|
<div className='flex gap-5 items-center font-semibold'>
|
||||||
<Link href={'/'}>T3 Supabase Template</Link>
|
<Link className='flex flex-row my-auto gap-2' href='/'>
|
||||||
|
<Image src='/icons/favicon-96x96.png' alt='T3 Logo' width={50} height={50} />
|
||||||
|
<h1 className='my-auto text-2xl'>T3 Supabase Template</h1>
|
||||||
|
</Link>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<Button asChild>
|
<Button asChild>
|
||||||
<Link href='https://git.gbrown.org/gib/T3-Template'>
|
<Link href='https://git.gbrown.org/gib/T3-Template'>
|
||||||
|
@ -31,7 +31,6 @@ export type UploadStorageProps = {
|
|||||||
export type ReplaceStorageProps = {
|
export type ReplaceStorageProps = {
|
||||||
bucket: string;
|
bucket: string;
|
||||||
prevPath: string;
|
prevPath: string;
|
||||||
path: string;
|
|
||||||
file: File;
|
file: File;
|
||||||
options?: {
|
options?: {
|
||||||
cacheControl?: string;
|
cacheControl?: string;
|
||||||
@ -137,21 +136,22 @@ export const uploadFile = async ({
|
|||||||
export const replaceFile = async ({
|
export const replaceFile = async ({
|
||||||
bucket,
|
bucket,
|
||||||
prevPath,
|
prevPath,
|
||||||
path,
|
|
||||||
file,
|
file,
|
||||||
options = {},
|
options = {},
|
||||||
}: ReplaceStorageProps): Promise<Result<string>> => {
|
}: ReplaceStorageProps): Promise<Result<string>> => {
|
||||||
try {
|
try {
|
||||||
|
options.upsert = true;
|
||||||
const supabase = await createServerClient();
|
const supabase = await createServerClient();
|
||||||
|
//const deleteFileData = await deleteFile({
|
||||||
|
//bucket,
|
||||||
|
//path: [...prevPath],
|
||||||
|
//});
|
||||||
const { data, error } = await supabase.storage
|
const { data, error } = await supabase.storage
|
||||||
.from(bucket)
|
.from(bucket)
|
||||||
.update(path, file, options);
|
//.update(path, file, options);
|
||||||
|
.update(prevPath, file, options);
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
if (!data?.path) throw new Error('No path returned from upload');
|
if (!data?.path) throw new Error('No path returned from upload');
|
||||||
const deleteFileData = await deleteFile({
|
|
||||||
bucket,
|
|
||||||
path: [...prevPath],
|
|
||||||
});
|
|
||||||
return { success: true, data: data.path };
|
return { success: true, data: data.path };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useRef } from 'react';
|
import { useState, useRef } from 'react';
|
||||||
import { deleteFile, uploadFile } from '@/lib/actions';
|
import { deleteFile, replaceFile, uploadFile } from '@/lib/actions';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { useAuth } from '@/components/context/auth';
|
import { useAuth } from '@/components/context/auth';
|
||||||
import { resizeImage } from '@/lib/hooks';
|
import { resizeImage } from '@/lib/hooks';
|
||||||
@ -43,6 +43,21 @@ export const useFileUpload = () => {
|
|||||||
console.error('Error deleting file:', deleteResult.error);
|
console.error('Error deleting file:', deleteResult.error);
|
||||||
throw new Error(deleteResult.error || `Failed to delete ${prevPath}`);
|
throw new Error(deleteResult.error || `Failed to delete ${prevPath}`);
|
||||||
} else console.log('Delete sucessful!')
|
} else console.log('Delete sucessful!')
|
||||||
|
console.log('Deleted file path: ', deleteResult.data)
|
||||||
|
const updateResult = await replaceFile({
|
||||||
|
bucket,
|
||||||
|
prevPath: prevPath,
|
||||||
|
file,
|
||||||
|
options: {
|
||||||
|
contentType: file.type,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!updateResult.success) {
|
||||||
|
console.error('Error updating file:', updateResult.error);
|
||||||
|
} else {
|
||||||
|
console.log('We used the new update function hopefully it worked!');
|
||||||
|
return { success: true, path: updateResult.data };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fileToUpload = file;
|
let fileToUpload = file;
|
||||||
|
@ -58,6 +58,12 @@ create policy "Avatar images are publicly accessible." on storage.objects
|
|||||||
create policy "Anyone can upload an avatar." on storage.objects
|
create policy "Anyone can upload an avatar." on storage.objects
|
||||||
for insert with check (bucket_id = 'avatars');
|
for insert with check (bucket_id = 'avatars');
|
||||||
|
|
||||||
|
create policy "Anyone can update an avatar." on storage.objects
|
||||||
|
for update using (bucket_id = 'avatars');
|
||||||
|
|
||||||
|
create policy "Anyone can delete an avatar." on storage.objects
|
||||||
|
for delete using (bucket_id = 'avatars');
|
||||||
|
|
||||||
-- -- Create a table for public statuses
|
-- -- Create a table for public statuses
|
||||||
-- CREATE TABLE statuses (
|
-- CREATE TABLE statuses (
|
||||||
-- id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
|
-- id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||||
|