diff --git a/03_source/mobile/package-lock.json b/03_source/mobile/package-lock.json index 10c6ea8..e669229 100644 --- a/03_source/mobile/package-lock.json +++ b/03_source/mobile/package-lock.json @@ -62,6 +62,7 @@ "@types/react-dom": "^18.0.11", "@types/react-router": "^5.1.20", "@types/react-router-dom": "^5.3.3", + "@types/react-star-ratings": "^2.3.3", "@vitejs/plugin-react": "^3.1.0", "husky": "^8.0.3", "lint-staged": "^13.2.0", @@ -1326,6 +1327,16 @@ "@types/react-router": "*" } }, + "node_modules/@types/react-star-ratings": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/react-star-ratings/-/react-star-ratings-2.3.3.tgz", + "integrity": "sha512-8vLqJG1uRA2SmYBBMPWpv6QWHLvZ/a3XmELCIf4xh4VFXT7QkkrcthiLSMjQ4ibDiUtYYpyLB0JoR1lBHSHA2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -7054,6 +7065,15 @@ "@types/react-router": "*" } }, + "@types/react-star-ratings": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/react-star-ratings/-/react-star-ratings-2.3.3.tgz", + "integrity": "sha512-8vLqJG1uRA2SmYBBMPWpv6QWHLvZ/a3XmELCIf4xh4VFXT7QkkrcthiLSMjQ4ibDiUtYYpyLB0JoR1lBHSHA2A==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/slice-ansi/-/slice-ansi-4.0.0.tgz", diff --git a/03_source/mobile/package.json b/03_source/mobile/package.json index 83ac144..887588b 100644 --- a/03_source/mobile/package.json +++ b/03_source/mobile/package.json @@ -61,6 +61,7 @@ "@types/react-dom": "^18.0.11", "@types/react-router": "^5.1.20", "@types/react-router-dom": "^5.3.3", + "@types/react-star-ratings": "^2.3.3", "@vitejs/plugin-react": "^3.1.0", "husky": "^8.0.3", "lint-staged": "^13.2.0", diff --git a/03_source/mobile/src/App.tsx b/03_source/mobile/src/App.tsx index 12405b8..4956ab0 100644 --- a/03_source/mobile/src/App.tsx +++ b/03_source/mobile/src/App.tsx @@ -70,6 +70,7 @@ import DummyPayPage from './pages/DummyEventPayPage'; import DummyEventPayPage from './pages/DummyEventPayPage'; import PaymentSuccess from './pages/PaymentSuccess'; import PaymentFailed from './pages/PaymentFailed'; +import CarousellMe from './pages/CarousellMe'; setupIonicReact(); diff --git a/03_source/mobile/src/PATHS.ts b/03_source/mobile/src/PATHS.ts index 4dd960d..53bdb53 100644 --- a/03_source/mobile/src/PATHS.ts +++ b/03_source/mobile/src/PATHS.ts @@ -23,11 +23,11 @@ const PATHS = { getOrderDetail: (id: string) => `/order_detail/${id}`, // Tab navigation routes TAB_NOT_IMPLEMENTED: '/tabs/not_implemented', - EVENT_LIST: `/tabs/events`, - MESSAGE_LIST: `/tabs/messages`, + EVENT_LIST: '/tabs/events', + MESSAGE_LIST: '/tabs/messages', NEARBY_LIST: '/tabs/nearby', ORDERS_LIST: '/tabs/orders', - FAVOURITES_LIST: `/tabs/favourites`, + FAVOURITES_LIST: '/tabs/favourites', PROFILE: '/tabs/my_profile', // partyUser @@ -102,5 +102,8 @@ const PATHS = { DEMO_SLIDING_PROFILE: '/demo-sliding-profile', DEMO_STICKY_BOTTOM_SHEET_EXAMPLE: '/demo-sticky-bottom-sheet-example', DEMO_STORAGE_EXAMPLE: '/demo-storage-example', + + // + CAROUSELL_ME: '/tabs/carousell_me', }; export default PATHS; diff --git a/03_source/mobile/src/data/AppContext.tsx b/03_source/mobile/src/data/AppContext.tsx index d1375ad..6fd5e4b 100644 --- a/03_source/mobile/src/data/AppContext.tsx +++ b/03_source/mobile/src/data/AppContext.tsx @@ -1,14 +1,23 @@ import React, { createContext, PropsWithChildren, useReducer } from 'react'; import { initialState, AppState, reducers } from './state'; +import { Storage, Drivers } from '@ionic/storage'; + +const browser_store = new Storage({ + name: '__mydb', + driverOrder: [Drivers.LocalStorage, Drivers.IndexedDB], +}); + export interface AppContextState { state: AppState; dispatch: React.Dispatch; + browser_store: any; } export const AppContext = createContext({ state: initialState, dispatch: () => undefined, + browser_store: {}, }); export const AppContextProvider: React.FC = ({ children }) => { @@ -19,9 +28,13 @@ export const AppContextProvider: React.FC = ({ children }) => value={{ state: store, dispatch, + // + browser_store, }} > {children} ); }; + +// diff --git a/03_source/mobile/src/pages/CarousellMe/AboutContent.tsx b/03_source/mobile/src/pages/CarousellMe/AboutContent.tsx new file mode 100644 index 0000000..6160b81 --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/AboutContent.tsx @@ -0,0 +1,130 @@ +import React, { useState } from 'react'; +// import { useTranslation } from 'react-i18next'; +// import StarRatings from 'react-star-ratings'; + +import { + IonHeader, + IonTitle, + IonToolbar, + IonContent, + IonPage, + IonButtons, + IonBadge, + IonMenuButton, + IonButton, + IonIcon, + IonDatetime, + IonSelectOption, + IonList, + IonItem, + IonLabel, + IonSelect, + IonPopover, + IonText, + IonCard, + IonCardHeader, + IonCardTitle, + IonCardSubtitle, + IonCardContent, + IonTabs, + IonTabBar, + IonTabButton, + IonSegment, + IonSegmentButton, + useIonRouter, +} from '@ionic/react'; +import './style.scss'; +import { + locationSharp, + settingsOutline, + qrCode, + shareSocialOutline, + ellipsisHorizontal, + ellipsisVertical, + logoFacebook, + phonePortraitSharp, + pricetagSharp, + archiveSharp, + calendar, + personCircle, + map, + informationCircle, + mailSharp, + chevronDownSharp, + chevronForwardSharp, +} from 'ionicons/icons'; +// import AboutPopover from '../../components/AboutPopover'; +import { format, parseISO } from 'date-fns'; + +import lookForVisitorSvg from './look_for_visitor.svg'; + +type SessionListProps = { + hide: boolean; +}; + +const AboutContent: React.FC = ({ hide }) => { + let route = useIonRouter(); + + if (hide) return <>; + return ( + <> +
+
+ + + 25 + + Followers + {' '} + + 0 + + Followers + +
+ +
+ +

