This commit is contained in:
louiscklaw
2025-03-16 01:20:16 +08:00
parent a3fccbf5ea
commit f5ce9889c6
3112 changed files with 919640 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
Chrome >=79
ChromeAndroid >=79
Firefox >=70
Edge >=79
Safari >=14
iOS >=14

View File

@@ -0,0 +1,31 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/dist
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
/.nx
/.nx/cache
/.vscode/*
!/.vscode/extensions.json
.idea
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Optional eslint cache
.eslintcache

View File

@@ -0,0 +1,10 @@
import { defineConfig } from "cypress";
export default defineConfig({
e2e: {
baseUrl: "http://localhost:5173",
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
});

View File

@@ -0,0 +1,6 @@
describe('My First Test', () => {
it('Visits the app root url', () => {
cy.visit('/')
cy.contains('ion-content', 'Tab 1 page')
})
})

View File

@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@@ -0,0 +1,37 @@
/// <reference types="cypress" />
// ***********************************************
// This example commands.ts shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
//
// declare global {
// namespace Cypress {
// interface Chainable {
// login(email: string, password: string): Chainable<void>
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }

View File

@@ -0,0 +1,20 @@
// ***********************************************************
// This example support/e2e.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@@ -0,0 +1,30 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
export default tseslint.config(
{ ignores: ['dist', 'cypress.config.ts'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
},
},
)

View File

@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Ionic App</title>
<base href="/" />
<meta name="color-scheme" content="light dark" />
<meta
name="viewport"
content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<link rel="manifest" href="/manifest.json" />
<link rel="shortcut icon" type="image/png" href="/favicon.png" />
<!-- add to homescreen for ios -->
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-title" content="Ionic App" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@@ -0,0 +1,5 @@
{
"name": "ionic-instagram-clone",
"integrations": {},
"type": "react-vite"
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,75 @@
{
"name": "ionic-instagram-clone",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite --host 0.0.0.0 --cors --force --clearScreen",
"build": "tsc && vite build",
"preview": "vite preview",
"test.e2e": "cypress run",
"test.unit": "vitest",
"lint": "eslint"
},
"dependencies": {
"@capacitor/app": "7.0.0",
"@capacitor/core": "7.0.1",
"@capacitor/haptics": "7.0.0",
"@capacitor/keyboard": "7.0.0",
"@capacitor/status-bar": "7.0.0",
"@ionic/react": "^8.0.0",
"@ionic/react-router": "^8.0.0",
"@types/react-router": "^5.1.20",
"@types/react-router-dom": "^5.3.3",
"ionicons": "^7.0.0",
"pullstate": "^1.25.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^5.3.4",
"react-router-dom": "^5.3.4",
"sass": "^1.85.1",
"web-vitals": "^4.2.4",
"workbox-background-sync": "^7.3.0",
"workbox-broadcast-update": "^7.3.0",
"workbox-cacheable-response": "^7.3.0",
"workbox-core": "^7.3.0",
"workbox-expiration": "^7.3.0",
"workbox-google-analytics": "^7.3.0",
"workbox-navigation-preload": "^7.3.0",
"workbox-precaching": "^7.3.0",
"workbox-range-requests": "^7.3.0",
"workbox-routing": "^7.3.0",
"workbox-strategies": "^7.3.0",
"workbox-streams": "^7.3.0"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"devDependencies": {
"@capacitor/cli": "7.0.1",
"@testing-library/dom": ">=7.21.4",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@vitejs/plugin-legacy": "^5.0.0",
"@vitejs/plugin-react": "^4.0.1",
"cypress": "^13.5.0",
"eslint": "^9.20.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^15.15.0",
"jsdom": "^22.1.0",
"terser": "^5.4.0",
"typescript": "^5.1.6",
"typescript-eslint": "^8.24.0",
"vite": "~5.2.0",
"vitest": "^0.34.6"
},
"description": "An Ionic project"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -0,0 +1 @@
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 B

View File

@@ -0,0 +1,21 @@
{
"short_name": "Ionic App",
"name": "My Ionic App",
"icons": [
{
"src": "assets/icon/favicon.png",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "assets/icon/icon.png",
"type": "image/png",
"sizes": "512x512",
"purpose": "maskable"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#ffffff",
"background_color": "#ffffff"
}

View File

@@ -0,0 +1,8 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders without crashing', () => {
const { baseElement } = render(<App />);
expect(baseElement).toBeDefined();
});

View File

@@ -0,0 +1,169 @@
import { Redirect, Route } from "react-router-dom";
import {
IonApp,
IonIcon,
IonLabel,
IonRouterOutlet,
IonTabBar,
IonTabButton,
IonTabs,
setupIonicReact,
} from "@ionic/react";
import { IonReactRouter } from "@ionic/react-router";
import {
bagOutline,
ellipse,
home,
playCircleOutline,
searchOutline,
square,
triangle,
} from "ionicons/icons";
import Tab1 from "./pages/Tab1";
import Tab2 from "./pages/Tab2";
import Tab3 from "./pages/Tab3";
/* Core CSS required for Ionic components to work properly */
import "@ionic/react/css/core.css";
/* Basic CSS for apps built with Ionic */
import "@ionic/react/css/normalize.css";
import "@ionic/react/css/structure.css";
import "@ionic/react/css/typography.css";
/* Optional CSS utils that can be commented out */
import "@ionic/react/css/padding.css";
import "@ionic/react/css/float-elements.css";
import "@ionic/react/css/text-alignment.css";
import "@ionic/react/css/text-transformation.css";
import "@ionic/react/css/flex-utils.css";
import "@ionic/react/css/display.css";
/**
* Ionic Dark Mode
* -----------------------------------------------------
* For more info, please see:
* https://ionicframework.com/docs/theming/dark-mode
*/
/* import '@ionic/react/css/palettes/dark.always.css'; */
/* import '@ionic/react/css/palettes/dark.class.css'; */
import "@ionic/react/css/palettes/dark.system.css";
/* Theme variables */
import "./theme/variables.css";
import Home from "./pages/Home";
import { ProfileStore } from "./pages/ProfileStore";
import MyProfile from "./pages/MyProfile";
import Profile from "./pages/Profile";
setupIonicReact();
function App() {
const profile = ProfileStore.useState((s) => s.profile);
return (
<>
<IonApp>
<IonReactRouter>
<IonTabs>
<IonRouterOutlet>
<Route exact path="/home">
<Home />
</Route>
<Route exact path="/myprofile">
<MyProfile />
</Route>
<Route exact path="/profile/:id">
<Profile />
</Route>
<Route exact path="/tab1">
<Tab1 />
</Route>
<Route exact path="/tab2">
<Tab2 />
</Route>
<Route path="/tab3">
<Tab3 />
</Route>
<Route exact path="/">
<Redirect to="/home" />
</Route>
</IonRouterOutlet>
{/* */}
<IonTabBar slot="bottom">
<IonTabButton tab="home" href="/home">
<IonIcon icon={home} />
</IonTabButton>
<IonTabButton tab="tab2" href="/tab2">
<IonIcon icon={searchOutline} />
</IonTabButton>
<IonTabButton tab="tab3" href="/tab3">
<IonIcon icon={playCircleOutline} />
</IonTabButton>
<IonTabButton tab="tab4" href="/tab3">
<IonIcon icon={bagOutline} />
</IonTabButton>
<IonTabButton tab="tab5" href="/myprofile">
<img alt="tab avatar" src={profile.avatar} />
</IonTabButton>
</IonTabBar>
</IonTabs>
</IonReactRouter>
</IonApp>
</>
);
}
const App2: React.FC = () => (
<IonApp>
<IonReactRouter>
<IonTabs>
<IonRouterOutlet>
<Route exact path="/home">
<Home />
</Route>
<Route exact path="/tab1">
<Tab1 />
</Route>
<Route exact path="/tab2">
<Tab2 />
</Route>
<Route path="/tab3">
<Tab3 />
</Route>
<Route exact path="/">
<Redirect to="/tab1" />
</Route>
</IonRouterOutlet>
{/* */}
<IonTabBar slot="bottom">
<IonTabButton tab="home" href="/home">
<IonIcon icon={home} />
</IonTabButton>
<IonTabButton tab="tab2" href="/tab2">
<IonIcon icon={searchOutline} />
</IonTabButton>
<IonTabButton tab="tab3" href="/tab3">
<IonIcon icon={playCircleOutline} />
</IonTabButton>
<IonTabButton tab="tab4" href="/tab3">
<IonIcon icon={bagOutline} />
</IonTabButton>
<IonTabButton tab="tab5" href="/myprofile">
<img alt="tab avatar" src={"profile.avatar"} />
</IonTabButton>
</IonTabBar>
</IonTabs>
</IonReactRouter>
</IonApp>
);
export default App;

