Remove deprecated mobile_notworking.del project files and configurations
@@ -1,15 +0,0 @@
|
|||||||
# http://editorconfig.org
|
|
||||||
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
insert_final_newline = false
|
|
||||||
trim_trailing_whitespace = false
|
|
@@ -1,2 +0,0 @@
|
|||||||
VITE_SUPABASE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE
|
|
||||||
VITE_SUPABASE_URL=http://192.168.10.89:8000
|
|
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"projects": {
|
|
||||||
"default": "ionic-react-conference-app"
|
|
||||||
}
|
|
||||||
}
|
|
93
03_source/mobile_notworking.del/.gitignore
vendored
@@ -1,93 +0,0 @@
|
|||||||
**/*log
|
|
||||||
**/*log.*
|
|
||||||
**/*del
|
|
||||||
**/*del.*
|
|
||||||
**/*copy*
|
|
||||||
*copy*
|
|
||||||
**/_backup/*
|
|
||||||
**/*bak
|
|
||||||
**/*bak.*
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
.firebase
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# Typescript v1 declaration files
|
|
||||||
typings/
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
|
||||||
.env
|
|
||||||
|
|
||||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# production
|
|
||||||
/dist
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
.env.local
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
.stencil/
|
|
@@ -1 +0,0 @@
|
|||||||
/* /index.html 200
|
|
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"siteId": "86675615-6271-4145-8ffe-9c78dc4d34a3"
|
|
||||||
}
|
|
@@ -1 +0,0 @@
|
|||||||
**/_backup
|
|
@@ -1,34 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
arrowParens: 'avoid',
|
|
||||||
bracketSpacing: true,
|
|
||||||
htmlWhitespaceSensitivity: 'strict',
|
|
||||||
insertPragma: false,
|
|
||||||
jsxBracketSameLine: false,
|
|
||||||
jsxSingleQuote: false,
|
|
||||||
printWidth: 120,
|
|
||||||
proseWrap: 'preserve',
|
|
||||||
quoteProps: 'as-needed',
|
|
||||||
requirePragma: false,
|
|
||||||
semi: true,
|
|
||||||
singleQuote: true,
|
|
||||||
tabWidth: 2,
|
|
||||||
trailingComma: 'all',
|
|
||||||
useTabs: false,
|
|
||||||
endOfLine: 'lf',
|
|
||||||
//
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ['*.yml'],
|
|
||||||
options: {
|
|
||||||
singleQuote: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['*.md', '*.mdx'],
|
|
||||||
options: {
|
|
||||||
useTabs: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
plugins: [require.resolve('prettier-plugin-organize-imports')],
|
|
||||||
};
|
|
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"javascript.updateImportsOnFileMove.enabled": "always",
|
|
||||||
"typescript.updateImportsOnFileMove.enabled": "always",
|
|
||||||
"git.ignoreLimitWarning": true
|
|
||||||
}
|
|
@@ -1 +0,0 @@
|
|||||||
FROM node:20-bullseye-slim
|
|
@@ -1,23 +0,0 @@
|
|||||||
Copyright 2015-present Drifty Co.
|
|
||||||
http://drifty.com/
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
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.
|
|
@@ -1,59 +0,0 @@
|
|||||||
# Ionic React Conference Application
|
|
||||||
|
|
||||||
This application is purely a kitchen-sink demo of the Ionic Framework and React.
|
|
||||||
|
|
||||||
**There is not an actual Ionic Conference at this time.** This project is just to show off Ionic components in a real-world application.
|
|
||||||
|
|
||||||
## Angular and Vue versions
|
|
||||||
|
|
||||||
We've built versions of this Conference app in Angular and Vue for developers that would prefer to use one of those framework options:
|
|
||||||
|
|
||||||
https://github.com/ionic-team/ionic-conference-app
|
|
||||||
|
|
||||||
https://github.com/ionic-team/ionic-vue-conference-app
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
- [Getting Started](#getting-started)
|
|
||||||
- [App Preview](#app-preview)
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
- [Download the installer](https://nodejs.org/) for Node LTS.
|
|
||||||
- Install the ionic CLI globally: `npm install -g ionic`
|
|
||||||
- Clone this repository: `git clone https://github.com/ionic-team/ionic-react-conference-app.git`.
|
|
||||||
- Run `npm install` from the project root.
|
|
||||||
- Run `ionic serve` in a terminal from the project root.
|
|
||||||
- Profit. :tada:
|
|
||||||
|
|
||||||
## App Preview
|
|
||||||
|
|
||||||
### [Menu](https://github.com/ionic-team/ionic-react-conference-app/blob/main/src/components/Menu.tsx)
|
|
||||||
|
|
||||||
| Material Design | iOS |
|
|
||||||
| -------------------------------------------------------- | ------------------------------------------------ |
|
|
||||||
|  |  |
|
|
||||||
|
|
||||||
### [Schedule Page](https://github.com/ionic-team/ionic-react-conference-app/blob/main/src/pages/SchedulePage.tsx)
|
|
||||||
|
|
||||||
| Material Design | iOS |
|
|
||||||
| ---------------------------------------------------------------- | -------------------------------------------------------- |
|
|
||||||
|  |  |
|
|
||||||
|
|
||||||
### [Speakers Page](https://github.com/ionic-team/ionic-react-conference-app/blob/main/src/pages/SpeakerList.tsx)
|
|
||||||
|
|
||||||
| Material Design | iOS |
|
|
||||||
| ---------------------------------------------------------------- | -------------------------------------------------------- |
|
|
||||||
|  |  |
|
|
||||||
|
|
||||||
### [Speaker Detail Page](https://github.com/ionic-team/ionic-react-conference-app/blob/main/src/pages/SpeakerDetail.tsx)
|
|
||||||
|
|
||||||
| Material Design | iOS |
|
|
||||||
| ---------------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
||||||
|  |  |
|
|
||||||
|
|
||||||
### [About Page](https://github.com/ionic-team/ionic-react-conference-app/blob/main/src/pages/About.tsx)
|
|
||||||
|
|
||||||
| Material Design | iOS |
|
|
||||||
| ---------------------------------------------------------- | -------------------------------------------------- |
|
|
||||||
|  |  |
|
|
@@ -1,14 +0,0 @@
|
|||||||
// Only used for jest
|
|
||||||
module.exports = {
|
|
||||||
presets: [
|
|
||||||
[
|
|
||||||
'@babel/preset-env',
|
|
||||||
{
|
|
||||||
targets: {
|
|
||||||
node: 'current',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@babel/preset-typescript',
|
|
||||||
],
|
|
||||||
};
|
|
@@ -1,63 +0,0 @@
|
|||||||
const execSync = require('child_process').execSync;
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
// const chalk = require('chalk');
|
|
||||||
|
|
||||||
// Function to get all subdirectories of a given directory
|
|
||||||
function getDirectories(srcPath, excludeDirs) {
|
|
||||||
return fs
|
|
||||||
.readdirSync(srcPath)
|
|
||||||
.filter(file => !excludeDirs.includes(file) && fs.lstatSync(path.join(srcPath, file)).isDirectory())
|
|
||||||
.map(name => path.join(srcPath, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current working directory
|
|
||||||
const cwd = process.cwd();
|
|
||||||
const backup_dir = path.join(cwd, '_backup');
|
|
||||||
|
|
||||||
// Path to app-head directory
|
|
||||||
const appHeadDir = path.join(cwd, 'src');
|
|
||||||
|
|
||||||
// Check if app-head exists
|
|
||||||
if (!fs.existsSync(appHeadDir)) {
|
|
||||||
console.error(`Error: ${appHeadDir} does not exist.`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute reset.bat scripts
|
|
||||||
try {
|
|
||||||
// execSync(`cmd /c "cd ${appHeadDir} && scripts\\reset.bat"`, { stdio: 'inherit' });
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`Error executing reset.bat script: ${err.message}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define excluded directories
|
|
||||||
const excludedDirs = ['.next', 'node_modules', '.git', 'volumes', '_mp4'];
|
|
||||||
|
|
||||||
// Copy app-head directory and its contents to a new directory with an increasing number suffix
|
|
||||||
let maxNum = 0;
|
|
||||||
const directories = getDirectories(backup_dir, excludedDirs);
|
|
||||||
for (const dir of directories) {
|
|
||||||
const match = dir.match(/^.+draft-(\d+).*$/);
|
|
||||||
if (match) {
|
|
||||||
const num = parseInt(match[1], 10);
|
|
||||||
if (num > maxNum) {
|
|
||||||
maxNum = num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var zerofilled = ('0000' + (maxNum + 1)).slice(-4);
|
|
||||||
const targetDir = path.join(backup_dir, `draft-${zerofilled}`);
|
|
||||||
fs.mkdirSync(targetDir);
|
|
||||||
|
|
||||||
// Copy app-head directory and its contents to targetDir, excluding specified directories
|
|
||||||
fs.cpSync(appHeadDir, targetDir, {
|
|
||||||
filter: src => !excludedDirs.includes(path.basename(src)),
|
|
||||||
recursive: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(' DONE DONE DONE \n' + ' DONE DONE DONE \n' + ' DONE DONE DONE \n');
|
|
||||||
|
|
||||||
console.log(`Successfully backup to ${targetDir}.`);
|
|
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"appId": "io.ionic.starter",
|
|
||||||
"appName": "ionic-react-conference-app",
|
|
||||||
"bundledWebRuntime": false,
|
|
||||||
"npmClient": "npm",
|
|
||||||
"webDir": "dist",
|
|
||||||
"cordova": {}
|
|
||||||
}
|
|
@@ -1,7 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
docker compose up -d
|
|
||||||
|
|
||||||
docker compose exec -it mobile bash
|
|
@@ -1,11 +0,0 @@
|
|||||||
name: 009_poc_chatroom_implement
|
|
||||||
|
|
||||||
services:
|
|
||||||
mobile:
|
|
||||||
build: .
|
|
||||||
ports:
|
|
||||||
- 3000:3000
|
|
||||||
command: sleep infinity
|
|
||||||
volumes:
|
|
||||||
- .:/app
|
|
||||||
working_dir: /app
|
|
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"hosting": {
|
|
||||||
"public": "build",
|
|
||||||
"rewrites": [
|
|
||||||
{
|
|
||||||
"source": "**",
|
|
||||||
"destination": "/index.html"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"source": "**/static/**",
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"key": "Cache-Control",
|
|
||||||
"value": "max-age=31536000"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "**/*.@(jpg|jpeg|gif|png|svg)",
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"key": "Cache-Control",
|
|
||||||
"value": "max-age=31536000"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,32 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<title>Ionic Conference App</title>
|
|
||||||
<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="/assets/icon/favicon.png" />
|
|
||||||
|
|
||||||
<!-- add to homescreen for ios -->
|
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
||||||
<meta name="apple-mobile-web-app-title" content="Ionic Conference 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>
|
|
||||||
<script
|
|
||||||
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAmAwkv8-x2I--ne-NtA3C_4E_-xLCsFJs"
|
|
||||||
async
|
|
||||||
defer
|
|
||||||
></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "ionic-react-conference-app",
|
|
||||||
"integrations": {
|
|
||||||
"capacitor": {}
|
|
||||||
},
|
|
||||||
"type": "react"
|
|
||||||
}
|
|
13057
03_source/mobile_notworking.del/package-lock.json
generated
@@ -1,76 +0,0 @@
|
|||||||
{
|
|
||||||
"browserslist": [
|
|
||||||
">0.2%",
|
|
||||||
"not dead",
|
|
||||||
"not ie <= 11",
|
|
||||||
"not op_mini all"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"@capacitor/camera": "^4.1.5",
|
|
||||||
"@capacitor/core": "^4.6.1",
|
|
||||||
"@capacitor/preferences": "^4.0.2",
|
|
||||||
"@ionic/pwa-elements": "^3.3.0",
|
|
||||||
"@ionic/react": "8.2.6",
|
|
||||||
"@ionic/react-router": "8.2.6",
|
|
||||||
"@supabase/auth-helpers-nextjs": "^0.9.0",
|
|
||||||
"@supabase/auth-helpers-react": "^0.3.1",
|
|
||||||
"@supabase/ssr": "latest",
|
|
||||||
"@supabase/supabase-js": "2.44.0",
|
|
||||||
"date-fns": "^2.25.0",
|
|
||||||
"dayjs": "^1.11.13",
|
|
||||||
"i18next": "^22.4.14",
|
|
||||||
"i18next-browser-languagedetector": "^7.0.1",
|
|
||||||
"ionicons": "^7.1.2",
|
|
||||||
"lodash": "^4.17.21",
|
|
||||||
"prettier-plugin-organize-imports": "^4.0.0",
|
|
||||||
"pullstate": "^1.22.1",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"react-i18next": "^12.2.0",
|
|
||||||
"react-markdown": "^9.0.1",
|
|
||||||
"react-router": "^5.3.4",
|
|
||||||
"react-router-dom": "^5.3.4",
|
|
||||||
"react-use": "^17.2.4",
|
|
||||||
"reselect": "^4.0.0",
|
|
||||||
"sass": "^1.59.3",
|
|
||||||
"swiper": "^9.1.1",
|
|
||||||
"typescript": "5.3.3"
|
|
||||||
},
|
|
||||||
"description": "An Ionic project",
|
|
||||||
"devDependencies": {
|
|
||||||
"@capacitor/cli": "^4.6.1",
|
|
||||||
"@testing-library/react": "^9.3.1",
|
|
||||||
"@types/googlemaps": "^3.38.0",
|
|
||||||
"@types/jest": "24.0.18",
|
|
||||||
"@types/node": "20.11.5",
|
|
||||||
"@types/react": "18.2.48",
|
|
||||||
"@types/react-dom": "18.2.18",
|
|
||||||
"@types/react-router": "^5.1.20",
|
|
||||||
"@types/react-router-dom": "^5.3.3",
|
|
||||||
"@vitejs/plugin-legacy": "^5.4.1",
|
|
||||||
"@vitejs/plugin-react": "^4.3.1",
|
|
||||||
"lint-staged": "^13.2.0",
|
|
||||||
"prettier": "^2.8.6",
|
|
||||||
"vite": "^5.4.0"
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"extends": "react-app"
|
|
||||||
},
|
|
||||||
"lint-staged": {
|
|
||||||
"src/**/*.{js,jsx,ts,tsx,json,md}": [
|
|
||||||
"prettier --write",
|
|
||||||
"git add"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"name": "ionic-react-conference-app",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"build": "tsc && vite build",
|
|
||||||
"dev": "vite --host 0.0.0.0 --cors --force --strictPort",
|
|
||||||
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,md}\"",
|
|
||||||
"precommit": "npm run format && lint-staged",
|
|
||||||
"preview": "vite preview",
|
|
||||||
"start": "npm run dev"
|
|
||||||
},
|
|
||||||
"version": "0.0.1"
|
|
||||||
}
|
|
@@ -1,55 +0,0 @@
|
|||||||
歡迎使用香港單身派對!本服務協議及社區規範(以下簡稱“協議”)旨在規範您使用香港單身派對(以下簡稱“平台”)的行為,確保平台的健康運營和用戶的良好體驗。請在使用平台前仔細閱讀並遵守本協議。
|
|
||||||
|
|
||||||
1. 服務內容
|
|
||||||
香港單身派對提供以下服務:
|
|
||||||
|
|
||||||
(a) 附近的人交友:使用地理定位功能幫助您認識附近的用戶。
|
|
||||||
|
|
||||||
(b) 線上活動參與:平台不定期舉辦各類線上活動,包括但不限於遊戲配對、主題討論和社交聚會。
|
|
||||||
|
|
||||||
2. 用戶權利與義務
|
|
||||||
|
|
||||||
(a) 用戶有權使用平台提供的服務,並參與平台組織的活動。
|
|
||||||
|
|
||||||
(b) 用戶應確保其在平台上發布的個人照片及資料真實且屬於本人,不得使用他人的照片或虛假信息。
|
|
||||||
|
|
||||||
(c) 用戶應遵守相關法律法規,不得利用平台進行任何非法活動。
|
|
||||||
|
|
||||||
(d) 用戶應尊重其他用戶的隱私權和個人信息安全,不得收集、使用或披露其他用戶的信息。
|
|
||||||
|
|
||||||
(e) 用戶應維護良好的社區環境,不得發布任何違背公序良俗、含有歧視、騷擾、暴力或色情等內容的信息。
|
|
||||||
|
|
||||||
(f) 用戶不得抄襲、模仿或以其他方式侵犯平台的知識產權,包括但不限於應用程序的界面設計、功能實現和內容創作。
|
|
||||||
|
|
||||||
(g) 本平台僅限於 18 歲以上的用戶使用。未滿 18 歲的用戶不得使用平台,一旦發現,其帳號將被終止。
|
|
||||||
|
|
||||||
3. 社區規範
|
|
||||||
(a) 禁止發布任何形式的廣告、垃圾信息或欺詐信息。
|
|
||||||
(b) 用戶應保持友好互動,避免使用侮辱性、攻擊性的語言。
|
|
||||||
(c) 用戶不得發布、傳播任何侵犯他人知識產權或其他合法權益的信息。
|
|
||||||
(d) 用戶應遵守平台的操作規範,不得進行任何破壞平台正常運行的行為。
|
|
||||||
|
|
||||||
4. 隱私保護
|
|
||||||
平台將採取合理措施保護用戶的個人信息,並遵守相關隱私保護法律法規。用戶同意平台在提供服務過程中收集和使用其個人信息。
|
|
||||||
|
|
||||||
5. 違規處理
|
|
||||||
用戶違反本協議時,平台將視情節輕重給予警告或終止其帳號的使用權。嚴重違反者將被永久禁止使用平台服務。
|
|
||||||
|
|
||||||
6. 協議的變更與終止
|
|
||||||
|
|
||||||
(a) 平台有權根據需要對本協議進行修改,並在平台上公佈最新版本。
|
|
||||||
|
|
||||||
(b) 如用戶違反本協議,平台有權限制或終止其使用平台的權利。
|
|
||||||
|
|
||||||
7. 爭議解決
|
|
||||||
因本協議引起的任何爭議,雙方應首先嘗試友好協商解決;協商不成時,任何一方可直接提出訴訟。
|
|
||||||
|
|
||||||
8. 其他
|
|
||||||
|
|
||||||
(a) 本平台保留對所有情況的最終決定權和解釋權。
|
|
||||||
|
|
||||||
(b) 本協議的附件是協議不可分割的一部分,與協議具有同等法律效力。
|
|
||||||
|
|
||||||
用戶在使用平台服務時,即表示已閱讀、理解並同意遵守本協議。
|
|
||||||
|
|
||||||
~ 完 ~
|
|
@@ -1,53 +0,0 @@
|
|||||||
歡迎使用香港單身派對!本服務協議及社區規範(以下簡稱“協議”)旨在規範您使用香港單身派對(以下簡稱“平台”)的行為,確保平台的健康運營和用戶的良好體驗。請在使用平台前仔細閱讀並遵守本協議。
|
|
||||||
|
|
||||||
1. 服務內容
|
|
||||||
香港單身派對提供以下服務:
|
|
||||||
|
|
||||||
(a) 附近的人交友:使用地理定位功能幫助您認識附近的用戶。
|
|
||||||
|
|
||||||
(b) 線上活動參與:平台不定期舉辦各類線上活動,包括但不限於遊戲配對、主題討論和社交聚會。
|
|
||||||
|
|
||||||
2. 用戶權利與義務
|
|
||||||
|
|
||||||
(a) 用戶有權使用平台提供的服務,並參與平台組織的活動。
|
|
||||||
|
|
||||||
(b) 用戶應確保其在平台上發布的個人照片及資料真實且屬於本人,不得使用他人的照片或虛假信息。
|
|
||||||
|
|
||||||
(c) 用戶應遵守相關法律法規,不得利用平台進行任何非法活動。
|
|
||||||
|
|
||||||
(d) 用戶應尊重其他用戶的隱私權和個人信息安全,不得收集、使用或披露其他用戶的信息。
|
|
||||||
|
|
||||||
(e) 用戶應維護良好的社區環境,不得發布任何違背公序良俗、含有歧視、騷擾、暴力或色情等內容的信息。
|
|
||||||
|
|
||||||
(f) 用戶不得抄襲、模仿或以其他方式侵犯平台的知識產權,包括但不限於應用程序的界面設計、功能實現和內容創作。
|
|
||||||
|
|
||||||
(g) 本平台僅限於 18 歲以上的用戶使用。未滿 18 歲的用戶不得使用平台,一旦發現,其帳號將被終止。
|
|
||||||
|
|
||||||
3. 社區規範
|
|
||||||
(a) 禁止發布任何形式的廣告、垃圾信息或欺詐信息。
|
|
||||||
(b) 用戶應保持友好互動,避免使用侮辱性、攻擊性的語言。
|
|
||||||
(c) 用戶不得發布、傳播任何侵犯他人知識產權或其他合法權益的信息。
|
|
||||||
(d) 用戶應遵守平台的操作規範,不得進行任何破壞平台正常運行的行為。
|
|
||||||
|
|
||||||
4. 隱私保護
|
|
||||||
平台將採取合理措施保護用戶的個人信息,並遵守相關隱私保護法律法規。用戶同意平台在提供服務過程中收集和使用其個人信息。
|
|
||||||
|
|
||||||
5. 違規處理
|
|
||||||
用戶違反本協議時,平台將視情節輕重給予警告或終止其帳號的使用權。嚴重違反者將被永久禁止使用平台服務。
|
|
||||||
|
|
||||||
6. 協議的變更與終止
|
|
||||||
|
|
||||||
(a) 平台有權根據需要對本協議進行修改,並在平台上公佈最新版本。
|
|
||||||
|
|
||||||
(b) 如用戶違反本協議,平台有權限制或終止其使用平台的權利。
|
|
||||||
|
|
||||||
7. 爭議解決
|
|
||||||
因本協議引起的任何爭議,雙方應首先嘗試友好協商解決;協商不成時,任何一方可直接提出訴訟。
|
|
||||||
|
|
||||||
8. 其他
|
|
||||||
|
|
||||||
(a) 本平台保留對所有情況的最終決定權和解釋權。
|
|
||||||
|
|
||||||
(b) 本協議的附件是協議不可分割的一部分,與協議具有同等法律效力。
|
|
||||||
|
|
||||||
用戶在使用平台服務時,即表示已閱讀、理解並同意遵守本協議。
|
|
@@ -1,45 +0,0 @@
|
|||||||
## Privacy Policy
|
|
||||||
|
|
||||||
Welcome to Hong Kong Single Party. We understand the importance of privacy to you and are committed to taking all reasonable measures to protect your personal information. This Privacy Policy aims to explain how we collect, use, store, and protect your personal information, as well as the rights you have regarding your information.
|
|
||||||
|
|
||||||
1. Collection of Personal Information
|
|
||||||
To provide the best social experience, we may collect the following types of personal information when you use [Friend Circle]:
|
|
||||||
|
|
||||||
- Basic Information: Such as your name, gender, date of birth, phone number, and email address.
|
|
||||||
- Location Information: When you enable location services, we may collect information about the location of your device to recommend nearby activities and friends.
|
|
||||||
- Usage Information: Including the time, frequency, preference settings, and interaction data of your use of [Friend Circle].
|
|
||||||
- Activity Participation Information: When you participate in our offline activities, we may collect information about your participation and feedback.
|
|
||||||
|
|
||||||
2. Use of Personal Information
|
|
||||||
We use your personal information mainly for the following purposes:
|
|
||||||
|
|
||||||
- Service Provision and Improvement: To ensure that [Friend Circle] functions properly and to continuously optimize and improve it.
|
|
||||||
- Personalized Experience: To provide you with a more personalized service and recommendations based on your preferences and usage habits.
|
|
||||||
- Activity Organization: To organize and arrange offline activities that interest you, including game matching and social gatherings.
|
|
||||||
- Communication: To send you important notifications, such as activity updates, new features, or customer service-related information.
|
|
||||||
- Security: To protect the security of your account and to prevent and investigate potential security issues.
|
|
||||||
|
|
||||||
3. Storage and Protection of Personal Information
|
|
||||||
We will take appropriate technical and organizational measures to protect your personal information against unauthorized access, disclosure, alteration, or destruction. We will store your personal information on secure servers and restrict employee access to this information.
|
|
||||||
|
|
||||||
4. Sharing and Transfer of Personal Information
|
|
||||||
We will not sell or provide your personal information to third parties unless:
|
|
||||||
|
|
||||||
- We have your explicit consent.
|
|
||||||
- It is necessary to share information with trusted third-party partners to provide a service or feature you have requested.
|
|
||||||
- Required by law or to comply with legal processes.
|
|
||||||
|
|
||||||
5. Your Rights
|
|
||||||
You have the following rights regarding your personal information:
|
|
||||||
|
|
||||||
- Right to Access: You have the right to access the personal information we hold about you.
|
|
||||||
- Right to Rectification: You have the right to request corrections if you find any inaccuracies in the information we hold.
|
|
||||||
- Right to Erasure: Under certain circumstances, you have the right to request the deletion of your personal information.
|
|
||||||
|
|
||||||
6. Policy Updates
|
|
||||||
We may update this Privacy Policy from time to time. All updates will be published within the [Friend Circle] application and will be reflected in the new effective date of the updated Privacy Policy.
|
|
||||||
|
|
||||||
7. Contact Us
|
|
||||||
If you have any questions or concerns about this Privacy Policy, or if you wish to exercise any of the above rights, please contact us using “Contact us”of Hong Kong Single party. Our team will respond as soon as possible. Please wait.
|
|
||||||
|
|
||||||
Hong Kong Single Party. We will continue to strive to provide you with a safer and more reliable social experience.
|
|
@@ -1,45 +0,0 @@
|
|||||||
## Privacy Policy
|
|
||||||
|
|
||||||
Welcome to Hong Kong Single Party. We understand the importance of privacy to you and are committed to taking all reasonable measures to protect your personal information. This Privacy Policy aims to explain how we collect, use, store, and protect your personal information, as well as the rights you have regarding your information.
|
|
||||||
|
|
||||||
1. Collection of Personal Information
|
|
||||||
To provide the best social experience, we may collect the following types of personal information when you use [Friend Circle]:
|
|
||||||
|
|
||||||
- Basic Information: Such as your name, gender, date of birth, phone number, and email address.
|
|
||||||
- Location Information: When you enable location services, we may collect information about the location of your device to recommend nearby activities and friends.
|
|
||||||
- Usage Information: Including the time, frequency, preference settings, and interaction data of your use of [Friend Circle].
|
|
||||||
- Activity Participation Information: When you participate in our offline activities, we may collect information about your participation and feedback.
|
|
||||||
|
|
||||||
2. Use of Personal Information
|
|
||||||
We use your personal information mainly for the following purposes:
|
|
||||||
|
|
||||||
- Service Provision and Improvement: To ensure that [Friend Circle] functions properly and to continuously optimize and improve it.
|
|
||||||
- Personalized Experience: To provide you with a more personalized service and recommendations based on your preferences and usage habits.
|
|
||||||
- Activity Organization: To organize and arrange offline activities that interest you, including game matching and social gatherings.
|
|
||||||
- Communication: To send you important notifications, such as activity updates, new features, or customer service-related information.
|
|
||||||
- Security: To protect the security of your account and to prevent and investigate potential security issues.
|
|
||||||
|
|
||||||
3. Storage and Protection of Personal Information
|
|
||||||
We will take appropriate technical and organizational measures to protect your personal information against unauthorized access, disclosure, alteration, or destruction. We will store your personal information on secure servers and restrict employee access to this information.
|
|
||||||
|
|
||||||
4. Sharing and Transfer of Personal Information
|
|
||||||
We will not sell or provide your personal information to third parties unless:
|
|
||||||
|
|
||||||
- We have your explicit consent.
|
|
||||||
- It is necessary to share information with trusted third-party partners to provide a service or feature you have requested.
|
|
||||||
- Required by law or to comply with legal processes.
|
|
||||||
|
|
||||||
5. Your Rights
|
|
||||||
You have the following rights regarding your personal information:
|
|
||||||
|
|
||||||
- Right to Access: You have the right to access the personal information we hold about you.
|
|
||||||
- Right to Rectification: You have the right to request corrections if you find any inaccuracies in the information we hold.
|
|
||||||
- Right to Erasure: Under certain circumstances, you have the right to request the deletion of your personal information.
|
|
||||||
|
|
||||||
6. Policy Updates
|
|
||||||
We may update this Privacy Policy from time to time. All updates will be published within the [Friend Circle] application and will be reflected in the new effective date of the updated Privacy Policy.
|
|
||||||
|
|
||||||
7. Contact Us
|
|
||||||
If you have any questions or concerns about this Privacy Policy, or if you wish to exercise any of the above rights, please contact us using “Contact us”of Hong Kong Single party. Our team will respond as soon as possible. Please wait.
|
|
||||||
|
|
||||||
Hong Kong Single Party. We will continue to strive to provide you with a safer and more reliable social experience.
|
|
@@ -1,467 +0,0 @@
|
|||||||
{
|
|
||||||
"schedule": [
|
|
||||||
{
|
|
||||||
"date": "2047-05-17",
|
|
||||||
"groups": [
|
|
||||||
{
|
|
||||||
"time": "8:00 am",
|
|
||||||
"sessions": [
|
|
||||||
{
|
|
||||||
"name": "Breakfast",
|
|
||||||
"timeStart": "8:00 am",
|
|
||||||
"timeEnd": "9:00 am",
|
|
||||||
"location": "Dining Hall",
|
|
||||||
"tracks": ["Food"],
|
|
||||||
"id": "1"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"time": "9:15 am",
|
|
||||||
"sessions": [
|
|
||||||
{
|
|
||||||
"name": "Getting Started with Ionic",
|
|
||||||
"location": "Hall 2",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Ted Turtle"],
|
|
||||||
"timeStart": "9:30 am",
|
|
||||||
"timeEnd": "9:45 am",
|
|
||||||
"tracks": ["Ionic"],
|
|
||||||
"id": "2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ionic Tooling",
|
|
||||||
"location": "Executive Ballroom",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Rachel Rabbit"],
|
|
||||||
"timeStart": "9:45 am",
|
|
||||||
"timeEnd": "10:00 am",
|
|
||||||
"tracks": ["Tooling"],
|
|
||||||
"id": "3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "University of Ionic",
|
|
||||||
"location": "Hall 3",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Ellie Elephant"],
|
|
||||||
"timeStart": "9:15 am",
|
|
||||||
"timeEnd": "9:30 am",
|
|
||||||
"tracks": ["Ionic"],
|
|
||||||
"id": "4"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"time": "10:00 am",
|
|
||||||
"sessions": [
|
|
||||||
{
|
|
||||||
"name": "Migrating to Ionic",
|
|
||||||
"location": "Hall 1",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Eva Eagle", "Lionel Lion"],
|
|
||||||
"timeStart": "10:00 am",
|
|
||||||
"timeEnd": "10:15 am",
|
|
||||||
"tracks": ["Ionic"],
|
|
||||||
"id": "5"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "What's New in Angular",
|
|
||||||
"location": "Hall 3",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Rachel Rabbit"],
|
|
||||||
"timeStart": "10:15 am",
|
|
||||||
"timeEnd": "10:30 am",
|
|
||||||
"tracks": ["Angular"],
|
|
||||||
"id": "6"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "The Evolution of Ionicons",
|
|
||||||
"location": "Hall 2",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Isabella Iguana", "Eva Eagle"],
|
|
||||||
"timeStart": "10:15 am",
|
|
||||||
"timeEnd": "10:30 am",
|
|
||||||
"tracks": ["Design"],
|
|
||||||
"id": "7"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ionic Pro",
|
|
||||||
"location": "Grand Ballroom A",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Charlie Cheetah"],
|
|
||||||
"timeStart": "10:45 am",
|
|
||||||
"timeEnd": "11:00 am",
|
|
||||||
"tracks": ["Services"],
|
|
||||||
"id": "8"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"time": "11:00 am",
|
|
||||||
"sessions": [
|
|
||||||
{
|
|
||||||
"name": "Ionic Workshop",
|
|
||||||
"location": "Hall 1",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Karl Kitten", "Lionel Lion"],
|
|
||||||
"timeStart": "11:00 am",
|
|
||||||
"timeEnd": "11:45 am",
|
|
||||||
"tracks": ["Workshop"],
|
|
||||||
"id": "9"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Community Interaction",
|
|
||||||
"location": "Hall 3",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Lionel Lion", "Gino Giraffe"],
|
|
||||||
"timeStart": "11:30 am",
|
|
||||||
"timeEnd": "11:50 am",
|
|
||||||
"tracks": ["Communication"],
|
|
||||||
"id": "10"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Navigation in Ionic",
|
|
||||||
"location": "Grand Ballroom A",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Rachel Rabbit", "Eva Eagle"],
|
|
||||||
"timeStart": "11:30 am",
|
|
||||||
"timeEnd": "12:00 pm",
|
|
||||||
"tracks": ["Navigation"],
|
|
||||||
"id": "11"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"time": "12:00 pm",
|
|
||||||
"sessions": [
|
|
||||||
{
|
|
||||||
"name": "Lunch",
|
|
||||||
"location": "Dining Hall",
|
|
||||||
"description": "Come grab lunch with all the Ionic fanatics and talk all things Ionic",
|
|
||||||
"timeStart": "12:00 pm",
|
|
||||||
"timeEnd": "1:00 pm",
|
|
||||||
"tracks": ["Food"],
|
|
||||||
"id": "12"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"time": "1:00 pm",
|
|
||||||
"sessions": [
|
|
||||||
{
|
|
||||||
"name": "Ionic in the Enterprise",
|
|
||||||
"location": "Hall 1",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Paul Puppy"],
|
|
||||||
"timeStart": "1:00 pm",
|
|
||||||
"timeEnd": "1:15 pm",
|
|
||||||
"tracks": ["Communication"],
|
|
||||||
"id": "13"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ionic Worldwide",
|
|
||||||
"location": "Hall 1",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Gino Giraffe"],
|
|
||||||
"timeStart": "1:15 pm",
|
|
||||||
"timeEnd": "1:30 pm",
|
|
||||||
"tracks": ["Communication"],
|
|
||||||
"id": "14"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "The Ionic Package",
|
|
||||||
"location": "Grand Ballroom B",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Molly Mouse", "Burt Bear"],
|
|
||||||
"timeStart": "1:30 pm",
|
|
||||||
"timeEnd": "2:00 pm",
|
|
||||||
"tracks": ["Services"],
|
|
||||||
"id": "15"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"time": "2:00 pm",
|
|
||||||
"sessions": [
|
|
||||||
{
|
|
||||||
"name": "Push Notifications in Ionic",
|
|
||||||
"location": "Hall 2",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Burt Bear", "Charlie Cheetah"],
|
|
||||||
"timeStart": "2:00 pm",
|
|
||||||
"timeEnd": "2:30 pm",
|
|
||||||
"tracks": ["Services"],
|
|
||||||
"id": "16"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ionic Documentation",
|
|
||||||
"location": "Grand Ballroom B",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Donald Duck"],
|
|
||||||
"timeStart": "2:30 pm",
|
|
||||||
"timeEnd": "2:45 pm",
|
|
||||||
"tracks": ["Documentation"],
|
|
||||||
"id": "17"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "UX in Ionic",
|
|
||||||
"location": "Hall 3",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Isabella Iguana", "Ellie Elephant"],
|
|
||||||
"timeStart": "2:45 pm",
|
|
||||||
"timeEnd": "3:00 pm",
|
|
||||||
"tracks": ["Design"],
|
|
||||||
"id": "18"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"time": "3:00",
|
|
||||||
"sessions": [
|
|
||||||
{
|
|
||||||
"name": "Angular Directives in Ionic",
|
|
||||||
"location": "Hall 1",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Ted Turtle"],
|
|
||||||
"timeStart": "3:00 pm",
|
|
||||||
"timeEnd": "3:30 pm",
|
|
||||||
"tracks": ["Angular"],
|
|
||||||
"id": "19"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Mobile States",
|
|
||||||
"location": "Hall 2",
|
|
||||||
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
|
|
||||||
"speakerNames": ["Rachel Rabbit"],
|
|
||||||
"timeStart": "3:30 pm",
|
|
||||||
"timeEnd": "3:45 pm",
|
|
||||||
"tracks": ["Navigation"],
|
|
||||||
"id": "20"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
"speakers": [
|
|
||||||
{
|
|
||||||
"name": "Burt Bear",
|
|
||||||
"profilePic": "/assets/img/speakers/bear.jpg",
|
|
||||||
"instagram": "ionicframework",
|
|
||||||
"twitter": "ionicframework",
|
|
||||||
"about": "Burt is a Bear. Burt's interests include poetry, dashing space heroes, and lions.",
|
|
||||||
"title": "Software Engineer",
|
|
||||||
"location": "Everywhere",
|
|
||||||
"email": "burt@example.com",
|
|
||||||
"phone": "+1-541-754-3010",
|
|
||||||
"id": "1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Charlie Cheetah",
|
|
||||||
"profilePic": "/assets/img/speakers/cheetah.jpg",
|
|
||||||
"instagram": "ionicframework",
|
|
||||||
"twitter": "ionicframework",
|
|
||||||
"about": "Charlie is a Cheetah. Charlie's interests include country music, plush animals, pyrotechnics, and skeletons.",
|
|
||||||
"title": "Software Engineer",
|
|
||||||
"location": "Everywhere",
|
|
||||||
"email": "charlie@example.com",
|
|
||||||
"phone": "+1-541-754-3010",
|
|
||||||
"id": "2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Donald Duck",
|
|
||||||
"profilePic": "/assets/img/speakers/duck.jpg",
|
|
||||||
"instagram": "ionicframework",
|
|
||||||
"twitter": "ionicframework",
|
|
||||||
"about": "Donald is a Duck. Donald's interests include carpentry, superheroes, merpeople, and glam rock.",
|
|
||||||
"title": "Software Engineer",
|
|
||||||
"location": "Everywhere",
|
|
||||||
"email": "donald@example.com",
|
|
||||||
"phone": "+1-541-754-3010",
|
|
||||||
"id": "3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Eva Eagle",
|
|
||||||
"profilePic": "/assets/img/speakers/eagle.jpg",
|
|
||||||
"instagram": "ionicframework",
|
|
||||||
"twitter": "ionicframework",
|
|
||||||
"about": "Eva is an Eagle. Eva's interests include ants, seashells, and cupcakes.",
|
|
||||||
"title": "Developer Advocate",
|
|
||||||
"location": "Everywhere",
|
|
||||||
"email": "eva@example.com",
|
|
||||||
"phone": "+1-541-754-3010",
|
|
||||||
"id": "4"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ellie Elephant",
|
|
||||||
"profilePic": "/assets/img/speakers/elephant.jpg",
|
|
||||||
"instagram": "ionicframework",
|
|
||||||
"twitter": "ionicframework",
|
|
||||||
"about": "Ellie is an Elephant. Ellie's interests include pocket watches, pool, hand fans, and ninjas.",
|
|
||||||
"title": "Software Engineer",
|
|
||||||
"location": "Everywhere",
|
|
||||||
"email": "ellie@example.com",
|
|
||||||
"phone": "+1-541-754-3010",
|
|
||||||
"id": "5"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Gino Giraffe",
|
|
||||||
"profilePic": "/assets/img/speakers/giraffe.jpg",
|
|
||||||
"instagram": "ionicframework",
|
|
||||||
"twitter": "ionicframework",
|
|
||||||
"about": "Gino is a Giraffe. Gino's interests include candy-making, unicorns, and birdhouses.",
|
|
||||||
"title": "Software Engineer",
|
|
||||||
"location": "Everywhere",
|
|
||||||
"email": "gino@example.com",
|
|
||||||
"phone": "+1-541-754-3010",
|
|
||||||
"id": "6"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Isabella Iguana",
|
|
||||||
"profilePic": "/assets/img/speakers/iguana.jpg",
|
|
||||||
"instagram": "ionicframework",
|
|
||||||
"twitter": "ionicframework",
|
|
||||||
"about": "Isabella is an Iguana. Isabella's interests include crystals, architecture, and candle-making.",
|
|
||||||
"title": "Software Engineer",
|
|
||||||
"location": "Everywhere",
|
|
||||||
"email": "isabella@example.com",
|
|
||||||
"phone": "+1-541-754-3010",
|
|
||||||
"id": "7"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Karl Kitten",
|
|
||||||
"profilePic": "/assets/img/speakers/kitten.jpg",
|
|
||||||
"instagram": "ionicframework",
|
|
||||||
"twitter": "ionicframework",
|
|
||||||
"about": "Karl is a Kitten. Karl's interests include skiing, jewelry, and needlepoint.",
|
|
||||||
"title": "Developer Advocate",
|
|
||||||
"location": "Everywhere",
|
|
||||||
"email": "karl@example.com",
|
|
||||||
"phone": "+1-541-754-3010",
|
|
||||||
"id": "8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Lionel Lion",
|
|
||||||
"profilePic": "/assets/img/speakers/lion.jpg",
|
|
||||||
"instagram": "ionicframework",
|
|
||||||
"twitter": "ionicframework",
|
|
||||||
"about": "Lionel is a Lion. Lionel's interests include lizards and mathematics.",
|
|
||||||
"title": "Developer Advocate",
|
|
||||||
"location": "Everywhere",
|
|
||||||
"email": "lionel@example.com",
|
|
||||||
"phone": "+1-541-754-3010",
|
|
||||||
"id": "9"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Molly Mouse",
|
|
||||||
"profilePic": "/assets/img/speakers/mouse.jpg",
|
|
||||||
"instagram": "ionicframework",
|
|
||||||
"twitter": "ionicframework",
|
|
||||||
"about": "Molly is a Mouse. Molly's interests include werewolves and magic.",
|
|
||||||
"title": "Software Engineer",
|
|
||||||
"location": "Everywhere",
|
|
||||||
"email": "molly@example.com",
|
|
||||||
"phone": "+1-541-754-3010",
|
|
||||||
"id": "10"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Paul Puppy",
|
|
||||||
"profilePic": "/assets/img/speakers/puppy.jpg",
|
|
||||||
"instagram": "ionicframework",
|
|
||||||
"twitter": "ionicframework",
|
|
||||||
"about": "Paul is a Puppy. Paul's interests include maps, whales, and dragons.",
|
|
||||||
"title": "Software Engineer",
|
|
||||||
"location": "Everywhere",
|
|
||||||
"email": "paul@example.com",
|
|
||||||
"phone": "+1-541-754-3010",
|
|
||||||
"id": "11"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Rachel Rabbit",
|
|
||||||
"profilePic": "/assets/img/speakers/rabbit.jpg",
|
|
||||||
"instagram": "ionicframework",
|
|
||||||
"twitter": "ionicframework",
|
|
||||||
"about": "Rachel is a Rabbit. Rachel's interests include clowns, skeletons, and yo-yos.",
|
|
||||||
"title": "Senior Software Engineer",
|
|
||||||
"location": "Everywhere",
|
|
||||||
"email": "rachel@example.com",
|
|
||||||
"phone": "+1-541-754-3010",
|
|
||||||
"id": "12"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ted Turtle",
|
|
||||||
"profilePic": "/assets/img/speakers/turtle.jpg",
|
|
||||||
"instagram": "ionicframework",
|
|
||||||
"twitter": "ionicframework",
|
|
||||||
"about": "Ted is a Turtle. Ted's interests include butterflies, skiing, and cupcakes.",
|
|
||||||
"title": "Software Engineer",
|
|
||||||
"location": "Everywhere",
|
|
||||||
"email": "ted@example.com",
|
|
||||||
"phone": "+1-541-754-3010",
|
|
||||||
"id": "13"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
"map": [
|
|
||||||
{
|
|
||||||
"name": "Monona Terrace Convention Center",
|
|
||||||
"lat": 43.071584,
|
|
||||||
"lng": -89.38012,
|
|
||||||
"center": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ionic HQ",
|
|
||||||
"lat": 43.074395,
|
|
||||||
"lng": -89.381056
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Afterparty - Brocach Irish Pub",
|
|
||||||
"lat": 43.07336,
|
|
||||||
"lng": -89.38335
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
"tracks": [
|
|
||||||
{
|
|
||||||
"name": "Angular",
|
|
||||||
"icon": "logo-angular"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Documentation",
|
|
||||||
"icon": "document"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Food",
|
|
||||||
"icon": "restaurant"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ionic",
|
|
||||||
"icon": "logo-ionic"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Tooling",
|
|
||||||
"icon": "hammer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Design",
|
|
||||||
"icon": "color-palette"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Services",
|
|
||||||
"icon": "cog"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Workshop",
|
|
||||||
"icon": "construct"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Communication",
|
|
||||||
"icon": "call"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Navigation",
|
|
||||||
"icon": "compass"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@@ -1,26 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"name": "Map Center",
|
|
||||||
"lat": 43.071584,
|
|
||||||
"lng": -89.38012
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"name": "Monona Terrace Convention Center",
|
|
||||||
"lat": 43.071584,
|
|
||||||
"lng": -89.38012
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"name": "Ionic HQ",
|
|
||||||
"lat": 43.074395,
|
|
||||||
"lng": -89.381056
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 4,
|
|
||||||
"name": "Afterparty - Brocach Irish Pub",
|
|
||||||
"lat": 43.07336,
|
|
||||||
"lng": -89.38335
|
|
||||||
}
|
|
||||||
]
|
|
Before Width: | Height: | Size: 930 B |
Before Width: | Height: | Size: 1004 KiB |
Before Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 599 KiB |
Before Width: | Height: | Size: 700 KiB |
Before Width: | Height: | Size: 12 KiB |
@@ -1,2 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#3880FF" d="M256 139.3c-64.3 0-116.7 52.3-116.7 116.7 0 64.3 52.3 116.7 116.7 116.7S372.7 320.3 372.7 256 320.3 139.3 256 139.3z"/><circle fill="#3880FF" cx="423.5" cy="96.5" r="53.2"/><path fill="#3880FF" d="M489 149.9l-2.2-4.9-3.6 4c-8.7 9.9-19.8 17.5-32.1 22.1l-3.4 1.3 1.4 3.3c10.6 25.5 16 52.5 16 80.2 0 115.3-93.8 209.2-209.2 209.2S46.8 371.3 46.8 256 140.7 46.8 256 46.8c31.3 0 61.5 6.8 89.6 20.2l3.3 1.6 1.4-3.3c5.1-12 13.3-22.7 23.6-31l4.2-3.4-4.8-2.5C336.8 9.6 297.3 0 256 0 114.8 0 0 114.8 0 256s114.8 256 256 256 256-114.8 256-256c0-36.9-7.7-72.6-23-106.1z"/></svg>
|
|
||||||
|
|
Before Width: | Height: | Size: 655 B |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 8.6 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#FFF" d="M256 139.3c-64.3 0-116.7 52.3-116.7 116.7 0 64.3 52.3 116.7 116.7 116.7S372.7 320.3 372.7 256 320.3 139.3 256 139.3z"/><circle fill="#FFF" cx="423.5" cy="96.5" r="53.2"/><path fill="#FFF" d="M489 149.9l-2.2-4.9-3.6 4c-8.7 9.9-19.8 17.5-32.1 22.1l-3.4 1.3 1.4 3.3c10.6 25.5 16 52.5 16 80.2 0 115.3-93.8 209.2-209.2 209.2S46.8 371.3 46.8 256 140.7 46.8 256 46.8c31.3 0 61.5 6.8 89.6 20.2l3.3 1.6 1.4-3.3c5.1-12 13.3-22.7 23.6-31l4.2-3.4-4.8-2.5C336.8 9.6 297.3 0 256 0 114.8 0 0 114.8 0 256s114.8 256 256 256 256-114.8 256-256c0-36.9-7.7-72.6-23-106.1z"/></svg>
|
|
Before Width: | Height: | Size: 643 B |
Before Width: | Height: | Size: 184 KiB |
Before Width: | Height: | Size: 2.0 MiB |
Before Width: | Height: | Size: 910 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 35 KiB |
@@ -1 +0,0 @@
|
|||||||
<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>
|
|
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 3.8 KiB |
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "Ionic React Conf",
|
|
||||||
"name": "Ionic React Conf",
|
|
||||||
"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"
|
|
||||||
}
|
|
Before Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 145 KiB |
Before Width: | Height: | Size: 187 KiB |
Before Width: | Height: | Size: 730 KiB |
Before Width: | Height: | Size: 213 KiB |
Before Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 148 KiB |
Before Width: | Height: | Size: 203 KiB |
Before Width: | Height: | Size: 740 KiB |
Before Width: | Height: | Size: 209 KiB |
@@ -1,6 +0,0 @@
|
|||||||
/*
|
|
||||||
* App Global CSS
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
* Put style rules here that you want to apply globally. These styles are for
|
|
||||||
* the entire app and not just one component.
|
|
||||||
*/
|
|
@@ -1,7 +0,0 @@
|
|||||||
import { render } from '@testing-library/react';
|
|
||||||
import App from './App';
|
|
||||||
|
|
||||||
it('renders without crashing', () => {
|
|
||||||
const { asFragment, container } = render(<App />);
|
|
||||||
expect(asFragment()).toMatchSnapshot();
|
|
||||||
});
|
|
@@ -1,273 +0,0 @@
|
|||||||
import { IonApp, IonNav, IonRouterOutlet, IonSplitPane, setupIonicReact } from '@ionic/react';
|
|
||||||
import { IonReactRouter } from '@ionic/react-router';
|
|
||||||
import React, { useContext, useEffect } from 'react';
|
|
||||||
import { Route } from 'react-router-dom';
|
|
||||||
|
|
||||||
import Menu from './components/Menu';
|
|
||||||
|
|
||||||
/* 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/display.css';
|
|
||||||
import '@ionic/react/css/flex-utils.css';
|
|
||||||
import '@ionic/react/css/float-elements.css';
|
|
||||||
import '@ionic/react/css/padding.css';
|
|
||||||
import '@ionic/react/css/text-alignment.css';
|
|
||||||
import '@ionic/react/css/text-transformation.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.system.css";
|
|
||||||
import '@ionic/react/css/palettes/dark.class.css';
|
|
||||||
|
|
||||||
/* Theme variables */
|
|
||||||
import './theme/variables.css';
|
|
||||||
|
|
||||||
/* Global styles */
|
|
||||||
import './App.scss';
|
|
||||||
import RedirectToLogin from './components/RedirectToLogin';
|
|
||||||
import { AppContext, AppContextProvider } from './data/AppContext';
|
|
||||||
import { connect } from './data/connect';
|
|
||||||
import { loadConfData } from './data/sessions/sessions.actions';
|
|
||||||
import { loadUserData, setIsLoggedIn, setUsername } from './data/user/user.actions';
|
|
||||||
import { Schedule } from './models/Schedule';
|
|
||||||
import Account from './pages/Account';
|
|
||||||
import Login from './pages/Login';
|
|
||||||
import MainTabs from './pages/MainTabs';
|
|
||||||
import Signup from './pages/Signup';
|
|
||||||
import Support from './pages/Support';
|
|
||||||
import Tutorial from './pages/Tutorial';
|
|
||||||
|
|
||||||
//
|
|
||||||
import { Redirect } from 'react-router';
|
|
||||||
import { AccountPage } from './pages/SBAccount';
|
|
||||||
import LoginPage from './pages/SBLogin';
|
|
||||||
|
|
||||||
//
|
|
||||||
import SBLogout from './pages/SBLogout';
|
|
||||||
import StartupLoading from './pages/debug/StartupLoading';
|
|
||||||
import WelcomePage from './pages/debug/WelcomePage';
|
|
||||||
import Helloworld from './pages/debug/helloworld';
|
|
||||||
|
|
||||||
//
|
|
||||||
import SBLoginError from './SBLoginError';
|
|
||||||
import './i18n';
|
|
||||||
import UnlockMemberShip from './pages/UnlockMembership';
|
|
||||||
import EventDetail from './pages/event_detail';
|
|
||||||
//
|
|
||||||
|
|
||||||
// A wrapper for <Route> that redirects to the login
|
|
||||||
// screen if you're not yet authenticated.
|
|
||||||
function PrivateRoute({ children, ...rest }) {
|
|
||||||
const { session } = useContext(AppContext);
|
|
||||||
|
|
||||||
return <Route {...rest} render={({ location }) => (session ? children : <Redirect to="/sblogin" />)} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
setupIonicReact();
|
|
||||||
|
|
||||||
const App: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<AppContextProvider>
|
|
||||||
<IonicAppConnected />
|
|
||||||
</AppContextProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface StateProps {
|
|
||||||
darkMode: boolean;
|
|
||||||
schedule: Schedule;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DispatchProps {
|
|
||||||
loadConfData: typeof loadConfData;
|
|
||||||
loadUserData: typeof loadUserData;
|
|
||||||
setIsLoggedIn: typeof setIsLoggedIn;
|
|
||||||
setUsername: typeof setUsername;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IonicAppProps extends StateProps, DispatchProps {}
|
|
||||||
|
|
||||||
function AppOutlet() {
|
|
||||||
return (
|
|
||||||
<IonRouterOutlet id="main">
|
|
||||||
{/*
|
|
||||||
We use IonRoute here to keep the tabs state intact,
|
|
||||||
which makes transitions between tabs and non tab pages smooth
|
|
||||||
*/}
|
|
||||||
{/* <Route path="/tabs" render={() => <MainTabs />} /> */}
|
|
||||||
<Route path="/account" component={Account} />
|
|
||||||
<Route path="/login" component={Login} />
|
|
||||||
<Route path="/signup" component={Signup} />
|
|
||||||
<Route path="/support" component={Support} />
|
|
||||||
<Route path="/tutorial" component={Tutorial} />
|
|
||||||
<Route path="/login_error" component={SBLoginError} />
|
|
||||||
<Route
|
|
||||||
path="/logout"
|
|
||||||
render={() => {
|
|
||||||
return <RedirectToLogin setIsLoggedIn={setIsLoggedIn} setUsername={setUsername} />;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Route path="/debug">
|
|
||||||
<Route path="/debug/helloworld" component={Helloworld} />
|
|
||||||
<Route path="/debug/StartupLoading" component={StartupLoading} />
|
|
||||||
<Route path="/debug/WelcomePage" component={WelcomePage} />
|
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route path="/helloworld" component={Helloworld} />
|
|
||||||
|
|
||||||
<Route path="/event_detail/1" render={() => <EventDetail />} exact={true} />
|
|
||||||
|
|
||||||
{/* */}
|
|
||||||
<Route path="/privacy/en" render={() => <PrivacyPage />} exact={true} />
|
|
||||||
|
|
||||||
{/* nested route / cascade route example */}
|
|
||||||
<Route path="/tabs">
|
|
||||||
<MainTabs />
|
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route exact path="/unlock-membership" render={() => <UnlockMemberShip />} />
|
|
||||||
<Route exact path="/sblogout" render={() => <SBLogout />} />
|
|
||||||
<Route exact path="/sblogin" render={() => <LoginPage />} />
|
|
||||||
|
|
||||||
<PrivateRoute path="/sbaccount">
|
|
||||||
<AccountPage />
|
|
||||||
</PrivateRoute>
|
|
||||||
|
|
||||||
{/* <Route path="/" component={HomeOrTutorial} exact /> */}
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path="/"
|
|
||||||
render={() => <RedirectToLogin setIsLoggedIn={setIsLoggedIn} setUsername={setUsername} />}
|
|
||||||
/>
|
|
||||||
</IonRouterOutlet>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const IonicApp: React.FC<IonicAppProps> = ({
|
|
||||||
darkMode,
|
|
||||||
schedule,
|
|
||||||
setIsLoggedIn,
|
|
||||||
setUsername,
|
|
||||||
loadConfData,
|
|
||||||
loadUserData,
|
|
||||||
}) => {
|
|
||||||
useEffect(() => {
|
|
||||||
loadUserData();
|
|
||||||
loadConfData();
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return schedule.groups.length === 0 ? (
|
|
||||||
<div></div>
|
|
||||||
) : (
|
|
||||||
<IonApp className={`${darkMode ? 'ion-palette-dark' : ''}`}>
|
|
||||||
<IonReactRouter>
|
|
||||||
<IonSplitPane contentId="main">
|
|
||||||
{/* */}
|
|
||||||
<Menu />
|
|
||||||
{/* <AppOutlet /> */}
|
|
||||||
<IonNav root={() => <AppOutlet />}></IonNav>;
|
|
||||||
</IonSplitPane>
|
|
||||||
</IonReactRouter>
|
|
||||||
</IonApp>
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO: obsoleted, delete this
|
|
||||||
// return schedule.groups.length === 0 ? (
|
|
||||||
// <div></div>
|
|
||||||
// ) : (
|
|
||||||
// <IonApp className={`${darkMode ? 'ion-palette-dark' : ''}`}>
|
|
||||||
// <IonReactRouter>
|
|
||||||
// <IonSplitPane contentId="main">
|
|
||||||
// {/* */}
|
|
||||||
// <Menu />
|
|
||||||
|
|
||||||
// <IonRouterOutlet id="main">
|
|
||||||
// {/*
|
|
||||||
// We use IonRoute here to keep the tabs state intact,
|
|
||||||
// which makes transitions between tabs and non tab pages smooth
|
|
||||||
// */}
|
|
||||||
// {/* <Route path="/tabs" render={() => <MainTabs />} /> */}
|
|
||||||
// <Route path="/account" component={Account} />
|
|
||||||
// <Route path="/login" component={Login} />
|
|
||||||
// <Route path="/signup" component={Signup} />
|
|
||||||
// <Route path="/support" component={Support} />
|
|
||||||
// <Route path="/tutorial" component={Tutorial} />
|
|
||||||
// <Route
|
|
||||||
// path="/logout"
|
|
||||||
// render={() => {
|
|
||||||
// return <RedirectToLogin setIsLoggedIn={setIsLoggedIn} setUsername={setUsername} />;
|
|
||||||
// }}
|
|
||||||
// />
|
|
||||||
|
|
||||||
// <Route path="/debug">
|
|
||||||
// <Route path="/debug/helloworld" component={Helloworld} />
|
|
||||||
// <Route path="/debug/StartupLoading" component={StartupLoading} />
|
|
||||||
// <Route path="/debug/WelcomePage" component={WelcomePage} />
|
|
||||||
// </Route>
|
|
||||||
|
|
||||||
// <Route path="/helloworld" component={Helloworld} />
|
|
||||||
|
|
||||||
// <Route path="/event_detail/1" render={() => <EventDetail />} exact={true} />
|
|
||||||
|
|
||||||
// {/* nested route / cascade route example */}
|
|
||||||
// <Route path="/tabs">
|
|
||||||
// <MainTabs />
|
|
||||||
// </Route>
|
|
||||||
|
|
||||||
// <PrivateRoute path="/sbaccount">
|
|
||||||
// <AccountPage />
|
|
||||||
// </PrivateRoute>
|
|
||||||
|
|
||||||
// <Route
|
|
||||||
// path="/sblogout"
|
|
||||||
// render={() => {
|
|
||||||
// return <SBLogout />;
|
|
||||||
// }}
|
|
||||||
// />
|
|
||||||
|
|
||||||
// <Route
|
|
||||||
// exact
|
|
||||||
// path="/sblogin"
|
|
||||||
// render={() => {
|
|
||||||
// return <LoginPage />;
|
|
||||||
// }}
|
|
||||||
// />
|
|
||||||
|
|
||||||
// <Route path="/" component={HomeOrTutorial} exact />
|
|
||||||
// </IonRouterOutlet>
|
|
||||||
// </IonSplitPane>
|
|
||||||
// </IonReactRouter>
|
|
||||||
// </IonApp>
|
|
||||||
// );
|
|
||||||
};
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
|
|
||||||
const IonicAppConnected = connect<{}, StateProps, DispatchProps>({
|
|
||||||
mapStateToProps: state => ({
|
|
||||||
darkMode: state.user.darkMode,
|
|
||||||
schedule: state.data.schedule,
|
|
||||||
}),
|
|
||||||
mapDispatchToProps: {
|
|
||||||
loadConfData,
|
|
||||||
loadUserData,
|
|
||||||
setIsLoggedIn,
|
|
||||||
setUsername,
|
|
||||||
},
|
|
||||||
component: IonicApp,
|
|
||||||
});
|
|
@@ -1,5 +0,0 @@
|
|||||||
const SBLoginError = () => {
|
|
||||||
return <>Login Error</>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SBLoginError;
|
|
@@ -1,280 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`renders without crashing 1`] = `
|
|
||||||
<DocumentFragment>
|
|
||||||
<ion-app>
|
|
||||||
<ion-split-pane
|
|
||||||
content-id="main"
|
|
||||||
>
|
|
||||||
<ion-menu
|
|
||||||
content-id="main"
|
|
||||||
>
|
|
||||||
<ion-header>
|
|
||||||
<ion-toolbar>
|
|
||||||
<ion-title>
|
|
||||||
Menu
|
|
||||||
</ion-title>
|
|
||||||
</ion-toolbar>
|
|
||||||
</ion-header>
|
|
||||||
<ion-content
|
|
||||||
class="outer-content"
|
|
||||||
>
|
|
||||||
<ion-list>
|
|
||||||
<ion-list-header>
|
|
||||||
Navigate
|
|
||||||
</ion-list-header>
|
|
||||||
<ion-menu-toggle
|
|
||||||
auto-hide="false"
|
|
||||||
>
|
|
||||||
<ion-item>
|
|
||||||
<ion-icon
|
|
||||||
slot="start"
|
|
||||||
/>
|
|
||||||
<ion-label>
|
|
||||||
Schedule
|
|
||||||
</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
</ion-menu-toggle>
|
|
||||||
<ion-menu-toggle
|
|
||||||
auto-hide="false"
|
|
||||||
>
|
|
||||||
<ion-item>
|
|
||||||
<ion-icon
|
|
||||||
slot="start"
|
|
||||||
/>
|
|
||||||
<ion-label>
|
|
||||||
Speakers
|
|
||||||
</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
</ion-menu-toggle>
|
|
||||||
<ion-menu-toggle
|
|
||||||
auto-hide="false"
|
|
||||||
>
|
|
||||||
<ion-item>
|
|
||||||
<ion-icon
|
|
||||||
slot="start"
|
|
||||||
/>
|
|
||||||
<ion-label>
|
|
||||||
Map
|
|
||||||
</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
</ion-menu-toggle>
|
|
||||||
<ion-menu-toggle
|
|
||||||
auto-hide="false"
|
|
||||||
>
|
|
||||||
<ion-item>
|
|
||||||
<ion-icon
|
|
||||||
slot="start"
|
|
||||||
/>
|
|
||||||
<ion-label>
|
|
||||||
About
|
|
||||||
</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
</ion-menu-toggle>
|
|
||||||
</ion-list>
|
|
||||||
<ion-list>
|
|
||||||
<ion-list-header>
|
|
||||||
Account
|
|
||||||
</ion-list-header>
|
|
||||||
<ion-menu-toggle
|
|
||||||
auto-hide="false"
|
|
||||||
>
|
|
||||||
<ion-item>
|
|
||||||
<ion-icon
|
|
||||||
slot="start"
|
|
||||||
/>
|
|
||||||
<ion-label>
|
|
||||||
Account
|
|
||||||
</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
</ion-menu-toggle>
|
|
||||||
<ion-menu-toggle
|
|
||||||
auto-hide="false"
|
|
||||||
>
|
|
||||||
<ion-item>
|
|
||||||
<ion-icon
|
|
||||||
slot="start"
|
|
||||||
/>
|
|
||||||
<ion-label>
|
|
||||||
Support
|
|
||||||
</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
</ion-menu-toggle>
|
|
||||||
<ion-menu-toggle
|
|
||||||
auto-hide="false"
|
|
||||||
>
|
|
||||||
<ion-item>
|
|
||||||
<ion-icon
|
|
||||||
slot="start"
|
|
||||||
/>
|
|
||||||
<ion-label>
|
|
||||||
Logout
|
|
||||||
</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
</ion-menu-toggle>
|
|
||||||
</ion-list>
|
|
||||||
<ion-list>
|
|
||||||
<ion-list-header>
|
|
||||||
Tutorial
|
|
||||||
</ion-list-header>
|
|
||||||
<ion-item>
|
|
||||||
<ion-icon
|
|
||||||
slot="start"
|
|
||||||
/>
|
|
||||||
Show Tutorial
|
|
||||||
</ion-item>
|
|
||||||
</ion-list>
|
|
||||||
</ion-content>
|
|
||||||
</ion-menu>
|
|
||||||
<ion-router-outlet
|
|
||||||
id="main"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style="display: flex; position: absolute; top: 0px; left: 0px; right: 0px; bottom: 0px; flex-direction: column; width: 100%; height: 100%; contain: layout size style;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="tabs-inner"
|
|
||||||
style="position: relative; flex: 1; contain: layout size style;"
|
|
||||||
>
|
|
||||||
<ion-router-outlet>
|
|
||||||
<div
|
|
||||||
class="ion-page ion-page-invisible"
|
|
||||||
>
|
|
||||||
<ion-header>
|
|
||||||
<ion-toolbar
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
<ion-buttons
|
|
||||||
slot="start"
|
|
||||||
>
|
|
||||||
<ion-menu-button />
|
|
||||||
</ion-buttons>
|
|
||||||
<ion-segment>
|
|
||||||
<ion-segment-button
|
|
||||||
value="all"
|
|
||||||
>
|
|
||||||
All
|
|
||||||
</ion-segment-button>
|
|
||||||
<ion-segment-button
|
|
||||||
value="favorites"
|
|
||||||
>
|
|
||||||
Favorites
|
|
||||||
</ion-segment-button>
|
|
||||||
</ion-segment>
|
|
||||||
<ion-buttons
|
|
||||||
slot="end"
|
|
||||||
>
|
|
||||||
<ion-button>
|
|
||||||
<ion-icon
|
|
||||||
slot="icon-only"
|
|
||||||
/>
|
|
||||||
</ion-button>
|
|
||||||
</ion-buttons>
|
|
||||||
</ion-toolbar>
|
|
||||||
<ion-toolbar
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
<ion-searchbar
|
|
||||||
placeholder="Search"
|
|
||||||
/>
|
|
||||||
</ion-toolbar>
|
|
||||||
</ion-header>
|
|
||||||
<ion-content>
|
|
||||||
<ion-refresher
|
|
||||||
slot="fixed"
|
|
||||||
>
|
|
||||||
<ion-refresher-content />
|
|
||||||
</ion-refresher>
|
|
||||||
<ion-list>
|
|
||||||
<ion-list-header>
|
|
||||||
No Sessions Found
|
|
||||||
</ion-list-header>
|
|
||||||
</ion-list>
|
|
||||||
<ion-list
|
|
||||||
style="display: none;"
|
|
||||||
/>
|
|
||||||
</ion-content>
|
|
||||||
<ion-fab
|
|
||||||
horizontal="end"
|
|
||||||
slot="fixed"
|
|
||||||
vertical="bottom"
|
|
||||||
>
|
|
||||||
<ion-fab-button>
|
|
||||||
<ion-icon />
|
|
||||||
</ion-fab-button>
|
|
||||||
<ion-fab-list
|
|
||||||
side="top"
|
|
||||||
>
|
|
||||||
<ion-fab-button
|
|
||||||
color="vimeo"
|
|
||||||
>
|
|
||||||
<ion-icon />
|
|
||||||
</ion-fab-button>
|
|
||||||
<ion-fab-button
|
|
||||||
color="google"
|
|
||||||
>
|
|
||||||
<ion-icon />
|
|
||||||
</ion-fab-button>
|
|
||||||
<ion-fab-button
|
|
||||||
color="twitter"
|
|
||||||
>
|
|
||||||
<ion-icon />
|
|
||||||
</ion-fab-button>
|
|
||||||
<ion-fab-button
|
|
||||||
color="facebook"
|
|
||||||
>
|
|
||||||
<ion-icon />
|
|
||||||
</ion-fab-button>
|
|
||||||
</ion-fab-list>
|
|
||||||
</ion-fab>
|
|
||||||
</div>
|
|
||||||
</ion-router-outlet>
|
|
||||||
</div>
|
|
||||||
<ion-tab-bar
|
|
||||||
current-path="/tabs/schedule"
|
|
||||||
selected-tab="schedule"
|
|
||||||
slot="bottom"
|
|
||||||
>
|
|
||||||
<ion-tab-button
|
|
||||||
href="/tabs/schedule"
|
|
||||||
tab="schedule"
|
|
||||||
>
|
|
||||||
<ion-icon />
|
|
||||||
<ion-label>
|
|
||||||
Schedule
|
|
||||||
</ion-label>
|
|
||||||
</ion-tab-button>
|
|
||||||
<ion-tab-button
|
|
||||||
href="/tabs/speakers"
|
|
||||||
tab="speakers"
|
|
||||||
>
|
|
||||||
<ion-icon />
|
|
||||||
<ion-label>
|
|
||||||
Speakers
|
|
||||||
</ion-label>
|
|
||||||
</ion-tab-button>
|
|
||||||
<ion-tab-button
|
|
||||||
href="/tabs/map"
|
|
||||||
tab="map"
|
|
||||||
>
|
|
||||||
<ion-icon />
|
|
||||||
<ion-label>
|
|
||||||
Map
|
|
||||||
</ion-label>
|
|
||||||
</ion-tab-button>
|
|
||||||
<ion-tab-button
|
|
||||||
href="/tabs/about"
|
|
||||||
tab="about"
|
|
||||||
>
|
|
||||||
<ion-icon />
|
|
||||||
<ion-label>
|
|
||||||
About
|
|
||||||
</ion-label>
|
|
||||||
</ion-tab-button>
|
|
||||||
</ion-tab-bar>
|
|
||||||
</div>
|
|
||||||
</ion-router-outlet>
|
|
||||||
</ion-split-pane>
|
|
||||||
</ion-app>
|
|
||||||
</DocumentFragment>
|
|
||||||
`;
|
|
@@ -1,35 +0,0 @@
|
|||||||
import { IonItem, IonLabel, IonList } from '@ionic/react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
interface AboutPopoverProps {
|
|
||||||
dismiss: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AboutPopover: React.FC<AboutPopoverProps> = ({ dismiss }) => {
|
|
||||||
const close = (url: string) => {
|
|
||||||
window.open(url, '_blank');
|
|
||||||
dismiss();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IonList>
|
|
||||||
<IonItem button onClick={() => close('https://ionicframework.com/docs')}>
|
|
||||||
<IonLabel>Learn Ionic</IonLabel>
|
|
||||||
</IonItem>
|
|
||||||
<IonItem button onClick={() => close('https://ionicframework.com/docs/react')}>
|
|
||||||
<IonLabel>Documentation</IonLabel>
|
|
||||||
</IonItem>
|
|
||||||
<IonItem button onClick={() => close('https://showcase.ionicframework.com')}>
|
|
||||||
<IonLabel>Showcase</IonLabel>
|
|
||||||
</IonItem>
|
|
||||||
<IonItem button onClick={() => close('https://github.com/ionic-team/ionic-framework')}>
|
|
||||||
<IonLabel>GitHub Repo</IonLabel>
|
|
||||||
</IonItem>
|
|
||||||
<IonItem button onClick={dismiss}>
|
|
||||||
<IonLabel>Support</IonLabel>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AboutPopover;
|
|
@@ -1,27 +0,0 @@
|
|||||||
.avatar {
|
|
||||||
display: block;
|
|
||||||
margin: auto;
|
|
||||||
min-height: 150px;
|
|
||||||
}
|
|
||||||
.avatar .avatar_wrapper {
|
|
||||||
margin: 16px auto 16px;
|
|
||||||
border-radius: 50%;
|
|
||||||
overflow: hidden;
|
|
||||||
height: 150px;
|
|
||||||
aspect-ratio: 1;
|
|
||||||
background: var(--ion-color-step-50);
|
|
||||||
border: thick solid var(--ion-color-step-200);
|
|
||||||
}
|
|
||||||
.avatar .avatar_wrapper:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.avatar .avatar_wrapper ion-icon.no-avatar {
|
|
||||||
width: 100%;
|
|
||||||
height: 115%;
|
|
||||||
}
|
|
||||||
.avatar img {
|
|
||||||
display: block;
|
|
||||||
object-fit: cover;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
@@ -1,56 +0,0 @@
|
|||||||
import { Camera, CameraResultType } from '@capacitor/camera';
|
|
||||||
import { IonIcon } from '@ionic/react';
|
|
||||||
import { person } from 'ionicons/icons';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { supabase } from '../supabaseClient';
|
|
||||||
import './Avatar.css';
|
|
||||||
export function Avatar({ url, onUpload }: { url: string; onUpload: (e: any, file: string) => Promise<void> }) {
|
|
||||||
const [avatarUrl, setAvatarUrl] = useState<string | undefined>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (url) {
|
|
||||||
downloadImage(url);
|
|
||||||
}
|
|
||||||
}, [url]);
|
|
||||||
const uploadAvatar = async () => {
|
|
||||||
try {
|
|
||||||
const photo = await Camera.getPhoto({
|
|
||||||
resultType: CameraResultType.DataUrl,
|
|
||||||
});
|
|
||||||
|
|
||||||
const file = await fetch(photo.dataUrl!)
|
|
||||||
.then(res => res.blob())
|
|
||||||
.then(blob => new File([blob], 'my-file', { type: `image/${photo.format}` }));
|
|
||||||
|
|
||||||
const fileName = `${Math.random()}-${new Date().getTime()}.${photo.format}`;
|
|
||||||
let { error: uploadError } = await supabase.storage.from('avatars').upload(fileName, file);
|
|
||||||
if (uploadError) {
|
|
||||||
throw uploadError;
|
|
||||||
}
|
|
||||||
onUpload(null, fileName);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const downloadImage = async (path: string) => {
|
|
||||||
try {
|
|
||||||
const { data, error } = await supabase.storage.from('avatars').download(path);
|
|
||||||
if (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
const url = URL.createObjectURL(data!);
|
|
||||||
setAvatarUrl(url);
|
|
||||||
} catch (error: any) {
|
|
||||||
console.log('Error downloading image: ', error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="avatar">
|
|
||||||
<div className="avatar_wrapper" onClick={uploadAvatar}>
|
|
||||||
{avatarUrl ? <img src={avatarUrl} /> : <IonIcon icon={person} className="no-avatar" />}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -1,10 +0,0 @@
|
|||||||
import { IonIcon } from '@ionic/react';
|
|
||||||
import { checkmarkDone, star } from 'ionicons/icons';
|
|
||||||
|
|
||||||
export const ChatBottomDetails = ({ message }) => (
|
|
||||||
<span className="chat-bottom-details" id={`chatTime_${message.id}`}>
|
|
||||||
<span>{message.date}</span>
|
|
||||||
{message.sent && <IonIcon icon={checkmarkDone} color="primary" style={{ fontSize: '0.8rem' }} />}
|
|
||||||
{message.starred && <IonIcon icon={star} />}
|
|
||||||
</span>
|
|
||||||
);
|
|
@@ -1,73 +0,0 @@
|
|||||||
import {
|
|
||||||
IonIcon,
|
|
||||||
IonItem,
|
|
||||||
IonItemOption,
|
|
||||||
IonItemOptions,
|
|
||||||
IonItemSliding,
|
|
||||||
IonNavLink,
|
|
||||||
IonText,
|
|
||||||
IonThumbnail,
|
|
||||||
} from '@ionic/react';
|
|
||||||
import { checkmarkDone } from 'ionicons/icons';
|
|
||||||
import ChatHelloworld from '../../pages/chat';
|
|
||||||
import { ContactStore } from '../../store';
|
|
||||||
import { getContacts } from '../../store/Selectors';
|
|
||||||
|
|
||||||
import HKPartyIonDeleteIcon from '../HKPartyIonDeleteIcon';
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
const ChatItem = ({ chat }) => {
|
|
||||||
const contacts = ContactStore.useState(getContacts);
|
|
||||||
const { chats, contact_id } = chat;
|
|
||||||
const { read, date, preview, received } = chats[chats.length - 1];
|
|
||||||
const contact = contacts.filter(c => c.id === contact_id)[0];
|
|
||||||
const notificationCount = chats.filter(chat => chat.read === false).length;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<IonItemSliding>
|
|
||||||
{/*
|
|
||||||
<IonItemOptions side="start">
|
|
||||||
<IonItemOption color="success">Archive</IonItemOption>
|
|
||||||
</IonItemOptions>
|
|
||||||
*/}
|
|
||||||
|
|
||||||
{/* */}
|
|
||||||
<IonNavLink routerDirection="forward" component={() => <ChatHelloworld />}>
|
|
||||||
<IonItem
|
|
||||||
className="chat-row"
|
|
||||||
// routerLink={`/tabs/messages/${contact.id}`}
|
|
||||||
|
|
||||||
lines="full"
|
|
||||||
detail={false}
|
|
||||||
>
|
|
||||||
<IonThumbnail slot="start" style={{ '--border-radius': '50%' }}>
|
|
||||||
<img alt="Silhouette of mountains" src="https://ionicframework.com/docs/img/demos/thumbnail.svg" />
|
|
||||||
</IonThumbnail>
|
|
||||||
<div className="chat-row-content">
|
|
||||||
<IonText>{contact.name}</IonText>
|
|
||||||
<IonText>{read && received && <IonIcon icon={checkmarkDone} color="primary" />}</IonText>
|
|
||||||
<IonText>{preview}</IonText>
|
|
||||||
</div>
|
|
||||||
<div slot="end">
|
|
||||||
<div>
|
|
||||||
<IonText>{date}</IonText>
|
|
||||||
<IonText>
|
|
||||||
{notificationCount > 0 && <IonText className="chat-notification">{notificationCount}</IonText>}
|
|
||||||
</IonText>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</IonItem>
|
|
||||||
</IonNavLink>
|
|
||||||
{/* */}
|
|
||||||
<IonItemOptions side="end">
|
|
||||||
<IonItemOption color="danger">
|
|
||||||
<HKPartyIonDeleteIcon size={'large'} />
|
|
||||||
</IonItemOption>
|
|
||||||
</IonItemOptions>
|
|
||||||
</IonItemSliding>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ChatItem;
|
|
@@ -1,20 +0,0 @@
|
|||||||
.chat-row {
|
|
||||||
.chat-row-content {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
.chat-content {
|
|
||||||
background-color: pink;
|
|
||||||
}
|
|
||||||
// img {
|
|
||||||
// border-radius: 500px;
|
|
||||||
// height: 2.5rem;
|
|
||||||
// width: 2.5rem;
|
|
||||||
// margin-right: 1.5rem;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ion-label {
|
|
||||||
// h1 {
|
|
||||||
// font-size: 1rem;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
@@ -1,14 +0,0 @@
|
|||||||
const Quote = ({ message, contact, repliedMessage }) => (
|
|
||||||
<div className="in-chat-reply-to-container">
|
|
||||||
<h1>{contact.name}</h1>
|
|
||||||
<p>{repliedMessage.preview}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const ChatRepliedQuote = ({ message, contact, repliedMessage }) => {
|
|
||||||
if (message.reply && repliedMessage) {
|
|
||||||
return <Quote message={message} contact={contact} repliedMessage={repliedMessage} />;
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
};
|
|
@@ -1,52 +0,0 @@
|
|||||||
import {
|
|
||||||
IonButton,
|
|
||||||
IonButtons,
|
|
||||||
IonContent,
|
|
||||||
IonHeader,
|
|
||||||
IonItem,
|
|
||||||
IonLabel,
|
|
||||||
IonList,
|
|
||||||
IonTitle,
|
|
||||||
IonToolbar,
|
|
||||||
} from '@ionic/react';
|
|
||||||
import { ContactStore } from '../store';
|
|
||||||
import { getContacts } from '../store/Selectors';
|
|
||||||
|
|
||||||
import './ContactModal.scss';
|
|
||||||
|
|
||||||
const ContactModal = ({ close }) => {
|
|
||||||
const contacts = ContactStore.useState(getContacts);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ height: '100%' }}>
|
|
||||||
<IonHeader>
|
|
||||||
<IonToolbar>
|
|
||||||
<IonTitle>New Chat</IonTitle>
|
|
||||||
<IonButtons slot="end">
|
|
||||||
<IonButton fill="clear" onClick={close}>
|
|
||||||
Cancel
|
|
||||||
</IonButton>
|
|
||||||
</IonButtons>
|
|
||||||
</IonToolbar>
|
|
||||||
</IonHeader>
|
|
||||||
|
|
||||||
<IonContent>
|
|
||||||
<IonList>
|
|
||||||
{contacts.map(contact => {
|
|
||||||
return (
|
|
||||||
<IonItem key={`contact_${contact.id}`} lines="full" className="contact-item">
|
|
||||||
<img src={contact.avatar} alt="contact avatar" />
|
|
||||||
<IonLabel>
|
|
||||||
<h1>{contact.name}</h1>
|
|
||||||
<p>Available</p>
|
|
||||||
</IonLabel>
|
|
||||||
</IonItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</IonList>
|
|
||||||
</IonContent>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ContactModal;
|
|
@@ -1,14 +0,0 @@
|
|||||||
.contact-item {
|
|
||||||
img {
|
|
||||||
border-radius: 500px;
|
|
||||||
height: 2.5rem;
|
|
||||||
width: 2.5rem;
|
|
||||||
margin-right: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-label {
|
|
||||||
h1 {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,8 +0,0 @@
|
|||||||
import { IonIcon } from '@ionic/react';
|
|
||||||
import { trashOutline } from 'ionicons/icons';
|
|
||||||
|
|
||||||
const HKPartyIonDeleteIcon = ({ ...props }) => {
|
|
||||||
return <IonIcon icon={trashOutline} {...props} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HKPartyIonDeleteIcon;
|
|
@@ -1,8 +0,0 @@
|
|||||||
import { IonIcon } from '@ionic/react';
|
|
||||||
import { trashOutline } from 'ionicons/icons';
|
|
||||||
|
|
||||||
const HKPartyIonDeleteIcon = ({ ...props }) => {
|
|
||||||
return <IonIcon icon={trashOutline} {...props} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HKPartyIonDeleteIcon;
|
|
@@ -1,13 +0,0 @@
|
|||||||
import { IonHeader } from '@ionic/react';
|
|
||||||
|
|
||||||
const HKPartyIonHeader = ({ children, ...props }) => {
|
|
||||||
return (
|
|
||||||
<IonHeader translucent={true} className="ion-no-border" {...props}>
|
|
||||||
{/* */}
|
|
||||||
{children}
|
|
||||||
{/* */}
|
|
||||||
</IonHeader>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HKPartyIonHeader;
|
|
@@ -1,13 +0,0 @@
|
|||||||
import { IonPage } from '@ionic/react';
|
|
||||||
|
|
||||||
const HKPartyIonPage = ({ children, ...props }) => {
|
|
||||||
return (
|
|
||||||
<IonPage {...props}>
|
|
||||||
{/* */}
|
|
||||||
{children}
|
|
||||||
{/* */}
|
|
||||||
</IonPage>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HKPartyIonPage;
|
|
@@ -1,28 +0,0 @@
|
|||||||
import { IonToolbar } from '@ionic/react';
|
|
||||||
|
|
||||||
const HKPartyIonToolbar = ({ children, ...props }) => {
|
|
||||||
return (
|
|
||||||
<IonToolbar {...props}>
|
|
||||||
{/* */}
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'center',
|
|
||||||
color: 'black',
|
|
||||||
minHeight: '30px',
|
|
||||||
margin: '0 10px',
|
|
||||||
//
|
|
||||||
fontWeight: 'bold',
|
|
||||||
fontSize: '1.2rem',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
{/* */}
|
|
||||||
</IonToolbar>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HKPartyIonToolbar;
|
|
@@ -1,18 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Redirect } from 'react-router';
|
|
||||||
import { connect } from '../data/connect';
|
|
||||||
|
|
||||||
interface StateProps {
|
|
||||||
hasSeenTutorial: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HomeOrTutorial: React.FC<StateProps> = ({ hasSeenTutorial }) => {
|
|
||||||
return hasSeenTutorial ? <Redirect to="/tabs/schedule" /> : <Redirect to="/tutorial" />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect<{}, StateProps, {}>({
|
|
||||||
mapStateToProps: state => ({
|
|
||||||
hasSeenTutorial: state.user.hasSeenTutorial,
|
|
||||||
}),
|
|
||||||
component: HomeOrTutorial,
|
|
||||||
});
|
|
@@ -1,24 +0,0 @@
|
|||||||
import { IonContent, IonPage, IonSpinner, IonText } from '@ionic/react';
|
|
||||||
|
|
||||||
function Loading() {
|
|
||||||
return (
|
|
||||||
<IonPage>
|
|
||||||
<IonContent fullscreen={true} className="ion-padding">
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
height: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IonSpinner name="dots"></IonSpinner>
|
|
||||||
<IonText>{'Loading'}</IonText>
|
|
||||||
</div>
|
|
||||||
</IonContent>
|
|
||||||
</IonPage>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Loading;
|
|
@@ -1,52 +0,0 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
|
||||||
import { Location } from '../models/Location';
|
|
||||||
|
|
||||||
interface MapProps {
|
|
||||||
locations: Location[];
|
|
||||||
mapCenter: Location;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Map: React.FC<MapProps> = ({ mapCenter, locations }) => {
|
|
||||||
const mapEle = useRef<HTMLDivElement>(null);
|
|
||||||
const map = useRef<google.maps.Map>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
map.current = new google.maps.Map(mapEle.current, {
|
|
||||||
center: {
|
|
||||||
lat: mapCenter.lat,
|
|
||||||
lng: mapCenter.lng,
|
|
||||||
},
|
|
||||||
zoom: 16,
|
|
||||||
});
|
|
||||||
|
|
||||||
addMarkers();
|
|
||||||
|
|
||||||
google.maps.event.addListenerOnce(map.current, 'idle', () => {
|
|
||||||
if (mapEle.current) {
|
|
||||||
mapEle.current.classList.add('show-map');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function addMarkers() {
|
|
||||||
locations.forEach(markerData => {
|
|
||||||
let infoWindow = new google.maps.InfoWindow({
|
|
||||||
content: `<h5>${markerData.name}</h5>`,
|
|
||||||
});
|
|
||||||
|
|
||||||
let marker = new google.maps.Marker({
|
|
||||||
position: new google.maps.LatLng(markerData.lat, markerData.lng),
|
|
||||||
map: map.current!,
|
|
||||||
title: markerData.name,
|
|
||||||
});
|
|
||||||
|
|
||||||
marker.addListener('click', () => {
|
|
||||||
infoWindow.open(map.current!, marker);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [mapCenter, locations]);
|
|
||||||
|
|
||||||
return <div ref={mapEle} className="map-canvas"></div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Map;
|
|
@@ -1,88 +0,0 @@
|
|||||||
ion-menu ion-content {
|
|
||||||
--padding-top: 20px;
|
|
||||||
--padding-bottom: 20px;
|
|
||||||
|
|
||||||
--background: var(--ion-item-background, var(--ion-background-color, #fff));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove background transitions for switching themes */
|
|
||||||
ion-menu ion-item {
|
|
||||||
--transition: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-item.selected {
|
|
||||||
--color: var(--ion-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Material Design Menu
|
|
||||||
*/
|
|
||||||
ion-menu.md ion-list {
|
|
||||||
padding: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-menu.md ion-list-header {
|
|
||||||
padding-left: 18px;
|
|
||||||
padding-right: 18px;
|
|
||||||
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.1em;
|
|
||||||
font-size: min(0.875rem, 32px);
|
|
||||||
font-weight: 450;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-menu.md ion-item {
|
|
||||||
--padding-start: 18px;
|
|
||||||
|
|
||||||
margin-right: 10px;
|
|
||||||
|
|
||||||
border-radius: 0 50px 50px 0;
|
|
||||||
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-menu.md ion-item.selected {
|
|
||||||
--background: rgba(var(--ion-color-primary-rgb), 0.14);
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-menu.md ion-item.selected ion-icon {
|
|
||||||
color: var(--ion-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-menu.md ion-list-header,
|
|
||||||
ion-menu.md ion-item ion-icon {
|
|
||||||
color: var(--ion-color-step-650, #5f6368);
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-menu.md ion-list:not(:last-of-type) {
|
|
||||||
border-bottom: 1px solid var(--ion-color-step-150, #d7d8da);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* iOS Menu
|
|
||||||
*/
|
|
||||||
ion-menu.ios ion-list-header {
|
|
||||||
padding-left: 16px;
|
|
||||||
padding-right: 16px;
|
|
||||||
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-size: clamp(22px, 1.375rem, 40px);
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-menu.ios ion-list {
|
|
||||||
padding: 20px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-menu.ios ion-item {
|
|
||||||
--padding-start: 16px;
|
|
||||||
--min-height: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-menu.ios ion-item ion-icon {
|
|
||||||
font-size: 24px;
|
|
||||||
color: #73849a;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-menu.ios ion-item.selected ion-icon {
|
|
||||||
color: var(--ion-color-primary);
|
|
||||||
}
|
|
@@ -1,138 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { RouteComponentProps, useLocation, withRouter } from 'react-router';
|
|
||||||
|
|
||||||
import {
|
|
||||||
IonContent,
|
|
||||||
IonIcon,
|
|
||||||
IonItem,
|
|
||||||
IonLabel,
|
|
||||||
IonList,
|
|
||||||
IonListHeader,
|
|
||||||
IonMenu,
|
|
||||||
IonMenuToggle,
|
|
||||||
IonToggle,
|
|
||||||
} from '@ionic/react';
|
|
||||||
import {
|
|
||||||
calendarOutline,
|
|
||||||
hammer,
|
|
||||||
help,
|
|
||||||
informationCircleOutline,
|
|
||||||
logIn,
|
|
||||||
logOut,
|
|
||||||
mapOutline,
|
|
||||||
moonOutline,
|
|
||||||
peopleOutline,
|
|
||||||
person,
|
|
||||||
personAdd,
|
|
||||||
} from 'ionicons/icons';
|
|
||||||
|
|
||||||
import { connect } from '../data/connect';
|
|
||||||
import { setDarkMode } from '../data/user/user.actions';
|
|
||||||
|
|
||||||
import './Menu.css';
|
|
||||||
|
|
||||||
const routes = {
|
|
||||||
appPages: [
|
|
||||||
{ title: 'Schedule', path: '/tabs/schedule', icon: calendarOutline },
|
|
||||||
{ title: 'Speakers', path: '/tabs/speakers', icon: peopleOutline },
|
|
||||||
{ title: 'Map', path: '/tabs/map', icon: mapOutline },
|
|
||||||
{ title: 'About', path: '/tabs/about', icon: informationCircleOutline },
|
|
||||||
{ title: 'Message', path: '/tabs/message', icon: informationCircleOutline },
|
|
||||||
{ title: 'Profile', path: '/tabs/profile', icon: informationCircleOutline },
|
|
||||||
],
|
|
||||||
loggedInPages: [
|
|
||||||
{ title: 'Account', path: '/account', icon: person },
|
|
||||||
{ title: 'Support', path: '/support', icon: help },
|
|
||||||
{ title: 'Logout', path: '/logout', icon: logOut },
|
|
||||||
],
|
|
||||||
loggedOutPages: [
|
|
||||||
{ title: 'Login', path: '/login', icon: logIn },
|
|
||||||
{ title: 'Support', path: '/support', icon: help },
|
|
||||||
{ title: 'Signup', path: '/signup', icon: personAdd },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
interface Pages {
|
|
||||||
title: string;
|
|
||||||
path: string;
|
|
||||||
icon: string;
|
|
||||||
routerDirection?: string;
|
|
||||||
}
|
|
||||||
interface StateProps {
|
|
||||||
darkMode: boolean;
|
|
||||||
isAuthenticated: boolean;
|
|
||||||
menuEnabled: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DispatchProps {
|
|
||||||
setDarkMode: typeof setDarkMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MenuProps extends RouteComponentProps, StateProps, DispatchProps {}
|
|
||||||
|
|
||||||
const Menu: React.FC<MenuProps> = ({ darkMode, history, isAuthenticated, setDarkMode, menuEnabled }) => {
|
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
function renderlistItems(list: Pages[]) {
|
|
||||||
return list
|
|
||||||
.filter(route => !!route.path)
|
|
||||||
.map(p => (
|
|
||||||
<IonMenuToggle key={p.title} auto-hide="false">
|
|
||||||
<IonItem
|
|
||||||
detail={false}
|
|
||||||
routerLink={p.path}
|
|
||||||
routerDirection="none"
|
|
||||||
className={location.pathname.startsWith(p.path) ? 'selected' : undefined}
|
|
||||||
>
|
|
||||||
<IonIcon slot="start" icon={p.icon} />
|
|
||||||
<IonLabel>{p.title}</IonLabel>
|
|
||||||
</IonItem>
|
|
||||||
</IonMenuToggle>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IonMenu type="overlay" disabled={!menuEnabled} contentId="main">
|
|
||||||
<IonContent forceOverscroll={false}>
|
|
||||||
<IonList lines="none">
|
|
||||||
<IonListHeader>Conference</IonListHeader>
|
|
||||||
{renderlistItems(routes.appPages)}
|
|
||||||
</IonList>
|
|
||||||
<IonList lines="none">
|
|
||||||
<IonListHeader>Account</IonListHeader>
|
|
||||||
{isAuthenticated ? renderlistItems(routes.loggedInPages) : renderlistItems(routes.loggedOutPages)}
|
|
||||||
<IonItem>
|
|
||||||
<IonIcon slot="start" icon={moonOutline} aria-hidden="true"></IonIcon>
|
|
||||||
<IonToggle checked={darkMode} onClick={() => setDarkMode(!darkMode)}>
|
|
||||||
Dark Mode
|
|
||||||
</IonToggle>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
<IonList lines="none">
|
|
||||||
<IonListHeader>Tutorial</IonListHeader>
|
|
||||||
<IonItem
|
|
||||||
button
|
|
||||||
onClick={() => {
|
|
||||||
history.push('/tutorial');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IonIcon slot="start" icon={hammer} />
|
|
||||||
<IonLabel>Show Tutorial</IonLabel>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
</IonContent>
|
|
||||||
</IonMenu>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect<{}, StateProps, {}>({
|
|
||||||
mapStateToProps: state => ({
|
|
||||||
darkMode: state.user.darkMode,
|
|
||||||
isAuthenticated: state.user.isLoggedin,
|
|
||||||
menuEnabled: state.data.menuEnabled,
|
|
||||||
}),
|
|
||||||
mapDispatchToProps: {
|
|
||||||
setDarkMode,
|
|
||||||
},
|
|
||||||
component: withRouter(Menu),
|
|
||||||
});
|
|
@@ -1,23 +0,0 @@
|
|||||||
import { IonRouterContext } from '@ionic/react';
|
|
||||||
import React, { useContext, useEffect } from 'react';
|
|
||||||
|
|
||||||
interface RedirectToLoginProps {
|
|
||||||
setIsLoggedIn: Function;
|
|
||||||
setUsername: Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RedirectToLogin: React.FC<RedirectToLoginProps> = ({ setIsLoggedIn, setUsername }) => {
|
|
||||||
const ionRouterContext = useContext(IonRouterContext);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsLoggedIn(false);
|
|
||||||
setUsername(undefined);
|
|
||||||
ionRouterContext.push('/tabs/schedule');
|
|
||||||
|
|
||||||
console.error('who call this ?');
|
|
||||||
}, [setIsLoggedIn, setUsername]);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RedirectToLogin;
|
|
@@ -1,54 +0,0 @@
|
|||||||
import { CreateAnimation, IonButton, IonCol, IonIcon, IonLabel, IonRow } from '@ionic/react';
|
|
||||||
import { closeCircleOutline } from 'ionicons/icons';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
// import './style.css';
|
|
||||||
const ReplyTo = ({ contact, replyToMessage = false, replyToAnimationRef, setReplyToMessage, messageSent }) => {
|
|
||||||
const [cancellingReplyTo, setCancellingReplyTo] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
messageSent && cancelReplyTo();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [messageSent]);
|
|
||||||
|
|
||||||
const slideAnimation = {
|
|
||||||
property: 'transform',
|
|
||||||
fromValue: 'translateY(100px)',
|
|
||||||
toValue: 'translateY(0px)',
|
|
||||||
};
|
|
||||||
|
|
||||||
const replyToAnimation = {
|
|
||||||
duration: 300,
|
|
||||||
direction: !cancellingReplyTo ? 'normal' : 'reverse',
|
|
||||||
iterations: '1',
|
|
||||||
fromTo: [slideAnimation],
|
|
||||||
easing: 'ease-in-out',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cancel the reply-to
|
|
||||||
const cancelReplyTo = async () => {
|
|
||||||
setCancellingReplyTo(true);
|
|
||||||
await replyToAnimationRef.current.animation.play();
|
|
||||||
setCancellingReplyTo(false);
|
|
||||||
setReplyToMessage(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CreateAnimation ref={replyToAnimationRef} {...replyToAnimation}>
|
|
||||||
<IonRow className="ion-align-items-center chat-reply-to-row" id="replyTo">
|
|
||||||
<IonCol size="10" className="chat-reply-to-container">
|
|
||||||
<IonLabel className="chat-reply-to-name">{contact}</IonLabel>
|
|
||||||
<IonLabel className="chat-reply-to-message">{replyToMessage.preview}</IonLabel>
|
|
||||||
</IonCol>
|
|
||||||
|
|
||||||
<IonCol size="1">
|
|
||||||
<IonButton fill="clear" onClick={cancelReplyTo}>
|
|
||||||
<IonIcon size="large" icon={closeCircleOutline} color="primary" />
|
|
||||||
</IonButton>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
</CreateAnimation>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ReplyTo;
|
|
@@ -1,290 +0,0 @@
|
|||||||
.chat-page ion-header,
|
|
||||||
.chat-page ion-toolbar {
|
|
||||||
|
|
||||||
--min-height: 3.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-page ion-title {
|
|
||||||
|
|
||||||
margin-left: -3.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-page ion-title p {
|
|
||||||
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-contact {
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-content: center;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-contact img {
|
|
||||||
|
|
||||||
height: 2rem;
|
|
||||||
width: 2rem;
|
|
||||||
border-radius: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-contact-details {
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-contact-details p {
|
|
||||||
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-contact-details ion-text {
|
|
||||||
|
|
||||||
font-size: 0.7rem;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-bubble {
|
|
||||||
|
|
||||||
border-radius: 5px;
|
|
||||||
margin-left: 1rem;
|
|
||||||
margin-right: 1rem;
|
|
||||||
margin-top: 0.8rem;
|
|
||||||
|
|
||||||
padding: 0.5rem;
|
|
||||||
max-width: 80%;
|
|
||||||
clear: both;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
transition: 0.2s all linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-bubble:last-child {
|
|
||||||
|
|
||||||
margin-bottom: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble-sent {
|
|
||||||
|
|
||||||
background-color: var(--chat-bubble-sent-color);
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble-received {
|
|
||||||
|
|
||||||
background-color: var(--chat-bubble-received-color);
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-bubble p {
|
|
||||||
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-footer {
|
|
||||||
|
|
||||||
/* background-color: rgb(22, 22, 22); */
|
|
||||||
border-top: 1px solid rgb(47, 47, 47);
|
|
||||||
padding-top: 0.2rem;
|
|
||||||
padding-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-footer ion-textarea {
|
|
||||||
|
|
||||||
/* background-color: rgb(31, 31, 31); */
|
|
||||||
background-color: rgba(32, 32, 32, 0.1);
|
|
||||||
/* border: 1px solid rgb(36, 36, 36); */
|
|
||||||
color: white;
|
|
||||||
border-radius: 25px;
|
|
||||||
padding-left: 0.5rem;
|
|
||||||
caret-color: var(--ion-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-footer ion-icon {
|
|
||||||
|
|
||||||
font-size: 1.5rem;
|
|
||||||
margin-top: 0.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-input-container {
|
|
||||||
|
|
||||||
width: 70%;
|
|
||||||
margin-right: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-send-button {
|
|
||||||
|
|
||||||
margin: 0 !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
position: absolute;
|
|
||||||
right: 17px;
|
|
||||||
margin-top: -0.2rem !important;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-content: center;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-send-button ion-icon {
|
|
||||||
|
|
||||||
color: white;
|
|
||||||
background-color: var(--ion-color-primary);
|
|
||||||
font-size: 1.1rem;
|
|
||||||
border-radius: 500px;
|
|
||||||
padding: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-time {
|
|
||||||
|
|
||||||
color: rgb(165, 165, 165);
|
|
||||||
font-size: 0.75rem;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0 !important;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble-arrow {
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
float: left;
|
|
||||||
left: 6px;
|
|
||||||
margin-top: -8px;
|
|
||||||
/* top: 0px; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble-arrow.alt {
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
bottom: 0px;
|
|
||||||
left: auto;
|
|
||||||
right: -3px;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble-arrow:after {
|
|
||||||
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
border-top: 15px solid var(--chat-bubble-received-color);
|
|
||||||
border-left: 15px solid transparent;
|
|
||||||
border-radius: 4px 0 0 0px;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble-arrow.alt:after {
|
|
||||||
|
|
||||||
border-top: 15px solid var(--chat-bubble-sent-color);
|
|
||||||
transform: scaleX(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-reply-to-row {
|
|
||||||
|
|
||||||
bottom: 70px !important;
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
border-left: 4px solid rgb(224, 176, 18);
|
|
||||||
width: 100%;
|
|
||||||
background-color: rgb(22, 22, 22);
|
|
||||||
border-top: 1px solid rgb(47, 47, 47);
|
|
||||||
padding: 0.5rem;
|
|
||||||
padding-bottom: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-reply-to-container {
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-reply-to-name {
|
|
||||||
|
|
||||||
color: rgb(224, 176, 18);
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-reply-to-message {
|
|
||||||
|
|
||||||
font-size: 0.8rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.all-chats {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-bottom-details {
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
width: 100%;
|
|
||||||
align-content: center;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
margin-top: 0.4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-bottom-details ion-icon {
|
|
||||||
|
|
||||||
font-size: 0.6rem;
|
|
||||||
color: grey;
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
margin-top: 0.05rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-bottom-details span {
|
|
||||||
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
color: rgb(190, 190, 190);
|
|
||||||
}
|
|
||||||
|
|
||||||
.in-chat-reply-to-container {
|
|
||||||
|
|
||||||
/* background-color: rgba(0, 0, 0, 0.2); */
|
|
||||||
border-left: 3px solid rgb(224, 176, 18);
|
|
||||||
height: fit-content;
|
|
||||||
padding: 0.5rem;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.in-chat-reply-to-container h1 {
|
|
||||||
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
color: rgb(224, 176, 18);
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.in-chat-reply-to-container p {
|
|
||||||
|
|
||||||
color: rgb(167, 167, 167);
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-container {
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
bottom: 4.5rem;
|
|
||||||
height: 5rem;
|
|
||||||
background-color: red;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
@@ -1,102 +0,0 @@
|
|||||||
import { AlertButton, IonAlert, IonItemDivider, IonItemGroup, IonLabel, IonList, IonListHeader } from '@ionic/react';
|
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
||||||
import { connect } from '../data/connect';
|
|
||||||
import { addFavorite, removeFavorite } from '../data/sessions/sessions.actions';
|
|
||||||
import { Schedule, Session } from '../models/Schedule';
|
|
||||||
import SessionListItem from './SessionListItem';
|
|
||||||
|
|
||||||
interface OwnProps {
|
|
||||||
schedule: Schedule;
|
|
||||||
listType: 'all' | 'favorites';
|
|
||||||
hide: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StateProps {
|
|
||||||
favoriteSessions: number[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DispatchProps {
|
|
||||||
addFavorite: typeof addFavorite;
|
|
||||||
removeFavorite: typeof removeFavorite;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SessionListProps extends OwnProps, StateProps, DispatchProps {}
|
|
||||||
|
|
||||||
const SessionList: React.FC<SessionListProps> = ({
|
|
||||||
addFavorite,
|
|
||||||
removeFavorite,
|
|
||||||
favoriteSessions,
|
|
||||||
hide,
|
|
||||||
schedule,
|
|
||||||
listType,
|
|
||||||
}) => {
|
|
||||||
const scheduleListRef = useRef<HTMLIonListElement>(null);
|
|
||||||
const [showAlert, setShowAlert] = useState(false);
|
|
||||||
const [alertHeader, setAlertHeader] = useState('');
|
|
||||||
const [alertMessage, setAlertMessage] = useState('');
|
|
||||||
const [alertButtons, setAlertButtons] = useState<(AlertButton | string)[]>([]);
|
|
||||||
|
|
||||||
const handleShowAlert = useCallback((header: string, message: string, buttons: AlertButton[]) => {
|
|
||||||
setAlertHeader(header);
|
|
||||||
setAlertMessage(message);
|
|
||||||
setAlertButtons(buttons);
|
|
||||||
setShowAlert(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (scheduleListRef.current) {
|
|
||||||
scheduleListRef.current.closeSlidingItems();
|
|
||||||
}
|
|
||||||
}, [hide]);
|
|
||||||
|
|
||||||
if (schedule.groups.length === 0 && !hide) {
|
|
||||||
return (
|
|
||||||
<IonList>
|
|
||||||
<IonListHeader>No Sessions Found</IonListHeader>
|
|
||||||
</IonList>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<IonList ref={scheduleListRef} style={hide ? { display: 'none' } : {}}>
|
|
||||||
{schedule.groups.map((group, index: number) => (
|
|
||||||
<IonItemGroup key={`group-${index}`}>
|
|
||||||
<IonItemDivider sticky>
|
|
||||||
<IonLabel>{group.time}</IonLabel>
|
|
||||||
</IonItemDivider>
|
|
||||||
{group.sessions.map((session: Session, sessionIndex: number) => (
|
|
||||||
<SessionListItem
|
|
||||||
onShowAlert={handleShowAlert}
|
|
||||||
isFavorite={favoriteSessions.indexOf(session.id) > -1}
|
|
||||||
onAddFavorite={addFavorite}
|
|
||||||
onRemoveFavorite={removeFavorite}
|
|
||||||
key={`group-${index}-${sessionIndex}`}
|
|
||||||
session={session}
|
|
||||||
listType={listType}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</IonItemGroup>
|
|
||||||
))}
|
|
||||||
</IonList>
|
|
||||||
<IonAlert
|
|
||||||
isOpen={showAlert}
|
|
||||||
header={alertHeader}
|
|
||||||
message={alertMessage}
|
|
||||||
buttons={alertButtons}
|
|
||||||
onDidDismiss={() => setShowAlert(false)}
|
|
||||||
></IonAlert>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect<OwnProps, StateProps, DispatchProps>({
|
|
||||||
mapStateToProps: state => ({
|
|
||||||
favoriteSessions: state.data.favorites,
|
|
||||||
}),
|
|
||||||
mapDispatchToProps: {
|
|
||||||
addFavorite,
|
|
||||||
removeFavorite,
|
|
||||||
},
|
|
||||||
component: SessionList,
|
|
||||||
});
|
|
@@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* Material Design
|
|
||||||
*/
|
|
||||||
|
|
||||||
.md .session-list-filter ion-toolbar ion-button {
|
|
||||||
text-transform: capitalize;
|
|
||||||
letter-spacing: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.md .session-list-filter ion-checkbox {
|
|
||||||
--checkbox-background-checked: transparent;
|
|
||||||
--border-color: transparent;
|
|
||||||
--border-color-checked: transparent;
|
|
||||||
--checkmark-color: var(--ion-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.md .session-list-filter ion-list {
|
|
||||||
background: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* iOS
|
|
||||||
*/
|
|
||||||
|
|
||||||
.ios .session-list-filter ion-list-header {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ios .session-list-filter ion-checkbox {
|
|
||||||
color: var(--ion-color-primary);
|
|
||||||
}
|
|
@@ -1,152 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { getMode } from '@ionic/core';
|
|
||||||
import {
|
|
||||||
IonButton,
|
|
||||||
IonButtons,
|
|
||||||
IonCheckbox,
|
|
||||||
IonContent,
|
|
||||||
IonFooter,
|
|
||||||
IonHeader,
|
|
||||||
IonIcon,
|
|
||||||
IonItem,
|
|
||||||
IonList,
|
|
||||||
IonListHeader,
|
|
||||||
IonTitle,
|
|
||||||
IonToolbar,
|
|
||||||
} from '@ionic/react';
|
|
||||||
import {
|
|
||||||
call,
|
|
||||||
cog,
|
|
||||||
colorPalette,
|
|
||||||
compass,
|
|
||||||
construct,
|
|
||||||
document,
|
|
||||||
hammer,
|
|
||||||
logoAngular,
|
|
||||||
logoIonic,
|
|
||||||
restaurant,
|
|
||||||
} from 'ionicons/icons';
|
|
||||||
|
|
||||||
import './SessionListFilter.css';
|
|
||||||
|
|
||||||
import { connect } from '../data/connect';
|
|
||||||
import { updateFilteredTracks } from '../data/sessions/sessions.actions';
|
|
||||||
|
|
||||||
interface OwnProps {
|
|
||||||
onDismissModal: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StateProps {
|
|
||||||
allTracks: string[];
|
|
||||||
filteredTracks: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DispatchProps {
|
|
||||||
updateFilteredTracks: typeof updateFilteredTracks;
|
|
||||||
}
|
|
||||||
|
|
||||||
type SessionListFilterProps = OwnProps & StateProps & DispatchProps;
|
|
||||||
|
|
||||||
const SessionListFilter: React.FC<SessionListFilterProps> = ({
|
|
||||||
allTracks,
|
|
||||||
filteredTracks,
|
|
||||||
onDismissModal,
|
|
||||||
updateFilteredTracks,
|
|
||||||
}) => {
|
|
||||||
const ios = getMode() === 'ios';
|
|
||||||
|
|
||||||
const toggleTrackFilter = (track: string) => {
|
|
||||||
if (filteredTracks.indexOf(track) > -1) {
|
|
||||||
updateFilteredTracks(filteredTracks.filter(x => x !== track));
|
|
||||||
} else {
|
|
||||||
updateFilteredTracks([...filteredTracks, track]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeselectAll = () => {
|
|
||||||
updateFilteredTracks([]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSelectAll = () => {
|
|
||||||
updateFilteredTracks([...allTracks]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const iconMap: { [key: string]: any } = {
|
|
||||||
Angular: logoAngular,
|
|
||||||
Documentation: document,
|
|
||||||
Food: restaurant,
|
|
||||||
Ionic: logoIonic,
|
|
||||||
Tooling: hammer,
|
|
||||||
Design: colorPalette,
|
|
||||||
Services: cog,
|
|
||||||
Workshop: construct,
|
|
||||||
Navigation: compass,
|
|
||||||
Communication: call,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<IonHeader translucent={true} className="session-list-filter">
|
|
||||||
<IonToolbar>
|
|
||||||
<IonButtons slot="start">
|
|
||||||
{ios && <IonButton onClick={onDismissModal}>Cancel</IonButton>}
|
|
||||||
{!ios && <IonButton onClick={handleDeselectAll}>Reset</IonButton>}
|
|
||||||
</IonButtons>
|
|
||||||
|
|
||||||
<IonTitle>Filter Sessions</IonTitle>
|
|
||||||
|
|
||||||
<IonButtons slot="end">
|
|
||||||
<IonButton onClick={onDismissModal} strong>
|
|
||||||
Done
|
|
||||||
</IonButton>
|
|
||||||
</IonButtons>
|
|
||||||
</IonToolbar>
|
|
||||||
</IonHeader>
|
|
||||||
|
|
||||||
<IonContent className="session-list-filter">
|
|
||||||
<IonList lines={ios ? 'inset' : 'full'}>
|
|
||||||
<IonListHeader>Tracks</IonListHeader>
|
|
||||||
|
|
||||||
{allTracks.map(track => (
|
|
||||||
<IonItem key={track}>
|
|
||||||
{ios && <IonIcon slot="start" icon={iconMap[track]} color="medium" aria-hidden="true" />}
|
|
||||||
<IonCheckbox
|
|
||||||
onIonChange={() => toggleTrackFilter(track)}
|
|
||||||
checked={filteredTracks.indexOf(track) !== -1}
|
|
||||||
color="primary"
|
|
||||||
value={track}
|
|
||||||
>
|
|
||||||
{track}
|
|
||||||
</IonCheckbox>
|
|
||||||
</IonItem>
|
|
||||||
))}
|
|
||||||
</IonList>
|
|
||||||
</IonContent>
|
|
||||||
|
|
||||||
{ios && (
|
|
||||||
<IonFooter>
|
|
||||||
<IonToolbar>
|
|
||||||
<IonButtons slot="start">
|
|
||||||
<IonButton onClick={handleDeselectAll}>Deselect All</IonButton>
|
|
||||||
</IonButtons>
|
|
||||||
<IonButtons slot="end">
|
|
||||||
<IonButton onClick={handleSelectAll}>Select All</IonButton>
|
|
||||||
</IonButtons>
|
|
||||||
</IonToolbar>
|
|
||||||
</IonFooter>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect<OwnProps, StateProps, DispatchProps>({
|
|
||||||
mapStateToProps: state => ({
|
|
||||||
allTracks: state.data.allTracks,
|
|
||||||
filteredTracks: state.data.filteredTracks,
|
|
||||||
}),
|
|
||||||
mapDispatchToProps: {
|
|
||||||
updateFilteredTracks,
|
|
||||||
},
|
|
||||||
component: SessionListFilter,
|
|
||||||
});
|
|