接受 payme / 支付寳 / FPS:

+

Accepting HSBC payme / alipay / FPS

+

更多資料 / for more detail:

+

https://louiscklaw.github.io/carousell

+

歡迎PM查詢

+
+
+
+ + {/* private information */} + + +

Private information

+
+ { + route.push('/tabs/carousell_me/OffersMade'); + }} + > + My Offers + +
+ + ); +}; + +export default AboutContent; diff --git a/03_source/mobile/src/pages/CarousellMe/ListingsContent/index.tsx b/03_source/mobile/src/pages/CarousellMe/ListingsContent/index.tsx new file mode 100644 index 0000000..133631d --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/ListingsContent/index.tsx @@ -0,0 +1,147 @@ +import React, { useState } from 'react'; +// import { useTranslation } from 'react-i18next'; +// import StarRatings from 'react-star-ratings'; + +import { + IonHeader, + IonTitle, + IonToolbar, + IonContent, + IonPage, + IonButtons, + IonBadge, + IonMenuButton, + IonButton, + IonIcon, + IonDatetime, + IonSelectOption, + IonList, + IonItem, + IonLabel, + IonSelect, + IonPopover, + IonText, + IonCard, + IonCardHeader, + IonCardTitle, + IonCardSubtitle, + IonCardContent, + IonTabs, + IonTabBar, + IonTabButton, + IonSegment, + IonSegmentButton, + useIonRouter, + IonSearchbar, + IonRow, + IonCol, +} from '@ionic/react'; +import './style.scss'; +import { + locationSharp, + settingsOutline, + qrCode, + shareSocialOutline, + ellipsisHorizontal, + ellipsisVertical, + logoFacebook, + phonePortraitSharp, + pricetagSharp, + archiveSharp, + calendar, + personCircle, + map, + informationCircle, + mailSharp, + chevronDownSharp, + chevronForwardSharp, + filterSharp, +} from 'ionicons/icons'; +// import AboutPopover from '../../components/AboutPopover'; +import { format, parseISO } from 'date-fns'; + +// import lookForVisitorSvg from './look_for_visitor.svg'; + +const SellingButton: React.FC = () => { + return ( + <> +
+ console.log('helloworld')}> + Silhouette of mountains + + + {'#html #css #開發 #指導 #代做 #電子平台 #programming #coding #javascript #python #react #debug' + .split('') + .slice(0, 20)} + + Card Subtitle + + + View Insights + +
+ + ); +}; + +type SessionListProps = { + hide: boolean; +}; + +const ListingsContent: React.FC = ({ hide }) => { + let route = useIonRouter(); + let [openExplain, setOpenExplain] = useState(false); + + if (hide) return <>; + return ( + <> +
+
+ 33 Listings + + +
+ setOpenExplain(true)}> + + Filters + + + {`Status: ${'All'}`} + + + {`In: All Categories`} + +
+
+ {/* Sell listing */} +
+ + + + + + + + +
+
+ + ); +}; + +export default ListingsContent; diff --git a/03_source/mobile/src/pages/CarousellMe/ListingsContent/look_for_visitor.svg b/03_source/mobile/src/pages/CarousellMe/ListingsContent/look_for_visitor.svg new file mode 100644 index 0000000..d9e16f8 --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/ListingsContent/look_for_visitor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/03_source/mobile/src/pages/CarousellMe/ListingsContent/style.scss b/03_source/mobile/src/pages/CarousellMe/ListingsContent/style.scss new file mode 100644 index 0000000..1562768 --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/ListingsContent/style.scss @@ -0,0 +1,8 @@ +#listing-content { + ion-button { + --padding-start: 5px; + --padding-end: 5px; + --padding-top: 5px; + --padding-bottom: 5px; + } +} diff --git a/03_source/mobile/src/pages/CarousellMe/MyProfile/index.tsx b/03_source/mobile/src/pages/CarousellMe/MyProfile/index.tsx new file mode 100644 index 0000000..8bd5a3a --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/MyProfile/index.tsx @@ -0,0 +1,201 @@ +import { + IonBackButton, + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonInput, + IonItem, + IonLabel, + IonList, + IonMenuButton, + IonPage, + IonPopover, + IonRow, + IonText, + IonTitle, + IonToolbar, + useIonRouter, + useIonViewDidEnter, +} from '@ionic/react'; +import { star, starOutline, checkmarkDoneOutline } from 'ionicons/icons'; +import React, { useContext, useEffect, useRef, useState } from 'react'; +// import AboutPopover from '../../components/AboutPopover'; +import { useIonAlert } from '@ionic/react'; + +import './style.scss'; +import { AppContext } from '../../../data/AppContext'; +import { + createGesture, + // Gesture +} from '@ionic/react'; +import { useTranslation } from 'react-i18next'; +// import { ImgPrinterSvg, ImgRiceSvg } from 'src/pages/tabs/carousell_me/settings/svgs'; + +interface AboutProps {} + +const MyProfile: React.FC = () => { + const [showPopover, setShowPopover] = useState(false); + const [popoverEvent, setPopoverEvent] = useState(); + const [location, setLocation] = useState<'madison' | 'austin' | 'chicago' | 'seattle'>('madison'); + const [conferenceDate, setConferenceDate] = useState('2047-05-17T00:00:00-05:00'); + const [showAlert, hideAlert] = useIonAlert(); + + const { browser_store } = useContext(AppContext); + + const refRectangle = useRef(null); + const [swipeType, setSwipeType] = useState(); + const [deltaX, setDeltaX] = useState(); + const [velocityX, setVelocityX] = useState(); + const [swipeVerdict, setSwipeVerdict] = useState('helloworld'); + + const route = useIonRouter(); + const [username, setUsername] = useState('hello user'); + const [usernameError, setUsernameError] = useState(false); + + useEffect(() => { + if (swipeVerdict == 'swipe-left') + if (route.canGoBack() == true) { + route.goBack(); + } else { + route.push('/tabs/schedule'); + } + }, [swipeVerdict]); + + const onMove = (detail: any) => { + const type = detail.type; + const currentX = detail.currentX; + const deltaX = detail.deltaX; + const velocityX = detail.velocityX; + setSwipeType(type); + setDeltaX(deltaX); + setVelocityX(velocityX); + + if (type == 'pan') + if (Math.abs(deltaX) > 50) + if (velocityX > 0) { + setSwipeVerdict('swipe-right'); + } else { + setSwipeVerdict('swipe-left'); + } + }; + + useIonViewDidEnter(() => { + let gesture: any = {}; + if (refRectangle?.current) { + gesture = createGesture({ + gestureName: 'helloworld', + el: refRectangle.current, + onMove: (detail) => { + onMove(detail); + }, + }); + + gesture.enable(); + } + + return () => { + gesture?.destroy(); + }; + }, [refRectangle]); + + const handleStoreWrite = () => { + (async () => { + await browser_store.set('hello', 'world'); + })(); + }; + + const handleStoreRead = () => { + (async () => { + console.log(await browser_store.get('hello')); + })(); + }; + + const handleStoreClear = () => { + (async () => { + await browser_store.clear(); + })(); + }; + + const handleStoreRemove = () => { + (async () => { + await browser_store.remove('hello'); + })(); + }; + + const handleStoreKeys = () => { + (async () => { + console.dir(await browser_store.keys()); + })(); + }; + + const handleStoreLength = () => { + (async () => { + console.dir(await browser_store.length()); + })(); + }; + + const userInput = 'javascript:alert("Oh no!")'; + const { t } = useTranslation(); + + return ( + + + + + + + + + + + + + + {t('My Profile')} + + + + + + + {t('My Profile')} + + + + + + + Public Profile + + +
+
Username
+
+ +
+
+
+ + Public Profile + +
+
+
+
+ ); +}; + +export default React.memo(MyProfile); diff --git a/03_source/mobile/src/pages/CarousellMe/MyProfile/style.scss b/03_source/mobile/src/pages/CarousellMe/MyProfile/style.scss new file mode 100644 index 0000000..3a5e059 --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/MyProfile/style.scss @@ -0,0 +1,2 @@ +#sample-blank-page { +} diff --git a/03_source/mobile/src/pages/CarousellMe/QuickAndAutoreply/index.tsx b/03_source/mobile/src/pages/CarousellMe/QuickAndAutoreply/index.tsx new file mode 100644 index 0000000..1f37b1a --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/QuickAndAutoreply/index.tsx @@ -0,0 +1,159 @@ +import { + IonBackButton, + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonPopover, + IonTitle, + IonToolbar, + useIonRouter, + useIonViewDidEnter, +} from '@ionic/react'; +import { star, starOutline } from 'ionicons/icons'; +import React, { useContext, useEffect, useRef, useState } from 'react'; +// import AboutPopover from '../../components/AboutPopover'; +import { useIonAlert } from '@ionic/react'; + +import './style.scss'; +import { AppContext } from '../../../data/AppContext'; +import { createGesture, Gesture } from '@ionic/react'; + +interface AboutProps {} + +const MyProfile: React.FC = () => { + const [showPopover, setShowPopover] = useState(false); + const [popoverEvent, setPopoverEvent] = useState(); + const [location, setLocation] = useState<'madison' | 'austin' | 'chicago' | 'seattle'>('madison'); + const [conferenceDate, setConferenceDate] = useState('2047-05-17T00:00:00-05:00'); + const [showAlert, hideAlert] = useIonAlert(); + + const { browser_store } = useContext(AppContext); + + const refRectangle = useRef(null); + const [swipeType, setSwipeType] = useState(); + const [deltaX, setDeltaX] = useState(); + const [velocityX, setVelocityX] = useState(); + const [swipeVerdict, setSwipeVerdict] = useState('helloworld'); + + const route = useIonRouter(); + + useEffect(() => { + if (swipeVerdict == 'swipe-left') + if (route.canGoBack() == true) { + route.goBack(); + } else { + route.push('/tabs/schedule'); + } + }, [swipeVerdict]); + + const onMove = (detail: any) => { + const type = detail.type; + const currentX = detail.currentX; + const deltaX = detail.deltaX; + const velocityX = detail.velocityX; + setSwipeType(type); + setDeltaX(deltaX); + setVelocityX(velocityX); + + if (type == 'pan') + if (Math.abs(deltaX) > 50) + if (velocityX > 0) { + setSwipeVerdict('swipe-right'); + } else { + setSwipeVerdict('swipe-left'); + } + }; + + useIonViewDidEnter(() => { + let gesture: any = {}; + if (refRectangle?.current) { + gesture = createGesture({ + gestureName: 'helloworld', + el: refRectangle.current, + onMove: (detail) => { + onMove(detail); + }, + }); + + gesture.enable(); + } + + return () => { + gesture?.destroy(); + }; + }, [refRectangle]); + + const handleStoreWrite = () => { + (async () => { + await browser_store.set('hello', 'world'); + })(); + }; + + const handleStoreRead = () => { + (async () => { + console.log(await browser_store.get('hello')); + })(); + }; + + const handleStoreClear = () => { + (async () => { + await browser_store.clear(); + })(); + }; + + const handleStoreRemove = () => { + (async () => { + await browser_store.remove('hello'); + })(); + }; + + const handleStoreKeys = () => { + (async () => { + console.dir(await browser_store.keys()); + })(); + }; + + const handleStoreLength = () => { + (async () => { + console.dir(await browser_store.length()); + })(); + }; + + const userInput = 'javascript:alert("Oh no!")'; + + return ( + + + + + + + +

Offers Made

+
+ + + {}}> + {false ? ( + + ) : ( + + )} + + +
+
+ My Profile + {/* + setShowPopover(false)}> + setShowPopover(false)} /> + + */} +
+ ); +}; + +export default React.memo(MyProfile); diff --git a/03_source/mobile/src/pages/CarousellMe/QuickAndAutoreply/style.scss b/03_source/mobile/src/pages/CarousellMe/QuickAndAutoreply/style.scss new file mode 100644 index 0000000..3a5e059 --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/QuickAndAutoreply/style.scss @@ -0,0 +1,2 @@ +#sample-blank-page { +} diff --git a/03_source/mobile/src/pages/CarousellMe/ReviewsContent/AmazingChat.svg b/03_source/mobile/src/pages/CarousellMe/ReviewsContent/AmazingChat.svg new file mode 100644 index 0000000..1589a5a --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/ReviewsContent/AmazingChat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/03_source/mobile/src/pages/CarousellMe/ReviewsContent/index.tsx b/03_source/mobile/src/pages/CarousellMe/ReviewsContent/index.tsx new file mode 100644 index 0000000..869462d --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/ReviewsContent/index.tsx @@ -0,0 +1,207 @@ +import React, { useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import StarRatings from 'react-star-ratings'; + +import { + IonHeader, + IonTitle, + IonToolbar, + IonContent, + IonPage, + IonButtons, + IonBadge, + IonMenuButton, + IonButton, + IonIcon, + IonDatetime, + IonSelectOption, + IonList, + IonItem, + IonLabel, + IonSelect, + IonPopover, + IonText, + IonCard, + IonCardHeader, + IonCardTitle, + IonCardSubtitle, + IonCardContent, + IonTabs, + IonTabBar, + IonTabButton, + IonSegment, + IonSegmentButton, + useIonRouter, + IonAvatar, + IonModal, +} from '@ionic/react'; +import './style.scss'; +import { + locationSharp, + settingsOutline, + qrCode, + shareSocialOutline, + ellipsisHorizontal, + ellipsisVertical, + logoFacebook, + phonePortraitSharp, + pricetagSharp, + archiveSharp, + calendar, + personCircle, + map, + informationCircle, + mailSharp, + chevronDownSharp, + chevronForwardSharp, +} from 'ionicons/icons'; +// import AboutPopover from '../../components/AboutPopover'; +import { format, parseISO } from 'date-fns'; + +import './style.scss'; +import AmazingChatSvg from './AmazingChat.svg'; + +import lookForVisitorSvg from './look_for_visitor.svg'; + +type SessionListProps = { + hide: boolean; +}; + +const SampleReview: React.FC = () => { + const modal = useRef(null); + + function dismiss() { + modal.current?.dismiss(); + } + + let [openExplain, setOpenExplain] = useState(false); + + return ( + <> + +
+