View File

@@ -0,0 +1,24 @@
.container {
text-align: center;
position: absolute;
left: 0;
right: 0;
top: 50%;
transform: translateY(-50%);
}
.container strong {
font-size: 20px;
line-height: 26px;
}
.container p {
font-size: 16px;
line-height: 22px;
color: #8c8c8c;
margin: 0;
}
.container a {
text-decoration: none;
}

View File

@@ -0,0 +1,16 @@
import './ExploreContainer.css';
interface ContainerProps {
name: string;
}
const ExploreContainer: React.FC<ContainerProps> = ({ name }) => {
return (
<div className="container">
<strong>{name}</strong>
<p>Explore <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">UI Components</a></p>
</div>
);
};
export default ExploreContainer;

View File

@@ -0,0 +1,221 @@
.postsContainer {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
.postContainer {
display: flex;
flex-direction: column;
margin-top: 1rem;
}
.postProfile {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
align-content: center;
padding-right: 0.75rem;
padding-left: 0.75rem;
}
.postProfile ion-router-link {
display: flex !important;
flex-direction: row !important;
}
.postProfileInfo ion-avatar {
height: 2.2rem;
width: 2.2rem;
}
.postProfileInfo p {
margin: 0;
padding: 0;
margin-left: 0.5rem;
font-weight: 500;
font-size: 0.9rem;
color: black;
}
.postProfileInfo {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
align-content: center;
}
.postImage {
border-top: 1px solid rgb(216, 216, 216);
margin-top: 0.5rem;
height: 20rem;
width: 100%;
}
.postImageLike {
font-size: 10rem;
color: rgb(231, 231, 231);
position: absolute;
left: 32vmin;
margin-top: 20vmin;
display: none;
}
.postActionsContainer {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
align-content: center;
padding-right: 0.75rem;
padding-left: 0.75rem;
margin-top: 0.5rem;
}
.postActions {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.postActions ion-icon,
.postBookmark ion-icon {
font-size: 1.5rem;
}
.postActions ion-icon:not(:first-child) {
padding-left: 0.7rem;
}
.postLikesContainer {
padding-left: 0.75rem;
margin-top: 0.5rem;
}
.postLikesContainer p {
margin: 0;
padding: 0;
font-weight: 200 !important;
font-size: 0.8rem;
}
.postLikedName {
font-weight: 600;
}
.postCaption {
padding-left: 0.75rem;
padding-right: 0.75rem;
margin-top: 0.3rem;
}
.postCaption p {
margin: 0;
padding: 0;
font-weight: 200 !important;
font-size: 0.8rem;
}
.postName {
color: black !important;
font-weight: 600 !important;
}
.postName ion-router-link {
color: black;
}
.postComments {
padding-left: 0.75rem;
padding-right: 0.75rem;
margin-top: 0.5rem;
}
.postComments p {
margin: 0;
padding: 0;
color:rgb(175, 175, 175);
font-size: 0.8rem;
}
.postAddComment {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
align-content: center;
padding-left: 0.75rem;
padding-right: 0.75rem;
}
.postAddCommentProfile {
display: flex;
flex-direction: row;
justify-content: center;
align-content: center;
align-items: center;
margin-top: 0.5rem;
}
.postAddCommentProfile ion-avatar {
height: 1.9rem;
width: 1.9rem;
}
.postAddCommentProfile p {
padding-left: 0.75rem;
font-size: 0.8rem;
color: rgb(175, 175, 175);
}
.postAddCommentActions {
}
.postAddCommentActions ion-icon {
padding-left: 0.5rem;
}
.postTime {
padding-left: 0.75rem;
padding-right: 0.75rem;
margin-top: 0rem;
}
.postTime p {
margin: 0;
padding: 0;
color: rgb(175, 175, 175);
font-size: 0.6rem;
}

View File

@@ -0,0 +1,130 @@
import { IonAvatar, IonIcon, IonRouterLink } from "@ionic/react";
import {
addCircleOutline,
bookmarkOutline,
chatbubbleOutline,
ellipsisVertical,
heart,
heartOutline,
paperPlaneOutline,
} from "ionicons/icons";
import { likePost } from "../pages/PostStore";
import { ProfilesStore } from "../pages/ProfilesStore";
import { ProfileStore } from "../pages/ProfileStore";
import styles from "./Feed.module.scss";
const Feed = (props: any) => {
const { posts } = props;
const profile = ProfileStore.useState((s) => s.profile);
const profiles = ProfilesStore.useState((s) => s.profiles);
const addLike = (event: any, postID: any, liked: any) => {
likePost(event, postID, liked);
};
return (
<div className={styles.postsContainer}>
{posts.map((post: any, index: any) => {
const postProfile = profiles.filter((p) => p.id === post.profile_id)[0];
return (
<div key={index} className={styles.postContainer}>
<div className={styles.postProfile}>
<div className={styles.postProfileInfo}>
<IonRouterLink routerLink={`/profile/${postProfile.id}`}>
<IonAvatar>
<img alt="post avatar" src={postProfile.avatar} />
</IonAvatar>
</IonRouterLink>
<IonRouterLink routerLink={`/profile/${postProfile.id}`}>
<p>{postProfile.username}</p>
</IonRouterLink>
</div>
<div className={styles.postProfileMore}>
<IonIcon icon={ellipsisVertical} />
</div>
</div>
<div
className={styles.postImage}
style={{
backgroundImage: `url(${post.image})`,
backgroundPosition: "center, center",
backgroundSize: "cover",
}}
>
<IonIcon
id={`postLike_${post.id}`}
className={`animated__animated animate__heartBeat ${styles.postImageLike}`}
icon={heart}
color="light"
/>
</div>
<div className={styles.postActionsContainer}>
<div className={styles.postActions}>
<IonIcon
className="animate__animated"
color={post.liked ? "danger" : "dark"}
icon={post.liked ? heart : heartOutline}
onClick={(e) => addLike(e, post.id, post.liked)}
/>
<IonIcon icon={chatbubbleOutline} />
<IonIcon icon={paperPlaneOutline} />
</div>
<div className={styles.postBookmark}>
<IonIcon icon={bookmarkOutline} />
</div>
</div>
<div className={styles.postLikesContainer}>
<p>
Liked by{" "}
<span className={styles.postLikedName}>alanmontgomery</span> and{" "}
<span className={styles.postLikedName}>2 others</span>
</p>
</div>
<div className={styles.postCaption}>
<p>
<span className={styles.postName}>
<IonRouterLink routerLink={`/profile/${postProfile.id}`}>
{postProfile.username}
</IonRouterLink>
</span>{" "}
{post.caption}
</p>
</div>
<div className={styles.postComments}>
<p>View all {post.comments.length} comments</p>
</div>
<div className={styles.postAddComment}>
<div className={styles.postAddCommentProfile}>
<IonAvatar>
<img alt="add comment avatar" src={profile.avatar} />
</IonAvatar>
<p className="ion-margin-left">Add a comment...</p>
</div>
<div className={styles.postAddCommentActions}>
<IonIcon icon={heart} color="danger" />
<IonIcon icon={addCircleOutline} color="medium" />
</div>
</div>
<div className={styles.postTime}>
<p>{post.time}</p>
</div>
</div>
);
})}
</div>
);
};
export default Feed;

View File

@@ -0,0 +1,98 @@
$border: linear-gradient(to bottom, #d82b7e, #f57939);
.stories {
height: fit-content;
margin-top: -0.7rem;
}
.storiesContainer {
overflow-x: scroll;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
display: flex;
flex-direction: row;
width: 100%;
}
.storiesContainer::-webkit-scrollbar {
display: none;
}
.story,
.yourStory {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
align-content: center;
margin: 0 auto;
width: 4rem !important;
margin-left: 1rem;
}
.story:first-child,
.yourStory:first-child {
margin-left: 0.75rem;
}
.story p,
.yourStory p {
text-align: center;
margin: 0;
padding: 0;
margin-top: 0.2rem;
color: rgb(95, 95, 95);
font-size: 0.7rem;
font-weight: 400;
width: 120%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
position: relative;
margin-top: 5rem;
}
.story img,
.yourStory img {
height: 3.5rem !important;
width: 3.5rem !important;
position: absolute;
border-radius: 500px;
background: $border;
padding: 0.1rem;
}
.yourStory img {
background:rgb(214, 214, 214);
}
.storyAdd {
position: absolute;
color: white;
background-color: var(--ion-color-primary);
width: 1rem;
height: 1rem;
text-align: center;
margin: 0 auto;
display: flex;
flex-direction: row;
align-items: center;
align-content: center;
justify-content: center;
border-radius: 500px;
border: 2px solid white;
bottom: 20px;
right: 0;
padding: 0.5rem;
font-size: 0.9rem;
}

View File

@@ -0,0 +1,30 @@
import { IonCol, IonRouterLink, IonRow } from "@ionic/react";
import styles from "./Stories.module.scss";
const Stories = (props: any) => {
const { profiles } = props;
return (
<IonRow className={styles.stories}>
<div className={styles.storiesContainer}>
{profiles.map((story: any, index: any) => {
return (
<IonCol
key={index}
className={index === 0 ? styles.yourStory : styles.story}
>
<img alt="story avatar" src={story.avatar} />
{index === 0 && <div className={styles.storyAdd}>+</div>}
<IonRouterLink routerLink={`/profile/${story.id}`}>
<p>{index === 0 ? "Your story" : story.username}</p>
</IonRouterLink>
</IonCol>
);
})}
</div>
</IonRow>
);
};
export default Stories;

View File

@@ -0,0 +1,11 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
const container = document.getElementById('root');
const root = createRoot(container!);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);

View File

@@ -0,0 +1,198 @@
.postsContainer {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
.postContainer {
display: flex;
flex-direction: column;
margin-top: 1rem;
}
.postProfile {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
align-content: center;
padding-right: 0.75rem;
padding-left: 0.75rem;
}
.postProfileInfo ion-avatar {
height: 2.2rem;
width: 2.2rem;
}
.postProfileInfo p {
margin: 0;
padding: 0;
margin-left: 0.5rem;
font-weight: 500;
font-size: 0.9rem;
}
.postProfileInfo {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
align-content: center;
}
.postImage {
border-top: 1px solid rgb(216, 216, 216);
margin-top: 0.5rem;
height: 20rem;
width: 100%;
}
.postActionsContainer {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
align-content: center;
padding-right: 0.75rem;
padding-left: 0.75rem;
margin-top: 0.5rem;
}
.postActions {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.postActions ion-icon,
.postBookmark ion-icon {
font-size: 1.5rem;
}
.postActions ion-icon:not(:first-child) {
padding-left: 0.7rem;
}
.postLikesContainer {
padding-left: 0.75rem;
margin-top: 0.5rem;
}
.postLikesContainer p {
margin: 0;
padding: 0;
font-weight: 200 !important;
font-size: 0.8rem;
}
.postLikedName {
font-weight: 600;
}
.postCaption {
padding-left: 0.75rem;
padding-right: 0.75rem;
margin-top: 0.3rem;
}
.postCaption p {
margin: 0;
padding: 0;
font-weight: 200 !important;
font-size: 0.8rem;
}
.postName {
font-weight: 600 !important;
}
.postComments {
padding-left: 0.75rem;
padding-right: 0.75rem;
margin-top: 0.5rem;
}
.postComments p {
margin: 0;
padding: 0;
color:rgb(175, 175, 175);
font-size: 0.8rem;
}
.postAddComment {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
align-content: center;
padding-left: 0.75rem;
padding-right: 0.75rem;
}
.postAddCommentProfile {
display: flex;
flex-direction: row;
justify-content: center;
align-content: center;
align-items: center;
margin-top: 0.5rem;
}
.postAddCommentProfile ion-avatar {
height: 1.9rem;
width: 1.9rem;
}
.postAddCommentProfile p {
padding-left: 0.75rem;
font-size: 0.8rem;
color: rgb(175, 175, 175);
}
.postAddCommentActions {
}
.postAddCommentActions ion-icon {
padding-left: 0.5rem;
}
.postTime {
padding-left: 0.75rem;
padding-right: 0.75rem;
margin-top: 0rem;
}
.postTime p {
margin: 0;
padding: 0;
color: rgb(175, 175, 175);
font-size: 0.6rem;
}

View File

@@ -0,0 +1,60 @@
import {
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonPage,
IonToolbar,
} from "@ionic/react";
import {
addCircleOutline,
heartOutline,
paperPlaneOutline,
} from "ionicons/icons";
import { PostStore } from "./PostStore";
import { ProfilesStore } from "./ProfilesStore";
import Stories from "../components/Stories";
import Feed from "../components/Feed";
const Home: React.FC = () => {
const profiles = ProfilesStore.useState((s) => s.profiles);
const posts = PostStore.useState((s) => s.posts);
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<img
alt="main logo"
src="/assets/logo.png"
style={{ width: "7rem" }}
/>
</IonButtons>
<IonButtons slot="end">
<IonButton color="dark">
<IonIcon icon={addCircleOutline} />
</IonButton>
<IonButton color="dark">
<IonIcon icon={heartOutline} />
</IonButton>
<IonButton color="dark">
<IonIcon icon={paperPlaneOutline} />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<Stories profiles={profiles} />
<Feed posts={posts} />
</IonContent>
</IonPage>
);
};
export default Home;

View File

@@ -0,0 +1,193 @@
import {
IonButton,
IonButtons,
IonCardSubtitle,
IonCardTitle,
IonCol,
IonContent,
IonGrid,
IonHeader,
IonIcon,
IonPage,
IonRow,
IonToolbar,
useIonViewWillEnter,
} from "@ionic/react";
import {
addCircleOutline,
bookmarksOutline,
chevronDown,
gridOutline,
menuOutline,
} from "ionicons/icons";
import { useState } from "react";
import styles from "./Profile.module.scss";
import { ProfilesStore } from "./ProfilesStore";
import { ProfileStore } from "./ProfileStore";
const MyProfile = () => {
const currentProfile = ProfileStore.useState((s) => s.profile);
const profiles = ProfilesStore.useState((s) => s.profiles);
const [profile, setProfile] = useState(false);
useIonViewWillEnter(() => {
const profileID = "123321";
const tempProfile = profiles.filter(
(p: any) => parseInt(p.id) === parseInt(profileID)
)[0];
setProfile(true);
});
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<p className={styles.username}>
{"profile.username"}
<IonIcon icon={chevronDown} />
</p>
</IonButtons>
<IonButtons slot="end">
<IonButton color="dark">
<IonIcon icon={addCircleOutline} />
</IonButton>
<IonButton color="dark">
<IonIcon icon={menuOutline} />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonGrid>
<IonRow className="ion-text-center ion-justify-content-between ion-align-self-center ion-align-items-center">
<IonCol size="4">
<img
src={
"https://random-image-pepebigotes.vercel.app/api/random-image"
}
alt="profile avatar"
className={styles.profileAvatar}
/>
</IonCol>
<IonCol>
<IonRow className="ion-text-center ion-justify-content-between ion-align-items-center ion-align-self-center ion-align">
<IonCol size="4" className="ion-text-center">
<IonCardTitle className={styles.value}>
{'"profile.posts" && "profile.posts.length"'}
</IonCardTitle>
<IonCardSubtitle className={styles.label}>
Posts
</IonCardSubtitle>
</IonCol>
<IonCol size="4" className="ion-text-center">
<IonCardTitle className={styles.value}>
{"profile.followers"}
</IonCardTitle>
<IonCardSubtitle className={styles.label}>
Followers
</IonCardSubtitle>
</IonCol>
<IonCol size="4" className="ion-text-center">
<IonCardTitle className={styles.value}>
{"profile.following"}
</IonCardTitle>
<IonCardSubtitle className={styles.label}>
Following
</IonCardSubtitle>
</IonCol>
</IonRow>
</IonCol>
</IonRow>
<IonRow>
<IonCol size="12" className={styles.profileInfo}>
<p className={styles.profileUsername}>
{"profile.firstname"} {"profile.surname"}
</p>
<p className={styles.profileTitle}>{"profile.title"}</p>
<p className={styles.profileBio}>{"profile.bio"}</p>
<a className={styles.profileLink} href={"profile.link"}>
{"profile.link"}
</a>
</IonCol>
</IonRow>
<IonRow className={styles.profileActions}>
<IonCol size="4">
<IonButton
className={styles.lightButton}
expand="block"
fill="outline"
>
Edit Profile
</IonButton>
</IonCol>
<IonCol size="4">
<IonButton
className={styles.lightButton}
fill="outline"
expand="block"
>
Promotions
</IonButton>
</IonCol>
<IonCol size="4">
<IonButton
className={styles.lightButton}
fill="outline"
expand="block"
>
Insights
</IonButton>
</IonCol>
</IonRow>
</IonGrid>
<IonRow className="ion-text-center ion-justify-content-center ion-align-items-center ion-align-self-center">
<IonCol
size="6"
className="ion-justify-content-center ion-align-items-center ion-align-self-center"
style={{ borderBottom: "2px solid black", marginBottom: "2px" }}
>
<IonIcon style={{ fontSize: "1.5rem" }} icon={gridOutline} />
</IonCol>
<IonCol
size="6"
className="ion-justify-content-center ion-align-items-center ion-align-self-center"
>
<IonIcon style={{ fontSize: "1.5rem" }} icon={bookmarksOutline} />
</IonCol>
</IonRow>
<IonRow className="ion-no-padding ion-no-margin">
{/* FIXME */}
{
// profile.posts
[0, 1, 2, 3, 4].map((post: any, index: any) => {
return (
<IonCol className={styles.postCol} key={index} size="4">
<img
alt="post"
src="https://random-image-pepebigotes.vercel.app/api/random-image"
/>
</IonCol>
);
})
}
</IonRow>
</IonContent>
</IonPage>
);
};
export default MyProfile;

View File

@@ -0,0 +1,141 @@
import { Store } from "pullstate";
export const PostStore = new Store({
posts: [
{
id: 1,
image: "https://random-image-pepebigotes.vercel.app/api/random-image",
caption: "Ioniconf 2021! Register Now!",
likes: 73,
liked: false,
profile_id: 6,
time: "1 hour ago",
comments: [
{
profile_id: 3,
comment: "Test",
},
],
},
{
id: 2,
image:
"https://creativetacos.com/wp-content/uploads/2019/08/Free-Chipper-Personal-Finance-App-Kit.jpg",
caption: "Ionic React Hub! UI Components, Templates, Clones and more!",
likes: 73,
liked: true,
profile_id: 1,
time: "1 hour ago",
comments: [
{
profile_id: 3,
comment: "Test",
},
{
profile_id: 3,
comment: "Test",
},
{
profile_id: 3,
comment: "Test",
},
],
},
{
id: 3,
image: "https://cdn.buttercms.com/AIcP6e8FRx6fgsKa7bvy",
caption: "Join the first ever Ionic Event!",
likes: 73,
liked: false,
profile_id: 4,
time: "2 hours ago",
comments: [
{
profile_id: 1,
comment: "Test",
},
{
profile_id: 2,
comment: "Test",
},
],
},
{
id: 4,
image: "https://ionicframework.com/img/meta/ionic-framework-og.png",
caption: "Build cross platform mobile apps with the Ionic Framework!",
likes: 73,
liked: false,
profile_id: 2,
time: "3 hours ago",
comments: [
{
profile_id: 1,
comment: "Test",
},
{
profile_id: 2,
comment: "Test",
},
],
},
],
});
export const likePost = (event, postID, liked) => {
event.target.classList.add("animate__heartBeat");
if (!liked) {
document.getElementById(`postLike_${postID}`).style.display = "inline";
}
setTimeout(() => {
event.target.classList.remove("animate__heartBeat");
document.getElementById(`postLike_${postID}`).style.display = "none";
}, 850);
PostStore.update((s) => {
s.posts.find((p, index) =>
parseInt(p.id) === parseInt(postID)
? (s.posts[index].liked = liked ? false : true)
: false
);
});
if (liked) {
PostStore.update((s) => {
s.posts.find((p, index) =>
parseInt(p.id) === parseInt(postID)
? (s.posts[index].likes = s.posts[index].likes++)
: false
);
});
} else {
PostStore.update((s) => {
s.posts.find((p, index) =>
parseInt(p.id) === parseInt(postID)
? (s.posts[index].likes = s.posts[index].likes--)
: false
);
});
}
};
export const addPost = (newPost) => {
PostStore.update((s) => {
s.posts = [...s.posts, newPost];
});
};
export const addCommentToPost = (newComment, postID) => {
PostStore.update((s) => {
s.posts.find((p, index) =>
parseInt(p.id) === parseInt(postID)
? (s.posts[index].comments = [...s.posts[index].comments, newComment])
: false
);
});
};

View File

@@ -0,0 +1,100 @@
.username {
display: flex;
flex-direction: row;
justify-content: center;
align-content: center;
align-items: center;
font-weight: 600;
}
.username ion-icon {
font-size: 0.8rem;
margin-left: 0.3rem;
}
.label {
color: black;
font-size: 0.7rem;
font-weight: 500;
font-family: Arial, Helvetica, sans-serif !important;
text-transform: lowercase;
}
.label::first-letter {
text-transform: uppercase;
}
.value {
font-size: 1rem;
}
.profileAvatar {
border-radius: 500px;
width: 5.5rem;
height: auto;
}
.profileInfo {
display: flex;
flex-direction: column;
padding-left: 0.75rem;
padding-right: 0.75rem;
}
.profileInfo p,
.profileInfo a {
// padding: 0.1rem 0 0.1rem 0;
margin: 0;
font-size: 0.9rem;
}
.profileUsername {
font-weight: 600;
}
.profileTitle {
color: rgb(136, 136, 136);
}
.profileLink {
color: rgb(22, 60, 131);
text-decoration: none;
}
.profileActions ion-button {
height: 2.3rem;
--border-radius: 5px;
font-weight: 600;
}
.lightButton {
--color: rgb(65, 65, 65);
--color-activated: rgb(65, 65, 65);
--background-hover: white;
--background-focused: white;
--background-activated: white;
--border-color: rgb(231, 231, 231);
--border-width: 2px;
font-size: 0.8rem;
}
.postCol {
--ion-grid-column-padding: 0rem;
padding: 0.1rem;
padding-bottom: 0.01rem !important;
padding-top: 0.01rem !important;
}

View File

@@ -0,0 +1,177 @@
import {
IonBackButton,
IonButton,
IonButtons,
IonCardSubtitle,
IonCardTitle,
IonCol,
IonContent,
IonGrid,
IonHeader,
IonIcon,
IonPage,
IonRow,
IonTitle,
IonToolbar,
useIonViewWillEnter,
} from "@ionic/react";
import {
addCircleOutline,
arrowBackOutline,
bookmarksOutline,
chevronDown,
ellipsisVertical,
gridOutline,
menuOutline,
personOutline,
} from "ionicons/icons";
import ExploreContainer from "../components/ExploreContainer";
import "./Tab1.css";
import { useParams } from "react-router";
import { ProfilesStore } from "./ProfilesStore";
import { ProfileStore } from "./ProfileStore";
import { useState } from "react";
import styles from "./Profile.module.scss";
const Profile: React.FC = () => {
const params = useParams();
const profiles = ProfilesStore.useState((s) => s.profiles);
const currentProfile = ProfileStore.useState((s) => s.profile);
const [profile, setProfile] = useState<any>(false);
useIonViewWillEnter(() => {
// FIXME
// const profileID = params.id;
const profileID = "123321";
const tempProfile = profiles.filter(
(p: any) => parseInt(p.id) === parseInt(profileID)
)[0];
setProfile(tempProfile);
});
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Tab 1</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonGrid>
<IonRow className="ion-text-center ion-justify-content-between ion-align-self-center ion-align-items-center">
<IonCol size="4">
<img
src={profile.avatar}
alt="profile avatar"
className={styles.profileAvatar}
/>
</IonCol>
<IonCol>
<IonRow className="ion-text-center ion-justify-content-between ion-align-items-center ion-align-self-center ion-align">
<IonCol size="4" className="ion-text-center">
<IonCardTitle className={styles.value}>
{profile.posts && profile.posts.length}
</IonCardTitle>
<IonCardSubtitle className={styles.label}>
Posts
</IonCardSubtitle>
</IonCol>
<IonCol size="4" className="ion-text-center">
<IonCardTitle className={styles.value}>
{profile.followers}
</IonCardTitle>
<IonCardSubtitle className={styles.label}>
Followers
</IonCardSubtitle>
</IonCol>
<IonCol size="4" className="ion-text-center">
<IonCardTitle className={styles.value}>
{profile.following}
</IonCardTitle>
<IonCardSubtitle className={styles.label}>
Following
</IonCardSubtitle>
</IonCol>
</IonRow>
</IonCol>
</IonRow>
<IonRow>
<IonCol size="12" className={styles.profileInfo}>
<p className={styles.profileUsername}>
{profile.firstname} {profile.surname}
</p>
<p className={styles.profileTitle}>{profile.title}</p>
<p className={styles.profileBio}>{profile.bio}</p>
<a className={styles.profileLink} href={profile.link}>
{profile.link}
</a>
</IonCol>
</IonRow>
<IonRow className={styles.profileActions}>
<IonCol size="5">
<IonButton expand="block" color="primary">
Follow
</IonButton>
</IonCol>
<IonCol size="5">
<IonButton
className={styles.lightButton}
fill="outline"
expand="block"
>
Message
</IonButton>
</IonCol>
<IonCol size="2">
<IonButton className={styles.lightButton} fill="outline">
<IonIcon icon={chevronDown} />
</IonButton>
</IonCol>
</IonRow>
</IonGrid>
<IonRow className="ion-text-center ion-justify-content-center ion-align-items-center ion-align-self-center">
<IonCol
size="6"
className="ion-justify-content-center ion-align-items-center ion-align-self-center"
style={{ borderBottom: "2px solid black", marginBottom: "2px" }}
>
<IonIcon style={{ fontSize: "1.5rem" }} icon={gridOutline} />
</IonCol>
<IonCol
size="6"
className="ion-justify-content-center ion-align-items-center ion-align-self-center"
>
<IonIcon style={{ fontSize: "1.5rem" }} icon={bookmarksOutline} />
</IonCol>
</IonRow>
<IonRow className="ion-no-padding ion-no-margin">
FIXME
{/* {profile.posts &&
profile.posts.map((post, index) => {
return (
<IonCol className={styles.postCol} key={index} size="4">
<img
alt="post"
src="https://images.pexels.com/photos/699122/pexels-photo-699122.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260"
/>
</IonCol>
);
})} */}
</IonRow>
</IonContent>
</IonPage>
);
};
export default Profile;

View File

@@ -0,0 +1,21 @@
import { Store } from "pullstate";
export const ProfileStore = new Store({
profile: {
id: 1,
firstname: "Alan",
surname: "Montgomery",
avatar: "/assets/alan.jpg",
followers: 0,
following: 0,
},
posts: [],
feed: [],
});
export const addProfilePost = (newPost: any): void => {
// FIXME:
// ProfileStore.update((s) => {
// s.posts = [...s.posts, newPost];
// });
};

View File

@@ -0,0 +1,210 @@
import { Store } from "pullstate";
export const ProfilesStore = new Store({
profiles: [
{
id: 1,
firstname: "Alan",
surname: "Montgomery",
username: "alanmontgomery",
title: "Mobile Team Lead",
bio: "Full Stack 🤓 Mobile Team Lead/Senior React Dev",
link: "alanmontgomery.co.uk",
avatar: "/assets/alan.jpg",
followers: "1,470",
following: "230",
posts: [
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
],
},
{
id: 2,
firstname: "Max",
surname: "Lynch",
username: "maxlynch",
title: "CEO Ionic",
bio: "Co-founder/CEO @ionicframework. Created @capacitorjs. Gamer. @ManUtd fan.",
link: "maxlynch.com",
avatar: "https://random-image-pepebigotes.vercel.app/api/random-image",
followers: "21.1K",
following: "1,200",
posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}],
},
{
id: 3,
firstname: "Ben",
surname: "Sperry",
username: "bensperry",
title: "CDO Ionic",
bio: "Co-founder / CDO @ionicframework. Creator of @ionicons. Product designer. Pixel junkie. Forest explorer.",
link: "bensperry.com",
avatar: "https://random-image-pepebigotes.vercel.app/api/random-image",
followers: "800",
following: "700",
posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}],
},
{
id: 4,
firstname: "Matt",
surname: "Netkow",
username: "mattnetkow",
title: "Head of Product Marketing",
bio: "I help web developers build cross-platform Web Native apps. @IonicFramework: Head of Product Marketing",
link: "webnative.tech",
avatar: "https://random-image-pepebigotes.vercel.app/api/random-image",
followers: "1,200",
following: "900",
posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}],
},
{
id: 5,
firstname: "Liam",
surname: "DeBeasi",
username: "liamdebeasi",
title: "Software Engineer",
bio: "Software Engineer at @ionicframework",
link: "liamdebeasi.com",
avatar: "https://random-image-pepebigotes.vercel.app/api/random-image",
followers: "871",
following: "510",
posts: [
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
],
},
{
id: 6,
firstname: "Mike",
surname: "Hartington",
username: "mikehartington",
title: "Senior Dev Rel",
bio: "Google Developer Expert. Mediocre at best. he/him. npx mhartington",
link: "mhartington.io",
avatar: "https://random-image-pepebigotes.vercel.app/api/random-image",
followers: "12.3K",
following: "2,200",
posts: [
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
],
},
{
id: 7,
firstname: "Adam",
surname: "Bradley",
username: "adambradley",
title: "Director of Technology",
bio: "Proud dad, husband, veteran & dogs best friend. Typos are my own",
link: "ionicframework.com",
avatar: "https://random-image-pepebigotes.vercel.app/api/random-image",
followers: "613",
following: "571",
posts: [
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
],
},
{
id: 8,
firstname: "Brody",
surname: "Kidd",
username: "brodykidd",
title: "Enterprise Account Manager",
bio: "Enterprise Account Manager | @ionicframework | @getcapacitor | @stenciljs",
link: "ionicframework.com",
avatar: "https://random-image-pepebigotes.vercel.app/api/random-image",
followers: "677",
following: "219",
posts: [{}, {}, {}, {}, {}, {}, {}],
},
],
});
export const addProfilePost = (newPost) => {
ProfilesStore.update((s) => {
s.posts = [...s.posts, newPost];
});
};

