*update: model improvement

*add: contextual menu
*update: ipc models
*update: post cards ux handlers & events listeners
This commit is contained in:
srgooglo 2020-09-17 21:23:11 +02:00
parent 2ed0835232
commit a863864644
14 changed files with 285 additions and 79 deletions

View File

@ -1,5 +1,4 @@
import { defineConfig } from 'umi'; import { defineConfig } from 'umi';
const { resolve, join } = require('path'); const { resolve, join } = require('path');
export default defineConfig({ export default defineConfig({

View File

@ -11,6 +11,7 @@ module.exports = {
api_prefix: 'ycorejs_apiv3', api_prefix: 'ycorejs_apiv3',
app_settings_storage: 'app_settings', app_settings_storage: 'app_settings',
endpoint_global: 'https://comty.pw',
session_token_storage: 'cid', session_token_storage: 'cid',
session_data_storage: 'data', session_data_storage: 'data',

View File

@ -41,17 +41,16 @@ if (!gotTheLock) {
app.quit(); app.quit();
} }
function contextualMenu(cords){ function contextualMenu(payload){
if (!cords) { if (!payload) {
return false return false
} }
log.log(cords)
const menu = new Menu() const menu = new Menu()
const menuItem = new MenuItem({ const menuItem = new MenuItem({
label: 'Inspect Element', label: 'Inspect Element',
click: () => { click: () => {
mainWindow.inspectElement(cords.x, cords.y) mainWindow.inspectElement(payload.x, payload.y)
} }
}) })
menu.append(menuItem) menu.append(menuItem)
@ -156,8 +155,8 @@ function createWindow() {
{ {
label: '🔄 Reload', label: '🔄 Reload',
click: () => { click: () => {
app.relaunch(); mainWindow.close()
mainWindow.close(); app.relaunch()
} }
}, },
{ {
@ -264,11 +263,15 @@ ipcMain.handle('appRestart', () => {
mainWindow.close(); mainWindow.close();
}); });
ipcMain.handle('app_notify', (payload) => { ipcMain.handle('app_notify', (event, payload) => {
notify(payload) notify(payload)
}) })
ipcMain.handle('contextualMenu', (payload) => { ipcMain.handle('contextualMenu', (event, payload) => {
log.log(payload)
contextualMenu(payload) contextualMenu(payload)
}) })
ipcMain.handle('inspectElement', (event, payload) => {
log.log(payload)
mainWindow.inspectElement(payload.x, payload.y)
})

View File

@ -3,7 +3,7 @@
"UUID": "C8mVSr-4nmPp2-pr5Vrz-CU4kg4", "UUID": "C8mVSr-4nmPp2-pr5Vrz-CU4kg4",
"title": "Comty™", "title": "Comty™",
"DevBuild": true, "DevBuild": true,
"version": "0.9.17", "version": "0.9.18",
"stage": "dev-pre", "stage": "dev-pre",
"description": "", "description": "",
"author": "RageStudio", "author": "RageStudio",
@ -41,6 +41,7 @@
"babel-core": "^6.26.3", "babel-core": "^6.26.3",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"cookie_js": "^1.4.0", "cookie_js": "^1.4.0",
"dotenv": "^8.2.0",
"electron-config": "^2.0.0", "electron-config": "^2.0.0",
"electron-context-menu": "^2.3.0", "electron-context-menu": "^2.3.0",
"electron-is": "^3.0.0", "electron-is": "^3.0.0",

View File

@ -1,21 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 74.31418 77.6027" style="opacity: 90%"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 74.31418 77.6027">
<defs> <defs>
<clipPath id="a" transform="translate(-62.852 -42.79726)"> <clipPath id="a" transform="translate(-62.852 -42.79726)">
<path d="M67.69373,97.48126A37.88848,37.88848,0,0,1,64.32235,81.962V51.74457a8,8,0,0,1,8-8h30.81344A40.20284,40.20284,0,0,1,115.504,45.80379V60.70226H99.244v5.1882a17.26642,17.26642,0,0,0-12.40516,13.0078H80.273v18.583Z" style="fill: none"/> <path d="M67.69373,97.48126A37.88884,37.88884,0,0,1,64.32235,81.962V47.74457a4,4,0,0,1,4-4h34.81344A40.202,40.202,0,0,1,115.504,45.80379V60.70226H99.244v5.1882a17.26642,17.26642,0,0,0-12.40516,13.0078H80.273v18.583Z" style="fill: none"/>
</clipPath> </clipPath>
</defs> </defs>
<g> <g>
<path <path d="M120.3477,73.49586l16.068-8.392a1.421,1.421,0,0,0,.581-1.925l-.004-.009a39.55488,39.55488,0,0,0-53.655-14.202q-.636.369-1.259.763a37.94591,37.94591,0,0,0-16.316,42.476,38.68947,38.68947,0,0,0,8.549,15.373,39.509,39.509,0,0,0,44.645,9.645,38.33887,38.33887,0,0,0,9.525-5.922,1.41255,1.41255,0,0,0,.121-1.993l-.019-.02-11.979-12.759a1.4336,1.4336,0,0,0-1.902-.165,17.03956,17.03956,0,0,1-3.628,2.287,17.77554,17.77554,0,0,1-9.363,1.361c-8.267-1.065-12.512-7.864-12.815-8.361a17.70265,17.70265,0,0,1-2.409-10.684,17.95939,17.95939,0,0,1,6.646-12.087,17.73033,17.73033,0,0,1,24.61,3.044c.283.361.549.734.802,1.117a1.413,1.413,0,0,0,1.802.453" transform="translate(-62.852 -42.79726)" style="fill: #ff6064"/>
d="M120.3477,73.49586l16.068-8.392a1.421,1.421,0,0,0,.581-1.925l-.004-.009a39.55488,39.55488,0,0,0-53.655-14.202q-.636.369-1.259.763a37.94591,37.94591,0,0,0-16.316,42.476,38.68947,38.68947,0,0,0,8.549,15.373,39.509,39.509,0,0,0,44.645,9.645,38.33887,38.33887,0,0,0,9.525-5.922,1.41255,1.41255,0,0,0,.121-1.993l-.019-.02-11.979-12.759a1.4336,1.4336,0,0,0-1.902-.165,17.03956,17.03956,0,0,1-3.628,2.287,17.77554,17.77554,0,0,1-9.363,1.361c-8.267-1.065-12.512-7.864-12.815-8.361a17.70265,17.70265,0,0,1-2.409-10.684,17.95939,17.95939,0,0,1,6.646-12.087,17.73033,17.73033,0,0,1,24.61,3.044c.283.361.549.734.802,1.117a1.413,1.413,0,0,0,1.802.453"
transform="translate(-62.852 -42.79726)"
style="fill: #333333"/>
<g style="clip-path: url(#a)"> <g style="clip-path: url(#a)">
<rect width="17.421" height="17.905" style="fill: #333333"/> <rect width="17.421" height="17.905" style="fill: #ff7c8c"/>
<rect x="17.421" width="18.971" height="17.905" style="fill: #333333"/> <rect x="17.421" width="18.971" height="17.905" style="fill: #ff4971"/>
<rect x="36.392" width="16.26" height="17.905" style="fill: #333333"/> <rect x="36.392" width="16.26" height="17.905" style="fill: #ff4457"/>
<rect y="17.905" width="17.421" height="18.196" style="fill: #333333"/> <rect y="17.905" width="17.421" height="18.196" style="fill: #ff4971"/>
<rect y="36.101" width="17.421" height="18.583" style="fill: #333333"/> <rect y="36.101" width="17.421" height="18.583" style="fill: #ff4457"/>
<rect x="17.421" y="17.905" width="18.971" height="18.196" style="fill: #333333"/> <rect x="17.421" y="17.905" width="18.971" height="18.196" style="fill: #ff4457"/>
</g> </g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,29 @@
.contextualMenu {
position: absolute;
background-color: rgba(36, 36, 36, 0.7);
border-radius: 4px;
padding: 10px 5px;
z-index: 1000;
width: 250px;
height: auto;
color: #e3e3e3;
display: flex;
flex-direction: column;
font-size: 14px;
> div{
cursor: pointer;
display: flex;
align-items: center;
border-radius: 4px;
padding: 0 0 0 10px;
color: #e3e3e3;
width: 100%;
height: 35px;
}
> div:hover{
background-color: #e3e3e3;
color: rgba(36, 36, 36, 0.7);
}
}

View File

@ -0,0 +1,70 @@
import React from 'react'
import * as antd from 'antd'
import * as Icons from 'components/Icons'
import styles from './index.less'
export interface ContextMenu_props {
visible: boolean;
fragment: object;
yPos: number;
xPos: number;
app: any;
dispatch: any;
}
export default class ContextMenu extends React.Component<ContextMenu_props>{
constructor(props){
super(props)
this.setWrapperRef = this.setWrapperRef.bind(this)
this.handleClickOutside = this.handleClickOutside.bind(this)
this.eventListener = document.addEventListener('click', this.handleClickOutside, false)
}
setWrapperRef(node){
this.wrapperRef = node
}
handleClickOutside(event) {
if ( this.props.visible || this.wrapperRef && !this.wrapperRef.contains(event.target)) {
window.contextMenu.toogle()
this.listening = false
document.removeEventListener('click', this.eventListener, false)
}
}
componentDidUpdate(){
if (!this.listening) {
this.listening = true
this.eventListener
}
}
render(){
if (this.props.visible) {
return(
<div
id="contextualMenu"
ref={this.setWrapperRef}
className={styles.contextualMenu}
style={{
top: this.props.yPos,
left: this.props.xPos,
}}>
{this.props.fragment}
</div>
)
}
return null
}
}
ContextMenu.defaultProps = {
visible: false,
fragment: null,
xPos: 0,
yPos: 0
}

View File

@ -2,6 +2,7 @@
.left_sider_wrapper { .left_sider_wrapper {
-webkit-app-region: no-drag;
user-select: none; user-select: none;
border-color: transparent; border-color: transparent;
font-size: 15px; font-size: 15px;
@ -29,6 +30,7 @@
} }
.ant-menu-item { .ant-menu-item {
-webkit-app-region: no-drag;
transition: @transition-ease-inout; transition: @transition-ease-inout;
border-radius: 4px 8px 8px 4px; border-radius: 4px 8px 8px 4px;
@ -72,8 +74,9 @@
.left_sider_header { .left_sider_header {
cursor: pointer; cursor: pointer;
margin: 15px 0 0 22px; margin: 15px 0 0 22px;
-webkit-user-drag: none;
.logotype{ .logotype{
-webkit-app-region: no-drag;
max-height: 70px; max-height: 70px;
height: 35px; height: 35px;
} }

View File

@ -1,5 +1,6 @@
import Sider from './Sider' import Sider from './Sider'
import Overlay from './Overlay' import Overlay from './Overlay'
import WindowNavbar from './WindowNavbar' import WindowNavbar from './WindowNavbar'
import ContextMenu from './ContextMenu/index.tsx'
export { Sider, Overlay, WindowNavbar } export { Sider, Overlay, WindowNavbar, ContextMenu }

View File

@ -9,8 +9,10 @@ import classnames from 'classnames'
import settings from 'core/libs/settings' import settings from 'core/libs/settings'
import { router } from 'core/cores' import { router } from 'core/cores'
import { notify } from 'core/libs/interface'
import LikeBtn from './components/like' import LikeBtn from './components/like'
import { connect } from 'umi' import { connect } from 'umi'
import config from 'config'
const { Meta } = antd.Card const { Meta } = antd.Card
@ -45,6 +47,21 @@ export default class PostCard extends React.Component {
}) })
} }
generatePostURI(id){
if(config.app_config.endpoint_global && id){
return `${config.app_config.endpoint_global}/post/${id}`
}
return null
}
goElementById(id){
document.getElementById(id).scrollIntoView({
behavior: "smooth",
block: "center",
inline: "center"
})
}
toogleMoreMenu(){ toogleMoreMenu(){
this.setState({ visibleMoreMenu: !this.state.visibleMoreMenu }) this.setState({ visibleMoreMenu: !this.state.visibleMoreMenu })
} }
@ -53,6 +70,15 @@ export default class PostCard extends React.Component {
} }
writeToClipboard(text){
navigator.clipboard.writeText(text)
.then(() => {
notify.info('Copy to clipboard')
}, () => {
/* failure */
})
}
renderReportedPost(){ renderReportedPost(){
if(this.state.ReportIgnore) return null if(this.state.ReportIgnore) return null
return ( return (
@ -134,7 +160,6 @@ export default class PostCard extends React.Component {
} }
render() { render() {
const handleContextMenu = e => this.handleDispatchInvoke("contextualMenu", {cords: {x: e.clientX, y: e.clientY} })
const actions = [ const actions = [
<LikeBtn count={this.state.payload.post_likes} liked={core.booleanFix(this.state.payload.is_liked)} />, <LikeBtn count={this.state.payload.post_likes} liked={core.booleanFix(this.state.payload.is_liked)} />,
<Icons.Share2 />, <Icons.Share2 />,
@ -148,8 +173,8 @@ export default class PostCard extends React.Component {
<antd.Card <antd.Card
className={settings("post_hidebar") ? null : styles.showMode} className={settings("post_hidebar") ? null : styles.showMode}
onDoubleClick={() => null} onDoubleClick={() => null}
onClick={this.handleClick} onClick={() => this.goElementById(this.state.payload.id)}
onContextMenu={handleContextMenu} onContextMenu={() => this.writeToClipboard(this.generatePostURI(this.state.payload.id))}
actions={actions} actions={actions}
hoverable hoverable
> >

View File

@ -18,6 +18,7 @@
} }
.post_card_wrapper { .post_card_wrapper {
user-select: none;
box-shadow: @post_card_wrapper_shadow; box-shadow: @post_card_wrapper_shadow;
border-radius: @post_card_general_border-rd; border-radius: @post_card_general_border-rd;
max-width: 510px; max-width: 510px;
@ -182,6 +183,7 @@
margin: 23px 24px 23px 24px; margin: 23px 24px 23px 24px;
h3 { h3 {
user-select: all;
font-family: @__Global_general_font_family; font-family: @__Global_general_font_family;
color: @post_card_wrapper_post_content_color; color: @post_card_wrapper_post_content_color;
font-weight: @post_card_wrapper_post_content_weight; font-weight: @post_card_wrapper_post_content_weight;

View File

@ -4,7 +4,7 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import {withRouter, connect} from 'umi' import {withRouter, connect} from 'umi'
import { import {
AppLayout, AppLayout
} from 'components' } from 'components'
import { enquireScreen, unenquireScreen } from 'enquire-js' import { enquireScreen, unenquireScreen } from 'enquire-js'
import store from 'store' import store from 'store'
@ -13,26 +13,83 @@ import classnames from 'classnames'
import { app_config } from 'config' import { app_config } from 'config'
import { theme } from 'core/libs/style' import { theme } from 'core/libs/style'
import * as antd from 'antd' import * as antd from 'antd'
import * as Icons from 'components/Icons'
import styles from './PrimaryLayout.less' import styles from './PrimaryLayout.less'
const contextMenuList = [
{
key: "inspect_element",
title: "Inspect",
icon: <Icons.Command />
}
]
const { Content } = antd.Layout const { Content } = antd.Layout
const { Sider, Overlay } = AppLayout const { Sider, Overlay, ContextMenu } = AppLayout
const isActive = (key) => { return key? key.active : false } const isActive = (key) => { return key? key.active : false }
const currentTheme = theme.get()
@withRouter @withRouter
@connect(({ app, loading }) => ({ app, loading })) @connect(({ app, loading }) => ({ app, loading }))
class PrimaryLayout extends React.Component { class PrimaryLayout extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
collapsed: app_config.default_collapse_sider ? true : false, collapsed: app_config.default_collapse_sider ? true : false,
isMobile: false, isMobile: false
}, },
window.PrimaryComponent = this;
this.handleContextMenu = window.addEventListener("contextmenu", (e) => {
e.preventDefault()
window.contextMenu.open({ xPos: e.clientX, yPos: e.clientY, fragment: this.generateContextMenu() })
},false )
window.DarkMode = isActive(currentTheme["darkmode"])? true : false
window.contextMenu = this.props.app.contextMenu
window.contextMenu.toogle = () => {
this.props.dispatch({
type: "app/updateState",
payload: {contextMenu: {...this.props.app.contextMenu, visible: !this.props.app.contextMenu.visible} }
})
}
window.contextMenu.open = (payload) => {
if (!payload) return false
const fragment = payload.fragment || null
const xPos = payload.xPos || null
const yPos = payload.yPos || null
this.props.dispatch({
type: "app/updateState",
payload: {contextMenu: {...this.props.app.contextMenu, xPos, yPos, fragment, visible: true}}
})
}
}
handleContextMenuActions = {
inspect_element: (e) =>{
this.props.dispatch({
type: "app/ipcInvoke",
payload: {
key: "inspectElement",
payload: { x: e.clientX, y: e.clientY }
}
})
}
}
generateContextMenu() {
return contextMenuList.map((e) => {
return(
<div onClick={this.handleContextMenuActions[e.key]} key={e.key}>
{e.icon}{e.title}
</div>
)
})
} }
componentDidMount() { componentDidMount() {
this.handleContextMenu
this.enquireHandler = enquireScreen(mobile => { this.enquireHandler = enquireScreen(mobile => {
const { isMobile } = this.state const { isMobile } = this.state
if (isMobile !== mobile) { if (isMobile !== mobile) {
@ -44,6 +101,7 @@ class PrimaryLayout extends React.Component {
} }
componentWillUnmount() { componentWillUnmount() {
window.removeEventListener("contextmenu", this.handleContextMenu)
unenquireScreen(this.enquireHandler) unenquireScreen(this.enquireHandler)
} }
@ -54,10 +112,10 @@ class PrimaryLayout extends React.Component {
} }
render() { render() {
const { location, dispatch, children } = this.props const { location, dispatch, children, app } = this.props
const { collapsed, isMobile } = this.state const { collapsed, isMobile } = this.state
const { onCollapseChange } = this const { onCollapseChange } = this
const currentTheme = theme.get() const { contextMenu } = app
const app_theme = isActive(currentTheme["darkmode"])? "dark" : null const app_theme = isActive(currentTheme["darkmode"])? "dark" : null
const breakpoint = { const breakpoint = {
@ -83,9 +141,16 @@ class PrimaryLayout extends React.Component {
app_theme app_theme
} }
window.DarkMode = isActive(currentTheme["darkmode"])? true : false
return ( return (
<React.Fragment> <React.Fragment>
<ContextMenu
visible={contextMenu.visible}
fragment={contextMenu.fragment}
xPos={contextMenu.xPos}
yPos={contextMenu.yPos}
onClose={this.handleCloseContextMenu}
/>
{isActive(currentTheme['backgroundImage']) {isActive(currentTheme['backgroundImage'])
?<div style={{ ?<div style={{
backgroundImage: `url(${currentTheme.backgroundImage.src})`, backgroundImage: `url(${currentTheme.backgroundImage.src})`,

View File

@ -27,6 +27,12 @@ export default {
session_data: null, session_data: null,
session_uuid: null, session_uuid: null,
contextMenu: {
visible: false,
fragment: "WooW, whats happening here!",
yPos: 0,
xPos: 0
},
overlayActive: false, overlayActive: false,
overlayElement: null, overlayElement: null,
embedded: false, embedded: false,
@ -43,7 +49,6 @@ export default {
setup({ dispatch }) { setup({ dispatch }) {
try { try {
const electron = window.require("electron") const electron = window.require("electron")
dispatch({ type: 'updateState', payload: { electron, embedded: true } }) dispatch({ type: 'updateState', payload: { electron, embedded: true } })
} catch (error) { } catch (error) {
console.log(error) console.log(error)
@ -95,7 +100,6 @@ export default {
yield put({ type: 'handleUpdateData' }) yield put({ type: 'handleUpdateData' })
} }
// if (session) { // if (session) {
// if (pathMatchRegexp(['/', '/login'], window.location.pathname)) { // if (pathMatchRegexp(['/', '/login'], window.location.pathname)) {
// app.router.push({ pathname: `${app_config.MainPath}` }); // app.router.push({ pathname: `${app_config.MainPath}` });
@ -331,8 +335,17 @@ export default {
} }
console.log('INVOKING => ', payload) console.log('INVOKING => ', payload)
const ipc = state.electron.ipcRenderer const ipc = state.electron.ipcRenderer
ipc.invoke(payload.key, payload.payload) ipc.invoke(payload.key, payload.payload)
}, },
ipcSend(state, {payload}){
if (!payload || !state.embedded) {
return false
}
console.log('send INVOKING => ', payload)
const ipc = state.electron.ipcRenderer
ipc.send(payload.key, payload.payload)
},
allNotificationsRead(state) { allNotificationsRead(state) {
state.notifications = []; state.notifications = [];
}, },

View File

@ -37,15 +37,15 @@ export default class Explore extends React.Component {
if (this.props.app.session_valid) { if (this.props.app.session_valid) {
this.request() this.request()
} }
window.addEventListener('contextmenu', (e) => {
this.props.dispatch({
type: "app/ipcInvoke",
payload: { key: "contextualMenu", payload: {x: e.clientX, y: e.clientY} }
})
},false)
} }
render() { render() {
if(!this.props.app.session_valid){
return (<antd.Result status="403" title="You need to login for view this!" />)
}
if (!this.state.feed){ if (!this.state.feed){
return ( return (
<antd.Card bordered="false" > <antd.Card bordered="false" >
@ -55,9 +55,7 @@ export default class Explore extends React.Component {
} }
return( return(
<> <div>
<PostCreator />
<List <List
//loadMore={loadMore} //loadMore={loadMore}
dataSource={this.state.feed} dataSource={this.state.feed}
@ -67,8 +65,7 @@ export default class Explore extends React.Component {
</div> </div>
)} )}
/> />
</> </div>
) )
} }
} }