Dialog header

+ + + +
+ Amazing chat +
+
+

You enjoyed chatting with this seller,

+

it felt like you were talking to and old friend

+
+ + + Got it! + +
+
+ +
+
+
+ + Silhouette of a person's head + +
+ Man1130 + + {}} + numberOfStars={5} + name="rating" + starDimension="1rem" + starSpacing="0px" + // + /> + review from buyer 2 months ago + +
+
+ +
+ setOpenExplain(true)}> + + Amazing + + + + Knows + + + + extra mile + +
+ + 除左幫我寫左個program之外仲幫左我好多 願意答問題同幫我解惑 + +
+ +
+ #html #css #開發 #指導 #代做 #電子平台 #programming #coding #javascript #python #react + #debug HK$100 +
+
+
+
+ + ); +}; + +const ReviewsContent: React.FC = ({ hide }) => { + let route = useIonRouter(); + + if (hide) return <>; + return ( + <> +
+ + + + + + +
+ + ); +}; + +export default ReviewsContent; diff --git a/03_source/mobile/src/pages/CarousellMe/ReviewsContent/style.scss b/03_source/mobile/src/pages/CarousellMe/ReviewsContent/style.scss new file mode 100644 index 0000000..42626c4 --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/ReviewsContent/style.scss @@ -0,0 +1,26 @@ +ion-modal#explain-review { + --width: 75vw; + --min-width: 250px; + --height: fit-content; + --border-radius: 6px; + --box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4); +} + +ion-modal#explain-review h1 { + margin: 20px 20px 10px 20px; +} + +ion-modal#explain-review ion-icon { + margin-right: 6px; + + width: 48px; + height: 48px; + + padding: 4px 0; + + color: #aaaaaa; +} + +ion-modal#explain-review .wrapper { + margin-bottom: 10px; +} diff --git a/03_source/mobile/src/pages/CarousellMe/index.tsx b/03_source/mobile/src/pages/CarousellMe/index.tsx new file mode 100644 index 0000000..aa04a46 --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/index.tsx @@ -0,0 +1,358 @@ +import React, { useCallback, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import StarRatings from 'react-star-ratings'; +import * as selectors from '../../data/selectors'; + +import { + IonHeader, + IonTitle, + IonToolbar, + IonContent, + IonPage, + IonButtons, + IonBadge, + IonMenuButton, + IonButton, + IonIcon, + IonDatetime, + IonSelectOption, + IonList, + IonItem, + IonLabel, + IonSelect, + IonPopover, + IonText, + IonCard, + IonCardHeader, + IonCardTitle, + IonCardSubtitle, + IonCardContent, + IonTabs, + IonTabBar, + IonTabButton, + IonSegment, + IonSegmentButton, + useIonRouter, +} from '@ionic/react'; +import './style.scss'; +import { + locationSharp, + settingsOutline, + qrCode, + shareSocialOutline, + ellipsisHorizontal, + ellipsisVertical, + logoFacebook, + phonePortraitSharp, + pricetagSharp, + archiveSharp, + calendar, + personCircle, + map, + informationCircle, + mailSharp, + chevronDownSharp, + chevronForwardSharp, + heartSharp, + heartOutline, + chatbubblesOutline, +} from 'ionicons/icons'; +import AboutPopover from '../../components/AboutPopover'; +import { format, parseISO } from 'date-fns'; + +import lookForVisitorSvg from './look_for_visitor.svg'; + +import AboutContent from './AboutContent'; +import ReviewsContent from './ReviewsContent'; +import ListingsContent from './ListingsContent'; +import { triggerShare } from '../../util/triggerShare'; +import { connect } from '../../data/connect'; +// import { toProperCase } from 'src/util/toProperCase'; + +interface OwnProps {} + +interface StateProps { + events: Event[]; +} + +interface DispatchProps {} + +interface CarousellMeProps extends OwnProps, StateProps, DispatchProps {} + +const CarousellMe: React.FC = () => { + const [showPopover, setShowPopover] = useState(false); + const [popoverEvent, setPopoverEvent] = useState(); + const [location, setLocation] = useState<'madison' | 'austin' | 'chicago' | 'seattle'>('madison'); + const [conferenceDate, setConferenceDate] = useState('2047-05-17T00:00:00-05:00'); + const { t } = useTranslation(); + + const route = useIonRouter(); + + const selectOptions = { + header: 'Select a Location', + }; + + const presentPopover = (e: React.MouseEvent) => { + setPopoverEvent(e.nativeEvent); + setShowPopover(true); + }; + + function displayDate(date: string, dateFormat: string) { + return format(parseISO(date), dateFormat); + } + + const changeRating = () => { + console.log('change rating'); + }; + + // listing , reviews, about + const [segment, setSegment] = useState<'listings' | 'reviews' | 'about'>('listings'); + + const handleSettingPageClick = () => { + route.push('/tabs/carousell_me/settings'); + }; + + const [isCopied, setIsCopied] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const handleShareLink = useCallback(() => { + triggerShare(`https://louiscklaw.github.io`, 'louis portfolio') + .then(() => { + if (navigator.clipboard) setIsCopied(true); + console.debug('share:copied'); + }) + .finally(() => { + console.log('share:end'); + // setIsOpen(false); + }); + }, []); + + return ( + + + + + + + + + + + 1 + + + + + +
+ {/* Instead of loading an image each time the select changes, use opacity to transition them */} +
+
+ +
+
+ + +
+ + + + + + + + + + + +
+
+ +
+
Louis Law
+
+ @louiscklaw +
+ 5.0 + + (12) + + Joined 5y 6m +
+ + {/* location */} +
+
+ Verified + + + +
+ +
+ + + Hong Kong + +
+
+ {/* location */} + + {/* conbutton and caroubiz */} +
+ + + 409 + + + + + caroubiz + +
+ {/* conbutton and caroubiz */} + + {/* profile visitor */} + route.push('/tabs/carousell_me/insights')} + style={{ margin: '0rem' }} + > + +
+ +
+ +