View File

@@ -0,0 +1,25 @@
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react';
import ExploreContainer from '../components/ExploreContainer';
import './Tab1.css';
const Tab1: React.FC = () => {
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Tab 1</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Tab 1</IonTitle>
</IonToolbar>
</IonHeader>
<ExploreContainer name="Tab 1 page" />
</IonContent>
</IonPage>
);
};
export default Tab1;

View File

@@ -0,0 +1,25 @@
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react';
import ExploreContainer from '../components/ExploreContainer';
import './Tab2.css';
const Tab2: React.FC = () => {
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Tab 2</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Tab 2</IonTitle>
</IonToolbar>
</IonHeader>
<ExploreContainer name="Tab 2 page" />
</IonContent>
</IonPage>
);
};
export default Tab2;

View File

@@ -0,0 +1,31 @@
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
} from "@ionic/react";
import ExploreContainer from "../components/ExploreContainer";
import "./Tab3.css";
const Tab3: React.FC = () => {
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Tab 3</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Tab 3</IonTitle>
</IonToolbar>
</IonHeader>
<ExploreContainer name="Tab 3 page" />
</IonContent>
</IonPage>
);
};
export default Tab3;

View File

@@ -0,0 +1,14 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';
// Mock matchmedia
window.matchMedia = window.matchMedia || function() {
return {
matches: false,
addListener: function() {},
removeListener: function() {}
};
};

