Commit 8ee1c779 authored by sliao's avatar sliao

feat: sdm

parent 5e3ddf56
...@@ -2,3 +2,4 @@ node_modules ...@@ -2,3 +2,4 @@ node_modules
.ray .ray
dist dist
app.config.ts app.config.ts
.DS_Store
\ No newline at end of file
This diff is collapsed.
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
"scripts": { "scripts": {
"lint": "eslint src --ext .js,.jsx,.ts,.tsx --fix", "lint": "eslint src --ext .js,.jsx,.ts,.tsx --fix",
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"start": "ray start -t tuya --verbose",
"start:native": "ray start -t native --verbose", "start:native": "ray start -t native --verbose",
"start:tuya": "ray start -t tuya --verbose", "start:tuya": "ray start -t tuya --verbose",
"build:native": "ray build -t native --verbose", "build:native": "ray build -t native --verbose",
...@@ -15,12 +16,17 @@ ...@@ -15,12 +16,17 @@
"@ray-js/panel-sdk": "^1.1.3", "@ray-js/panel-sdk": "^1.1.3",
"@ray-js/ray": "^0.6.13", "@ray-js/ray": "^0.6.13",
"@ray-js/sdm-react": "^1.0.6", "@ray-js/sdm-react": "^1.0.6",
"@tuya-miniapp/sdm": "^1.1.0-beta.0",
"clsx": "^1.2.1",
"deep-object-diff": "^1.1.0", "deep-object-diff": "^1.1.0",
"react-redux": "^7.2.1", "react-redux": "^7.2.1",
"redux": "^4.1.2", "redux": "^4.1.2",
"redux-actions": "^2.6.5", "redux-actions": "^2.6.5",
"redux-logger": "^3.0.6", "redux-logger": "^3.0.6",
"redux-thunk": "^2.4.0" "redux-thunk": "^2.4.0",
"ahooks": "^3.7.1",
"core-js": "^3.23.5",
"dayjs": "^1.11.5"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^7.2.1", "@commitlint/cli": "^7.2.1",
...@@ -37,7 +43,11 @@ ...@@ -37,7 +43,11 @@
"husky": "^1.2.0", "husky": "^1.2.0",
"lint-staged": "^10.2.11", "lint-staged": "^10.2.11",
"prettier": "^1.16.4", "prettier": "^1.16.4",
"typescript": "^4.4.3" "typescript": "^4.4.3",
"@types/react": "^17.0.24",
"@types/react-dom": "^17.0.9",
"react": "^17.0.2",
"react-dom": "^17.0.2"
}, },
"husky": { "husky": {
"hooks": { "hooks": {
......
...@@ -10,5 +10,5 @@ ...@@ -10,5 +10,5 @@
"TYKit": "2.3.4", "TYKit": "2.3.4",
"DeviceKit": "2.4.7" "DeviceKit": "2.4.7"
}, },
"productId": "hcwquhbmkmjatays" "productId": "88lyidpn8pajyg2o"
} }
\ No newline at end of file
...@@ -3,18 +3,30 @@ import { kit } from '@ray-js/panel-sdk'; ...@@ -3,18 +3,30 @@ import { kit } from '@ray-js/panel-sdk';
import 'ray'; import 'ray';
import '@/i18n'; import '@/i18n';
import composeLayout from './composeLayout'; import composeLayout from './composeLayout';
import { SdmProvider } from '@ray-js/sdm-react';
import { devices } from '@/devices';
import { ProductName } from '@/constants';
const { initPanelEnvironment } = kit; const { initPanelEnvironment } = kit;
interface Props {
children: React.ReactNode
}
initPanelEnvironment({ useDefaultOffline: true }); initPanelEnvironment({ useDefaultOffline: true });
class App extends React.Component { class App extends React.Component<Props> {
componentDidMount() { componentDidMount() {
console.info('app did mount '); console.info('app did mount ');
} }
onLaunch(e) {
console.info(e, '=== App onLaunch');
}
render() { render() {
return this.props.children; return <SdmProvider value={devices[ProductName]} >{this.props.children}</SdmProvider>;
} }
} }
export default composeLayout(App); export default App
import React from 'react';
import { DpBooleanAction, DpCommonAction } from '@tuya-miniapp/sdm';
import { useSdmProps } from '@ray-js/sdm-react';
import {devices} from '@/devices'
import styles from '../../pages/home/index.module.less';
import { Image, View, Text } from '@ray-js/components'
import horizontalOff from '../../../public/icon-horizontal-off.png'
import horizontalOn from '../../../public/icon-horizontal-on.png'
import verticalOn from '../../../public/icon-vertical-on.png'
import verticalOff from '../../../public/icon-vertical-off.png'
export const FanVertical = React.memo(() => {
const fanHorizontalCode = 'fan_horizontal'
const fanVerticalCode = 'fan_vertical'
const fanVerticalSwitchCode = 'switch_vertical'
const fanHorizontalSwitchCode = 'switch_horizontal'
const fanHorizontalVal = useSdmProps((props) => props[fanHorizontalCode])
const fanVerticalVal = useSdmProps((props) => props[fanVerticalCode])
const fanHorizontalSwitch = useSdmProps((props) => props[fanHorizontalSwitchCode])
const fanVerticalSwitch = useSdmProps((props) => props[fanVerticalSwitchCode])
const device = devices.fan
const actions = device.model.actions
const fanHorizontalAction = actions[fanHorizontalCode] as DpCommonAction<string>
const fanVerticalAction = actions[fanVerticalCode] as DpCommonAction<string>
const fanHorizontalSwitchAction = actions[fanHorizontalSwitchCode] as DpBooleanAction
const fanVerticalSwitchAction = actions[fanVerticalSwitchCode] as DpBooleanAction
const verticalMenu = [
{
title:'Vertical',
icon:verticalOn,
offIcon: verticalOff,
mode:'vertical',
value: fanVerticalVal
}, {
title:'Horizontal',
icon:horizontalOn,
offIcon: horizontalOff,
mode:'horizontal',
value: fanHorizontalVal
}
]
function handleVerticalMenu(index) {
if (index == 0) {
// if (!fanVerticalSwitch) {
// fanVerticalAction.set(String(90))
// fanVerticalSwitchAction.on()
// } else {
// fanVerticalSwitchAction.off()
// }
fanVerticalSwitchAction.toggle()
}else {
// if (!fanHorizontalSwitch) {
// fanHorizontalAction.set(String(90))
// fanHorizontalSwitchAction.on()
// }else {
// fanHorizontalSwitchAction.off()
// }
fanHorizontalSwitchAction.toggle()
}
}
function submenuItem(index, arr = verticalMenu, clickFun = handleVerticalMenu) {
const isOn = index == 0 ? fanVerticalSwitch : fanHorizontalSwitch
const {title, icon, offIcon} = arr[index]
return (
<View className={styles.button}>
<Image className={styles.menuImage} src={isOn ? icon:offIcon} onClick={() => {clickFun(index)}}/>
<Text className={styles.bTitle}>{title}</Text>
</View>
)
}
const clickNum = (val, index) => {
console.log(val, index)
if (index == 0 && fanVerticalSwitch) {
fanVerticalAction.set(String(val))
}else if (index == 1 && fanHorizontalSwitch) {
fanHorizontalAction.set(String(val))
}
}
function verticalItem(index) {
const item = verticalMenu[index]
const isBlack = (index == 0 && fanVerticalSwitch) || (index == 1 && fanHorizontalSwitch)
const textColor = (subIndex) => {
return {color: isBlack && (Number(item.value) == (subIndex * 30)) ? '#000000':'rgba(0,0,0,0.4)',
width:'33.3%',
textAlign:'center',
lineHeight:'64px',
fontWeight:'500'
}}
return (
<View className={styles.verticalItem}>
{submenuItem(index, verticalMenu, handleVerticalMenu)}
<View className={styles.verticalNumBg}>
<View style={textColor(1)} onClick={() => {clickNum(30, index)}}>30</View>
<View style={textColor(2)} onClick={() => {clickNum(60, index)}}>60</View>
<View style={textColor(3)} onClick={() => {clickNum(90, index)}}>90</View>
</View>
</View>
)
}
return (
<View>
{
verticalItem(0)
}
{
verticalItem(1)
}
</View>
)
})
\ No newline at end of file
export { default as Connect } from './connect'; export { default as Connect } from './connect';
export { LightPanel } from './light-panel';
export {FanVertical} from './fan-vertical';
\ No newline at end of file
.lightModal {
display: flex;
flex-direction: column;
// margin: auto;
width: 340px;
height: 424px;
background-color: white;
border-radius: 24px;
// position: absolute;
// top: calc(50vh - 175px);
margin-top: 90px;
padding: 20px 0 40px;
color: #000000;
z-index: 9999;
}
.lightModalTop {
display: flex;
align-items: center;
padding: 0 20px;
width: 100%;
justify-content: space-between;
font-size: 16px;
margin-bottom: 40px;
}
.lightModalClose {
width: 24px;
height: 24px;
}
.lightMiddle {
// width: 100%;
height: 155px;
border-radius: 16px 16px 0 0;
background: linear-gradient(-90deg, #cdecfe 0%, #ffffff 45.46%, #ffffff 53.61%, #ffca5c 100%);
position: relative;
margin: 0 3px;
}
.modalback {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
display: flex;
// align-items: center;
justify-content: center;
}
.moveCircle {
width: 28px;
height: 28px;
border: 3px solid #ffffff;
border-radius: 14px;
position: absolute;
background-color: #ffca5c;
z-index: 10001;
}
.progressBg {
// width: 100%;
height: 40px;
border-radius: 0 0 16px 16px;
background-color: #e5e5e5;
display: flex;
align-items: center;
padding-left: 16px;
font-size: 16px;
position: relative;
overflow: hidden;
margin: 0 3px;
}
.progressSun {
width: 21px;
height: 21px;
margin-right: 9px;
z-index: 9999;
}
.progressTitle {
z-index: 9999;
}
.progressBar {
background-color: white;
position: absolute;
left: 1px;
top: 1px;
bottom: 1px;
z-index: 2;
}
.lightBtn {
width: 310px;
height: 56px;
border-radius: 24px;
align-items: center;
justify-content: center;
display: flex;
background-color: rgba(0, 0, 0, 0.5);
align-self: center;
margin-top: 48px;
}
.lightBtnIcon {
width: 18px;
height: 26px;
margin-right: 14px;
}
.lightBtnTitle {
color: white;
font-size: 18px;
}
.lightBtnSubtitle {
color: rgba(255, 255, 255, 0.5);
font-size: 14px;
}
import React, {useState} from 'react';
import clsx from 'clsx';
// import { View } from '@ray-js/ray';
import { DpBooleanAction, DpCommonAction } from '@tuya-miniapp/sdm';
import { useSdmProps } from '@ray-js/sdm-react';
import {devices} from '@/devices'
import styles from './index.module.less';
import { Image, View } from '@ray-js/components'
import iconClose from '../../../public/icon-close.png'
import iconLightWhite from '../../../public/icon-light-white.png'
import iconSun from '../../../public/icon-sun.png'
export interface Props {
isShow: boolean;
setModal: (boolean) => void;
}
export const LightPanel = React.memo<Props>(props => {
const brightRange = [10, 1000]
const cctRange = [0, 1000]
const brightCode = 'bright_value'
const temperatureCode = 'temp_value'
const switchCode = 'switch_led'
let { isShow, setModal} = props;
const brightVal = useSdmProps(dpState => {
const val = dpState[brightCode]
const percent = getValueInRange(val || 10, brightRange).percent
return percent
});
const temperatureVal = useSdmProps(dpState => {
const val = dpState[temperatureCode]
const percent = getValueInRange(val || 0, cctRange).percent
return percent
});
const switchVal = useSdmProps(dpState => dpState[switchCode]);
const device = devices.fan
const actions = device.model.actions
const switchAction = actions[switchCode] as DpBooleanAction
const brightAction = actions[brightCode] as DpCommonAction<number>
const temperatureAction = actions[temperatureCode] as DpCommonAction<number>
const [point, setPoint] = useState({pageX: getOrignPointX(), pageY: 234})
const [selectColor, setColor] = useState(pointToRGB(point.pageX))
function getOrignPointX() {
const percent = getValueInRange(temperatureVal || 10, cctRange).percent
return 28 + 319 * Number((percent / 100).toFixed())
}
//0-value是值 1-value是百分比 左边最小值是0,右边最大值range[1],起始位置rang[0]
function getValueInRange(value, range, type = 0) {
const percent = type == 0 ? (value / range[1]) : (value / 100)
return {
value: type == 0 ? value : (percent * range[1]).toFixed(),
percent: type == 0 ? (percent * 100).toFixed() : value
}
}
function touchStart(e) {
console.log(e, 'start touch');
handlePoint(e)
}
function handleTouch(e) {
// console.log(e, 'handle touch');
// const point = e.changedTouches.length && e.changedTouches[0]
// console.log(point, 'pppppppoint');
// handlePoint(e)
}
function touchEnd(e) {
console.log(e, 'end touch');
handlePoint(e)
}
function handlePoint(e) {
const {pageX, pageY} = e.changedTouches.length && e.changedTouches[0]
let x = pageX
let y = pageY
const xRange = [28, 347]
if (pageX < xRange[0]) {
x = xRange[0]
}
if (pageX > xRange[1]) {
x = xRange[1]
}
if (pageY < 197) {
y = 197
}
if (pageY > 320) {
y =320
}
setPoint({pageX: x, pageY: y})
const percent = (x - xRange[0]) / (xRange[1] - xRange[0])
const value = Number((cctRange[1] * percent).toFixed())
setColor(pointToRGB(x))
temperatureAction.set(value)
}
function pointToRGB(pageX) {
const half = (347 - 28) / 2
const middle = 28 + half
const leftColor = {h: 40, s:100, l:68}
const rightColor = {h:202,s:96, l:90}
let newL = leftColor.l
let color = leftColor
if (pageX < middle) {
newL = 68 + (pageX - 28) * 32 / 161
leftColor.l = newL
color = leftColor
} else {
newL = 100 - (pageX - middle) * 10 / 161
rightColor.l = newL
color = rightColor
}
return HSLToRGB(color)
}
function HSLToRGB({h, s, l}) {
s /= 100;
l /= 100;
const k = n => (n + h / 30) % 12;
const a = s * Math.min(l, 1 - l);
const f = n =>
l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
const color = `rgb(${255 * f(0)}, ${255 * f(8)}, ${255 * f(4)})`
console.log(color)
return color
};
const handleLightOn = () => {
switchAction.toggle()
}
function progressStart(e) {
console.log(e, 'progressStart');
handleProgress(e)
}
function progressMove(e) {
console.log(e, 'progressMove');
// handleProgress(e)
}
function progressEnd(e) {
console.log(e, 'progressEnd');
handleProgress(e)
}
function handleProgress(e) {
let {pageX, pageY} = e.changedTouches.length && e.changedTouches[0]
const xRange = [20,335]
let differ = pageX - xRange[0]
if (differ < 0) {
differ = 0
} else if (differ > xRange[1]) {
differ = xRange[1]
}
const percent = differ / (xRange[1] - xRange[0]);
const value = Number((brightRange[1] * percent).toFixed())
brightAction.set(value)
}
return (
<View className={styles.modalback} style={{background:'rgba(0, 0, 0, 0.1)',backdropFilter:'blur(35px)', visibility: isShow ? 'visible':'hidden' }}>
<View className={styles.lightModal}>
<View className={styles.lightModalTop}>
<View>Light
</View>
<Image className={styles.lightModalClose} src={iconClose} onClick={() => {
setModal(false)
}}></Image>
</View>
<View
className={styles.lightMiddle}
onTouchStart={touchStart}
onTouchMove={handleTouch}
onTouchEnd={touchEnd}
>
</View>
<View className={styles.progressBg} onTouchStart={progressStart}
onTouchMove={progressMove}
onTouchEnd={progressEnd}>
<Image className={styles.progressSun} src={iconSun}></Image>
<View className={styles.progressTitle}>{brightVal}%</View>
<View className={styles.progressBar} style={{width: brightVal + '%'}}></View>
</View>
<View className={styles.lightBtn} style={{backgroundColor: switchVal ? '#6395F6':'rgba(0,0,0,0.5)'}} onClick={handleLightOn}>
<Image className={styles.lightBtnIcon} src={iconLightWhite}></Image>
<View className={styles.lightBtnTitle}>{switchVal ? 'ON':'OFF'}</View>
<View className={styles.lightBtnSubtitle}>{switchVal ? '/OFF':'/ON'}</View>
</View>
</View>
<View className={styles.moveCircle} style={{top: point.pageY - 14 + 'px', left: point.pageX - 14 + 'px', backgroundColor: selectColor}}></View>
</View>
)
})
\ No newline at end of file
export const ProductName = 'fan';
import { SmartDeviceModel } from '@tuya-miniapp/sdm';
// import { ProductName } from '@/constants';
type SmartDeviceSchema = typeof import('@/devices/schema').fanSchema;
// type SmartDevices = {
// [ProductName]?: import('@tuya-miniapp/sdm').SmartDeviceModel<SmartDeviceSchema>;
// };
export const devices = {
fan: new SmartDeviceModel<SmartDeviceSchema>(),
};
Object.keys(devices).forEach((k: keyof typeof devices) => {
devices[k].init();
});
This diff is collapsed.
import { GlobalConfig } from '@ray-js/types'; import { GlobalConfig } from '@ray-js/types';
export const wechat = { // export const wechat = {
window: { // window: {
backgroundColor: '#E3EEF6', // backgroundColor: '#E3EEF6',
navigationBarTitleText: 'Fan', // navigationBarTitleText: 'Fan',
navigationBarBackgroundColor: '#E3EEF6', // navigationBarBackgroundColor: '#E3EEF6',
navigationBarTextStyle: 'black', // navigationBarTextStyle: 'black',
}, // },
} // }
export const web = { // export const web = {
window: { // window: {
backgroundColor: '#E3EEF6', // backgroundColor: '#E3EEF6',
navigationBarTitleText: 'Fan', // navigationBarTitleText: 'Fan',
}, // },
}; // };
export const tuya = { export const tuya = {
window: { window: {
......
...@@ -172,119 +172,7 @@ ...@@ -172,119 +172,7 @@
height: 64px; height: 64px;
} }
.lightModal {
display: flex;
flex-direction: column;
// margin: auto;
width: 340px;
height: 424px;
background-color: white;
border-radius: 24px;
// position: absolute;
// top: calc(50vh - 175px);
margin-top: 90px;
padding: 20px 0 40px;
color: #000000;
z-index: 9999;
}
.lightModalTop {
display: flex;
align-items: center;
padding: 0 20px;
width: 100%;
justify-content: space-between;
font-size: 16px;
margin-bottom: 40px;
}
.lightModalClose {
width: 24px;
height: 24px;
}
.lightMiddle {
// width: 100%;
height: 155px;
border-radius: 16px 16px 0 0;
background: linear-gradient(-90deg, #cdecfe 0%, #ffffff 45.46%, #ffffff 53.61%, #ffca5c 100%);
position: relative;
margin: 0 3px;
}
.modalback {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
display: flex;
// align-items: center;
justify-content: center;
}
.moveCircle {
width: 28px;
height: 28px;
border: 3px solid #ffffff;
border-radius: 14px;
position: absolute;
background-color: #ffca5c;
z-index: 10001;
}
.progressBg {
// width: 100%;
height: 40px;
border-radius: 0 0 16px 16px;
background-color: #e5e5e5;
display: flex;
align-items: center;
padding-left: 16px;
font-size: 16px;
position: relative;
overflow: hidden;
margin: 0 3px;
}
.progressSun {
width: 21px;
height: 21px;
margin-right: 9px;
z-index: 9999;
}
.progressTitle {
z-index: 9999;
}
.progressBar {
background-color: white;
position: absolute;
left: 1px;
top: 1px;
bottom: 1px;
z-index: 2;
}
.lightBtn {
width: 310px;
height: 56px;
border-radius: 24px;
align-items: center;
justify-content: center;
display: flex;
background-color: rgba(0, 0, 0, 0.5);
align-self: center;
margin-top: 48px;
}
.lightBtnIcon {
width: 18px;
height: 26px;
margin-right: 14px;
}
.lightBtnTitle {
color: white;
font-size: 18px;
}
.lightBtnSubtitle {
color: rgba(255, 255, 255, 0.5);
font-size: 14px;
}
.swiperItem { .swiperItem {
display: flex; display: flex;
...@@ -301,8 +189,6 @@ ...@@ -301,8 +189,6 @@
padding-left: 20px; padding-left: 20px;
} }
.verticalBg {
}
.verticalItem { .verticalItem {
display: flex; display: flex;
......
This diff is collapsed.
import { Routes, TabBar } from '@ray-js/types'; import { Routes} from '@ray-js/types';
export const routes: Routes = [ export const routes: Routes = [
{ {
...@@ -6,16 +6,15 @@ export const routes: Routes = [ ...@@ -6,16 +6,15 @@ export const routes: Routes = [
path: '/pages/home/index', path: '/pages/home/index',
name: 'Home', name: 'Home',
}, },
{ // {
route: '/common/page4/index', // route: '/common/page4/index',
path: '/pages/common/page4/index', // path: '/pages/common/page4/index',
name: 'Page4', // name: 'Page4',
}, // },
{ // {
route: '/common/page6/index', // route: '/common/page6/index',
path: '/pages/common/page6/index', // path: '/pages/common/page6/index',
name: 'Page6', // name: 'Page6',
}, // },
]; ];
export const tabBar = {};
...@@ -3,11 +3,8 @@ ...@@ -3,11 +3,8 @@
"compilerOptions": { "compilerOptions": {
"target": "ES2015", "target": "ES2015",
"module": "CommonJS", "module": "CommonJS",
"lib": [ "lib": ["ESNext", "DOM"],
"ESNext", "declaration": true,
"DOM"
],
"declaration": false,
"declarationMap": false, "declarationMap": false,
"emitDeclarationOnly": false, "emitDeclarationOnly": false,
"moduleResolution": "node", "moduleResolution": "node",
...@@ -17,28 +14,13 @@ ...@@ -17,28 +14,13 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"noEmitOnError": false, "noEmitOnError": false,
"jsx": "react", "jsx": "react",
"baseUrl": "./",
"paths": { "paths": {
"ray": [ "ray": ["./.ray"],
"./.ray" "@/*": ["./src/*"],
], "@/components/*": ["./src/components/*"]
"@/*": [
"./src/*"
]
}, },
"typeRoots": [ "types": []
"./node_modules/@types",
"./typings",
"./node_modules/@tuya-miniapp"
],
}, },
"include": [ "include": ["src/**/*.*", ".ray/**/*.*", "typings"],
"src/**/*.*", "exclude": ["node_modules", "dist", "public"]
"ray/**/*.*",
"typings"
],
"exclude": [
"node_modules",
"dist"
]
} }
import { ProductName } from '@/constants';
type SmartDeviceSchema = typeof import('@/devices/schema').fanSchema;
type SmartDevices = {
[ProductName]?: import('@tuya-miniapp/sdm').SmartDeviceModel<SmartDeviceSchema>;
};
declare module '@ray-js/sdm-react' {
// export const SdmProvider: React.FC<{
// value: SmartDeviceModel<SmartDeviceSchema>;
// children: React.ReactNode;
// }>;
// export type SmartDeviceInstanceData = {
// devInfo: ReturnType<SmartDevices[ProductName]['getDevInfo']>;
// dpSchema: ReturnType<SmartDevices[ProductName]['getDpSchema']>;
// network: ReturnType<SmartDevices[ProductName]['getNetwork']>;
// bluetooth: ReturnType<SmartDevices[ProductName]['getBluetooth']>;
// };
// export function useProps(): SmartDevices[ProductName]['model']['props'];
// export function useProps<Value extends any>(
// selector: (props?: SmartDevices[ProductName]['model']['props']) => Value,
// equalityFn?: (a: Value, b: Value) => boolean
// ): Value;
// export function useDevice(): SmartDeviceInstanceData;
// export function useDevice<Device extends any>(
// selector: (device: SmartDeviceInstanceData) => Device,
// equalityFn?: (a: Device, b: Device) => boolean
// ): Device;
export function useActions(): SmartDevices[ProductName]['model']['actions'];
}
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment