diff --git a/firebase.json b/firebase.json
index 6889100..6e01456 100644
--- a/firebase.json
+++ b/firebase.json
@@ -13,6 +13,26 @@
}
]
},
+ "database": {
+ "rules": "database.rules.json"
+ },
+ "functions": [
+ {
+ "source": "functions",
+ "logLevel": "debug",
+ "codebase": "default",
+ "ignore": [
+ "node_modules",
+ ".git",
+ "firebase-debug.log",
+ "firebase-debug.*.log"
+ ],
+ "predeploy": [
+ "npm --prefix \"$RESOURCE_DIR\" run lint",
+ "npm --prefix \"$RESOURCE_DIR\" run build"
+ ]
+ }
+ ],
"storage": {
"rules": "storage.rules"
}
diff --git a/functions/.eslintrc.js b/functions/.eslintrc.js
index 5790a6a..854a7ad 100644
--- a/functions/.eslintrc.js
+++ b/functions/.eslintrc.js
@@ -5,28 +5,28 @@ module.exports = {
node: true,
},
extends: [
- "eslint:recommended",
- "plugin:import/errors",
- "plugin:import/warnings",
- "plugin:import/typescript",
- "google",
- "plugin:@typescript-eslint/recommended",
+ 'eslint:recommended',
+ 'plugin:import/errors',
+ 'plugin:import/warnings',
+ 'plugin:import/typescript',
+ 'google',
+ 'plugin:@typescript-eslint/recommended',
],
- parser: "@typescript-eslint/parser",
+ parser: '@typescript-eslint/parser',
parserOptions: {
- project: ["tsconfig.json", "tsconfig.dev.json"],
- sourceType: "module",
+ project: ['tsconfig.json', 'tsconfig.dev.json'],
+ sourceType: 'module',
},
ignorePatterns: [
- "/lib/**/*", // Ignore built files.
+ '/lib/**/*', // Ignore built files.
],
plugins: [
- "@typescript-eslint",
- "import",
+ '@typescript-eslint',
+ 'import',
],
rules: {
- "quotes": ["error", "single"],
- "import/no-unresolved": 0,
- "indent": ["error", 2],
+ 'quotes': ['error', 'single'],
+ 'import/no-unresolved': 0,
+ 'indent': ['error', 2],
},
};
diff --git a/functions/src/index.ts b/functions/src/index.ts
index 3248f9f..223c1e5 100644
--- a/functions/src/index.ts
+++ b/functions/src/index.ts
@@ -3,5 +3,5 @@ require('dotenv').config();
import {initializeApp} from 'firebase-admin/app';
initializeApp();
-export {sendContactMessage} from './sendgrid';
+export {sendContactMessageV2} from './sendgrid';
export {checkRecaptcha} from './recaptcha';
diff --git a/functions/src/recaptcha.ts b/functions/src/recaptcha.ts
index 98a3955..6819452 100644
--- a/functions/src/recaptcha.ts
+++ b/functions/src/recaptcha.ts
@@ -10,7 +10,8 @@ export const checkRecaptcha = onRequest((req, res) => {
// 'http://localhost:8080'
res.set('Access-Control-Allow-Origin', 'https://anthonybuzzelli.dev');
res.setHeader('Content-Type', 'application/json');
- const token = req.query.token;
+ const token = req.body.token;
+ // recommended from Claude const token = req.body.token
console.log(token, 'what is here');
try {
const response = await axios.get(`https://recaptcha.google.com/recaptcha/api/siteverify?secret=${SECRET_KEY}&response=${token}`);
diff --git a/functions/src/sendgrid.ts b/functions/src/sendgrid.ts
index 6981300..6e2acc1 100644
--- a/functions/src/sendgrid.ts
+++ b/functions/src/sendgrid.ts
@@ -5,7 +5,7 @@ const sgMail = require('@sendgrid/mail');
const SENDGRID_API_KEY = process.env.SENDGRID_API_KEY;
sgMail.setApiKey(SENDGRID_API_KEY);
-export const sendContactMessage = onValueWritten(
+export const sendContactMessageV2 = onValueWritten(
'messages/{pushkey}', async (change) => {
const dataAfterChange = change.data.after.val();
if (change.data.before.val() || !dataAfterChange.subject) {
diff --git a/src/app/animations/nav-animation.ts b/src/app/animations/nav-animation.ts
new file mode 100644
index 0000000..094bebd
--- /dev/null
+++ b/src/app/animations/nav-animation.ts
@@ -0,0 +1,52 @@
+import { NavOptions, createAnimation } from '@ionic/core';
+
+interface TransitionOptions extends NavOptions {
+ progressCallback?: (ani: Animation | undefined) => void;
+ baseEl: any;
+ enteringEl: HTMLElement;
+ // leavingEl: HTMLElement | undefined;
+ leavingEl: HTMLElement;
+}
+
+function getIonPageElement(element: HTMLElement) {
+ if (element.classList.contains('ion-page')) {
+ return element;
+ }
+
+ const ionPage = element.querySelector(
+ ':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs'
+ );
+ if (ionPage) {
+ return ionPage;
+ }
+
+ return element;
+}
+
+export function pageTransition(_: HTMLElement, opts: TransitionOptions) {
+ const DURATION = 600;
+
+ const rootTransition = createAnimation()
+ .duration(opts.duration || DURATION)
+ .easing('cubic-bezier(0.3,0,0.66,1)');
+
+ const enteringPage = createAnimation()
+ .addElement(getIonPageElement(opts.enteringEl))
+ .beforeRemoveClass('ion-page-invisible');
+
+ const leavingPage = createAnimation().addElement(
+ getIonPageElement(opts.leavingEl)
+ );
+
+ if (opts.direction === 'forward') {
+ enteringPage.fromTo('transform', 'translateX(100%)', 'translateX(0)');
+ leavingPage.fromTo('opacity', '1', '0.25');
+ } else {
+ leavingPage.fromTo('transform', 'translateX(0)', 'translateX(100%)');
+ enteringPage.fromTo('opacity', '0.25', '1');
+ }
+
+ rootTransition.addAnimation(enteringPage);
+ rootTransition.addAnimation(leavingPage);
+ return rootTransition;
+}
\ No newline at end of file
diff --git a/src/app/app.component.html b/src/app/app.component.html
index b37a044..edde976 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -2,9 +2,7 @@
- Inbox
- hi@ionicframework.com
-
+ Anthony Buzzelli
@@ -12,16 +10,9 @@
-
-
- Labels
-
-
-
- {{ label }}
-
-
+
+
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index 0aeb6fe..3a1f063 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -29,17 +29,13 @@ ion-menu.md ion-list#inbox-list {
ion-menu.md ion-list#inbox-list ion-list-header {
font-size: 22px;
font-weight: 600;
-
min-height: 20px;
}
ion-menu.md ion-list#labels-list ion-list-header {
font-size: 16px;
-
margin-bottom: 18px;
-
color: #757575;
-
min-height: 26px;
}
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 1ba91b4..59805df 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,33 +1,37 @@
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { RouterLink, RouterLinkActive } from '@angular/router';
-import { IonApp, IonSplitPane, IonMenu, IonContent, IonList, IonListHeader, IonNote, IonMenuToggle, IonItem, IonIcon, IonLabel, IonRouterOutlet } from '@ionic/angular/standalone';
+import { IonApp, IonMenu, IonContent, IonList, IonListHeader, IonNote, IonMenuToggle, IonItem, IonIcon, IonLabel, IonRouterOutlet } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons';
import { mailOutline, mailSharp, paperPlaneOutline, paperPlaneSharp, heartOutline, heartSharp, archiveOutline, archiveSharp, trashOutline, trashSharp, warningOutline, warningSharp, bookmarkOutline, bookmarkSharp } from 'ionicons/icons';
+import { LoadingComponent } from './loading/loading.component';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss'],
standalone: true,
- imports: [RouterLink, RouterLinkActive, CommonModule, IonApp, IonSplitPane, IonMenu, IonContent, IonList, IonListHeader, IonNote, IonMenuToggle, IonItem, IonIcon, IonLabel, IonRouterOutlet],
+ imports: [RouterLink, RouterLinkActive, CommonModule, IonApp, IonMenu, IonContent, IonList, IonListHeader, IonNote, IonMenuToggle, IonItem, IonIcon, IonLabel, IonRouterOutlet, LoadingComponent],
})
export class AppComponent {
+ // loading = true;
+ // private router = inject(Router);
public appPages = [
- {title:'Home', url: '/home', icon: 'mail'},
+ { title:'Home', url: '/home', icon: 'mail' },
{ title: 'About', url: '/about', icon: 'mail' },
{ title: 'Projects', url: '/projects', icon: 'mail' },
- { title: 'Contact', url: '/contact', icon: 'mail' },
-
- { title: 'Inbox', url: '/folder/inbox', icon: 'mail' },
- { title: 'Outbox', url: '/folder/outbox', icon: 'paper-plane' },
- { title: 'Favorites', url: '/folder/favorites', icon: 'heart' },
- { title: 'Archived', url: '/folder/archived', icon: 'archive' },
- { title: 'Trash', url: '/folder/trash', icon: 'trash' },
- { title: 'Spam', url: '/folder/spam', icon: 'warning' },
- ];
- public labels = ['Family', 'Friends', 'Notes', 'Work', 'Travel', 'Reminders'];
+ { title: 'Contact', url: '/contact', icon: 'mail' }
+ ]
+
constructor() {
addIcons({ mailOutline, mailSharp, paperPlaneOutline, paperPlaneSharp, heartOutline, heartSharp, archiveOutline, archiveSharp, trashOutline, trashSharp, warningOutline, warningSharp, bookmarkOutline, bookmarkSharp });
+
+ // this.router.events.subscribe(event => {
+ // if (event instanceof NavigationStart) {
+ // this.loading = true;
+ // } else if (event instanceof NavigationEnd) {
+ // this.loading = false;
+ // }
+ // })
}
}
diff --git a/src/app/contact/contact.component.ts b/src/app/contact/contact.component.ts
index d43cd56..344cbad 100644
--- a/src/app/contact/contact.component.ts
+++ b/src/app/contact/contact.component.ts
@@ -1,8 +1,8 @@
import { Component, OnInit, inject } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { CommonModule } from '@angular/common';
-// import { HttpClient } from '@angular/common/http';
-// import { ReCaptchaV3Service } from 'ng-recaptcha';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { ReCaptchaV3Service } from 'ng-recaptcha';
import { Router } from '@angular/router';
import { Database, push, ref, set } from '@angular/fire/database';
import { UIService } from '../ui.service';
@@ -21,9 +21,9 @@ import { FooterComponent } from '../footer/footer.component';
export class ContactComponent implements OnInit {
private db = inject(Database)
private uiService = inject(UIService)
- // private http = inject(HttpClient)
+ private http = inject(HttpClient)
private router = inject(Router)
- // private recaptchaService = inject(ReCaptchaV3Service)
+ private recaptchaService = inject(ReCaptchaV3Service)
constructor() { }
@@ -58,32 +58,69 @@ export class ContactComponent implements OnInit {
}
onSubmit() {
- this.isSubmitted = true
- const value = this.contactForm.value;
- const name = value.name;
- const email = value.email;
- const subject = value.subject;
- const message = value.message;
-
+ // this.isSubmitted = true
+ // const value = this.contactForm.value;
+ // const name = value.name;
+ // const email = value.email;
+ // const subject = value.subject;
+ // const message = value.message;
+ this.recaptchaService.execute('contact')
+ .subscribe((token) => {
+ console.log(token, 'token')
+ this.verifyRecaptcha(token);
+ })
- const formRequest = { name, email, subject, message};
- const messagesRef = ref(this.db, '/messages');
- const newMessageRef = push(messagesRef);
- set(newMessageRef, {...formRequest})
- .then(() => {
- // console.log(formRequest, 'What is showing here');
- this.contactForm.reset();
- this.uiService.presentToast('Your message was sent', 4000);
- this.router.navigate(['/']);
- })
- .catch((error) => {
- // console.log(error, 'Error in sending message');
- this.uiService.presentToast('Error in sending message', 4000);
- this.contactForm.reset();
- this.isSubmitted = false;
- this.router.navigate(['/']);
- throw error;
- });
+ // const formRequest = { name, email, subject, message};
+ // const messagesRef = ref(this.db, '/messages');
+ // const newMessageRef = push(messagesRef);
+ // set(newMessageRef, {...formRequest})
+ // .then(() => {
+ // // console.log(formRequest, 'What is showing here');
+ // this.contactForm.reset();
+ // this.uiService.presentToast('Your message was sent', 4000);
+ // this.router.navigate(['/']);
+ // })
+ // .catch((error) => {
+ // // console.log(error, 'Error in sending message');
+ // this.uiService.presentToast('Error in sending message', 4000);
+ // this.contactForm.reset();
+ // this.isSubmitted = false;
+ // this.router.navigate(['/']);
+ // throw error;
+ // });
+ }
+
+ verifyRecaptcha(token: string) {
+ const url = 'https://us-central1-ionicwebpage.cloudfunctions.net/checkRecaptcha';
+ const headers = new HttpHeaders().set('Content-Type', 'application/json')
+
+ this.http.post(url, { token }, { headers }).subscribe((response) => {
+ console.log(response, 'data')
+ this.isSubmitted = true
+ const value = this.contactForm.value;
+ const name = value.name;
+ const email = value.email;
+ const subject = value.subject;
+ const message = value.message;
+ const formRequest = { name, email, subject, message };
+ const messagesRef = ref(this.db, '/messages');
+ const newMessageRef = push(messagesRef);
+ set(newMessageRef, { ...formRequest })
+ .then(() => {
+ // console.log(formRequest, 'What is showing here');
+ this.contactForm.reset();
+ this.uiService.presentToast('Your message was sent', 4000);
+ this.router.navigate(['/']);
+ })
+ .catch((error) => {
+ // console.log(error, 'Error in sending message');
+ this.uiService.presentToast('Error in sending message', 4000);
+ this.contactForm.reset();
+ this.isSubmitted = false;
+ this.router.navigate(['/']);
+ throw error;
+ });
+ })
}
}
diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts
index 8f04b72..0fa47ae 100644
--- a/src/app/home/home.component.ts
+++ b/src/app/home/home.component.ts
@@ -3,7 +3,9 @@ import { AfterViewChecked, AfterViewInit, Component, ElementRef, HostListener, O
import { IonHeader, IonToolbar, IonButtons, IonMenuButton, IonTitle, IonContent } from '@ionic/angular/standalone';
import { HeaderComponent } from '../header/header.component';
-import * as THREE from 'three'
+import * as THREE from 'three';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
+// import model from '../../assets/facefull.glb'
@Component({
selector: 'app-home',
@@ -24,12 +26,15 @@ export class HomeComponent implements OnInit, AfterViewChecked, OnDestroy {
private rendererInitialized = false;
// public home!: string;
// private activatedRoute = inject(ActivatedRoute);
+ private model: any;
constructor() {
// this.render = this.render.bind(this);
}
ngOnInit() {
+ const loader = new GLTFLoader()
+ this.model = '../../assets/facefull.glb'
// this.home = this.activatedRoute.snapshot.paramMap.get('id') as string;
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
@@ -67,9 +72,26 @@ export class HomeComponent implements OnInit, AfterViewChecked, OnDestroy {
fragmentShader: fragmentShader
});
this.cube = new THREE.Mesh(geometry, material);
- this.scene.add(this.cube);
+ // this.scene.add(this.cube);
console.log(this.cube, 'cube')
console.log('this is running', this.renderer)
+
+ // this.loader = new GLTFLoader()
+ loader.load(this.model,(gltf)=>{
+ this.model = gltf.scene.children[0];
+ this.model.position.set(0, -1, -1.5);
+ this.model.rotation.set(0, 0, 0);
+ this.model.scale.set(4000,2000,2000);
+ console.log(this.model.scale);
+ this.scene.add(this.model);
+ this.model.traverse((o: { isMesh: any; material: THREE.MeshBasicMaterial; })=>{
+ if(o.isMesh){
+ console.log(o);
+ o.material = new THREE.MeshBasicMaterial({color:0xff0000})
+ }
+ });
+ console.log(this.model, 'model running?')
+ });
}
ngAfterViewChecked() {
diff --git a/src/app/loading/loading.component.html b/src/app/loading/loading.component.html
new file mode 100644
index 0000000..4d11774
--- /dev/null
+++ b/src/app/loading/loading.component.html
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/src/app/loading/loading.component.scss b/src/app/loading/loading.component.scss
new file mode 100644
index 0000000..8ec15b4
--- /dev/null
+++ b/src/app/loading/loading.component.scss
@@ -0,0 +1,17 @@
+.loading-screen {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: #fff;
+ z-index: 9999;
+}
+
+.loading-icon {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ /* Add your loading icon styles here */
+}
\ No newline at end of file
diff --git a/src/app/loading/loading.component.spec.ts b/src/app/loading/loading.component.spec.ts
new file mode 100644
index 0000000..b0cc5a5
--- /dev/null
+++ b/src/app/loading/loading.component.spec.ts
@@ -0,0 +1,24 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+import { IonicModule } from '@ionic/angular';
+
+import { LoadingComponent } from './loading.component';
+
+describe('LoadingComponent', () => {
+ let component: LoadingComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ LoadingComponent ],
+ imports: [IonicModule.forRoot()]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(LoadingComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ }));
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/loading/loading.component.ts b/src/app/loading/loading.component.ts
new file mode 100644
index 0000000..ef708ca
--- /dev/null
+++ b/src/app/loading/loading.component.ts
@@ -0,0 +1,17 @@
+import { Component } from '@angular/core';
+import { IonContent } from '@ionic/angular/standalone';
+
+@Component({
+ selector: 'app-loading',
+ templateUrl: './loading.component.html',
+ styleUrls: ['./loading.component.scss'],
+ standalone: true,
+ imports: [IonContent]
+})
+export class LoadingComponent {
+
+ constructor() { }
+
+
+
+}
diff --git a/src/assets/facefull.glb b/src/assets/facefull.glb
new file mode 100644
index 0000000..e3f73f9
Binary files /dev/null and b/src/assets/facefull.glb differ
diff --git a/src/main.ts b/src/main.ts
index 42c91d4..d2586a7 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -2,7 +2,8 @@ import { enableProdMode, importProvidersFrom } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { RouteReuseStrategy, provideRouter } from '@angular/router';
import { IonicRouteStrategy, provideIonicAngular } from '@ionic/angular/standalone';
-
+import { HttpClientModule } from '@angular/common/http';
+import { provideAnimations } from '@angular/platform-browser/animations'
import { routes } from './app/app.routes';
import { AppComponent } from './app/app.component';
import { environment } from './environments/environment';
@@ -14,6 +15,8 @@ import { getStorage, provideStorage } from '@angular/fire/storage';
import { RECAPTCHA_V3_SITE_KEY, RecaptchaV3Module } from "ng-recaptcha";
+import { pageTransition } from './app/animations/nav-animation';
+
if (environment.production) {
enableProdMode();
}
@@ -21,12 +24,14 @@ if (environment.production) {
bootstrapApplication(AppComponent, {
providers: [
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
- provideIonicAngular(),
+ provideIonicAngular({ navAnimation: pageTransition }),
provideRouter(routes),
+ provideAnimations(),
importProvidersFrom(provideFirebaseApp(() => initializeApp({"projectId":"ionicwebpage","appId":"1:952994598736:web:c051e724ce521f59cca655","databaseURL":"https://ionicwebpage.firebaseio.com","storageBucket":"ionicwebpage.appspot.com","apiKey":"AIzaSyBytj8gvFINALswEUnSwtUBBRoDfUuQDJw","authDomain":"ionicwebpage.firebaseapp.com","messagingSenderId":"952994598736"}))),
importProvidersFrom(provideAnalytics(() => getAnalytics())), ScreenTrackingService, importProvidersFrom(provideDatabase(() => getDatabase())),
importProvidersFrom(provideFunctions(() => getFunctions())),
importProvidersFrom(provideStorage(() => getStorage())),
- importProvidersFrom(RecaptchaV3Module), { provide: RECAPTCHA_V3_SITE_KEY, useValue: '6Lc5l8gpAAAAAFQXvzUkcbYTXVvj4UZKkJB_NwV-' }
+ importProvidersFrom(RecaptchaV3Module), { provide: RECAPTCHA_V3_SITE_KEY, useValue: '6Lc5l8gpAAAAAFQXvzUkcbYTXVvj4UZKkJB_NwV-' },
+ importProvidersFrom(HttpClientModule)
],
});