View File

@@ -0,0 +1,124 @@
/* Ionic Variables and Theming. For more info, please see:
http://ionicframework.com/docs/theming/ */
* {
font-family: Arial, Helvetica, sans-serif !important;
scroll-behavior: smooth;
}
::-webkit-scrollbar,
::-webkit-scrollbar-thumb {
width: 0px;
}
/** Ionic CSS Variables **/
:root {
/** primary **/
--ion-color-primary: #3880ff;
--ion-color-primary-rgb: 56, 128, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3171e0;
--ion-color-primary-tint: #4c8dff;
/** secondary **/
--ion-color-secondary: #3dc2ff;
--ion-color-secondary-rgb: 61, 194, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #36abe0;
--ion-color-secondary-tint: #50c8ff;
/** tertiary **/
--ion-color-tertiary: #5260ff;
--ion-color-tertiary-rgb: 82, 96, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #4854e0;
--ion-color-tertiary-tint: #6370ff;
/** success **/
--ion-color-success: #2dd36f;
--ion-color-success-rgb: 45, 211, 111;
--ion-color-success-contrast: #ffffff;
--ion-color-success-contrast-rgb: 255, 255, 255;
--ion-color-success-shade: #28ba62;
--ion-color-success-tint: #42d77d;
/** warning **/
--ion-color-warning: #ffc409;
--ion-color-warning-rgb: 255, 196, 9;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0ac08;
--ion-color-warning-tint: #ffca22;
/** danger **/
--ion-color-danger: #eb445a;
--ion-color-danger-rgb: 235, 68, 90;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #cf3c4f;
--ion-color-danger-tint: #ed576b;
/** dark **/
--ion-color-dark: #222428;
--ion-color-dark-rgb: 34, 36, 40;
--ion-color-dark-contrast: #ffffff;
--ion-color-dark-contrast-rgb: 255, 255, 255;
--ion-color-dark-shade: #1e2023;
--ion-color-dark-tint: #383a3e;
/** medium **/
--ion-color-medium: #92949c;
--ion-color-medium-rgb: 146, 148, 156;
--ion-color-medium-contrast: #ffffff;
--ion-color-medium-contrast-rgb: 255, 255, 255;
--ion-color-medium-shade: #808289;
--ion-color-medium-tint: #9d9fa6;
/** light **/
--ion-color-light: #f4f5f8;
--ion-color-light-rgb: 244, 245, 248;
--ion-color-light-contrast: #000000;
--ion-color-light-contrast-rgb: 0, 0, 0;
--ion-color-light-shade: #d7d8da;
--ion-color-light-tint: #f5f6f9;
}
:root {
--ion-toolbar-background: white;
--ion-tab-bar-color: black;
--ion-tab-bar-color-selected: black;
--ion-tab-bar-border-color: rgb(235, 235, 235);
}
ion-tab-button ion-icon {
font-size: 1.6rem;
}
ion-tab-button img {
border-radius: 500px;
height: 1.8rem;
border: 1px solid black;
}
ion-tab-bar {
height: 3rem;
}
ion-toolbar {
--border-style: none;
--padding-start: 1rem;
--padding-end: 1rem;
--padding-top: 0.5rem;
}
ion-toolbar ion-icon {
font-weight: 900 !important;
font-size: 1.6rem;
}
ion-toolbar ion-button:not(:last-child) {
padding-right: 0.3rem;
}