+ No profile visitors today +

+
+ +
List an item to get more visitors
+
+
+
+
+
+ {/* profile visitor */} +
+
+
+ + {/* listing / reviews / about */} + setSegment(e.detail.value as any)} + > + {t('Listings')} + {t('Reviwes')} + {t('About')} + + {/* listing / reviews / about */} + + + + +
+ + {/* + setShowPopover(false)}> + setShowPopover(false)} /> + + */} +
+ ); +}; + +// export default React.memo(CarousellMe); + +export default connect({ + mapStateToProps: (state) => ({ + events: selectors.getEvents(state), + }), + component: React.memo(CarousellMe), +}); diff --git a/03_source/mobile/src/pages/CarousellMe/look_for_visitor.svg b/03_source/mobile/src/pages/CarousellMe/look_for_visitor.svg new file mode 100644 index 0000000..d9e16f8 --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/look_for_visitor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/03_source/mobile/src/pages/CarousellMe/style.scss b/03_source/mobile/src/pages/CarousellMe/style.scss new file mode 100644 index 0000000..7e659d9 --- /dev/null +++ b/03_source/mobile/src/pages/CarousellMe/style.scss @@ -0,0 +1,118 @@ +#carousell-me-page { + .setting-icons { + ion-button::part(native) { + // https://ionicframework.com/docs/theming/css-shadow-parts + margin: 0; + padding: 0; + } + } + + ion-content { + --padding-bottom: 5rem; + } + + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 15%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('https://cdn.dribbble.com/users/438226/screenshots/16695232/media/145ec731468579d04291d11c4c389c65.png'); + filter: brightness(75%); + } + + .about-info { + position: absolute; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } + + .profile-test { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: flex-end; + + position: relative; + height: 50px; + + ion-icon { + font-size: 1.5rem; + margin-left: 1rem; + } + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/MainTabs.tsx b/03_source/mobile/src/pages/MainTabs.tsx index 2a477d0..cc19962 100644 --- a/03_source/mobile/src/pages/MainTabs.tsx +++ b/03_source/mobile/src/pages/MainTabs.tsx @@ -19,6 +19,7 @@ import PATHS from '../PATHS'; import Favourites from './Favourites'; import Helloworld from './Helloworld'; import TabAppRoute from '../TabAppRoute'; +import CarousellMe from './CarousellMe'; interface MainTabsProps {} @@ -43,6 +44,16 @@ const MainTabs: React.FC = () => { } exact={true} /> } exact={true} /> + {/* */} + } exact={true} /> + {/* + + + + + + */} + {/* */} @@ -63,10 +74,14 @@ const MainTabs: React.FC = () => { Nearby + {/* Order + */} + + Message diff --git a/03_source/mobile/src/util/toProperCase.tsx b/03_source/mobile/src/util/toProperCase.tsx new file mode 100644 index 0000000..6b203d3 --- /dev/null +++ b/03_source/mobile/src/util/toProperCase.tsx @@ -0,0 +1,5 @@ +export const toProperCase = (txt: string) => { + return txt.replace(/\w\S*/g, function (txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); +}; diff --git a/03_source/mobile/src/util/triggerShare.tsx b/03_source/mobile/src/util/triggerShare.tsx new file mode 100644 index 0000000..3b75f26 --- /dev/null +++ b/03_source/mobile/src/util/triggerShare.tsx @@ -0,0 +1,8 @@ +export const triggerShare = (url: string, title: string) => { + if (navigator.share) { + return navigator?.share({ title, url }); + } else if (navigator.clipboard) { + return navigator.clipboard.writeText(url); + } + return new Promise((resolve) => resolve('')); +}; diff --git a/03_source/mobile/src/utils/helloworld.ts b/03_source/mobile/src/utils/helloworld.ts new file mode 100644 index 0000000..35f468b --- /dev/null +++ b/03_source/mobile/src/utils/helloworld.ts @@ -0,0 +1 @@ +export const hello = 'world';