View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

View File

@@ -0,0 +1,18 @@
/// <reference types="vitest" />
import legacy from '@vitejs/plugin-legacy'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react(),
legacy()
],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/setupTests.ts',
}
})

View File

@@ -0,0 +1,9 @@
# NOTES
其實班靚媽幫襯你有可能因為你夠直接,一搵就搵到你,
## ionic-instgram-clone
i stopped at the middle of `ionic-instgram-clone`,
i found that the `ionic-instagram-clone` should be simple enough to re-structure/refactor to use ionic7
i like the layout and want to clone the layout from this source

Submodule quotation3_e-shop/_lab/ionic-instagram-clone-original added at 82f99df290

Binary file not shown.

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -ex
# ionic start --help
ionic start "ionic-instagram-clone" tabs --capacitor --type react --no-git
cd ionic-instagram-clone
npm i --save pullstate
npm i --save sass
npm i --save "web-vitals"
npm i --save "workbox-background-sync"
npm i --save "workbox-broadcast-update"
npm i --save "workbox-cacheable-response"
npm i --save "workbox-core"
npm i --save "workbox-expiration"
npm i --save "workbox-google-analytics"
npm i --save "workbox-navigation-preload"
npm i --save "workbox-precaching"
npm i --save "workbox-range-requests"
npm i --save "workbox-routing"
npm i --save "workbox-strategies"
npm i --save "workbox-streams"
cd ..
echo "done"

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

View File

@@ -0,0 +1,16 @@
# PRODUCT1
![](./5792a24f-90ee-4642-98d9-a91f8268aa2f.jfif)
📣📣預訂📣📣 台灣製造 台灣直送
2-3 星期到‼️
$68@1盒/ $130@2
1 盒 6 包
4m+ bb 可以食‼️‼️‼️
★100%無農藥殘留、無添加香精、無添加鹽、無添加糖、無添加油、無麩質,全素可食。
★質地綿密、入口即化從4個月以上寶寶至牙口不便銀髮族皆可食用老少咸宜。
★創意心型與條狀米菓,小手抓握一次一顆剛剛好。
★內含6份貼心獨立小包裝次次都吃的到最新鮮的產品。

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

View File

@@ -0,0 +1,24 @@
# PRODUCT3
<!-- ![](./850ecd91-c627-4b2c-b0dc-f290f6347874.jfif) -->
<!-- ![](../product2/shot1.jpeg) -->
📣📣台灣好物分享📣📣
依個真係真係勁大推比易敏感既bb試試
妹妹係去台灣之前下巴突然紅曬
好似生濕疹甘 又r損曬
因為真係查極唔同既cream 都依然冇改善 我又唔想佢查藥膏
所以去左台灣藥房搵‼️‼️‼️
佢地推介左依款膏(英國製造)
❤️成份天然
冇任何藥性成份
👉🏻👉🏻👉🏻蜂膠萃取物、甜杏仁油、蜂蠟、維他命E
取自蜜蜂為保護蜂巢所分泌的蜂膠,含有多重來自花粉產生的微量元素與維生素,可適度緩解與修護肌膚不適問題。*不含刺激成分,眼睛與臉部也可塗抹。

View File

@@ -0,0 +1 @@
代碼,類型,貨號,"GTIN, UPC, EAN, or ISBN",名稱,已發佈,是特色商品?,目錄的可見度,簡短內容說明,描述,折扣價開始日期,折扣價結束日期,稅金狀態,稅率類別,有庫存?,庫存,低庫存量,允許無庫存下單嗎?,單獨銷售?,"重量 (公斤)","長 (公分)","寬 (公分)","高 (公分)",允許客戶評論嗎?,購買備註,折扣價,原價,分類,標籤,運送類別,圖片,下載限制,下載點過期天數,上層,組合商品,追加銷售,交叉銷售,外部網址,按鈕文字,位置,Brands
1 代碼 類型 貨號 GTIN, UPC, EAN, or ISBN 名稱 已發佈 是特色商品? 目錄的可見度 簡短內容說明 描述 折扣價開始日期 折扣價結束日期 稅金狀態 稅率類別 有庫存? 庫存 低庫存量 允許無庫存下單嗎? 單獨銷售? 重量 (公斤) 長 (公分) 寬 (公分) 高 (公分) 允許客戶評論嗎? 購買備註 折扣價 原價 分類 標籤 運送類別 圖片 下載限制 下載點過期天數 上層 組合商品 追加銷售 交叉銷售 外部網址 按鈕文字 位置 Brands

View File

@@ -0,0 +1 @@
github: nezhar

View File

@@ -0,0 +1 @@
.env

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018 nezhar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,159 @@
# WPDC - WordPress Docker Compose
Easy WordPress development with Docker and Docker Compose.
With this project you can quickly run the following:
- [WordPress and WP CLI](https://hub.docker.com/_/wordpress/)
- [phpMyAdmin](https://hub.docker.com/r/phpmyadmin/phpmyadmin/)
- [MySQL](https://hub.docker.com/_/mysql/)
Contents:
- [Requirements](#requirements)
- [Configuration](#configuration)
- [Installation](#installation)
- [Usage](#usage)
## Requirements
Make sure you have the latest versions of **Docker** and **Docker Compose** installed on your machine.
Clone this repository or copy the files from this repository into a new folder. In the **docker-compose.yml** file you may change the IP address (in case you run multiple containers) or the database from MySQL to MariaDB.
Make sure to [add your user to the `docker` group](https://docs.docker.com/install/linux/linux-postinstall/#manage-docker-as-a-non-root-user) when using Linux.
## Configuration
Copy the example environment into `.env`
```
cp env.example .env
```
Edit the `.env` file to change the default IP address, MySQL root password and WordPress database name.
## Installation
Open a terminal and `cd` to the folder in which `docker-compose.yml` is saved and run:
```
docker-compose up
```
This creates two new folders next to your `docker-compose.yml` file.
* `wp-data` used to store and restore database dumps
* `wp-app` the location of your WordPress application
The containers are now built and running. You should be able to access the WordPress installation with the configured IP in the browser address. By default it is `http://127.0.0.1`.
For convenience you may add a new entry into your hosts file.
## Usage
### Starting containers
You can start the containers with the `up` command in daemon mode (by adding `-d` as an argument) or by using the `start` command:
```
docker-compose start
```
### Stopping containers
```
docker-compose stop
```
### Removing containers
To stop and remove all the containers use the`down` command:
```
docker-compose down
```
Use `-v` if you need to remove the database volume which is used to persist the database:
```
docker-compose down -v
```
### Project from existing source
Copy the `docker-compose.yml` file into a new directory. In the directory you create two folders:
* `wp-data` here you add the database dump
* `wp-app` here you copy your existing WordPress code
You can now use the `up` command:
```
docker-compose up
```
This will create the containers and populate the database with the given dump. You may set your host entry and change it in the database, or you simply overwrite it in `wp-config.php` by adding:
```
define('WP_HOME','http://wp-app.local');
define('WP_SITEURL','http://wp-app.local');
```
### Creating database dumps
```
./export.sh
```
### Developing a Theme
Configure the volume to load the theme in the container in the `docker-compose.yml`:
```
volumes:
- ./theme-name/trunk/:/var/www/html/wp-content/themes/theme-name
```
### Developing a Plugin
Configure the volume to load the plugin in the container in the `docker-compose.yml`:
```
volumes:
- ./plugin-name/trunk/:/var/www/html/wp-content/plugins/plugin-name
```
### WP CLI
The docker compose configuration also provides a service for using the [WordPress CLI](https://developer.wordpress.org/cli/commands/).
Sample command to install WordPress:
```
docker-compose run --rm wpcli core install --url=http://localhost --title=test --admin_user=admin --admin_email=test@example.com
```
Or to list installed plugins:
```
docker-compose run --rm wpcli plugin list
```
For an easier usage you may consider adding an alias for the CLI:
```
alias wp="docker-compose run --rm wpcli"
```
This way you can use the CLI command above as follows:
```
wp plugin list
```
### phpMyAdmin
You can also visit `http://127.0.0.1:8080` to access phpMyAdmin after starting the containers.
The default username is `root`, and the password is the same as supplied in the `.env` file.

View File

@@ -0,0 +1,69 @@
services:
wp:
image: wordpress:latest # https://hub.docker.com/_/wordpress/
ports:
- ${IP}:${PORT}:80 # change ip if required
volumes:
- ./volumes/config/wp_php.ini:/usr/local/etc/php/conf.d/conf.ini
- ./volumes/wp-app:/var/www/html # Full wordpress project
- ./src/plugin-helloworld/trunk/:/var/www/html/wp-content/plugins/plugin-helloworld # Plugin development
- ./src/theme-helloworld/trunk/:/var/www/html/wp-content/themes/theme-helloworld # Theme development
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: "${DB_NAME}"
WORDPRESS_DB_USER: root
WORDPRESS_DB_PASSWORD: "${DB_ROOT_PASSWORD}"
depends_on:
- db
links:
- db
wpcli:
image: wordpress:cli
volumes:
- ./volumes/config/wp_php.ini:/usr/local/etc/php/conf.d/conf.ini
- ./volumes/wp-app:/var/www/html
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: "${DB_NAME}"
WORDPRESS_DB_USER: root
WORDPRESS_DB_PASSWORD: "${DB_ROOT_PASSWORD}"
depends_on:
- db
- wp
pma:
image: phpmyadmin:latest # https://hub.docker.com/_/phpmyadmin
environment:
# https://docs.phpmyadmin.net/en/latest/setup.html#docker-environment-variables
PMA_HOST: db
PMA_PORT: 3306
MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}"
UPLOAD_LIMIT: 50M
ports:
- ${IP}:8080:80
links:
- db:db
volumes:
- ./volumes/config/pma_php.ini:/usr/local/etc/php/conf.d/conf.ini
- ./volumes/config/pma_config.php:/etc/phpmyadmin/config.user.inc.php
db:
image: mysql:latest # https://hub.docker.com/_/mysql/ - or mariadb https://hub.docker.com/_/mariadb
# platform: linux/x86_64 # Uncomment if your machine is running on arm (ex: Apple Silicon processor)
ports:
- ${IP}:3306:3306 # change ip if required
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci'
]
volumes:
- ./volumes/wp-data:/docker-entrypoint-initdb.d
- db_data:/var/lib/mysql
environment:
MYSQL_DATABASE: "${DB_NAME}"
MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}"
volumes:
db_data:

View File

@@ -0,0 +1,4 @@
IP=127.0.0.1
PORT=80
DB_ROOT_PASSWORD=password
DB_NAME=wordpress

View File

@@ -0,0 +1,14 @@
#!/bin/bash
_os="`uname`"
_now=$(date +"%m_%d_%Y")
_file="wp-data/data_$_now.sql"
# Export dump
EXPORT_COMMAND='exec mysqldump "$MYSQL_DATABASE" -uroot -p"$MYSQL_ROOT_PASSWORD"'
docker-compose exec db sh -c "$EXPORT_COMMAND" > $_file
if [[ $_os == "Darwin"* ]] ; then
sed -i '.bak' 1,1d $_file
else
sed -i 1,1d $_file # Removes the password warning from the file
fi

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -ex
docker compose kill
docker compose down
sleep 1
docker compose up -d
docker compose logs -f
echo "done"

View File

@@ -0,0 +1,88 @@
<?php
/**
* Kiosko functions and definitions
*
* @link https://developer.wordpress.org/themes/basics/theme-functions/
*
* @package Kiosko
* @since Kiosko 1.0
*/
if ( ! function_exists( 'kiosko_support' ) ) :
/**
* Sets up theme defaults and registers support for various WordPress features.
*
* @since Kiosko 1.0
*
* @return void
*/
function kiosko_support() {
// Enqueue editor styles.
add_editor_style( 'style.css' );
// Make theme available for translation.
load_theme_textdomain( 'kiosko' );
}
endif;
add_action( 'after_setup_theme', 'kiosko_support' );
if ( ! function_exists( 'kiosko_styles' ) ) :
/**
* Enqueue styles.
*
* @since Kiosko 1.0
*
* @return void
*/
function kiosko_styles() {
// Register theme stylesheet.
$theme_version = wp_get_theme()->get( 'Version' );
$version_string = is_string( $theme_version ) ? $theme_version : false;
wp_register_style(
'kiosko-style',
get_template_directory_uri() . '/style.css',
array(),
$version_string
);
// Enqueue theme stylesheet.
wp_enqueue_style( 'kiosko-style' );
// Enqueue the additional stylesheet for Twenty Twenty Three.
if ( class_exists( 'WooCommerce' ) && function_exists( 'WC' ) && defined( 'WC_ABSPATH' ) && file_exists( WC_ABSPATH . 'assets/css/twenty-twenty-three.css' ) ) {
wp_enqueue_style( 'woocommerce-twenty-twenty-three', WC()->plugin_url() . '/assets/css/twenty-twenty-three.css' );
wp_dequeue_style( 'woocommerce-general' );
}
}
endif;
add_action( 'wp_enqueue_scripts', 'kiosko_styles' );
if ( ! function_exists( 'kiosko_woocommerce_init' ) ) :
/**
* Initialize WooCommerce compatibility.
*
* @since Kiosko 1.0
* @return void
*/
function kiosko_woocommerce_init() {
if ( ! class_exists( 'WooCommerce' ) || ! defined( 'WC_ABSPATH' ) || ! file_exists( WC_ABSPATH . 'includes/theme-support/class-wc-twenty-twenty-three.php' ) ) {
return;
}
// Load WooCommerce compatibility file if WooCommerce is loaded and the compatibility file exists.
include_once WC_ABSPATH . 'includes/theme-support/class-wc-twenty-twenty-three.php';
}
endif;
add_action( 'after_setup_theme', 'kiosko_woocommerce_init' );

View File

@@ -0,0 +1,19 @@
<?php
/**
* Title: A 404 page
* Slug: kiosko/404
* Categories: text
*/
declare( strict_types = 1 );
?>
<!-- wp:heading {"level":1,"style":{"typography":{"lineHeight":1.1}},"textColor":"contrast","className":"wp-block-heading","fontSize":"large","anchor":"oops-that-page-can-t-be-found"} -->
<h1 class="wp-block-heading has-contrast-color has-text-color has-large-font-size" id="oops-that-page-can-t-be-found" style="line-height:1.1"><?php echo esc_html__( 'Oops! That page can&rsquo;t be found.', 'kiosko' ); ?></h1>
<!-- /wp:heading -->
<!-- wp:paragraph {"textColor":"contrast","className":"has-contrast-color has-text-color"} -->
<p class="has-contrast-color has-text-color has-contrast-color"><?php echo esc_html__( 'It looks like nothing was found at this location. Maybe try a search?', 'kiosko' ); ?></p>
<!-- /wp:paragraph -->
<!-- wp:search {"label":"","showLabel":false,"placeholder":"<?php echo esc_html_x( 'Search...', 'This is a placeholder text in a search field', 'kiosko' ); ?>","buttonText":"Search"} /-->

View File

@@ -0,0 +1,53 @@
<?php
/**
* Title: Comments
* Slug: kiosko/comments
* Categories: text
*/
declare( strict_types = 1 );
?>
<!-- wp:comments {"className":"wp-block-comments-query-loop"} -->
<div class="wp-block-comments wp-block-comments-query-loop">
<!-- wp:comments-title {"level":3} /-->
<!-- wp:comment-template -->
<!-- wp:group {"style":{"spacing":{"margin":{"top":"0","bottom":"var:preset|spacing|50"}}}} -->
<div class="wp-block-group" style="margin-top:0;margin-bottom:var(--wp--preset--spacing--50)">
<!-- wp:group {"layout":{"type":"flex","flexWrap":"nowrap"},"style":{"spacing":{"blockGap":"0.5em"}}} -->
<div class="wp-block-group">
<!-- wp:avatar {"size":40} /-->
<!-- wp:group -->
<div class="wp-block-group">
<!-- wp:comment-author-name /-->
<!-- wp:group {"layout":{"type":"flex"},"style":{"spacing":{"margin":{"top":"0px","bottom":"0px"},"blockGap":"0.875rem"}}} -->
<div class="wp-block-group" style="margin-top:0px;margin-bottom:0px">
<!-- wp:comment-date {"format":"F j, Y \\a\\t g:i a"} /-->
<!-- wp:comment-edit-link /-->
</div>
<!-- /wp:group -->
</div>
<!-- /wp:group -->
</div>
<!-- /wp:group -->
<!-- wp:comment-content /-->
<!-- wp:comment-reply-link /-->
</div>
<!-- /wp:group -->
<!-- /wp:comment-template -->
<!-- wp:comments-pagination {"paginationArrow":"arrow","layout":{"type":"flex","justifyContent":"space-between"}} -->
<!-- wp:comments-pagination-previous /-->
<!-- wp:comments-pagination-next /-->
<!-- /wp:comments-pagination -->
<!-- wp:post-comments-form {"style":{"spacing":{"margin":{"top":"var:preset|spacing|70"}}}} /-->
</div>
<!-- /wp:comments -->

View File

@@ -0,0 +1,108 @@
<?php
/**
* Title: Default footer
* Slug: kiosko/footer
* Categories: footer
* Block Types: core/template-part/footer
*/
declare( strict_types = 1 );
?>
<!-- wp:group {"style":{"elements":{"link":{"color":{"text":"var:preset|color|base"}}},"spacing":{"padding":{"top":"var:preset|spacing|70","bottom":"var:preset|spacing|70"}}},"backgroundColor":"primary","textColor":"base","layout":{"type":"constrained"}} -->
<div class="wp-block-group has-base-color has-primary-background-color has-text-color has-background has-link-color" style="padding-top:var(--wp--preset--spacing--70);padding-bottom:var(--wp--preset--spacing--70)">
<!-- wp:columns {"align":"wide","style":{"spacing":{"padding":{"top":"var:preset|spacing|70","bottom":"var:preset|spacing|70"}}}} -->
<div class="wp-block-columns alignwide" style="padding-top:var(--wp--preset--spacing--70);padding-bottom:var(--wp--preset--spacing--70)">
<!-- wp:column {"width":"40%","fontSize":"small"} -->
<div class="wp-block-column has-small-font-size" style="flex-basis:40%">
<!-- wp:site-title {"level":2,"isLink":false,"style":{"typography":{"textTransform":"uppercase"},"elements":{"link":{"color":{"text":"var:preset|color|base"}}}},"textColor":"base","fontSize":"large"} /-->
<!-- wp:paragraph -->
<p><?php echo esc_html__( 'We are a poster shop bringing you carefully created and unique art prints. We are a curious and creative team based in Tokyo, Japan, and we believe in diversity, value and quality.', 'kiosko' ); ?></p>
<!-- /wp:paragraph -->
<!-- wp:social-links {"iconColor":"base","iconColorValue":"#ffffff","size":"has-small-icon-size","style":{"spacing":{"blockGap":{"top":"var:preset|spacing|40","left":"var:preset|spacing|40"}}},"className":"is-style-logos-only"} -->
<ul class="wp-block-social-links has-small-icon-size has-icon-color is-style-logos-only">
<!-- wp:social-link {"url":"#","service":"wordpress"} /-->
<!-- wp:social-link {"url":"#","service":"tumblr"} /-->
<!-- wp:social-link {"url":"#","service":"instagram"} /-->
<!-- wp:social-link {"url":"#","service":"facebook"} /-->
</ul>
<!-- /wp:social-links -->
</div>
<!-- /wp:column -->
<!-- wp:column {"width":"20%"} -->
<div class="wp-block-column" style="flex-basis:20%">
<!-- wp:heading {"style":{"typography":{"textTransform":"uppercase"}},"fontSize":"large"} -->
<h2 class="wp-block-heading has-link-color has-large-font-size" style="text-transform:uppercase"><?php echo esc_html__( 'Location', 'kiosko' ); ?></h2>
<!-- /wp:heading -->
<!-- wp:group {"style":{"spacing":{"blockGap":"0rem"}},"layout":{"type":"constrained"},"fontSize":"small"} -->
<div class="wp-block-group has-small-font-size">
<!-- wp:paragraph -->
<p><?php echo esc_html__( '12-3 Udagawacho, Shibuya City, Tokyo 123-4567', 'kiosko' ); ?></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><?php echo esc_html__( 'Mon - Fri 10 am - 7 pm', 'kiosko' ); ?></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><?php echo esc_html__( 'Sat - Sun 12 am - 5 pm', 'kiosko' ); ?></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><?php echo esc_html__( '0123 456 7890', 'kiosko' ); ?></p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:group -->
</div>
<!-- /wp:column -->
<!-- wp:column {"width":"20%"} -->
<div class="wp-block-column" style="flex-basis:20%">
<!-- wp:heading {"style":{"typography":{"textTransform":"uppercase"}},"fontSize":"large"} -->
<h2 class="wp-block-heading has-large-font-size" style="text-transform:uppercase"><?php echo esc_html__( 'Shop', 'kiosko' ); ?></h2>
<!-- /wp:heading -->
<!-- wp:navigation {"layout":{"type":"flex","orientation":"vertical"},"style":{"spacing":{"blockGap":"0px"}},"fontSize":"small"} /-->
</div>
<!-- /wp:column -->
<!-- wp:column {"width":"20%"} -->
<div class="wp-block-column" style="flex-basis:20%">
<!-- wp:heading {"style":{"typography":{"textTransform":"uppercase","fontStyle":"normal","fontWeight":"400"}},"fontSize":"large"} -->
<h2 class="wp-block-heading has-large-font-size" style="font-style:normal;font-weight:400;text-transform:uppercase"><?php echo esc_html__( 'Info', 'kiosko' ); ?></h2>
<!-- /wp:heading -->
<!-- wp:navigation {"layout":{"type":"flex","orientation":"vertical"},"style":{"spacing":{"blockGap":"0px"}},"fontSize":"small"} /-->
</div>
<!-- /wp:column -->
</div>
<!-- /wp:columns -->
<!-- wp:group {"align":"wide","style":{"spacing":{"blockGap":"var:preset|spacing|30","margin":{"top":"0px","bottom":"0px"},"padding":{"bottom":"var:preset|spacing|70"}}},"layout":{"type":"flex","justifyContent":"left"}} -->
<div class="wp-block-group alignwide" style="margin-top:0px;margin-bottom:0px;padding-bottom:var(--wp--preset--spacing--70)">
<!-- wp:paragraph {"align":"right","fontSize":"small"} -->
<p class="has-text-align-right has-small-font-size">&#169; <?php echo date("Y"); ?></p>
<!-- /wp:paragraph -->
<!-- wp:site-title {"level":0,"isLink":false,"style":{"typography":{"textTransform":"none"},"elements":{"link":{"color":{"text":"var:preset|color|base"}}}},"textColor":"base","fontSize":"small"} /-->
<!-- wp:paragraph {"align":"right","fontSize":"small"} -->
<p class="has-text-align-right has-small-font-size"><?php
/* Translators: WordPress link. */
$wordpress_link = '<a href="' . esc_url( __( 'https://wordpress.org', 'kiosko' ) ) . '" rel="nofollow">WordPress</a>';
echo sprintf(
esc_html__( 'Designed with %1$s', 'kiosko' ),
$wordpress_link
);
?></p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:group -->
</div>
<!-- /wp:group -->

Some files were not shown because too many files have changed in this diff Show More