forked from Qortal/qortal-ui
Update UI
Refactor and added new functioms
This commit is contained in:
parent
940f9f82f8
commit
fa29ff4c43
@ -26,8 +26,8 @@ Easiest way to install the lastest required packages on Linux is via nvm.
|
|||||||
``` source ~/.profile ``` (For Debian based distro) <br/>
|
``` source ~/.profile ``` (For Debian based distro) <br/>
|
||||||
``` source ~/.bashrc ``` (For Fedora / CentOS) <br/>
|
``` source ~/.bashrc ``` (For Fedora / CentOS) <br/>
|
||||||
``` nvm ls-remote ``` (Fetch list of available versions) <br/>
|
``` nvm ls-remote ``` (Fetch list of available versions) <br/>
|
||||||
``` nvm install v18.17.1 ``` (LTS: Hydrogen supported by Electron V27) <br/>
|
``` nvm install v20.11.1 ``` (LTS: Iron supported by Electron V30) <br/>
|
||||||
``` npm --location=global install npm@10.5.0 ``` <br/>
|
``` npm --location=global install npm@10.7.0 ``` <br/>
|
||||||
|
|
||||||
Adding via binary package mirror will only work if you have set the package path. You can do a node or java build via ports instead by downloading ports with portsnap fetch method.
|
Adding via binary package mirror will only work if you have set the package path. You can do a node or java build via ports instead by downloading ports with portsnap fetch method.
|
||||||
|
|
||||||
|
29
build.js
29
build.js
@ -1,28 +1,23 @@
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
const uiCore = require('./core/ui-core.js')
|
const uiCore = require('./core/ui-core.js')
|
||||||
|
const config = require('./config/config.js')
|
||||||
|
const pluginsController = require('./plugins/default-plugins.js')
|
||||||
const generateBuildConfig = uiCore('generate_build_config')
|
const generateBuildConfig = uiCore('generate_build_config')
|
||||||
const build = uiCore('build')
|
const build = uiCore('build')
|
||||||
|
|
||||||
const config = require('./config/config.js')
|
|
||||||
|
|
||||||
const pluginsController = require('./plugins/default-plugins.js')
|
|
||||||
const buildDefalutPlugins = pluginsController('build')
|
const buildDefalutPlugins = pluginsController('build')
|
||||||
|
|
||||||
|
|
||||||
srcConfig = {
|
srcConfig = {
|
||||||
...config.build,
|
...config.build,
|
||||||
options: {
|
options: {
|
||||||
...config.build.options,
|
...config.build.options,
|
||||||
outputDir: path.join(__dirname, '/builtWWW'),
|
outputDir: path.join(__dirname, '/builtWWW'),
|
||||||
sassOutputDir: path.join(__dirname, '/builtWWW/styles.bundle.css'),
|
sassOutputDir: path.join(__dirname, '/builtWWW/styles.bundle.css')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { buildConfig, inlineConfigs } = generateBuildConfig(srcConfig)
|
const { buildConfig, inlineConfigs } = generateBuildConfig(srcConfig)
|
||||||
|
|
||||||
build(buildConfig.options, buildConfig.outputs, buildConfig.outputOptions, buildConfig.inputOptions, inlineConfigs)
|
build(buildConfig.options, buildConfig.outputs, buildConfig.outputOptions, buildConfig.inputOptions, inlineConfigs).then(() => {
|
||||||
.then(() => {
|
console.log("Building and Bundling Plugins")
|
||||||
console.log("Building and Bundling Plugins");
|
buildDefalutPlugins()
|
||||||
buildDefalutPlugins()
|
})
|
||||||
})
|
|
@ -2,13 +2,13 @@ const path = require('path')
|
|||||||
const defaultConfig = require('./default.config.js')
|
const defaultConfig = require('./default.config.js')
|
||||||
|
|
||||||
const build = {
|
const build = {
|
||||||
options: {
|
options: {
|
||||||
outputDir: path.join(__dirname, '../build'),
|
outputDir: path.join(__dirname, '../build'),
|
||||||
imgDir: path.join(__dirname, '../img')
|
imgDir: path.join(__dirname, '../img')
|
||||||
},
|
},
|
||||||
aliases: {
|
aliases: {
|
||||||
'qortal-ui-crypto': path.join(__dirname, '../crypto/api.js')
|
'qortal-ui-crypto': path.join(__dirname, '../crypto/api.js')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = build
|
module.exports = build
|
@ -1,8 +1,8 @@
|
|||||||
const defaultConfig = require('./default.config.js')
|
const defaultConfig = require('./default.config.js')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
name: 'Qortal',
|
name: 'Qortal',
|
||||||
symbol: 'Qort',
|
symbol: 'Qort',
|
||||||
addressVersion: 58, // Q for Qortal
|
addressVersion: 58, // Q for Qortal
|
||||||
logo: '/img/QORT_LOGO.svg'
|
logo: '/img/QORT_LOGO.svg'
|
||||||
}
|
}
|
@ -1,27 +1,33 @@
|
|||||||
let config = require('./default.config.js')
|
let config = require('./default.config.js')
|
||||||
let userConfig = {}
|
|
||||||
try {
|
|
||||||
userConfig = require('./customConfig.js')
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(e)
|
|
||||||
console.warn('Error loading user config')
|
|
||||||
}
|
|
||||||
const checkKeys = (storeObj, newObj) => {
|
|
||||||
for (const key in newObj) {
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(storeObj, key)) return
|
|
||||||
|
|
||||||
if (typeof newObj[key] === 'object') {
|
let userConfig = {}
|
||||||
storeObj[key] = checkKeys(storeObj[key], newObj[key])
|
|
||||||
} else {
|
try {
|
||||||
storeObj[key] = newObj[key]
|
userConfig = require('./customConfig.js')
|
||||||
}
|
} catch (e) {
|
||||||
}
|
console.warn(e)
|
||||||
return storeObj
|
console.warn('Error loading user config')
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkKeys = (storeObj, newObj) => {
|
||||||
|
for (const key in newObj) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(storeObj, key)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof newObj[key] === 'object') {
|
||||||
|
storeObj[key] = checkKeys(storeObj[key], newObj[key])
|
||||||
|
} else {
|
||||||
|
storeObj[key] = newObj[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return storeObj
|
||||||
}
|
}
|
||||||
|
|
||||||
const getConfig = customConfig => {
|
const getConfig = customConfig => {
|
||||||
config = checkKeys(config, customConfig)
|
config = checkKeys(config, customConfig)
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = getConfig(userConfig)
|
module.exports = getConfig(userConfig)
|
@ -4,10 +4,4 @@ const styles = require('./styles.config.js')
|
|||||||
const build = require('./build.config.js')
|
const build = require('./build.config.js')
|
||||||
const user = require('./user.config.js')
|
const user = require('./user.config.js')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = { coin, styles, build, user, crypto }
|
||||||
coin,
|
|
||||||
styles,
|
|
||||||
build,
|
|
||||||
user,
|
|
||||||
crypto
|
|
||||||
}
|
|
@ -1,5 +1,4 @@
|
|||||||
const uiCore = require('../core/ui-core.js')
|
const uiCore = require('../core/ui-core.js')
|
||||||
const defaultConfig = uiCore('default_config')
|
const defaultConfig = uiCore('default_config')
|
||||||
|
|
||||||
|
|
||||||
module.exports = defaultConfig
|
module.exports = defaultConfig
|
@ -1,10 +1,11 @@
|
|||||||
const user = require('./default.config.js').user
|
const user = require('./default.config.js').user
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
node: 0, // set to mainnet
|
node: 0, // set to mainnet
|
||||||
server: {
|
server: {
|
||||||
primary: {
|
primary: {
|
||||||
port: 12388, // set as default UI port
|
port: 12388, // set as default UI port
|
||||||
address: '0.0.0.0', // can specify an IP for a fixed bind
|
address: '0.0.0.0' // can specify an IP for a fixed bind
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
@ -4,132 +4,132 @@ const { makeSourceAbsolute } = require('../tooling/utils.js')
|
|||||||
const srcDir = '../src'
|
const srcDir = '../src'
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
inputFile: path.join(__dirname, '../src/main.js'),
|
inputFile: path.join(__dirname, '../src/main.js'),
|
||||||
outputDir: path.join(__dirname, '../build'),
|
outputDir: path.join(__dirname, '../build'),
|
||||||
sassOutputDir: path.join(__dirname, '../build/styles.bundle.css'),
|
sassOutputDir: path.join(__dirname, '../build/styles.bundle.css'),
|
||||||
imgDir: path.join(__dirname, '../img')
|
imgDir: path.join(__dirname, '../img')
|
||||||
}
|
}
|
||||||
|
|
||||||
const aliases = {
|
const aliases = {
|
||||||
'qortal-ui-crypto': '../../crypto/api.js'
|
'qortal-ui-crypto': '../../crypto/api.js'
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiComponents = {
|
const apiComponents = {
|
||||||
api: {
|
api: {
|
||||||
file: '../../crypto/api.js',
|
file: '../../crypto/api.js',
|
||||||
className: 'api'
|
className: 'api'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const functionalComponents = {
|
const functionalComponents = {
|
||||||
'loading-ripple': {
|
'loading-ripple': {
|
||||||
file: 'functional-components/loading-ripple.js',
|
file: 'functional-components/loading-ripple.js',
|
||||||
className: 'LoadingRipple'
|
className: 'LoadingRipple'
|
||||||
},
|
},
|
||||||
'confirm-transaction-dialog': {
|
'confirm-transaction-dialog': {
|
||||||
file: 'functional-components/confirm-transaction-dialog',
|
file: 'functional-components/confirm-transaction-dialog',
|
||||||
className: 'ConfirmTransactionDialog'
|
className: 'ConfirmTransactionDialog'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const inlineComponents = [
|
const inlineComponents = [
|
||||||
{
|
{
|
||||||
className: 'worker',
|
className: 'worker',
|
||||||
input: path.join(__dirname, srcDir, 'worker.js'),
|
input: path.join(__dirname, srcDir, 'worker.js'),
|
||||||
output: 'worker.js'
|
output: 'worker.js'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
className: 'PluginMainJSLoader',
|
className: 'PluginMainJSLoader',
|
||||||
input: path.join(__dirname, srcDir, '/plugins/plugin-mainjs-loader.js'),
|
input: path.join(__dirname, srcDir, '/plugins/plugin-mainjs-loader.js'),
|
||||||
output: 'plugins/plugin-mainjs-loader.js'
|
output: 'plugins/plugin-mainjs-loader.js'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const elementComponents = {
|
const elementComponents = {
|
||||||
'main-app': {
|
'main-app': {
|
||||||
file: 'components/main-app.js',
|
file: 'components/main-app.js',
|
||||||
className: 'MainApp',
|
className: 'MainApp',
|
||||||
children: {
|
children: {
|
||||||
'app-styles': {
|
'app-styles': {
|
||||||
file: 'styles/app-styles.js',
|
file: 'styles/app-styles.js',
|
||||||
className: 'AppStyles',
|
className: 'AppStyles',
|
||||||
children: {
|
children: {
|
||||||
'app-theme': {
|
'app-theme': {
|
||||||
className: 'AppTheme',
|
className: 'AppTheme',
|
||||||
file: 'styles/app-theme.js'
|
file: 'styles/app-theme.js'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'app-view': {
|
'app-view': {
|
||||||
file: 'components/app-view.js',
|
file: 'components/app-view.js',
|
||||||
className: 'AppView',
|
className: 'AppView',
|
||||||
children: {
|
children: {
|
||||||
'show-plugin': {
|
'show-plugin': {
|
||||||
file: 'components/show-plugin.js',
|
file: 'components/show-plugin.js',
|
||||||
className: 'ShowPlugin'
|
className: 'ShowPlugin'
|
||||||
},
|
},
|
||||||
'wallet-profile': {
|
'wallet-profile': {
|
||||||
file: 'components/wallet-profile.js',
|
file: 'components/wallet-profile.js',
|
||||||
className: 'WalletProfile'
|
className: 'WalletProfile'
|
||||||
},
|
},
|
||||||
'app-info': {
|
'app-info': {
|
||||||
file: 'components/app-info.js',
|
file: 'components/app-info.js',
|
||||||
className: 'AppInfo'
|
className: 'AppInfo'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'login-view': {
|
'login-view': {
|
||||||
file: 'components/login-view/login-view.js',
|
file: 'components/login-view/login-view.js',
|
||||||
className: 'LoginView',
|
className: 'LoginView',
|
||||||
children: {
|
children: {
|
||||||
'create-account-section': {
|
'create-account-section': {
|
||||||
file: 'components/login-view/create-account-section.js',
|
file: 'components/login-view/create-account-section.js',
|
||||||
className: 'CreateAccountSection'
|
className: 'CreateAccountSection'
|
||||||
},
|
},
|
||||||
'login-section': {
|
'login-section': {
|
||||||
file: 'components/login-view/login-section.js',
|
file: 'components/login-view/login-section.js',
|
||||||
className: 'LoginSection'
|
className: 'LoginSection'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'settings-view': {
|
'settings-view': {
|
||||||
file: 'components/settings-view/user-settings.js',
|
file: 'components/settings-view/user-settings.js',
|
||||||
className: 'UserSettings',
|
className: 'UserSettings',
|
||||||
children: {
|
children: {
|
||||||
'account-view': {
|
'account-view': {
|
||||||
file: 'components/settings-view/account-view.js',
|
file: 'components/settings-view/account-view.js',
|
||||||
className: 'AccountView'
|
className: 'AccountView'
|
||||||
},
|
},
|
||||||
'security-view': {
|
'security-view': {
|
||||||
file: 'components/settings-view/security-view.js',
|
file: 'components/settings-view/security-view.js',
|
||||||
className: 'SecurityView'
|
className: 'SecurityView'
|
||||||
},
|
},
|
||||||
'qr-login-view': {
|
'qr-login-view': {
|
||||||
file: 'components/settings-view/qr-login-view.js',
|
file: 'components/settings-view/qr-login-view.js',
|
||||||
className: 'QRLoginView'
|
className: 'QRLoginView'
|
||||||
},
|
},
|
||||||
'notifications-view': {
|
'notifications-view': {
|
||||||
file: 'components/settings-view/notifications-view.js',
|
file: 'components/settings-view/notifications-view.js',
|
||||||
className: 'NotificationsView'
|
className: 'NotificationsView'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'user-info-view': {
|
'user-info-view': {
|
||||||
file: 'components/user-info-view/user-info-view.js',
|
file: 'components/user-info-view/user-info-view.js',
|
||||||
className: 'UserInfoView'
|
className: 'UserInfoView'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
makeSourceAbsolute(path.join(__dirname, srcDir), elementComponents)
|
makeSourceAbsolute(path.join(__dirname, srcDir), elementComponents)
|
||||||
makeSourceAbsolute(path.join(__dirname, srcDir), functionalComponents)
|
makeSourceAbsolute(path.join(__dirname, srcDir), functionalComponents)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
options,
|
options,
|
||||||
elementComponents,
|
elementComponents,
|
||||||
functionalComponents,
|
functionalComponents,
|
||||||
inlineComponents,
|
inlineComponents,
|
||||||
apiComponents,
|
apiComponents,
|
||||||
aliases
|
aliases
|
||||||
}
|
}
|
@ -1,11 +1,11 @@
|
|||||||
const coin = {
|
const coin = {
|
||||||
name: 'Qortal',
|
name: 'Qortal',
|
||||||
symbol: 'QORT',
|
symbol: 'QORT',
|
||||||
addressCount: 1,
|
addressCount: 1,
|
||||||
addressVersion: 58,
|
addressVersion: 58,
|
||||||
decimals: 100000000,
|
decimals: 100000000,
|
||||||
logo: '/img/QORT_LOGO.png',
|
logo: '/img/QORT_LOGO.png',
|
||||||
icon: '/img/QORT_LOGO.png'
|
icon: '/img/QORT_LOGO.png'
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = coin
|
module.exports = coin
|
@ -1,11 +1,11 @@
|
|||||||
const crypto = {
|
const crypto = {
|
||||||
kdfThreads: 16,
|
kdfThreads: 16,
|
||||||
staticSalt: '4ghkVQExoneGqZqHTMMhhFfxXsVg2A75QeS1HCM5KAih', // Base58 encoded
|
staticSalt: '4ghkVQExoneGqZqHTMMhhFfxXsVg2A75QeS1HCM5KAih', // Base58 encoded
|
||||||
bcryptRounds: 11, // Note it's kinda bcryptRounds * log.2.16, cause it runs on all 16 threads
|
bcryptRounds: 11, // Note it's kinda bcryptRounds * log.2.16, cause it runs on all 16 threads
|
||||||
bcryptVersion: '2a',
|
bcryptVersion: '2a',
|
||||||
get staticBcryptSalt () {
|
get staticBcryptSalt() {
|
||||||
return `$${this.bcryptVersion}$${this.bcryptRounds}$IxVE941tXVUD4cW0TNVm.O`
|
return `$${this.bcryptVersion}$${this.bcryptRounds}$IxVE941tXVUD4cW0TNVm.O`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = crypto
|
module.exports = crypto
|
@ -1,40 +1,41 @@
|
|||||||
const styles = {
|
const styles = {
|
||||||
breakpoints: {
|
breakpoints: {
|
||||||
desktop: '',
|
desktop: '',
|
||||||
laptop: '',
|
laptop: '',
|
||||||
tablet: '',
|
tablet: '',
|
||||||
mobile: ''
|
mobile: ''
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
colors: {
|
colors: {
|
||||||
primary: '#03a9f4', /* Sets the text color to the theme primary color. */
|
primary: '#03a9f4', /* Sets the text color to the theme primary color. */
|
||||||
primaryBg: '#e8eaf6', /* Sets the background color to the theme primary color. */
|
primaryBg: '#e8eaf6', /* Sets the background color to the theme primary color. */
|
||||||
onPrimary: '#fff', /* Sets the text color to the color configured for text on the primary color. */
|
onPrimary: '#fff', /* Sets the text color to the color configured for text on the primary color. */
|
||||||
|
|
||||||
secondary: '#03a9f4', /* Sets the text color to the theme secondary color. */
|
secondary: '#03a9f4', /* Sets the text color to the theme secondary color. */
|
||||||
secondaryBg: '#fce4ec', /* Sets the background color to the theme secondary color. */
|
secondaryBg: '#fce4ec', /* Sets the background color to the theme secondary color. */
|
||||||
onSecondary: '#fff', /* Sets the text color to the color configured for text on the secondary color. */
|
onSecondary: '#fff', /* Sets the text color to the color configured for text on the secondary color. */
|
||||||
|
|
||||||
surface: '#fff', /* Sets the background color to the surface background color. */
|
surface: '#fff', /* Sets the background color to the surface background color. */
|
||||||
onSurface: '#333', /* Sets the text color to the color configured for text on the surface color. */
|
onSurface: '#333', /* Sets the text color to the color configured for text on the surface color. */
|
||||||
background: '#eee', /* Sets the background color to the theme background color. */
|
background: '#eee', /* Sets the background color to the theme background color. */
|
||||||
|
|
||||||
warning: '#FFA000',
|
warning: '#FFA000',
|
||||||
error: '#F44336'
|
error: '#F44336'
|
||||||
},
|
},
|
||||||
|
|
||||||
addressColors: [
|
addressColors: [
|
||||||
'#256480',
|
'#256480',
|
||||||
'#002530',
|
'#002530',
|
||||||
'#02564e',
|
'#02564e',
|
||||||
'#d32f2f',
|
'#d32f2f',
|
||||||
'#795548',
|
'#795548',
|
||||||
'#004d40',
|
'#004d40',
|
||||||
'#006064',
|
'#006064',
|
||||||
'#9c27b0',
|
'#9c27b0',
|
||||||
'#2196f3',
|
'#2196f3',
|
||||||
'#d81b60'
|
'#d81b60'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = styles
|
module.exports = styles
|
@ -1,42 +1,43 @@
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
const user = {
|
const user = {
|
||||||
node: 0,
|
node: 0,
|
||||||
nodeSettings: {
|
nodeSettings: {
|
||||||
pingInterval: 30 * 1000,
|
pingInterval: 30 * 1000
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
writeHosts: {
|
writeHosts: {
|
||||||
enabled: true,
|
enabled: true
|
||||||
},
|
},
|
||||||
relativeTo: path.join(__dirname, '../'),
|
relativeTo: path.join(__dirname, '../'),
|
||||||
primary: {
|
primary: {
|
||||||
domain: '0.0.0.0',
|
domain: '0.0.0.0',
|
||||||
address: '0.0.0.0',
|
address: '0.0.0.0',
|
||||||
port: 12388,
|
port: 12388,
|
||||||
directory: './src/',
|
directory: './src/',
|
||||||
page404: './src/404.html',
|
page404: './src/404.html',
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
tls: {
|
tls: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
options: {
|
options: {
|
||||||
key: '',
|
key: '',
|
||||||
cert: '',
|
cert: ''
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
constants: {
|
constants: {
|
||||||
pollingInterval: 30 * 1000, // How long between checking for new unconfirmed transactions and new blocks (in milliseconds).
|
pollingInterval: 30 * 1000, // How long between checking for new unconfirmed transactions and new blocks (in milliseconds).
|
||||||
workerURL: '/build/worker.js',
|
workerURL: '/build/worker.js'
|
||||||
},
|
},
|
||||||
|
|
||||||
// Notification Settings (All defaults to true)
|
// Notification Settings (All defaults to true)
|
||||||
notifications: {
|
notifications: {
|
||||||
q_chat: {
|
q_chat: {
|
||||||
playSound: true,
|
playSound: true,
|
||||||
showNotification: true,
|
showNotification: true
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = user
|
module.exports = user
|
@ -1,21 +1,22 @@
|
|||||||
let config = require('./config.js')
|
let config = require('./config.js')
|
||||||
|
|
||||||
const checkKeys = (storeObj, newObj) => {
|
const checkKeys = (storeObj, newObj) => {
|
||||||
for (const key in newObj) {
|
for (const key in newObj) {
|
||||||
if (!Object.prototype.hasOwnProperty.call(storeObj, key)) return
|
if (!Object.prototype.hasOwnProperty.call(storeObj, key)) return
|
||||||
|
|
||||||
if (typeof newObj[key] === 'object') {
|
if (typeof newObj[key] === 'object') {
|
||||||
storeObj[key] = checkKeys(storeObj[key], newObj[key])
|
storeObj[key] = checkKeys(storeObj[key], newObj[key])
|
||||||
} else {
|
} else {
|
||||||
storeObj[key] = newObj[key]
|
storeObj[key] = newObj[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return storeObj
|
|
||||||
|
return storeObj
|
||||||
}
|
}
|
||||||
|
|
||||||
const getConfig = customConfig => {
|
const getConfig = customConfig => {
|
||||||
config = checkKeys(config, customConfig)
|
config = checkKeys(config, customConfig)
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = getConfig
|
module.exports = getConfig
|
@ -6,6 +6,7 @@ html {
|
|||||||
--plugback: #ffffff;
|
--plugback: #ffffff;
|
||||||
--border: #d0d6de;
|
--border: #d0d6de;
|
||||||
--border2: #dde2e8;
|
--border2: #dde2e8;
|
||||||
|
--border3: #080808;
|
||||||
--copybutton: #707584;
|
--copybutton: #707584;
|
||||||
--chat-group: #080808;
|
--chat-group: #080808;
|
||||||
--chat-bubble: #9f9f9f0a;
|
--chat-bubble: #9f9f9f0a;
|
||||||
@ -83,6 +84,7 @@ html[theme="dark"] {
|
|||||||
--plugback: #0f1a2e;
|
--plugback: #0f1a2e;
|
||||||
--border: #0b305e;
|
--border: #0b305e;
|
||||||
--border2: #0b305e;
|
--border2: #0b305e;
|
||||||
|
--border3: #767676;
|
||||||
--copybutton: #d0d6de;
|
--copybutton: #d0d6de;
|
||||||
--chat-group: #ffffff;
|
--chat-group: #ffffff;
|
||||||
--chat-bubble: #9694941a;
|
--chat-bubble: #9694941a;
|
||||||
|
@ -945,7 +945,16 @@
|
|||||||
"gchange56": "Group Name To Search",
|
"gchange56": "Group Name To Search",
|
||||||
"gchange57": "Private Group Name Not Found",
|
"gchange57": "Private Group Name Not Found",
|
||||||
"gchange58": "Note that group name must be an exact match.",
|
"gchange58": "Note that group name must be an exact match.",
|
||||||
"gchange59": "Show / Hide Ticker"
|
"gchange59": "Show / Hide Ticker",
|
||||||
|
"gchange60": "Please enter an group name",
|
||||||
|
"gchange61": "Please enter an description",
|
||||||
|
"gchange62": "Are you sure to update this group?",
|
||||||
|
"gchange63": "On pressing confirm, the update group request will be sent!",
|
||||||
|
"gchange64": "Current Owner / New Owner",
|
||||||
|
"gchange65": "Only replace this address if you want to transfer the group!",
|
||||||
|
"gchange66": "Invalid Owner / New Owner Address",
|
||||||
|
"gchange67": "Group Update Successful!",
|
||||||
|
"gchange68": "Set Group Avatar"
|
||||||
},
|
},
|
||||||
"puzzlepage": {
|
"puzzlepage": {
|
||||||
"pchange1": "Puzzles",
|
"pchange1": "Puzzles",
|
||||||
|
@ -1,139 +1,121 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en-us">
|
<html lang="en-us">
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="Description" content="Qortal Platform UI">
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="/img/favicon/apple-icon-57x57.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="60x60" href="/img/favicon/apple-icon-60x60.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="/img/favicon/apple-icon-72x72.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="/img/favicon/apple-icon-76x76.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="/img/favicon/apple-icon-114x114.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/img/favicon/apple-icon-120x120.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="/img/favicon/apple-icon-144x144.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/img/favicon/apple-icon-152x152.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/img/favicon/apple-icon-180x180.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="192x192" href="/img/favicon/android-icon-192x192.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="96x96" href="/img/favicon/favicon-96x96.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="/img/favicon/manifest.json">
|
||||||
|
<meta name="msapplication-TileColor" content="var(--white)">
|
||||||
|
<meta name="msapplication-TileImage" content="/img/favicon/ms-icon-144x144.png">
|
||||||
|
<meta name="theme-color" content="var(--white)">
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
--scrollbarBG: #a1a1a1;
|
||||||
|
--thumbBG: #6a6c75;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
*::-webkit-scrollbar {
|
||||||
|
width: 11px;
|
||||||
|
}
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: var(--thumbBG) var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
*::-webkit-scrollbar-track {
|
||||||
|
background: var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
*::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--thumbBG);
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 3px solid var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: var(--plugback);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet" href="/build/styles.bundle.css">
|
||||||
|
<link rel="stylesheet" href="/font/material-icons.css">
|
||||||
|
<link rel="stylesheet" href="/font/switch-theme.css">
|
||||||
|
<title>Qortal UI</title>
|
||||||
|
<script>
|
||||||
|
const checkTheme = localStorage.getItem('qortalTheme')
|
||||||
|
if (checkTheme === 'dark') {
|
||||||
|
newtheme = 'dark';
|
||||||
|
} else {
|
||||||
|
newtheme = 'light';
|
||||||
|
}
|
||||||
|
document.querySelector('html').setAttribute('theme', newtheme);
|
||||||
|
|
||||||
<head>
|
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 });
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
const heap = new Uint8Array(memory.buffer);
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="Description" content="Qortal Platform UI">
|
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/img/favicon/apple-icon-57x57.png">
|
const sbrk = function (size, heap) {
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/img/favicon/apple-icon-60x60.png">
|
let brk = 512 * 1024; // stack top
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/img/favicon/apple-icon-72x72.png">
|
let old = brk;
|
||||||
<link rel="apple-touch-icon" sizes="76x76" href="/img/favicon/apple-icon-76x76.png">
|
brk += size;
|
||||||
<link rel="apple-touch-icon" sizes="114x114" href="/img/favicon/apple-icon-114x114.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="120x120" href="/img/favicon/apple-icon-120x120.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/img/favicon/apple-icon-144x144.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/img/favicon/apple-icon-152x152.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/img/favicon/apple-icon-180x180.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="/img/favicon/android-icon-192x192.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon/favicon-32x32.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="96x96" href="/img/favicon/favicon-96x96.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon/favicon-16x16.png">
|
|
||||||
<link rel="manifest" href="/img/favicon/manifest.json">
|
|
||||||
|
|
||||||
<meta name="msapplication-TileColor" content="var(--white)">
|
if (brk > heap.length)
|
||||||
<meta name="msapplication-TileImage" content="/img/favicon/ms-icon-144x144.png">
|
throw new Error("heap exhausted");
|
||||||
<meta name="theme-color" content="var(--white)">
|
|
||||||
|
|
||||||
<style>
|
return old;
|
||||||
html {
|
};
|
||||||
--scrollbarBG: #a1a1a1;
|
|
||||||
--thumbBG: #6a6c75;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar {
|
const importObject = {
|
||||||
width: 11px;
|
env: {
|
||||||
}
|
memory: memory
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
* {
|
function loadWebAssembly(filename, imports) {
|
||||||
scrollbar-width: thin;
|
// Fetch the file and compile it
|
||||||
scrollbar-color: var(--thumbBG) var(--scrollbarBG);
|
return fetch(filename)
|
||||||
}
|
.then(response => response.arrayBuffer())
|
||||||
|
.then(buffer => WebAssembly.compile(buffer))
|
||||||
|
.then(module => {
|
||||||
|
|
||||||
*::-webkit-scrollbar-track {
|
// Create the instance.
|
||||||
background: var(--scrollbarBG);
|
return new WebAssembly.Instance(module, importObject);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
*::-webkit-scrollbar-thumb {
|
const path = window.parent.location.origin + '/memory-pow/memory-pow.wasm.full'
|
||||||
background-color: var(--thumbBG);
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 3px solid var(--scrollbarBG);
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
loadWebAssembly(path)
|
||||||
body {
|
.then(wasmModule => {
|
||||||
margin: 0;
|
window.sbrk = sbrk
|
||||||
padding: 0;
|
window.memory = memory
|
||||||
background: var(--plugback);
|
window.heap = heap
|
||||||
overflow: hidden;
|
window.powInstance = wasmModule.instance;
|
||||||
}
|
window.computePow = wasmModule.exports.compute2;
|
||||||
</style>
|
});
|
||||||
<link rel="stylesheet" href="/build/styles.bundle.css">
|
|
||||||
<link rel="stylesheet" href="/font/material-icons.css">
|
|
||||||
<link rel="stylesheet" href="/font/switch-theme.css">
|
|
||||||
<title>Qortal UI</title>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const checkTheme = localStorage.getItem('qortalTheme')
|
|
||||||
if (checkTheme === 'dark') {
|
|
||||||
newtheme = 'dark';
|
|
||||||
} else {
|
|
||||||
newtheme = 'light';
|
|
||||||
}
|
|
||||||
document.querySelector('html').setAttribute('theme', newtheme);
|
|
||||||
|
|
||||||
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 });
|
|
||||||
const heap = new Uint8Array(memory.buffer);
|
|
||||||
|
|
||||||
const sbrk = function (size, heap) {
|
|
||||||
let brk = 512 * 1024; // stack top
|
|
||||||
let old = brk;
|
|
||||||
brk += size;
|
|
||||||
|
|
||||||
if (brk > heap.length)
|
|
||||||
throw new Error("heap exhausted");
|
|
||||||
|
|
||||||
return old;
|
|
||||||
};
|
|
||||||
|
|
||||||
const importObject = {
|
|
||||||
env: {
|
|
||||||
memory: memory
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function loadWebAssembly(filename, imports) {
|
|
||||||
// Fetch the file and compile it
|
|
||||||
return fetch(filename)
|
|
||||||
.then(response => response.arrayBuffer())
|
|
||||||
.then(buffer => WebAssembly.compile(buffer))
|
|
||||||
.then(module => {
|
|
||||||
|
|
||||||
// Create the instance.
|
|
||||||
return new WebAssembly.Instance(module, importObject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const path = window.parent.location.origin + '/memory-pow/memory-pow.wasm.full'
|
|
||||||
|
|
||||||
loadWebAssembly(path)
|
|
||||||
.then(wasmModule => {
|
|
||||||
window.sbrk = sbrk
|
|
||||||
window.memory = memory
|
|
||||||
window.heap = heap
|
|
||||||
window.powInstance = wasmModule.instance;
|
|
||||||
window.computePow = wasmModule.exports.compute2;
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<app-styles></app-styles>
|
|
||||||
<main>
|
|
||||||
|
|
||||||
<noscript>
|
|
||||||
You need to enable JavaScript to run this app. 😞
|
|
||||||
</noscript>
|
|
||||||
|
|
||||||
<main-app id="main-app"></main-app>
|
|
||||||
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<script type="module" src="/build/es6/main.js"></script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<app-styles></app-styles>
|
||||||
|
<main>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app. 😞
|
||||||
|
</noscript>
|
||||||
|
<main-app id="main-app"></main-app>
|
||||||
|
</main>
|
||||||
|
<script type="module" src="/build/es6/main.js"></script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -3,32 +3,33 @@ const Hapi = require('@hapi/hapi')
|
|||||||
const Inert = require('@hapi/inert')
|
const Inert = require('@hapi/inert')
|
||||||
|
|
||||||
function serverFactory(routes, address, port, tls) {
|
function serverFactory(routes, address, port, tls) {
|
||||||
this.server = new Hapi.Server({
|
this.server = new Hapi.Server({
|
||||||
routes: {
|
routes: {
|
||||||
files: {
|
files: {
|
||||||
relativeTo: Path.join(__dirname, '../')
|
relativeTo: Path.join(__dirname, '../')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
address: address,
|
address: address,
|
||||||
port: port,
|
port: port,
|
||||||
tls: tls
|
tls: tls
|
||||||
})
|
})
|
||||||
|
|
||||||
this.startServer = async () => {
|
this.startServer = async () => {
|
||||||
try {
|
try {
|
||||||
await this.server.register([Inert])
|
await this.server.register([Inert])
|
||||||
|
|
||||||
this.server.route(routes)
|
this.server.route(routes)
|
||||||
|
|
||||||
await this.server.start()
|
await this.server.start()
|
||||||
|
|
||||||
delete this.startServer
|
delete this.startServer
|
||||||
return this.server
|
|
||||||
} catch (e) {
|
return this.server
|
||||||
console.error(e)
|
} catch (e) {
|
||||||
throw e
|
console.error(e)
|
||||||
}
|
throw e
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = serverFactory
|
module.exports = serverFactory
|
@ -1,106 +1,106 @@
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
const routesOptions = {
|
const routesOptions = {
|
||||||
security: {
|
security: {
|
||||||
hsts: {
|
hsts: {
|
||||||
maxAge: 15768000,
|
maxAge: 15768000,
|
||||||
includeSubDomains: true,
|
includeSubDomains: true,
|
||||||
preload: true
|
preload: true
|
||||||
},
|
},
|
||||||
xframe: 'sameorigin'
|
xframe: 'sameorigin'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const createRoutes = config => [
|
const createRoutes = config => [
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
path: '/img/{param*}',
|
||||||
|
handler: {
|
||||||
|
directory: {
|
||||||
|
path: config.build.options.imgDir,
|
||||||
|
redirectToSlash: true,
|
||||||
|
index: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options: routesOptions
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
path: '/language/{param*}',
|
||||||
|
handler: {
|
||||||
|
directory: {
|
||||||
|
path: path.join(__dirname, '../../language'),
|
||||||
|
redirectToSlash: true,
|
||||||
|
index: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options: routesOptions
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
path: '/font/{param*}',
|
||||||
|
handler: {
|
||||||
|
directory: {
|
||||||
|
path: path.join(__dirname, '../../font'),
|
||||||
|
redirectToSlash: true,
|
||||||
|
index: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options: routesOptions
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
path: '/sound/{param*}',
|
||||||
|
handler: {
|
||||||
|
directory: {
|
||||||
|
path: path.join(__dirname, '../../sound/'),
|
||||||
|
redirectToSlash: true,
|
||||||
|
index: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options: routesOptions
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
path: '/emoji/{param*}',
|
||||||
|
handler: {
|
||||||
|
directory: {
|
||||||
|
path: path.join(__dirname, '../../emoji/'),
|
||||||
|
redirectToSlash: true,
|
||||||
|
index: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options: routesOptions
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
path: '/memory-pow/{param*}',
|
||||||
|
handler: {
|
||||||
|
directory: {
|
||||||
|
path: path.join(__dirname, '../../memory-pow/'),
|
||||||
|
redirectToSlash: true,
|
||||||
|
index: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options: routesOptions
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
path: '/getConfig',
|
||||||
|
handler: (request, h) => {
|
||||||
|
const response = {
|
||||||
|
config: {
|
||||||
|
...config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
delete response.config.user.tls
|
||||||
method: 'GET',
|
delete response.config.build
|
||||||
path: '/img/{param*}',
|
|
||||||
handler: {
|
|
||||||
directory: {
|
|
||||||
path: config.build.options.imgDir,
|
|
||||||
redirectToSlash: true,
|
|
||||||
index: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options: routesOptions
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: 'GET',
|
|
||||||
path: '/language/{param*}',
|
|
||||||
handler: {
|
|
||||||
directory: {
|
|
||||||
path: path.join(__dirname, '../../language'),
|
|
||||||
redirectToSlash: true,
|
|
||||||
index: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options: routesOptions
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: 'GET',
|
|
||||||
path: '/font/{param*}',
|
|
||||||
handler: {
|
|
||||||
directory: {
|
|
||||||
path: path.join(__dirname, '../../font'),
|
|
||||||
redirectToSlash: true,
|
|
||||||
index: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options: routesOptions
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: 'GET',
|
|
||||||
path: '/sound/{param*}',
|
|
||||||
handler: {
|
|
||||||
directory: {
|
|
||||||
path: path.join(__dirname, '../../sound/'),
|
|
||||||
redirectToSlash: true,
|
|
||||||
index: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options: routesOptions
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: 'GET',
|
|
||||||
path: '/emoji/{param*}',
|
|
||||||
handler: {
|
|
||||||
directory: {
|
|
||||||
path: path.join(__dirname, '../../emoji/'),
|
|
||||||
redirectToSlash: true,
|
|
||||||
index: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options: routesOptions
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: 'GET',
|
|
||||||
path: '/memory-pow/{param*}',
|
|
||||||
handler: {
|
|
||||||
directory: {
|
|
||||||
path: path.join(__dirname, '../../memory-pow/'),
|
|
||||||
redirectToSlash: true,
|
|
||||||
index: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options: routesOptions
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: 'GET',
|
|
||||||
path: '/getConfig',
|
|
||||||
handler: (request, h) => {
|
|
||||||
const response = {
|
|
||||||
config: {
|
|
||||||
...config
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete response.config.user.tls
|
return JSON.stringify(response)
|
||||||
delete response.config.build
|
},
|
||||||
return JSON.stringify(response)
|
options: routesOptions
|
||||||
},
|
}
|
||||||
options: routesOptions
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
|
||||||
module.exports = createRoutes
|
module.exports = createRoutes
|
@ -1,141 +1,140 @@
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
const createCommonRoutes = require('./createCommonRoutes.js')
|
const createCommonRoutes = require('./createCommonRoutes.js')
|
||||||
|
|
||||||
const createPrimaryRoutes = (config, plugins) => {
|
const createPrimaryRoutes = (config, plugins) => {
|
||||||
const routes = createCommonRoutes(config)
|
const routes = createCommonRoutes(config)
|
||||||
|
|
||||||
let myPlugins = plugins
|
let myPlugins = plugins
|
||||||
|
|
||||||
const pluginFolders = {}
|
const pluginFolders = {}
|
||||||
|
|
||||||
const routesOptions = {
|
const routesOptions = {
|
||||||
security: {
|
security: {
|
||||||
hsts: {
|
hsts: {
|
||||||
maxAge: 15768000,
|
maxAge: 15768000,
|
||||||
includeSubDomains: true,
|
includeSubDomains: true,
|
||||||
preload: true
|
preload: true
|
||||||
},
|
},
|
||||||
xframe: 'sameorigin'
|
xframe: 'sameorigin'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.reduce((obj, plugin) => {
|
plugins.reduce((obj, plugin) => {
|
||||||
obj[plugin.name] = plugin.folder
|
obj[plugin.name] = plugin.folder
|
||||||
return obj
|
return obj
|
||||||
}, pluginFolders)
|
}, pluginFolders)
|
||||||
|
|
||||||
|
|
||||||
routes.push(
|
routes.push(
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/',
|
path: '/',
|
||||||
handler: (request, reply) => {
|
handler: (request, reply) => {
|
||||||
return reply.redirect('/app')
|
return reply.redirect('/app')
|
||||||
},
|
},
|
||||||
options: routesOptions
|
options: routesOptions
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/{path*}',
|
path: '/{path*}',
|
||||||
handler: (request, h) => {
|
handler: (request, h) => {
|
||||||
const filePath = path.join(__dirname, '../../public/index.html')
|
const filePath = path.join(__dirname, '../../public/index.html')
|
||||||
const response = h.file(filePath, {
|
const response = h.file(filePath, {
|
||||||
confine: true
|
confine: true
|
||||||
})
|
})
|
||||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
options: routesOptions
|
options: routesOptions
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/getPlugins',
|
path: '/getPlugins',
|
||||||
handler: (request, h) => {
|
handler: (request, h) => {
|
||||||
return { plugins: myPlugins.map(p => p.name) }
|
return { plugins: myPlugins.map(p => p.name) }
|
||||||
},
|
},
|
||||||
options: routesOptions
|
options: routesOptions
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/build/{param*}',
|
path: '/build/{param*}',
|
||||||
handler: {
|
handler: {
|
||||||
directory: {
|
directory: {
|
||||||
path: config.build.options.outputDir,
|
path: config.build.options.outputDir,
|
||||||
redirectToSlash: true,
|
redirectToSlash: true,
|
||||||
index: true
|
index: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
options: routesOptions
|
options: routesOptions
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/src/{param*}',
|
path: '/src/{param*}',
|
||||||
handler: {
|
handler: {
|
||||||
directory: {
|
directory: {
|
||||||
path: path.join(__dirname, '../../src'),
|
path: path.join(__dirname, '../../src'),
|
||||||
redirectToSlash: true,
|
redirectToSlash: true,
|
||||||
index: true
|
index: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
options: routesOptions
|
options: routesOptions
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/plugin/{path*}',
|
path: '/plugin/{path*}',
|
||||||
handler: (request, h) => {
|
handler: (request, h) => {
|
||||||
|
|
||||||
const plugin = request.params.path.split('/')[0]
|
const plugin = request.params.path.split('/')[0]
|
||||||
const filePath = path.join(pluginFolders[plugin], '../', request.params.path)
|
const filePath = path.join(pluginFolders[plugin], '../', request.params.path)
|
||||||
|
|
||||||
const response = h.file(filePath, {
|
const response = h.file(filePath, {
|
||||||
confine: false
|
confine: false
|
||||||
})
|
})
|
||||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
options: routesOptions
|
options: routesOptions
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/plugin/404',
|
path: '/plugin/404',
|
||||||
handler: (request, h) => {
|
handler: (request, h) => {
|
||||||
const response = h.file(path.join(config.server.primary.page404))
|
const response = h.file(path.join(config.server.primary.page404))
|
||||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
options: routesOptions
|
options: routesOptions
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/qortal-components/plugin-mainjs-loader.html',
|
path: '/qortal-components/plugin-mainjs-loader.html',
|
||||||
handler: (request, h) => {
|
handler: (request, h) => {
|
||||||
const response = h.file(path.join(__dirname, '../../src/plugins/plugin-mainjs-loader.html'), {
|
const response = h.file(path.join(__dirname, '../../src/plugins/plugin-mainjs-loader.html'), {
|
||||||
confine: false
|
confine: false
|
||||||
})
|
})
|
||||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
options: routesOptions
|
options: routesOptions
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/qortal-components/plugin-mainjs-loader.js',
|
path: '/qortal-components/plugin-mainjs-loader.js',
|
||||||
handler: (request, h) => {
|
handler: (request, h) => {
|
||||||
const file = path.join(config.build.options.outputDir, '/plugins/plugin-mainjs-loader.js')
|
const file = path.join(config.build.options.outputDir, '/plugins/plugin-mainjs-loader.js')
|
||||||
|
|
||||||
const response = h.file(file, {
|
const response = h.file(file, {
|
||||||
confine: false
|
confine: false
|
||||||
})
|
})
|
||||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
options: routesOptions
|
options: routesOptions
|
||||||
},
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = createPrimaryRoutes
|
module.exports = createPrimaryRoutes
|
@ -3,22 +3,20 @@ const ServerFactory = require('./ServerFactory.js')
|
|||||||
const createPrimaryRoutes = require('./routes/createPrimaryRoutes.js')
|
const createPrimaryRoutes = require('./routes/createPrimaryRoutes.js')
|
||||||
|
|
||||||
const createServer = (config, plugins) => {
|
const createServer = (config, plugins) => {
|
||||||
this.start = async function () {
|
this.start = async function () {
|
||||||
const primaryServer = new ServerFactory(createPrimaryRoutes(config, plugins), config.user.server.primary.host, config.user.server.primary.port, config.user.tls.enabled ? config.user.tls.options : void 0)
|
const primaryServer = new ServerFactory(createPrimaryRoutes(config, plugins), config.user.server.primary.host, config.user.server.primary.port, config.user.tls.enabled ? config.user.tls.options : void 0)
|
||||||
primaryServer.startServer()
|
primaryServer.startServer().then(server => {
|
||||||
.then(server => {
|
console.log(`Qortal UI Server started at ${server.info.uri} and listening on ${server.info.address}`)
|
||||||
console.log(`Qortal UI Server started at ${server.info.uri} and listening on ${server.info.address}`)
|
}).catch(e => {
|
||||||
})
|
console.error(e)
|
||||||
.catch(e => {
|
})
|
||||||
console.error(e)
|
}
|
||||||
})
|
|
||||||
}
|
return this
|
||||||
return this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const serverExports = {
|
const serverExports = {
|
||||||
createServer
|
createServer
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = serverExports
|
module.exports = serverExports
|
@ -1,54 +1,55 @@
|
|||||||
import * as api from 'qortal-ui-crypto'
|
import * as api from 'qortal-ui-crypto'
|
||||||
import mykey from './functional-components/mykey-page.js'
|
import mykey from './functional-components/mykey-page'
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
export const checkApiKey = async (nodeConfig) => {
|
export const checkApiKey = async (nodeConfig) => {
|
||||||
|
let selectedNode = nodeConfig.knownNodes[nodeConfig.node]
|
||||||
|
let apiKey = selectedNode.apiKey
|
||||||
|
|
||||||
let selectedNode = nodeConfig.knownNodes[nodeConfig.node];
|
// Attempt to generate an API key
|
||||||
let apiKey = selectedNode.apiKey;
|
const generateUrl = '/admin/apikey/generate'
|
||||||
|
|
||||||
// Attempt to generate an API key
|
let generateRes = await api.request(generateUrl, {
|
||||||
const generateUrl = "/admin/apikey/generate";
|
method: 'POST'
|
||||||
let generateRes = await api.request(generateUrl, {
|
})
|
||||||
method: "POST"
|
|
||||||
});
|
|
||||||
|
|
||||||
if (generateRes != null && generateRes.error == null && generateRes.length >= 8) {
|
if (generateRes != null && generateRes.error == null && generateRes.length >= 8) {
|
||||||
console.log("Generated API key");
|
console.log('Generated API key')
|
||||||
apiKey = generateRes;
|
|
||||||
|
|
||||||
// Store the generated API key
|
apiKey = generateRes
|
||||||
selectedNode.apiKey = apiKey;
|
|
||||||
nodeConfig.knownNodes[nodeConfig.node] = selectedNode;
|
|
||||||
localStorage.setItem('myQortalNodes', JSON.stringify(nodeConfig.knownNodes));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log("Unable to generate API key");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now test the API key
|
// Store the generated API key
|
||||||
let testResult = await testApiKey(apiKey);
|
selectedNode.apiKey = apiKey
|
||||||
if (testResult === true) {
|
nodeConfig.knownNodes[nodeConfig.node] = selectedNode
|
||||||
console.log("API key test passed");
|
localStorage.setItem('myQortalNodes', JSON.stringify(nodeConfig.knownNodes))
|
||||||
}
|
} else {
|
||||||
else {
|
console.log("Unable to generate API key")
|
||||||
console.log("API key test failed");
|
}
|
||||||
mykey.show();
|
|
||||||
this.dispatchEvent(
|
// Now test the API key
|
||||||
|
let testResult = await testApiKey(apiKey)
|
||||||
|
|
||||||
|
if (testResult === true) {
|
||||||
|
console.log('API key test passed')
|
||||||
|
} else {
|
||||||
|
console.log('API key test failed')
|
||||||
|
|
||||||
|
mykey.show()
|
||||||
|
|
||||||
|
this.dispatchEvent(
|
||||||
new CustomEvent('disable-tour', {
|
new CustomEvent('disable-tour', {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
composed: true
|
composed: true
|
||||||
}),
|
})
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const testApiKey = async (apiKey) => {
|
export const testApiKey = async (apiKey) => {
|
||||||
const testUrl = "/admin/apikey/test?apiKey=" + apiKey;
|
const testUrl = '/admin/apikey/test?apiKey=' + apiKey
|
||||||
let testRes = await api.request(testUrl, {
|
|
||||||
method: "GET"
|
|
||||||
});
|
|
||||||
return testRes === true;
|
|
||||||
|
|
||||||
|
let testRes = await api.request(testUrl, {
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
|
||||||
|
return testRes === true
|
||||||
}
|
}
|
@ -1,5 +1,3 @@
|
|||||||
import WebWorker from 'web-worker:./computePowWorkerFile.js';
|
import WebWorker from 'web-worker:./computePowWorkerFile.js'
|
||||||
|
|
||||||
// You can add any initialization or configuration for the Web Worker here
|
export default WebWorker
|
||||||
|
|
||||||
export default WebWorker;
|
|
@ -1,147 +1,107 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../store.js'
|
import { store } from '../store'
|
||||||
import {translate} from '../../translate'
|
import { appInfoStyles } from '../styles/core-css'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { translate } from '../../translate'
|
||||||
|
|
||||||
class AppInfo extends connect(store)(LitElement) {
|
class AppInfo extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
nodeInfo: { type: Array },
|
nodeInfo: { type: Array },
|
||||||
coreInfo: { type: Array },
|
coreInfo: { type: Array },
|
||||||
nodeConfig: { type: Object },
|
nodeConfig: { type: Object },
|
||||||
theme: { type: String, reflect: true }
|
theme: { type: String, reflect: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [appInfoStyles]
|
||||||
* {
|
}
|
||||||
--mdc-theme-primary: rgb(3, 169, 244);
|
|
||||||
--paper-input-container-focus-color: var(--mdc-theme-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.normal {
|
constructor() {
|
||||||
--mdc-theme-primary: rgb(3, 169, 244);
|
super()
|
||||||
}
|
this.nodeInfo = []
|
||||||
|
this.coreInfo = []
|
||||||
|
this.nodeConfig = {}
|
||||||
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
|
}
|
||||||
|
|
||||||
#profileInMenu {
|
render() {
|
||||||
flex: 0 0 100px;
|
return html`
|
||||||
padding:12px;
|
<div id="profileInMenu">
|
||||||
border-top: 1px solid var(--border);
|
<span class="info">${translate("appinfo.uiversion")}: ${this.nodeConfig.version ? this.nodeConfig.version : ''}</span>
|
||||||
background: var(--sidetopbar);
|
${this._renderCoreVersion()}
|
||||||
}
|
<span class="info">${translate("appinfo.blockheight")}: ${this.nodeInfo.height ? this.nodeInfo.height : ''} <span class=${this.cssStatus}>${this._renderStatus()}</span></span>
|
||||||
|
<span class="info">${translate("appinfo.peers")}: ${this.nodeInfo.numberOfConnections ? this.nodeInfo.numberOfConnections : ''}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
.info {
|
firstUpdated() {
|
||||||
margin: 0;
|
this.getNodeInfo()
|
||||||
font-size: 14px;
|
this.getCoreInfo()
|
||||||
font-weight: 100;
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
padding-bottom: 8px;
|
|
||||||
color: var(--black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.blue {
|
setInterval(() => {
|
||||||
color: #03a9f4;
|
this.getNodeInfo()
|
||||||
margin: 0;
|
this.getCoreInfo()
|
||||||
font-size: 14px;
|
}, 60000)
|
||||||
font-weight: 200;
|
}
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.black {
|
async getNodeInfo() {
|
||||||
color: var(--black);
|
const appinfoNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
margin: 0;
|
const appinfoUrl = appinfoNode.protocol + '://' + appinfoNode.domain + ':' + appinfoNode.port
|
||||||
font-size: 14px;
|
const url = `${appinfoUrl}/admin/status`
|
||||||
font-weight: 200;
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
await fetch(url).then(response => {
|
||||||
super()
|
return response.json()
|
||||||
this.nodeInfo = []
|
}).then(data => {
|
||||||
this.coreInfo = []
|
this.nodeInfo = data
|
||||||
this.nodeConfig = {}
|
}).catch(err => {
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
console.error('Request failed', err)
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
async getCoreInfo() {
|
||||||
return html`
|
const appinfoNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
<div id="profileInMenu">
|
const appinfoUrl = appinfoNode.protocol + '://' + appinfoNode.domain + ':' + appinfoNode.port
|
||||||
<span class="info">${translate("appinfo.uiversion")}: ${this.nodeConfig.version ? this.nodeConfig.version : ''}</span>
|
const url = `${appinfoUrl}/admin/info`
|
||||||
${this._renderCoreVersion()}
|
|
||||||
<span class="info">${translate("appinfo.blockheight")}: ${this.nodeInfo.height ? this.nodeInfo.height : ''} <span class=${this.cssStatus}>${this._renderStatus()}</span></span>
|
|
||||||
<span class="info">${translate("appinfo.peers")}: ${this.nodeInfo.numberOfConnections ? this.nodeInfo.numberOfConnections : ''}
|
|
||||||
<a id="pageLink"></a>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated() {
|
await fetch(url).then(response => {
|
||||||
this.getNodeInfo()
|
return response.json()
|
||||||
this.getCoreInfo()
|
}).then(data => {
|
||||||
|
this.coreInfo = data
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('Request failed', err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
setInterval(() => {
|
_renderStatus() {
|
||||||
this.getNodeInfo()
|
if (this.nodeInfo.isMintingPossible === true && this.nodeInfo.isSynchronizing === true) {
|
||||||
this.getCoreInfo()
|
this.cssStatus = 'blue'
|
||||||
}, 60000)
|
return html`${translate("appinfo.minting")}`
|
||||||
}
|
} else if (this.nodeInfo.isMintingPossible === true && this.nodeInfo.isSynchronizing === false) {
|
||||||
|
this.cssStatus = 'blue'
|
||||||
|
return html`${translate("appinfo.minting")}`
|
||||||
|
} else if (this.nodeInfo.isMintingPossible === false && this.nodeInfo.isSynchronizing === true) {
|
||||||
|
this.cssStatus = 'black'
|
||||||
|
return html`(${translate("appinfo.synchronizing")}... ${this.nodeInfo.syncPercent !== undefined ? this.nodeInfo.syncPercent + '%' : ''})`
|
||||||
|
} else if (this.nodeInfo.isMintingPossible === false && this.nodeInfo.isSynchronizing === false) {
|
||||||
|
this.cssStatus = 'black'
|
||||||
|
return ''
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getNodeInfo() {
|
_renderCoreVersion() {
|
||||||
const appinfoNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
return html`<span class="info">${translate("appinfo.coreversion")}: ${this.coreInfo.buildVersion ? this.coreInfo.buildVersion : ''}</span>`
|
||||||
const appinfoUrl = appinfoNode.protocol + '://' + appinfoNode.domain + ':' + appinfoNode.port
|
}
|
||||||
const url = `${appinfoUrl}/admin/status`
|
|
||||||
|
|
||||||
await fetch(url).then(response => {
|
stateChanged(state) {
|
||||||
return response.json()
|
this.nodeConfig = state.app.nodeConfig
|
||||||
}).then(data => {
|
}
|
||||||
this.nodeInfo = data
|
|
||||||
}).catch(err => {
|
|
||||||
console.error('Request failed', err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async getCoreInfo() {
|
|
||||||
const appinfoNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
|
||||||
const appinfoUrl = appinfoNode.protocol + '://' + appinfoNode.domain + ':' + appinfoNode.port
|
|
||||||
const url = `${appinfoUrl}/admin/info`
|
|
||||||
|
|
||||||
await fetch(url).then(response => {
|
|
||||||
return response.json()
|
|
||||||
}).then(data => {
|
|
||||||
this.coreInfo = data
|
|
||||||
}).catch(err => {
|
|
||||||
console.error('Request failed', err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderStatus() {
|
|
||||||
if (this.nodeInfo.isMintingPossible === true && this.nodeInfo.isSynchronizing === true) {
|
|
||||||
this.cssStatus = 'blue'
|
|
||||||
return html`${translate("appinfo.minting")}`
|
|
||||||
} else if (this.nodeInfo.isMintingPossible === true && this.nodeInfo.isSynchronizing === false) {
|
|
||||||
this.cssStatus = 'blue'
|
|
||||||
return html`${translate("appinfo.minting")}`
|
|
||||||
} else if (this.nodeInfo.isMintingPossible === false && this.nodeInfo.isSynchronizing === true) {
|
|
||||||
this.cssStatus = 'black'
|
|
||||||
return html`(${translate("appinfo.synchronizing")}... ${this.nodeInfo.syncPercent !== undefined ? this.nodeInfo.syncPercent + '%' : ''})`
|
|
||||||
} else if (this.nodeInfo.isMintingPossible === false && this.nodeInfo.isSynchronizing === false) {
|
|
||||||
this.cssStatus = 'black'
|
|
||||||
return ''
|
|
||||||
} else {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderCoreVersion() {
|
|
||||||
return html`<span class="info">${translate("appinfo.coreversion")}: ${this.coreInfo.buildVersion ? this.coreInfo.buildVersion : ''}</span>`
|
|
||||||
}
|
|
||||||
|
|
||||||
stateChanged(state) {
|
|
||||||
this.nodeConfig = state.app.nodeConfig
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('app-info', AppInfo)
|
window.customElements.define('app-info', AppInfo)
|
File diff suppressed because it is too large
Load Diff
@ -1,26 +1,11 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../store.js'
|
import { store } from '../store'
|
||||||
|
|
||||||
class MyElement extends connect(store)(LitElement) {
|
class MyElement extends connect(store)(LitElement) {
|
||||||
static get properties () {
|
render () {
|
||||||
return {
|
return html`<style></style>`
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static get styles () {
|
|
||||||
return css``
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
return html`
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
stateChanged (state) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('my-element', MyElement)
|
window.customElements.define('my-element', MyElement)
|
@ -1,11 +1,13 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {store} from '../../store'
|
import { connect } from 'pwa-helpers'
|
||||||
import {connect} from 'pwa-helpers'
|
import { store } from '../../store'
|
||||||
import {translate} from '../../../translate'
|
import { parentEpml } from '../show-plugin'
|
||||||
import {parentEpml} from '../show-plugin'
|
import { syncIndicator2Styles } from '../../styles/core-css'
|
||||||
|
|
||||||
import '@material/mwc-icon'
|
import '@material/mwc-icon'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import {translate} from '../../../translate'
|
||||||
|
|
||||||
class SyncIndicator extends connect(store)(LitElement) {
|
class SyncIndicator extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
@ -18,6 +20,10 @@ class SyncIndicator extends connect(store)(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [syncIndicator2Styles]
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.blocksBehind = 0
|
this.blocksBehind = 0
|
||||||
@ -32,64 +38,6 @@ class SyncIndicator extends connect(store)(LitElement) {
|
|||||||
this.hasOpened = false
|
this.hasOpened = false
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
* {
|
|
||||||
--mdc-theme-text-primary-on-background: var(--black);
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host {
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 50px;
|
|
||||||
right: 25px;
|
|
||||||
z-index: 50000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.parent {
|
|
||||||
width: 360px;
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid var(--black);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
user-select: none;
|
|
||||||
background: var(--white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.column {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bootstrap-button {
|
|
||||||
font-family: Roboto, sans-serif;
|
|
||||||
font-size: 16px;
|
|
||||||
color: var(--mdc-theme-primary);
|
|
||||||
background-color: transparent;
|
|
||||||
padding: 8px 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
border: none;
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bootstrap-button:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: #03a8f475;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
${!this.hasCoreRunning ? html`
|
${!this.hasCoreRunning ? html`
|
||||||
@ -225,7 +173,7 @@ class SyncIndicator extends connect(store)(LitElement) {
|
|||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent('open-welcome-modal-sync', {
|
new CustomEvent('open-welcome-modal-sync', {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
composed: true,
|
composed: true
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -257,4 +205,4 @@ class SyncIndicator extends connect(store)(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('sync-indicator', SyncIndicator)
|
window.customElements.define('sync-indicator', SyncIndicator)
|
@ -1,16 +1,19 @@
|
|||||||
import {css, html, LitElement} from 'lit';
|
import { html, LitElement } from 'lit'
|
||||||
import {driver} from 'driver.js';
|
import { connect } from 'pwa-helpers'
|
||||||
import 'driver.js/dist/driver.css';
|
import { store } from '../../store'
|
||||||
import '@material/mwc-icon';
|
import { setNewTab } from '../../redux/app/app-actions'
|
||||||
import '@polymer/paper-spinner/paper-spinner-lite.js';
|
import { tourComponentStyles } from '../../styles/core-css'
|
||||||
import '@vaadin/tooltip';
|
import { driver } from 'driver.js'
|
||||||
import '@material/mwc-button';
|
import 'driver.js/dist/driver.css'
|
||||||
import {get, translate} from '../../../translate';
|
import './tour.css'
|
||||||
import '@polymer/paper-dialog/paper-dialog.js';
|
import '@material/mwc-button'
|
||||||
import {setNewTab} from '../../redux/app/app-actions.js';
|
import '@material/mwc-icon'
|
||||||
import {store} from '../../store.js';
|
import '@polymer/paper-dialog/paper-dialog.js'
|
||||||
import {connect} from 'pwa-helpers';
|
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
||||||
import './tour.css';
|
import '@vaadin/tooltip'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { get, translate } from '../../../translate'
|
||||||
|
|
||||||
class TourComponent extends connect(store)(LitElement) {
|
class TourComponent extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@ -18,242 +21,112 @@ class TourComponent extends connect(store)(LitElement) {
|
|||||||
getElements: { attribute: false },
|
getElements: { attribute: false },
|
||||||
dialogOpenedCongrats: { type: Boolean },
|
dialogOpenedCongrats: { type: Boolean },
|
||||||
hasViewedTour: { type: Boolean },
|
hasViewedTour: { type: Boolean },
|
||||||
disableTour: {type: Boolean}
|
disableTour: { type: Boolean },
|
||||||
};
|
nodeUrl: { type: String },
|
||||||
|
address: { type: String }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [tourComponentStyles]
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super()
|
||||||
this.dialogOpenedCongrats = false;
|
this.dialogOpenedCongrats = false
|
||||||
this._controlOpenWelcomeModal =
|
this._controlOpenWelcomeModal = this._controlOpenWelcomeModal.bind(this)
|
||||||
this._controlOpenWelcomeModal.bind(this);
|
this.hasName = false
|
||||||
this.hasName = false;
|
this.nodeUrl = ''
|
||||||
this.nodeUrl = this.getNodeUrl();
|
this.address = ''
|
||||||
this.myNode = this.getMyNode();
|
|
||||||
this._disableTour = this._disableTour.bind(this)
|
this._disableTour = this._disableTour.bind(this)
|
||||||
this.disableTour = false
|
this.disableTour = false
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
render() {
|
||||||
return css`
|
return html`
|
||||||
* {
|
<!-- Profile read-view -->
|
||||||
--mdc-theme-primary: rgb(3, 169, 244);
|
${this.dialogOpenedCongrats && this.hasViewedTour ? html`
|
||||||
--mdc-theme-secondary: var(--mdc-theme-primary);
|
<paper-dialog class="full-info-wrapper" ?opened="${this.dialogOpenedCongrats}">
|
||||||
--mdc-theme-surface: var(--white);
|
<h3>Congratulations!</h3>
|
||||||
--mdc-dialog-content-ink-color: var(--black);
|
<div style="display:flex;gap:15px;justify-content:center;margin-top:10px">
|
||||||
box-sizing: border-box;
|
${translate("tour.tour13")}
|
||||||
color: var(--black);
|
</div>
|
||||||
background: var(--white);
|
<div style="display:flex;gap:15px;justify-content:center;margin-top:10px">
|
||||||
}
|
${translate("tour.tour14")}
|
||||||
|
</div>
|
||||||
:host {
|
<div class="accept-button" @click=${this.visitQtube}>
|
||||||
box-sizing: border-box;
|
${translate("tour.tour15")}
|
||||||
position: fixed;
|
</div>
|
||||||
bottom: 25px;
|
<div style="width:100%;display:flex;justify-content:center;margin-top:10px">
|
||||||
right: 25px;
|
<div class="close-button" @click=${() => { this.onClose() }}>
|
||||||
z-index: 50000;
|
${translate("general.close")}
|
||||||
}
|
</div>
|
||||||
|
</div>
|
||||||
.full-info-wrapper {
|
</paper-dialog>
|
||||||
width: 100%;
|
` : ''}
|
||||||
min-width: 600px;
|
`
|
||||||
max-width: 600px;
|
|
||||||
text-align: center;
|
|
||||||
background: var(--white);
|
|
||||||
border: 1px solid var(--black);
|
|
||||||
border-radius: 15px;
|
|
||||||
padding: 25px;
|
|
||||||
box-shadow: 0px 10px 15px rgba(0, 0, 0, 0.1);
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
.accept-button {
|
|
||||||
font-family: Roboto, sans-serif;
|
|
||||||
letter-spacing: 0.3px;
|
|
||||||
font-weight: 300;
|
|
||||||
padding: 8px 5px;
|
|
||||||
border-radius: 3px;
|
|
||||||
text-align: center;
|
|
||||||
color: var(--black);
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
font-size: 18px;
|
|
||||||
justify-content: center;
|
|
||||||
outline: 1px solid var(--black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.accept-button:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: #03a8f485;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-button {
|
|
||||||
font-family: Roboto, sans-serif;
|
|
||||||
letter-spacing: 0.3px;
|
|
||||||
font-weight: 300;
|
|
||||||
padding: 8px 5px;
|
|
||||||
border-radius: 3px;
|
|
||||||
text-align: center;
|
|
||||||
color: #f44336;
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
font-size: 18px;
|
|
||||||
width:auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-button:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: #f4433663;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_controlOpenWelcomeModal() {
|
|
||||||
this.isSynced = true
|
|
||||||
|
|
||||||
const seenWelcomeSync = JSON.parse(
|
|
||||||
localStorage.getItem('welcome-sync') || 'false'
|
|
||||||
);
|
|
||||||
if (this.hasName) return;
|
|
||||||
if (seenWelcomeSync) return;
|
|
||||||
if(!this.hasViewedTour) return
|
|
||||||
this.dialogOpenedCongrats = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
openWelcomeModal() {
|
|
||||||
this.dispatchEvent(
|
|
||||||
new CustomEvent('send-tour-finished', {
|
|
||||||
bubbles: true,
|
|
||||||
composed: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
const seenWelcomeSync = JSON.parse(
|
|
||||||
localStorage.getItem('welcome-sync') || 'false'
|
|
||||||
);
|
|
||||||
if (this.hasName) return;
|
|
||||||
if (seenWelcomeSync) return;
|
|
||||||
if(!this.isSynced) return
|
|
||||||
this.dialogOpenedCongrats = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_disableTour(){
|
|
||||||
this.disableTour = true
|
|
||||||
driver.reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
window.addEventListener(
|
|
||||||
'open-welcome-modal-sync',
|
|
||||||
this._controlOpenWelcomeModal
|
|
||||||
);
|
|
||||||
window.addEventListener(
|
|
||||||
'disable-tour',
|
|
||||||
this._disableTour
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectedCallback() {
|
|
||||||
window.removeEventListener(
|
|
||||||
'open-welcome-modal-sync',
|
|
||||||
this._controlOpenWelcomeModal
|
|
||||||
);
|
|
||||||
window.addEventListener(
|
|
||||||
'disable-tour',
|
|
||||||
this._disableTour
|
|
||||||
);
|
|
||||||
super.disconnectedCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
getNodeUrl() {
|
|
||||||
const myNode =
|
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
]
|
|
||||||
|
|
||||||
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
|
||||||
}
|
|
||||||
getMyNode() {
|
|
||||||
return window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
async getName(recipient) {
|
|
||||||
try {
|
|
||||||
const endpoint = `${this.nodeUrl}/names/address/${recipient}`;
|
|
||||||
const res = await fetch(endpoint);
|
|
||||||
const getNames = await res.json();
|
|
||||||
|
|
||||||
if (Array.isArray(getNames) && getNames.length > 0) {
|
|
||||||
return getNames[0].name;
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async firstUpdated() {
|
async firstUpdated() {
|
||||||
|
this.getNodeUrl()
|
||||||
this.address = store.getState().app.selectedAddress.address
|
this.address = store.getState().app.selectedAddress.address
|
||||||
const hasViewedTour = JSON.parse(
|
|
||||||
localStorage.getItem(`hasViewedTour-${this.address}`) || 'false'
|
const hasViewedTour = JSON.parse(localStorage.getItem(`hasViewedTour-${this.address}`) || 'false')
|
||||||
);
|
const name = await this.getName(this.address)
|
||||||
const name = await this.getName(this.address);
|
|
||||||
if (name) {
|
if (name) {
|
||||||
this.hasName = true;
|
this.hasName = true
|
||||||
}
|
}
|
||||||
this.hasViewedTour = hasViewedTour;
|
|
||||||
|
this.hasViewedTour = hasViewedTour
|
||||||
|
|
||||||
if (!hasViewedTour) {
|
if (!hasViewedTour) {
|
||||||
try {
|
try {
|
||||||
if (name) {
|
if (name) {
|
||||||
this.hasViewedTour = true;
|
this.hasViewedTour = true
|
||||||
this.hasName = true;
|
this.hasName = true
|
||||||
localStorage.setItem(`hasViewedTour-${this.address}`, JSON.stringify(true))
|
localStorage.setItem(`hasViewedTour-${this.address}`, JSON.stringify(true))
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log({ error });
|
console.log({ error })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise((res) => {
|
await new Promise((res) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
res();
|
res()
|
||||||
}, 1000);
|
}, 1000)
|
||||||
});
|
})
|
||||||
|
|
||||||
if (!this.hasViewedTour && this.disableTour !== true) {
|
if (!this.hasViewedTour && this.disableTour !== true) {
|
||||||
const elements = this.getElements();
|
const elements = this.getElements()
|
||||||
let steps = [
|
|
||||||
{
|
let steps = [{
|
||||||
popover: {
|
popover: {
|
||||||
title: get("tour.tour6"),
|
title: get("tour.tour6"),
|
||||||
description: `
|
description: `
|
||||||
<div style="display:flex;justify-content:center;gap:15px">
|
<div style="display:flex;justify-content:center;gap:15px">
|
||||||
<img style="height:40px;width:auto;margin:15px 0px;" src="/img/qort.png" />
|
<img style="height:40px;width:auto;margin:15px 0px;" src="/img/qort.png" />
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;">
|
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;">
|
||||||
<div style="height:6px;width:6px;border-radius:50%;background:var(--black)"></div> <p style="margin:0px;padding:0px">${get("tour.tour7")}</p>
|
<div style="height:6px;width:6px;border-radius:50%;background:var(--black)"></div>
|
||||||
</div>
|
<p style="margin:0px;padding:0px">${get("tour.tour7")}</p>
|
||||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;">
|
</div>
|
||||||
<div style="height:6px;width:6px;border-radius:50%;background:var(--black)"></div> <p style="margin:0px;padding:0px">${get("tour.tour8")}</p>
|
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;">
|
||||||
</div>
|
<div style="height:6px;width:6px;border-radius:50%;background:var(--black)"></div>
|
||||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;margin-bottom:30px">
|
<p style="margin:0px;padding:0px">${get("tour.tour8")}</p>
|
||||||
<div style="height:6px;width:6px;border-radius:50%;background:var(--black)"></div> <p style="margin:0px;padding:0px">${get("tour.tour9")}</p>
|
</div>
|
||||||
</div>
|
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;margin-bottom:30px">
|
||||||
`,
|
<div style="height:6px;width:6px;border-radius:50%;background:var(--black)"></div>
|
||||||
// ... other options
|
<p style="margin:0px;padding:0px">${get("tour.tour9")}</p>
|
||||||
},
|
</div>
|
||||||
},
|
`
|
||||||
];
|
}
|
||||||
const step2 = elements['core-sync-status-id'];
|
}]
|
||||||
const step3 = elements['tab'];
|
|
||||||
const step4 = elements['checklist'];
|
const step2 = elements['core-sync-status-id']
|
||||||
|
const step3 = elements['tab']
|
||||||
|
const step4 = elements['checklist']
|
||||||
|
|
||||||
if (step2) {
|
if (step2) {
|
||||||
steps.push({
|
steps.push({
|
||||||
@ -261,58 +134,54 @@ class TourComponent extends connect(store)(LitElement) {
|
|||||||
popover: {
|
popover: {
|
||||||
title: get("tour.tour5"),
|
title: get("tour.tour5"),
|
||||||
description: `
|
description: `
|
||||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;margin-bottom:30px">
|
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;margin-bottom:30px">
|
||||||
<p style="margin:0px;padding:0px">${get("tour.tour1")}</p>
|
<p style="margin:0px;padding:0px">${get("tour.tour1")}</p>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;">
|
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;">
|
||||||
<span><img src="/img/synced.png" style="height: 24px; width: 24px; padding-top: 4px;" /></span>
|
<span><img src="/img/synced.png" style="height: 24px; width: 24px; padding-top: 4px;" /></span>
|
||||||
<p style="margin:0px;padding:0px">${get("tour.tour2")}</p>
|
<p style="margin:0px;padding:0px">${get("tour.tour2")}</p>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;">
|
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;">
|
||||||
<span><img src="/img/synced_minting.png" style="height: 24px; width: 24px; padding-top: 4px;" /></span>
|
<span><img src="/img/synced_minting.png" style="height: 24px; width: 24px; padding-top: 4px;" /></span>
|
||||||
<p style="margin:0px;padding:0px">${get("tour.tour3")}</p>
|
<p style="margin:0px;padding:0px">${get("tour.tour3")}</p>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;margin-bottom:30px">
|
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;margin-bottom:30px">
|
||||||
<span><img src="/img/syncing.png" style="height: 24px; width: 24px; padding-top: 4px;" /></span>
|
<span><img src="/img/syncing.png" style="height: 24px; width: 24px; padding-top: 4px;" /></span>
|
||||||
<p style="margin:0px;padding:0px">${get("tour.tour4")}</p>
|
<p style="margin:0px;padding:0px">${get("tour.tour4")}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
`,
|
`
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (step3) {
|
if (step3) {
|
||||||
steps.push({
|
steps.push({
|
||||||
element: step3,
|
element: step3,
|
||||||
popover: {
|
popover: {
|
||||||
title: 'Tab View',
|
title: 'Tab View',
|
||||||
description: `
|
description: `
|
||||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;margin-bottom:30px">
|
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;margin-bottom:30px">
|
||||||
<p style="margin:0px;padding:0px">${get("tour.tour10")}
|
<p style="margin:0px;padding:0px">${get("tour.tour10")}</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;">
|
||||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;">
|
<span><img src="/img/addplugin.webp" style="height: 36px; width: 36px; padding-top: 4px;" /></span>
|
||||||
<span><img src="/img/addplugin.webp" style="height: 36px; width: 36px; padding-top: 4px;" /></span>
|
<p style="margin:0px;padding:0px">
|
||||||
<p style="margin:0px;padding:0px">You can also bookmark other Q-Apps and Plugins by clicking on the ${get(
|
You can also bookmark other Q-Apps and Plugins by clicking on the ${get('tabmenu.tm19')} button
|
||||||
'tabmenu.tm19'
|
</p>
|
||||||
)} button</p>
|
</div>
|
||||||
</div>
|
`
|
||||||
`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (step4) {
|
|
||||||
steps.push(
|
|
||||||
{
|
|
||||||
element: step4,
|
|
||||||
popover: {
|
|
||||||
title: get("tour.tour11"),
|
|
||||||
description: get("tour.tour12"),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
);this.hasViewedTour
|
})
|
||||||
}
|
}
|
||||||
let currentStepIndex = 0;
|
|
||||||
|
if (step4) {
|
||||||
|
steps.push({ element: step4, popover: { title: get("tour.tour11"), description: get("tour.tour12")}})
|
||||||
|
this.hasViewedTour
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentStepIndex = 0
|
||||||
|
|
||||||
const driverObj = driver({
|
const driverObj = driver({
|
||||||
popoverClass: 'driverjs-theme',
|
popoverClass: 'driverjs-theme',
|
||||||
showProgress: true,
|
showProgress: true,
|
||||||
@ -321,25 +190,93 @@ class TourComponent extends connect(store)(LitElement) {
|
|||||||
allowClose: false,
|
allowClose: false,
|
||||||
onDestroyed: () => {
|
onDestroyed: () => {
|
||||||
localStorage.setItem(`hasViewedTour-${this.address}`, JSON.stringify(true))
|
localStorage.setItem(`hasViewedTour-${this.address}`, JSON.stringify(true))
|
||||||
this.hasViewedTour = true;
|
this.hasViewedTour = true
|
||||||
this.openWelcomeModal();
|
this.openWelcomeModal()
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
driverObj.drive();
|
driverObj.drive()
|
||||||
} else {
|
} else {
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent('send-tour-finished', {
|
new CustomEvent('send-tour-finished', {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
composed: true,
|
composed: true
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_controlOpenWelcomeModal() {
|
||||||
|
this.isSynced = true
|
||||||
|
|
||||||
|
const seenWelcomeSync = JSON.parse(localStorage.getItem('welcome-sync') || 'false')
|
||||||
|
|
||||||
|
if (this.hasName) return
|
||||||
|
if (seenWelcomeSync) return
|
||||||
|
if (!this.hasViewedTour) return
|
||||||
|
|
||||||
|
this.dialogOpenedCongrats = true
|
||||||
|
}
|
||||||
|
|
||||||
|
openWelcomeModal() {
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent('send-tour-finished', {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const seenWelcomeSync = JSON.parse(localStorage.getItem('welcome-sync') || 'false')
|
||||||
|
|
||||||
|
if (this.hasName) return
|
||||||
|
if (seenWelcomeSync) return
|
||||||
|
if (!this.isSynced) return
|
||||||
|
|
||||||
|
this.dialogOpenedCongrats = true
|
||||||
|
}
|
||||||
|
|
||||||
|
_disableTour() {
|
||||||
|
this.disableTour = true
|
||||||
|
driver.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback()
|
||||||
|
window.addEventListener('open-welcome-modal-sync', this._controlOpenWelcomeModal)
|
||||||
|
window.addEventListener('disable-tour', this._disableTour)
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
window.removeEventListener('open-welcome-modal-sync', this._controlOpenWelcomeModal)
|
||||||
|
window.addEventListener('disable-tour', this._disableTour)
|
||||||
|
super.disconnectedCallback()
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeUrl() {
|
||||||
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
const myNodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
|
this.nodeUrl = myNodeUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
async getName(recipient) {
|
||||||
|
try {
|
||||||
|
const endpoint = `${this.nodeUrl}/names/address/${recipient}`
|
||||||
|
const res = await fetch(endpoint)
|
||||||
|
const getNames = await res.json()
|
||||||
|
|
||||||
|
if (Array.isArray(getNames) && getNames.length > 0) {
|
||||||
|
return getNames[0].name
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitQtube() {
|
visitQtube() {
|
||||||
this.onClose();
|
this.onClose()
|
||||||
const query = `?service=APP&name=Q-Tube`;
|
const query = `?service=APP&name=Q-Tube`
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
setNewTab({
|
setNewTab({
|
||||||
url: `qdn/browser/index.html${query}`,
|
url: `qdn/browser/index.html${query}`,
|
||||||
@ -350,59 +287,16 @@ class TourComponent extends connect(store)(LitElement) {
|
|||||||
page: `qdn/browser/index.html${query}`,
|
page: `qdn/browser/index.html${query}`,
|
||||||
title: 'Q-Tube',
|
title: 'Q-Tube',
|
||||||
menus: [],
|
menus: [],
|
||||||
parent: false,
|
parent: false
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose() {
|
||||||
localStorage.setItem(`welcome-sync-${this.address}`, JSON.stringify(true))
|
localStorage.setItem(`welcome-sync-${this.address}`, JSON.stringify(true))
|
||||||
this.dialogOpenedCongrats = false;
|
this.dialogOpenedCongrats = false
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`
|
|
||||||
<!-- Profile read-view -->
|
|
||||||
${this.dialogOpenedCongrats && this.hasViewedTour
|
|
||||||
? html`
|
|
||||||
<paper-dialog
|
|
||||||
class="full-info-wrapper"
|
|
||||||
?opened="${this.dialogOpenedCongrats}"
|
|
||||||
>
|
|
||||||
<h3>Congratulations!</h3>
|
|
||||||
<div
|
|
||||||
style="display:flex;gap:15px;justify-content:center;margin-top:10px"
|
|
||||||
>
|
|
||||||
${translate("tour.tour13")}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style="display:flex;gap:15px;justify-content:center;margin-top:10px"
|
|
||||||
>
|
|
||||||
${translate("tour.tour14")}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="accept-button"
|
|
||||||
@click=${this.visitQtube}
|
|
||||||
>
|
|
||||||
${translate("tour.tour15")}
|
|
||||||
</div>
|
|
||||||
<div style="width:100%;display:flex;justify-content:center;margin-top:10px">
|
|
||||||
<div
|
|
||||||
class="close-button"
|
|
||||||
@click=${()=> {
|
|
||||||
this.onClose()
|
|
||||||
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
${translate("general.close")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</paper-dialog>
|
|
||||||
`
|
|
||||||
: ''}
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define('tour-component', TourComponent);
|
|
||||||
|
window.customElements.define('tour-component', TourComponent)
|
@ -1,10 +1,11 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {translate} from '../../translate'
|
|
||||||
import isElectron from 'is-electron'
|
import isElectron from 'is-electron'
|
||||||
|
|
||||||
import '@polymer/paper-icon-button/paper-icon-button.js'
|
import '@polymer/paper-icon-button/paper-icon-button.js'
|
||||||
import '@polymer/iron-icons/iron-icons.js'
|
import '@polymer/iron-icons/iron-icons.js'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { translate } from '../../translate'
|
||||||
|
|
||||||
class CheckForUpdate extends LitElement {
|
class CheckForUpdate extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
@ -17,11 +18,6 @@ class CheckForUpdate extends LitElement {
|
|||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = [
|
|
||||||
css`
|
|
||||||
`
|
|
||||||
]
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
${this.renderUpdateButton()}
|
${this.renderUpdateButton()}
|
||||||
@ -29,6 +25,7 @@ class CheckForUpdate extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
renderUpdateButton() {
|
renderUpdateButton() {
|
||||||
|
@ -1,82 +1,58 @@
|
|||||||
import {Sha256} from 'asmcrypto.js'
|
import { Sha256 } from 'asmcrypto.js'
|
||||||
|
|
||||||
|
function sbrk(size, heap) {
|
||||||
function sbrk(size, heap){
|
let brk = 512 * 1024 // stack top
|
||||||
let brk = 512 * 1024 // stack top
|
let old = brk
|
||||||
let old = brk
|
brk += size
|
||||||
brk += size
|
if (brk > heap.length) throw new Error('heap exhausted')
|
||||||
|
return old
|
||||||
if (brk > heap.length)
|
|
||||||
throw new Error('heap exhausted')
|
|
||||||
|
|
||||||
return old
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self.addEventListener('message', async e => {
|
self.addEventListener('message', async e => {
|
||||||
const response = await computePow(e.data.chatBytes, e.data.path, e.data.difficulty)
|
const response = await computePow(e.data.chatBytes, e.data.path, e.data.difficulty)
|
||||||
postMessage(response)
|
postMessage(response)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
|
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
|
||||||
const heap = new Uint8Array(memory.buffer)
|
const heap = new Uint8Array(memory.buffer)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const computePow = async (chatBytes, path, difficulty) => {
|
const computePow = async (chatBytes, path, difficulty) => {
|
||||||
|
let response = null
|
||||||
let response = null
|
await new Promise((resolve, reject) => {
|
||||||
|
const _chatBytesArray = Object.keys(chatBytes).map(function (key) { return chatBytes[key]; })
|
||||||
await new Promise((resolve, reject)=> {
|
const chatBytesArray = new Uint8Array(_chatBytesArray)
|
||||||
|
const chatBytesHash = new Sha256().process(chatBytesArray).finish().result
|
||||||
const _chatBytesArray = Object.keys(chatBytes).map(function (key) { return chatBytes[key]; });
|
const hashPtr = sbrk(32, heap)
|
||||||
const chatBytesArray = new Uint8Array(_chatBytesArray);
|
const hashAry = new Uint8Array(memory.buffer, hashPtr, 32)
|
||||||
const chatBytesHash = new Sha256().process(chatBytesArray).finish().result;
|
hashAry.set(chatBytesHash)
|
||||||
const hashPtr = sbrk(32, heap);
|
const workBufferLength = 8 * 1024 * 1024
|
||||||
const hashAry = new Uint8Array(memory.buffer, hashPtr, 32);
|
const workBufferPtr = sbrk(workBufferLength, heap)
|
||||||
hashAry.set(chatBytesHash);
|
const importObject = {
|
||||||
|
env: {
|
||||||
|
memory: memory
|
||||||
const workBufferLength = 8 * 1024 * 1024;
|
}
|
||||||
const workBufferPtr = sbrk(workBufferLength, heap);
|
}
|
||||||
|
function loadWebAssembly(filename, imports) {
|
||||||
|
// Fetch the file and compile it
|
||||||
|
return fetch(filename)
|
||||||
const importObject = {
|
.then(response => response.arrayBuffer())
|
||||||
env: {
|
.then(buffer => WebAssembly.compile(buffer))
|
||||||
memory: memory
|
.then(module => {
|
||||||
},
|
// Create the instance.
|
||||||
};
|
return new WebAssembly.Instance(module, importObject)
|
||||||
|
})
|
||||||
function loadWebAssembly(filename, imports) {
|
}
|
||||||
// Fetch the file and compile it
|
loadWebAssembly(path)
|
||||||
return fetch(filename)
|
.then(wasmModule => {
|
||||||
.then(response => response.arrayBuffer())
|
response = {
|
||||||
.then(buffer => WebAssembly.compile(buffer))
|
nonce: wasmModule.exports.compute2(hashPtr, workBufferPtr, workBufferLength, difficulty),
|
||||||
.then(module => {
|
chatBytesArray
|
||||||
|
}
|
||||||
// Create the instance.
|
resolve()
|
||||||
return new WebAssembly.Instance(module, importObject);
|
})
|
||||||
});
|
})
|
||||||
}
|
return response
|
||||||
|
|
||||||
|
|
||||||
loadWebAssembly(path)
|
|
||||||
.then(wasmModule => {
|
|
||||||
response = {
|
|
||||||
nonce : wasmModule.exports.compute2(hashPtr, workBufferPtr, workBufferLength, difficulty),
|
|
||||||
chatBytesArray
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve()
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
return response
|
|
||||||
}
|
}
|
@ -1,92 +1,68 @@
|
|||||||
import { Sha256 } from 'asmcrypto.js'
|
import { Sha256 } from 'asmcrypto.js'
|
||||||
|
|
||||||
|
function sbrk(size, heap) {
|
||||||
|
let brk = 512 * 1024 // stack top
|
||||||
function sbrk(size, heap){
|
let old = brk
|
||||||
let brk = 512 * 1024 // stack top
|
brk += size
|
||||||
let old = brk
|
if (brk > heap.length) throw new Error('heap exhausted')
|
||||||
brk += size
|
return old
|
||||||
|
|
||||||
if (brk > heap.length)
|
|
||||||
throw new Error('heap exhausted')
|
|
||||||
|
|
||||||
return old
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self.addEventListener('message', async e => {
|
self.addEventListener('message', async e => {
|
||||||
const response = await computePow(e.data.convertedBytes, e.data.path)
|
const response = await computePow(e.data.convertedBytes, e.data.path)
|
||||||
postMessage(response)
|
postMessage(response)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
|
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
|
||||||
const heap = new Uint8Array(memory.buffer)
|
const heap = new Uint8Array(memory.buffer)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const computePow = async (convertedBytes, path) => {
|
const computePow = async (convertedBytes, path) => {
|
||||||
|
let response = null
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
const _convertedBytesArray = Object.keys(convertedBytes).map(
|
||||||
|
function (key) {
|
||||||
|
return convertedBytes[key]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const convertedBytesArray = new Uint8Array(_convertedBytesArray)
|
||||||
|
const convertedBytesHash = new Sha256()
|
||||||
|
.process(convertedBytesArray)
|
||||||
|
.finish().result
|
||||||
|
const hashPtr = sbrk(32, heap)
|
||||||
|
const hashAry = new Uint8Array(
|
||||||
|
memory.buffer,
|
||||||
|
hashPtr,
|
||||||
|
32
|
||||||
|
)
|
||||||
|
hashAry.set(convertedBytesHash)
|
||||||
|
const difficulty = 14
|
||||||
|
const workBufferLength = 8 * 1024 * 1024
|
||||||
|
const workBufferPtr = sbrk(
|
||||||
|
workBufferLength,
|
||||||
|
heap
|
||||||
|
)
|
||||||
|
const importObject = {
|
||||||
|
env: {
|
||||||
|
memory: memory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function loadWebAssembly(filename, imports) {
|
||||||
|
return fetch(filename)
|
||||||
|
.then(response => response.arrayBuffer())
|
||||||
|
.then(buffer => WebAssembly.compile(buffer))
|
||||||
|
.then(module => {
|
||||||
|
return new WebAssembly.Instance(module, importObject)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loadWebAssembly(path)
|
||||||
|
.then(wasmModule => {
|
||||||
|
response = {
|
||||||
|
nonce: wasmModule.exports.compute2(hashPtr, workBufferPtr, workBufferLength, difficulty),
|
||||||
|
|
||||||
|
}
|
||||||
let response = null
|
resolve()
|
||||||
|
})
|
||||||
await new Promise((resolve, reject)=> {
|
})
|
||||||
|
return response
|
||||||
const _convertedBytesArray = Object.keys(convertedBytes).map(
|
|
||||||
function (key) {
|
|
||||||
return convertedBytes[key]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const convertedBytesArray = new Uint8Array(_convertedBytesArray)
|
|
||||||
const convertedBytesHash = new Sha256()
|
|
||||||
.process(convertedBytesArray)
|
|
||||||
.finish().result
|
|
||||||
const hashPtr = sbrk(32, heap)
|
|
||||||
const hashAry = new Uint8Array(
|
|
||||||
memory.buffer,
|
|
||||||
hashPtr,
|
|
||||||
32
|
|
||||||
)
|
|
||||||
|
|
||||||
hashAry.set(convertedBytesHash)
|
|
||||||
const difficulty = 14
|
|
||||||
const workBufferLength = 8 * 1024 * 1024
|
|
||||||
const workBufferPtr = sbrk(
|
|
||||||
workBufferLength,
|
|
||||||
heap
|
|
||||||
)
|
|
||||||
|
|
||||||
const importObject = {
|
|
||||||
env: {
|
|
||||||
memory: memory
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function loadWebAssembly(filename, imports) {
|
|
||||||
return fetch(filename)
|
|
||||||
.then(response => response.arrayBuffer())
|
|
||||||
.then(buffer => WebAssembly.compile(buffer))
|
|
||||||
.then(module => {
|
|
||||||
return new WebAssembly.Instance(module, importObject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
loadWebAssembly(path)
|
|
||||||
.then(wasmModule => {
|
|
||||||
response = {
|
|
||||||
nonce : wasmModule.exports.compute2(hashPtr, workBufferPtr, workBufferLength, difficulty),
|
|
||||||
|
|
||||||
}
|
|
||||||
resolve()
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
return response
|
|
||||||
}
|
}
|
@ -1,320 +1,303 @@
|
|||||||
import {html, LitElement} from 'lit';
|
import { html, LitElement } from 'lit'
|
||||||
import '@material/mwc-icon';
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../../store';
|
import { store } from '../../store'
|
||||||
import {connect} from 'pwa-helpers';
|
import { parentEpml } from '../show-plugin'
|
||||||
import '@vaadin/tooltip';
|
import { setCoinBalances } from '../../redux/app/app-actions'
|
||||||
import {parentEpml} from '../show-plugin';
|
|
||||||
import {setCoinBalances} from '../../redux/app/app-actions';
|
|
||||||
|
|
||||||
class CoinBalancesController extends connect(store)(LitElement) {
|
class CoinBalancesController extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
coinList: { type: Object },
|
coinList: { type: Object }
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.coinList = {}
|
this.coinList = {}
|
||||||
this.nodeUrl = this.getNodeUrl();
|
this.nodeUrl = this.getNodeUrl()
|
||||||
this.myNode = this.getMyNode();
|
this.myNode = this.getMyNode()
|
||||||
this.fetchBalance = this.fetchBalance.bind(this)
|
this.fetchBalance = this.fetchBalance.bind(this)
|
||||||
this._updateCoinList = this._updateCoinList.bind(this)
|
this._updateCoinList = this._updateCoinList.bind(this)
|
||||||
this.stop = false
|
this.stop = false
|
||||||
}
|
}
|
||||||
|
|
||||||
getNodeUrl() {
|
render() {
|
||||||
const myNode =
|
return html``
|
||||||
store.getState().app.nodeConfig.knownNodes[
|
}
|
||||||
store.getState().app.nodeConfig.node
|
|
||||||
]
|
|
||||||
|
|
||||||
|
getNodeUrl() {
|
||||||
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
}
|
}
|
||||||
|
|
||||||
getMyNode() {
|
getMyNode() {
|
||||||
return store.getState().app.nodeConfig.knownNodes[
|
return store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
store.getState().app.nodeConfig.node
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateQortWalletBalance() {
|
||||||
|
let qortAddress = store.getState().app.selectedAddress.address
|
||||||
|
|
||||||
async updateArrrWalletBalance() {
|
await parentEpml.request('apiCall', {
|
||||||
let _url = `/crosschain/arrr/walletbalance?apiKey=${this.myNode.apiKey}`
|
url: `/addresses/balance/${qortAddress}?apiKey=${this.myNode.apiKey}`,
|
||||||
let _body = store.getState().app.selectedAddress.arrrWallet.seed58
|
}).then((res) => {
|
||||||
|
this.qortWalletBalance = res
|
||||||
await parentEpml.request('apiCall', {
|
store.dispatch(
|
||||||
url: _url,
|
setCoinBalances({
|
||||||
method: 'POST',
|
type: 'qort',
|
||||||
body: _body,
|
fullValue: Number(res)
|
||||||
}).then((res) => {
|
})
|
||||||
if (isNaN(Number(res))) {
|
)
|
||||||
//...
|
}).catch(() => {
|
||||||
} else {
|
console.log('error')
|
||||||
this.arrrWalletBalance = (Number(res) / 1e8).toFixed(8)
|
})
|
||||||
store.dispatch(
|
|
||||||
setCoinBalances({
|
|
||||||
type: 'arrr',
|
|
||||||
fullValue: Number(res)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}).catch(()=> {
|
|
||||||
console.log('error')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
async updateQortWalletBalance() {
|
|
||||||
let qortAddress = store.getState().app.selectedAddress.address
|
|
||||||
|
|
||||||
await parentEpml.request('apiCall', {
|
|
||||||
url: `/addresses/balance/${qortAddress}?apiKey=${this.myNode.apiKey}`,
|
|
||||||
}).then((res) => {
|
|
||||||
this.qortWalletBalance = res
|
|
||||||
store.dispatch(
|
|
||||||
setCoinBalances({
|
|
||||||
type: 'qort',
|
|
||||||
fullValue: Number(res)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}).catch(()=> {
|
|
||||||
console.log('error')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateRvnWalletBalance() {
|
|
||||||
let _url = `/crosschain/rvn/walletbalance?apiKey=${this.myNode.apiKey}`
|
|
||||||
let _body = store.getState().app.selectedAddress.rvnWallet.derivedMasterPublicKey
|
|
||||||
|
|
||||||
await parentEpml.request('apiCall', {
|
|
||||||
url: _url,
|
|
||||||
method: 'POST',
|
|
||||||
body: _body,
|
|
||||||
}).then((res) => {
|
|
||||||
if (isNaN(Number(res))) {
|
|
||||||
//...
|
|
||||||
} else {
|
|
||||||
this.rvnWalletBalance = (Number(res) / 1e8).toFixed(8)
|
|
||||||
store.dispatch(
|
|
||||||
setCoinBalances({
|
|
||||||
type: 'rvn',
|
|
||||||
fullValue: Number(res)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}).catch(()=> {
|
|
||||||
console.log('error')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateDgbWalletBalance() {
|
|
||||||
let _url = `/crosschain/dgb/walletbalance?apiKey=${this.myNode.apiKey}`
|
|
||||||
let _body = store.getState().app.selectedAddress.dgbWallet.derivedMasterPublicKey
|
|
||||||
|
|
||||||
await parentEpml.request('apiCall', {
|
|
||||||
url: _url,
|
|
||||||
method: 'POST',
|
|
||||||
body: _body,
|
|
||||||
}).then((res) => {
|
|
||||||
if (isNaN(Number(res))) {
|
|
||||||
//...
|
|
||||||
} else {
|
|
||||||
this.dgbWalletBalance = (Number(res) / 1e8).toFixed(8)
|
|
||||||
store.dispatch(
|
|
||||||
setCoinBalances({
|
|
||||||
type: 'dgb',
|
|
||||||
fullValue: Number(res)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}).catch(()=> {
|
|
||||||
console.log('error')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateDogeWalletBalance() {
|
|
||||||
let _url = `/crosschain/doge/walletbalance?apiKey=${this.myNode.apiKey}`
|
|
||||||
let _body = store.getState().app.selectedAddress.dogeWallet.derivedMasterPublicKey
|
|
||||||
|
|
||||||
await parentEpml.request('apiCall', {
|
|
||||||
url: _url,
|
|
||||||
method: 'POST',
|
|
||||||
body: _body,
|
|
||||||
}).then((res) => {
|
|
||||||
if (isNaN(Number(res))) {
|
|
||||||
//...
|
|
||||||
} else {
|
|
||||||
this.dogeWalletBalance = (Number(res) / 1e8).toFixed(8)
|
|
||||||
store.dispatch(
|
|
||||||
setCoinBalances({
|
|
||||||
type: 'doge',
|
|
||||||
fullValue: Number(res)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}).catch(()=> {
|
|
||||||
console.log('error')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateBtcWalletBalance() {
|
|
||||||
let _url = `/crosschain/btc/walletbalance?apiKey=${this.myNode.apiKey}`
|
|
||||||
let _body = store.getState().app.selectedAddress.btcWallet.derivedMasterPublicKey
|
|
||||||
|
|
||||||
await parentEpml.request('apiCall', {
|
|
||||||
url: _url,
|
|
||||||
method: 'POST',
|
|
||||||
body: _body,
|
|
||||||
}).then((res) => {
|
|
||||||
if (isNaN(Number(res))) {
|
|
||||||
//...
|
|
||||||
} else {
|
|
||||||
this.btcWalletBalance = (Number(res) / 1e8).toFixed(8)
|
|
||||||
store.dispatch(
|
|
||||||
setCoinBalances({
|
|
||||||
type: 'btc',
|
|
||||||
fullValue: Number(res)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}).catch(()=> {
|
|
||||||
console.log('error')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateLtcWalletBalance() {
|
|
||||||
let _url = `/crosschain/ltc/walletbalance?apiKey=${this.myNode.apiKey}`
|
|
||||||
let _body = store.getState().app.selectedAddress.ltcWallet.derivedMasterPublicKey
|
|
||||||
|
|
||||||
await parentEpml.request('apiCall', {
|
|
||||||
url: _url,
|
|
||||||
method: 'POST',
|
|
||||||
body: _body,
|
|
||||||
}).then((res) => {
|
|
||||||
if (isNaN(Number(res))) {
|
|
||||||
//...
|
|
||||||
} else {
|
|
||||||
this.ltcWalletBalance = (Number(res) / 1e8).toFixed(8)
|
|
||||||
store.dispatch(
|
|
||||||
setCoinBalances({
|
|
||||||
type: 'ltc',
|
|
||||||
fullValue: Number(res)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
}).catch(()=> {
|
|
||||||
console.log('error')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateCoinList(event) {
|
|
||||||
const copyCoinList = {...this.coinList}
|
|
||||||
const coin = event.detail
|
|
||||||
if(!copyCoinList[coin]){
|
|
||||||
try {
|
|
||||||
if(coin === 'ltc'){
|
|
||||||
this.updateLtcWalletBalance()
|
|
||||||
} else if(coin === 'qort'){
|
|
||||||
this.updateQortWalletBalance()
|
|
||||||
} else if(coin === 'doge'){
|
|
||||||
this.updateDogeWalletBalance()
|
|
||||||
} else if(coin === 'btc'){
|
|
||||||
this.updateBtcWalletBalance()
|
|
||||||
} else if(coin === 'dgb'){
|
|
||||||
this.updateDgbWalletBalance()
|
|
||||||
} else if(coin === 'rvn'){
|
|
||||||
this.updateRvnWalletBalance()
|
|
||||||
}else if(coin === 'arrr'){
|
|
||||||
this.updateArrrWalletBalance()
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
copyCoinList[coin] = Date.now() + 120000;
|
|
||||||
this.coinList = copyCoinList
|
|
||||||
|
|
||||||
this.requestUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateBtcWalletBalance() {
|
||||||
|
let _url = `/crosschain/btc/walletbalance?apiKey=${this.myNode.apiKey}`
|
||||||
|
let _body = store.getState().app.selectedAddress.btcWallet.derivedMasterPublicKey
|
||||||
|
|
||||||
async fetchCoins(arrayOfCoins){
|
await parentEpml.request('apiCall', {
|
||||||
const getCoinBalances = (arrayOfCoins || []).map(
|
url: _url,
|
||||||
async (coin) => {
|
method: 'POST',
|
||||||
if(coin === 'ltc'){
|
body: _body
|
||||||
await this.updateLtcWalletBalance()
|
}).then((res) => {
|
||||||
} else if(coin === 'qort'){
|
if (isNaN(Number(res))) {
|
||||||
await this.updateQortWalletBalance()
|
//...
|
||||||
} else if(coin === 'doge'){
|
} else {
|
||||||
await this.updateDogeWalletBalance()
|
this.btcWalletBalance = (Number(res) / 1e8).toFixed(8)
|
||||||
} else if(coin === 'btc'){
|
store.dispatch(
|
||||||
await this.updateBtcWalletBalance()
|
setCoinBalances({
|
||||||
} else if(coin === 'dgb'){
|
type: 'btc',
|
||||||
await this.updateDgbWalletBalance()
|
fullValue: Number(res)
|
||||||
} else if(coin === 'rvn'){
|
})
|
||||||
await this.updateRvnWalletBalance()
|
)
|
||||||
}else if(coin === 'arrr'){
|
}
|
||||||
await this.updateArrrWalletBalance()
|
}).catch(() => {
|
||||||
}
|
console.log('error')
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all(getCoinBalances);
|
async updateLtcWalletBalance() {
|
||||||
|
let _url = `/crosschain/ltc/walletbalance?apiKey=${this.myNode.apiKey}`
|
||||||
|
let _body = store.getState().app.selectedAddress.ltcWallet.derivedMasterPublicKey
|
||||||
|
|
||||||
}
|
await parentEpml.request('apiCall', {
|
||||||
|
url: _url,
|
||||||
|
method: 'POST',
|
||||||
|
body: _body
|
||||||
|
}).then((res) => {
|
||||||
|
if (isNaN(Number(res))) {
|
||||||
|
//...
|
||||||
|
} else {
|
||||||
|
this.ltcWalletBalance = (Number(res) / 1e8).toFixed(8)
|
||||||
|
store.dispatch(
|
||||||
|
setCoinBalances({
|
||||||
|
type: 'ltc',
|
||||||
|
fullValue: Number(res)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
async fetchBalance(){
|
}
|
||||||
try {
|
}).catch(() => {
|
||||||
let arrayOfCoins = []
|
console.log('error')
|
||||||
const copyObject = {...this.coinList}
|
})
|
||||||
const currentDate = Date.now()
|
}
|
||||||
const array = Object.keys(this.coinList)
|
|
||||||
for (const key of array) {
|
|
||||||
const item = this.coinList[key]
|
|
||||||
|
|
||||||
if(item < currentDate){
|
async updateDogeWalletBalance() {
|
||||||
delete copyObject[key]
|
let _url = `/crosschain/doge/walletbalance?apiKey=${this.myNode.apiKey}`
|
||||||
} else {
|
let _body = store.getState().app.selectedAddress.dogeWallet.derivedMasterPublicKey
|
||||||
arrayOfCoins.push(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!this.stop){
|
|
||||||
this.stop = true
|
|
||||||
await this.fetchCoins(arrayOfCoins)
|
|
||||||
this.stop = false
|
|
||||||
}
|
|
||||||
this.coinList = copyObject
|
|
||||||
} catch (error) {
|
|
||||||
this.stop = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
await parentEpml.request('apiCall', {
|
||||||
super.connectedCallback();
|
url: _url,
|
||||||
this.intervalID = setInterval(this.fetchBalance, 45000);
|
method: 'POST',
|
||||||
window.addEventListener(
|
body: _body
|
||||||
'ping-coin-controller-with-coin',
|
}).then((res) => {
|
||||||
this._updateCoinList
|
if (isNaN(Number(res))) {
|
||||||
);
|
//...
|
||||||
|
} else {
|
||||||
|
this.dogeWalletBalance = (Number(res) / 1e8).toFixed(8)
|
||||||
|
store.dispatch(
|
||||||
|
setCoinBalances({
|
||||||
|
type: 'doge',
|
||||||
|
fullValue: Number(res)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
console.log('error')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateDgbWalletBalance() {
|
||||||
|
let _url = `/crosschain/dgb/walletbalance?apiKey=${this.myNode.apiKey}`
|
||||||
|
let _body = store.getState().app.selectedAddress.dgbWallet.derivedMasterPublicKey
|
||||||
|
|
||||||
|
await parentEpml.request('apiCall', {
|
||||||
|
url: _url,
|
||||||
|
method: 'POST',
|
||||||
|
body: _body
|
||||||
|
}).then((res) => {
|
||||||
|
if (isNaN(Number(res))) {
|
||||||
|
//...
|
||||||
|
} else {
|
||||||
|
this.dgbWalletBalance = (Number(res) / 1e8).toFixed(8)
|
||||||
|
store.dispatch(
|
||||||
|
setCoinBalances({
|
||||||
|
type: 'dgb',
|
||||||
|
fullValue: Number(res)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
console.log('error')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateRvnWalletBalance() {
|
||||||
|
let _url = `/crosschain/rvn/walletbalance?apiKey=${this.myNode.apiKey}`
|
||||||
|
let _body = store.getState().app.selectedAddress.rvnWallet.derivedMasterPublicKey
|
||||||
|
|
||||||
|
await parentEpml.request('apiCall', {
|
||||||
|
url: _url,
|
||||||
|
method: 'POST',
|
||||||
|
body: _body
|
||||||
|
}).then((res) => {
|
||||||
|
if (isNaN(Number(res))) {
|
||||||
|
//...
|
||||||
|
} else {
|
||||||
|
this.rvnWalletBalance = (Number(res) / 1e8).toFixed(8)
|
||||||
|
store.dispatch(
|
||||||
|
setCoinBalances({
|
||||||
|
type: 'rvn',
|
||||||
|
fullValue: Number(res)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
console.log('error')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateArrrWalletBalance() {
|
||||||
|
let _url = `/crosschain/arrr/walletbalance?apiKey=${this.myNode.apiKey}`
|
||||||
|
let _body = store.getState().app.selectedAddress.arrrWallet.seed58
|
||||||
|
|
||||||
|
await parentEpml.request('apiCall', {
|
||||||
|
url: _url,
|
||||||
|
method: 'POST',
|
||||||
|
body: _body,
|
||||||
|
}).then((res) => {
|
||||||
|
if (isNaN(Number(res))) {
|
||||||
|
//...
|
||||||
|
} else {
|
||||||
|
this.arrrWalletBalance = (Number(res) / 1e8).toFixed(8)
|
||||||
|
store.dispatch(
|
||||||
|
setCoinBalances({
|
||||||
|
type: 'arrr',
|
||||||
|
fullValue: Number(res)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
console.log('error')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateCoinList(event) {
|
||||||
|
const copyCoinList = { ...this.coinList }
|
||||||
|
const coin = event.detail
|
||||||
|
|
||||||
|
if (!copyCoinList[coin]) {
|
||||||
|
try {
|
||||||
|
if (coin === 'qort') {
|
||||||
|
this.updateQortWalletBalance()
|
||||||
|
} else if (coin === 'btc') {
|
||||||
|
this.updateBtcWalletBalance()
|
||||||
|
} else if (coin === 'ltc') {
|
||||||
|
this.updateLtcWalletBalance()
|
||||||
|
} else if (coin === 'doge') {
|
||||||
|
this.updateDogeWalletBalance()
|
||||||
|
} else if (coin === 'dgb') {
|
||||||
|
this.updateDgbWalletBalance()
|
||||||
|
} else if (coin === 'rvn') {
|
||||||
|
this.updateRvnWalletBalance()
|
||||||
|
} else if (coin === 'arrr') {
|
||||||
|
this.updateArrrWalletBalance()
|
||||||
|
}
|
||||||
|
} catch (error) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
copyCoinList[coin] = Date.now() + 120000
|
||||||
|
|
||||||
|
this.coinList = copyCoinList
|
||||||
|
this.requestUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchCoins(arrayOfCoins) {
|
||||||
|
const getCoinBalances = (arrayOfCoins || []).map(async (coin) => {
|
||||||
|
if (coin === 'qort') {
|
||||||
|
await this.updateQortWalletBalance()
|
||||||
|
} else if (coin === 'btc') {
|
||||||
|
await this.updateBtcWalletBalance()
|
||||||
|
} else if (coin === 'ltc') {
|
||||||
|
await this.updateLtcWalletBalance()
|
||||||
|
} else if (coin === 'doge') {
|
||||||
|
await this.updateDogeWalletBalance()
|
||||||
|
} else if (coin === 'dgb') {
|
||||||
|
await this.updateDgbWalletBalance()
|
||||||
|
} else if (coin === 'rvn') {
|
||||||
|
await this.updateRvnWalletBalance()
|
||||||
|
} else if (coin === 'arrr') {
|
||||||
|
await this.updateArrrWalletBalance()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
await Promise.all(getCoinBalances)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchBalance() {
|
||||||
|
try {
|
||||||
|
let arrayOfCoins = []
|
||||||
|
|
||||||
|
const copyObject = { ...this.coinList }
|
||||||
|
const currentDate = Date.now()
|
||||||
|
const array = Object.keys(this.coinList)
|
||||||
|
|
||||||
|
for (const key of array) {
|
||||||
|
const item = this.coinList[key]
|
||||||
|
|
||||||
|
if (item < currentDate) {
|
||||||
|
delete copyObject[key]
|
||||||
|
} else {
|
||||||
|
arrayOfCoins.push(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.stop) {
|
||||||
|
this.stop = true
|
||||||
|
|
||||||
|
await this.fetchCoins(arrayOfCoins)
|
||||||
|
|
||||||
|
this.stop = false
|
||||||
|
}
|
||||||
|
|
||||||
|
this.coinList = copyObject
|
||||||
|
} catch (error) {
|
||||||
|
this.stop = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback()
|
||||||
|
this.intervalID = setInterval(this.fetchBalance, 45000)
|
||||||
|
window.addEventListener('ping-coin-controller-with-coin', this._updateCoinList)
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
|
if (this.intervalID) { clearInterval(this.intervalID) }
|
||||||
super.disconnectedCallback();
|
window.removeEventListener('ping-coin-controller-with-coin', this._updateCoinList)
|
||||||
window.removeEventListener(
|
super.disconnectedCallback()
|
||||||
'ping-coin-controller-with-coin',
|
|
||||||
this._updateCoinList
|
|
||||||
);
|
|
||||||
if(this.intervalID){
|
|
||||||
clearInterval(this.intervalID);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html``;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('coin-balances-controller', CoinBalancesController);
|
window.customElements.define('coin-balances-controller', CoinBalancesController)
|
@ -1,221 +1,204 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {get} from '../../../translate'
|
import { connect } from 'pwa-helpers'
|
||||||
import '@material/mwc-icon'
|
import { store } from '../../store'
|
||||||
import '@vaadin/tooltip';
|
import { get } from '../../../translate'
|
||||||
|
import { chatSideNavHeadsStyles } from '../../styles/core-css'
|
||||||
import './friend-item-actions'
|
import './friend-item-actions'
|
||||||
|
import '@material/mwc-icon'
|
||||||
|
import '@vaadin/tooltip'
|
||||||
|
|
||||||
class ChatSideNavHeads extends LitElement {
|
class ChatSideNavHeads extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
selectedAddress: { type: Object },
|
selectedAddress: { type: Object },
|
||||||
config: { type: Object },
|
config: { type: Object },
|
||||||
chatInfo: { type: Object },
|
chatInfo: { type: Object },
|
||||||
iconName: { type: String },
|
iconName: { type: String },
|
||||||
activeChatHeadUrl: { type: String },
|
activeChatHeadUrl: { type: String },
|
||||||
isImageLoaded: { type: Boolean },
|
isImageLoaded: { type: Boolean },
|
||||||
setActiveChatHeadUrl: {attribute: false},
|
setActiveChatHeadUrl: { attribute: false },
|
||||||
openEditFriend: {attribute: false},
|
openEditFriend: { attribute: false },
|
||||||
closeSidePanel: {attribute: false, type: Object}
|
closeSidePanel: { attribute: false, type: Object }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [chatSideNavHeadsStyles]
|
||||||
:host {
|
}
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
padding: 10px 2px 10px 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-size: 14px;
|
|
||||||
transition: 0.2s background-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
li:hover {
|
constructor() {
|
||||||
background-color: var(--lightChatHeadHover);
|
super()
|
||||||
}
|
this.selectedAddress = {}
|
||||||
|
this.config = {
|
||||||
.active {
|
user: {
|
||||||
background: var(--menuactive);
|
node: {
|
||||||
border-left: 4px solid #3498db;
|
|
||||||
}
|
|
||||||
|
|
||||||
.img-icon {
|
|
||||||
font-size:40px;
|
|
||||||
color: var(--chat-group);
|
|
||||||
}
|
|
||||||
|
|
||||||
.status {
|
|
||||||
color: #92959e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clearfix {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clearfix:after {
|
|
||||||
visibility: hidden;
|
|
||||||
display: block;
|
|
||||||
font-size: 0;
|
|
||||||
content: " ";
|
|
||||||
clear: both;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.selectedAddress = {}
|
|
||||||
this.config = {
|
|
||||||
user: {
|
|
||||||
node: {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.chatInfo = {}
|
|
||||||
this.iconName = ''
|
|
||||||
this.activeChatHeadUrl = ''
|
|
||||||
this.isImageLoaded = false
|
|
||||||
this.imageFetches = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
createImage(imageUrl) {
|
|
||||||
const imageHTMLRes = new Image();
|
|
||||||
imageHTMLRes.src = imageUrl;
|
|
||||||
imageHTMLRes.style= "width:30px; height:30px; float: left; border-radius:50%; font-size:14px";
|
|
||||||
imageHTMLRes.onclick= () => {
|
|
||||||
this.openDialogImage = true;
|
|
||||||
}
|
|
||||||
imageHTMLRes.onload = () => {
|
|
||||||
this.isImageLoaded = true;
|
|
||||||
}
|
|
||||||
imageHTMLRes.onerror = () => {
|
|
||||||
if (this.imageFetches < 4) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.imageFetches = this.imageFetches + 1;
|
|
||||||
imageHTMLRes.src = imageUrl;
|
|
||||||
}, 500);
|
|
||||||
} else {
|
|
||||||
this.isImageLoaded = false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return imageHTMLRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let avatarImg = ""
|
|
||||||
if (this.chatInfo.name) {
|
|
||||||
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
|
|
||||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
|
||||||
const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.chatInfo.name}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`;
|
|
||||||
avatarImg = this.createImage(avatarUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<li style="display:flex; justify-content: space-between; align-items: center" @click=${(e) => {
|
|
||||||
const target = e.target
|
|
||||||
const popover =
|
|
||||||
this.shadowRoot.querySelector('friend-item-actions');
|
|
||||||
if (popover) {
|
|
||||||
popover.openPopover(target);
|
|
||||||
}
|
}
|
||||||
}} class="clearfix" id=${`friend-item-parent-${this.chatInfo.name}`}>
|
}
|
||||||
<div style="display:flex; flex-grow: 1; align-items: center">
|
}
|
||||||
${this.isImageLoaded ? html`${avatarImg}` : html``}
|
this.chatInfo = {}
|
||||||
${!this.isImageLoaded && !this.chatInfo.name && !this.chatInfo.groupName
|
this.iconName = ''
|
||||||
? html`<mwc-icon class="img-icon">account_circle</mwc-icon>`
|
this.activeChatHeadUrl = ''
|
||||||
: html``}
|
this.isImageLoaded = false
|
||||||
${!this.isImageLoaded && this.chatInfo.name
|
this.imageFetches = 0
|
||||||
? html`<div
|
}
|
||||||
style="width:30px; height:30px; float: left; border-radius:50%; background: ${this.activeChatHeadUrl === this.chatInfo.url
|
|
||||||
? "var(--chatHeadBgActive)"
|
|
||||||
: "var(--chatHeadBg)"}; color: ${this.activeChatHeadUrl ===
|
|
||||||
this.chatInfo.url
|
|
||||||
? "var(--chatHeadTextActive)"
|
|
||||||
: "var(--chatHeadText)"}; font-weight:bold; display: flex; justify-content: center; align-items: center; text-transform: capitalize"
|
|
||||||
>
|
|
||||||
${this.chatInfo.name.charAt(0)}
|
|
||||||
</div>`
|
|
||||||
: ""}
|
|
||||||
${!this.isImageLoaded && this.chatInfo.groupName
|
|
||||||
? html`<div
|
|
||||||
style="width:30px; height:30px; float: left; border-radius:50%; background: ${this.activeChatHeadUrl === this.chatInfo.url
|
|
||||||
? "var(--chatHeadBgActive)"
|
|
||||||
: "var(--chatHeadBg)"}; color: ${this.activeChatHeadUrl === this.chatInfo.url
|
|
||||||
? "var(--chatHeadTextActive)"
|
|
||||||
: "var(--chatHeadText)"}; font-weight:bold; display: flex; justify-content: center; align-items: center; text-transform: capitalize"
|
|
||||||
>
|
|
||||||
${this.chatInfo.groupName.charAt(0)}
|
|
||||||
</div>`
|
|
||||||
: ""}
|
|
||||||
<div>
|
|
||||||
<div class="name">
|
|
||||||
<span style="float:left; padding-left: 8px; color: var(--chat-group);">
|
|
||||||
${this.chatInfo.groupName
|
|
||||||
? this.chatInfo.groupName
|
|
||||||
: this.chatInfo.name !== undefined
|
|
||||||
? (this.chatInfo.alias || this.chatInfo.name)
|
|
||||||
: this.chatInfo.address.substr(0, 15)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
render() {
|
||||||
<div style="display:flex; align-items: center">
|
let avatarImg = ''
|
||||||
${this.chatInfo.willFollow ? html`
|
|
||||||
<mwc-icon id="willFollowIcon" style="color: var(--black)">connect_without_contact</mwc-icon>
|
|
||||||
<vaadin-tooltip
|
|
||||||
|
|
||||||
for="willFollowIcon"
|
if (this.chatInfo.name) {
|
||||||
position="top"
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
hover-delay=${200}
|
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
hide-delay=${1}
|
const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.chatInfo.name}/qortal_avatar?async=true`
|
||||||
text=${get('friends.friend11')}>
|
avatarImg = this.createImage(avatarUrl)
|
||||||
</vaadin-tooltip>
|
}
|
||||||
` : ''}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<friend-item-actions
|
|
||||||
for=${`friend-item-parent-${this.chatInfo.name}`}
|
|
||||||
message=${get('notifications.explanation')}
|
|
||||||
.openEditFriend=${()=> {
|
|
||||||
this.openEditFriend(this.chatInfo)
|
|
||||||
}}
|
|
||||||
name=${this.chatInfo.name}
|
|
||||||
.closeSidePanel=${this.closeSidePanel}
|
|
||||||
></friend-item-actions>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<li
|
||||||
|
style="display:flex; justify-content: space-between; align-items: center"
|
||||||
|
@click=${(e) => {
|
||||||
|
const target = e.target
|
||||||
|
const popover = this.shadowRoot.querySelector('friend-item-actions');
|
||||||
|
if (popover) {
|
||||||
|
popover.openPopover(target);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
class="clearfix" id=${`friend-item-parent-${this.chatInfo.name}`}
|
||||||
|
>
|
||||||
|
<div style="display:flex; flex-grow: 1; align-items: center">
|
||||||
|
${this.isImageLoaded ? html`${avatarImg}` : html``}
|
||||||
|
${!this.isImageLoaded && !this.chatInfo.name && !this.chatInfo.groupName ?
|
||||||
|
html`
|
||||||
|
<mwc-icon class="img-icon">account_circle</mwc-icon>
|
||||||
|
`
|
||||||
|
: html``
|
||||||
|
}
|
||||||
|
${!this.isImageLoaded && this.chatInfo.name ?
|
||||||
|
html`
|
||||||
|
<div
|
||||||
|
style="width:30px; height:30px; float: left; border-radius:50%; background: ${this.activeChatHeadUrl === this.chatInfo.url
|
||||||
|
? "var(--chatHeadBgActive)"
|
||||||
|
: "var(--chatHeadBg)"}; color: ${this.activeChatHeadUrl === this.chatInfo.url
|
||||||
|
? "var(--chatHeadTextActive)"
|
||||||
|
: "var(--chatHeadText)"}; font-weight:bold; display: flex; justify-content: center; align-items: center; text-transform: capitalize"
|
||||||
|
>
|
||||||
|
${this.chatInfo.name.charAt(0)}
|
||||||
|
</div>
|
||||||
|
` : ''
|
||||||
|
}
|
||||||
|
${!this.isImageLoaded && this.chatInfo.groupName ?
|
||||||
|
html`
|
||||||
|
<div
|
||||||
|
style="width:30px; height:30px; float: left; border-radius:50%; background: ${this.activeChatHeadUrl === this.chatInfo.url
|
||||||
|
? "var(--chatHeadBgActive)"
|
||||||
|
: "var(--chatHeadBg)"}; color: ${this.activeChatHeadUrl === this.chatInfo.url
|
||||||
|
? "var(--chatHeadTextActive)"
|
||||||
|
: "var(--chatHeadText)"}; font-weight:bold; display: flex; justify-content: center; align-items: center; text-transform: capitalize"
|
||||||
|
>
|
||||||
|
${this.chatInfo.groupName.charAt(0)}
|
||||||
|
</div>
|
||||||
|
` : ''
|
||||||
|
}
|
||||||
|
<div>
|
||||||
|
<div class="name">
|
||||||
|
<span style="float:left; padding-left: 8px; color: var(--chat-group);">
|
||||||
|
${this.chatInfo.groupName
|
||||||
|
? this.chatInfo.groupName
|
||||||
|
: this.chatInfo.name !== undefined
|
||||||
|
? (this.chatInfo.alias || this.chatInfo.name)
|
||||||
|
: this.chatInfo.address.substr(0, 15)
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex; align-items: center">
|
||||||
|
${this.chatInfo.willFollow ?
|
||||||
|
html`
|
||||||
|
<mwc-icon id="willFollowIcon" style="color: var(--black)">connect_without_contact</mwc-icon>
|
||||||
|
<vaadin-tooltip
|
||||||
|
for="willFollowIcon"
|
||||||
|
position="top"
|
||||||
|
hover-delay=${200}
|
||||||
|
hide-delay=${1}
|
||||||
|
text=${get('friends.friend11')}
|
||||||
|
></vaadin-tooltip>
|
||||||
|
` : ''
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<friend-item-actions
|
||||||
|
for=${`friend-item-parent-${this.chatInfo.name}`}
|
||||||
|
message=${get('notifications.explanation')}
|
||||||
|
.openEditFriend=${() => {
|
||||||
|
this.openEditFriend(this.chatInfo)
|
||||||
|
}}
|
||||||
|
name=${this.chatInfo.name}
|
||||||
|
.closeSidePanel=${this.closeSidePanel}
|
||||||
|
></friend-item-actions>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
shouldUpdate(changedProperties) {
|
createImage(imageUrl) {
|
||||||
if(changedProperties.has('activeChatHeadUrl')){
|
const imageHTMLRes = new Image()
|
||||||
return true
|
imageHTMLRes.src = imageUrl
|
||||||
}
|
imageHTMLRes.style = "width:30px; height:30px; float: left; border-radius:50%; font-size:14px"
|
||||||
if(changedProperties.has('chatInfo')){
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return !!changedProperties.has('isImageLoaded');
|
|
||||||
|
|
||||||
|
imageHTMLRes.onclick = () => {
|
||||||
|
this.openDialogImage = true
|
||||||
|
}
|
||||||
|
|
||||||
}
|
imageHTMLRes.onload = () => {
|
||||||
|
this.isImageLoaded = true
|
||||||
|
}
|
||||||
|
|
||||||
getUrl(chatUrl) {
|
imageHTMLRes.onerror = () => {
|
||||||
this.setActiveChatHeadUrl(chatUrl)
|
if (this.imageFetches < 4) {
|
||||||
}
|
setTimeout(() => {
|
||||||
|
this.imageFetches = this.imageFetches + 1
|
||||||
|
imageHTMLRes.src = imageUrl
|
||||||
|
}, 500)
|
||||||
|
} else {
|
||||||
|
this.isImageLoaded = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageHTMLRes
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldUpdate(changedProperties) {
|
||||||
|
if (changedProperties.has('activeChatHeadUrl')) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedProperties.has('chatInfo')) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!changedProperties.has('isImageLoaded')
|
||||||
|
}
|
||||||
|
|
||||||
|
getUrl(chatUrl) {
|
||||||
|
this.setActiveChatHeadUrl(chatUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard functions
|
||||||
|
getApiKey() {
|
||||||
|
const coreNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
return coreNode.apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmptyArray(arr) {
|
||||||
|
if (!arr) { return true }
|
||||||
|
return arr.length === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
round(number) {
|
||||||
|
return (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('chat-side-nav-heads', ChatSideNavHeads)
|
window.customElements.define('chat-side-nav-heads', ChatSideNavHeads)
|
@ -1,10 +1,11 @@
|
|||||||
import {css, html, LitElement} from 'lit';
|
import { html, LitElement } from 'lit'
|
||||||
import {translate,} from '../../../translate'
|
import { connect } from 'pwa-helpers'
|
||||||
import '@material/mwc-button';
|
import { store } from '../../store'
|
||||||
import '@material/mwc-dialog';
|
import { translate, } from '../../../translate'
|
||||||
import '@material/mwc-checkbox';
|
import { addFriendsModalStyles } from '../../styles/core-css'
|
||||||
import {connect} from 'pwa-helpers';
|
import '@material/mwc-button'
|
||||||
import {store} from '../../store';
|
import '@material/mwc-checkbox'
|
||||||
|
import '@material/mwc-dialog'
|
||||||
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
||||||
|
|
||||||
class AddFriendsModal extends connect(store)(LitElement) {
|
class AddFriendsModal extends connect(store)(LitElement) {
|
||||||
@ -21,199 +22,195 @@ class AddFriendsModal extends connect(store)(LitElement) {
|
|||||||
editContent: { type: Object },
|
editContent: { type: Object },
|
||||||
onClose: { attribute: false },
|
onClose: { attribute: false },
|
||||||
mySelectedFeeds: { type: Array },
|
mySelectedFeeds: { type: Array },
|
||||||
availableFeeedSchemas: {type: Array},
|
availableFeeedSchemas: { type: Array },
|
||||||
isLoadingSchemas: {type: Boolean}
|
isLoadingSchemas: { type: Boolean }
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.isOpen = false;
|
|
||||||
this.isLoading = false;
|
|
||||||
this.alias = '';
|
|
||||||
this.willFollow = true;
|
|
||||||
this.notes = '';
|
|
||||||
this.nodeUrl = this.getNodeUrl();
|
|
||||||
this.myNode = this.getMyNode();
|
|
||||||
this.mySelectedFeeds = [];
|
|
||||||
this.availableFeeedSchemas = [];
|
|
||||||
this.isLoadingSchemas= false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [addFriendsModalStyles]
|
||||||
* {
|
|
||||||
--mdc-theme-primary: rgb(3, 169, 244);
|
|
||||||
--mdc-theme-secondary: var(--mdc-theme-primary);
|
|
||||||
--mdc-theme-surface: var(--white);
|
|
||||||
--mdc-dialog-content-ink-color: var(--black);
|
|
||||||
--mdc-dialog-min-width: 400px;
|
|
||||||
--mdc-dialog-max-width: 1024px;
|
|
||||||
box-sizing:border-box;
|
|
||||||
}
|
|
||||||
.input {
|
|
||||||
width: 90%;
|
|
||||||
outline: 0;
|
|
||||||
border-width: 0 0 2px;
|
|
||||||
border-color: var(--mdc-theme-primary);
|
|
||||||
background-color: transparent;
|
|
||||||
padding: 10px;
|
|
||||||
font-family: Roboto, sans-serif;
|
|
||||||
font-size: 15px;
|
|
||||||
color: var(--chat-bubble-msg-color);
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input::selection {
|
|
||||||
background-color: var(--mdc-theme-primary);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input::placeholder {
|
|
||||||
opacity: 0.6;
|
|
||||||
color: var(--black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-button {
|
|
||||||
font-family: Roboto, sans-serif;
|
|
||||||
font-size: 16px;
|
|
||||||
color: var(--mdc-theme-primary);
|
|
||||||
background-color: transparent;
|
|
||||||
padding: 8px 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
border: none;
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-button-red {
|
|
||||||
font-family: Roboto, sans-serif;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #f44336;
|
|
||||||
background-color: transparent;
|
|
||||||
padding: 8px 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
border: none;
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-button-red:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: #f4433663;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-button:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: #03a8f475;
|
|
||||||
}
|
|
||||||
.checkbox-row {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
align-content: center;
|
|
||||||
font-family: Montserrat, sans-serif;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--black);
|
|
||||||
}
|
|
||||||
.modal-overlay {
|
|
||||||
display: block;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
background-color: rgba(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0.5
|
|
||||||
); /* Semi-transparent backdrop */
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-content {
|
|
||||||
position: fixed;
|
|
||||||
top: 50vh;
|
|
||||||
left: 50vw;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
background-color: var(--mdc-theme-surface);
|
|
||||||
width: 80vw;
|
|
||||||
max-width: 600px;
|
|
||||||
padding: 20px;
|
|
||||||
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px;
|
|
||||||
z-index: 1001;
|
|
||||||
border-radius: 5px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction:column;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.modal-overlay.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.avatar {
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-name {
|
|
||||||
display: flex;
|
|
||||||
gap: 20px;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 5px;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.inner-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
max-height: 75vh;
|
|
||||||
flex-grow: 1;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inner-content::-webkit-scrollbar-track {
|
|
||||||
background-color: whitesmoke;
|
|
||||||
border-radius: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inner-content::-webkit-scrollbar {
|
|
||||||
width: 12px;
|
|
||||||
border-radius: 7px;
|
|
||||||
background-color: whitesmoke;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inner-content::-webkit-scrollbar-thumb {
|
|
||||||
background-color: rgb(180, 176, 176);
|
|
||||||
border-radius: 7px;
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {}
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.isOpen = false
|
||||||
|
this.isLoading = false
|
||||||
|
this.alias = ''
|
||||||
|
this.willFollow = true
|
||||||
|
this.notes = ''
|
||||||
|
this.nodeUrl = this.getNodeUrl()
|
||||||
|
this.myNode = this.getMyNode()
|
||||||
|
this.mySelectedFeeds = []
|
||||||
|
this.availableFeeedSchemas = []
|
||||||
|
this.isLoadingSchemas = false
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<div class="modal-overlay ${this.isOpen ? '' : 'hidden'}">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="inner-content">
|
||||||
|
<div style="text-align:center">
|
||||||
|
<h1>
|
||||||
|
${this.editContent
|
||||||
|
? translate('friends.friend10')
|
||||||
|
: translate('friends.friend2')}
|
||||||
|
</h1>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
<p>${translate('friends.friend3')}</p>
|
||||||
|
<div class="checkbox-row">
|
||||||
|
<label
|
||||||
|
for="willFollow"
|
||||||
|
id="willFollowLabel"
|
||||||
|
style="color: var(--black);"
|
||||||
|
>
|
||||||
|
${translate('friends.friend5')}
|
||||||
|
</label>
|
||||||
|
<mwc-checkbox
|
||||||
|
style="margin-right: -15px;"
|
||||||
|
id="willFollow"
|
||||||
|
@change=${(e) => {
|
||||||
|
this.willFollow = e.target.checked;
|
||||||
|
}}
|
||||||
|
?checked=${this.willFollow}
|
||||||
|
></mwc-checkbox>
|
||||||
|
</div>
|
||||||
|
<div style="height:15px"></div>
|
||||||
|
<div style="display: flex;flex-direction: column;">
|
||||||
|
<label
|
||||||
|
for="name"
|
||||||
|
id="nameLabel"
|
||||||
|
style="color: var(--black);"
|
||||||
|
>
|
||||||
|
${translate('login.name')}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="name"
|
||||||
|
class="input"
|
||||||
|
?disabled=${true}
|
||||||
|
value=${this.userSelected ? this.userSelected.name : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style="height:15px"></div>
|
||||||
|
<div style="display: flex;flex-direction: column;">
|
||||||
|
<label
|
||||||
|
for="alias"
|
||||||
|
id="aliasLabel"
|
||||||
|
style="color: var(--black);"
|
||||||
|
>
|
||||||
|
${translate('friends.friend6')}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="alias"
|
||||||
|
placeholder=${translate('friends.friend7')}
|
||||||
|
class="input"
|
||||||
|
.value=${this.alias}
|
||||||
|
@change=${(e) => {
|
||||||
|
this.alias = e.target.value
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style="height:15px"></div>
|
||||||
|
<div style="margin-bottom:0;">
|
||||||
|
<textarea
|
||||||
|
class="input"
|
||||||
|
@change=${(e) => {
|
||||||
|
this.notes = e.target.value
|
||||||
|
}}
|
||||||
|
.value=${this.notes}
|
||||||
|
?disabled=${this.isLoading}
|
||||||
|
id="messageBoxAddFriend"
|
||||||
|
placeholder="${translate('friends.friend4')}"
|
||||||
|
rows="3"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
<div style="height:15px"></div>
|
||||||
|
<h2>${translate('friends.friend15')}</h2>
|
||||||
|
<div style="margin-bottom:0;">
|
||||||
|
<p>${translate('friends.friend16')}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
${this.isLoadingSchemas ?
|
||||||
|
html`
|
||||||
|
<div style="width:100%;display: flex; justify-content:center">
|
||||||
|
<paper-spinner-lite active></paper-spinner-lite>
|
||||||
|
</div>
|
||||||
|
` : ''
|
||||||
|
}
|
||||||
|
${this.availableFeeedSchemas.map((schema) => {
|
||||||
|
const isAlreadySelected = this.mySelectedFeeds.find((item) => item.name === schema.name);
|
||||||
|
let avatarImgApp;
|
||||||
|
const avatarUrl2 = `${this.nodeUrl}/arbitrary/THUMBNAIL/${schema.name}/qortal_avatar?async=true&apiKey=${this.myNode.apiKey}`;
|
||||||
|
avatarImgApp = html`<img src="${avatarUrl2}" style="max-width:100%; max-height:100%;" onerror="this.onerror=null; this.src='/img/incognito.png';"/>`;
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class="app-name"
|
||||||
|
style="background:${isAlreadySelected ? 'lightblue' : ''}"
|
||||||
|
@click=${() => {
|
||||||
|
const copymySelectedFeeds = [...this.mySelectedFeeds];
|
||||||
|
const findIndex = copymySelectedFeeds.findIndex((item) => item.name === schema.name);
|
||||||
|
if (findIndex === -1) {
|
||||||
|
if (this.mySelectedFeeds.length > 4) return
|
||||||
|
copymySelectedFeeds.push({name: schema.name, identifier: schema.identifier, service: schema.service});
|
||||||
|
this.mySelectedFeeds = copymySelectedFeeds;
|
||||||
|
} else {
|
||||||
|
this.mySelectedFeeds = copymySelectedFeeds.filter((item) => item.name !== schema.name);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="avatar">${avatarImgApp}</div>
|
||||||
|
<span style="color:${isAlreadySelected ? 'var(--white)' : 'var(--black)'};font-size:16px">${schema.name}</span>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:20px">
|
||||||
|
<button
|
||||||
|
class="modal-button-red"
|
||||||
|
?disabled="${this.isLoading}"
|
||||||
|
@click="${() => {
|
||||||
|
this.setIsOpen(false);
|
||||||
|
this.clearFields();
|
||||||
|
this.onClose();
|
||||||
|
}}"
|
||||||
|
>
|
||||||
|
${translate('general.close')}
|
||||||
|
</button>
|
||||||
|
${this.editContent ?
|
||||||
|
html`
|
||||||
|
<button ?disabled="${this.isLoading}" class="modal-button-red" @click=${() => {this.removeFriend();}}>
|
||||||
|
${translate('friends.friend14')}
|
||||||
|
</button>
|
||||||
|
` : ''
|
||||||
|
}
|
||||||
|
<button ?disabled="${this.isLoading}" class="modal-button" @click=${() => {this.addFriend();}}>
|
||||||
|
${this.editContent ? translate('friends.friend10') : translate('friends.friend2')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
getNodeUrl() {
|
getNodeUrl() {
|
||||||
const myNode =
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
store.getState().app.nodeConfig.knownNodes[
|
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
];
|
|
||||||
|
|
||||||
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getMyNode() {
|
getMyNode() {
|
||||||
return store.getState().app.nodeConfig.knownNodes[
|
return store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clearFields() {
|
clearFields() {
|
||||||
this.alias = '';
|
this.alias = ''
|
||||||
this.willFollow = true;
|
this.willFollow = true
|
||||||
this.notes = '';
|
this.notes = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
addFriend() {
|
addFriend() {
|
||||||
@ -223,10 +220,10 @@ class AddFriendsModal extends connect(store)(LitElement) {
|
|||||||
notes: this.notes,
|
notes: this.notes,
|
||||||
willFollow: this.willFollow,
|
willFollow: this.willFollow,
|
||||||
mySelectedFeeds: this.mySelectedFeeds
|
mySelectedFeeds: this.mySelectedFeeds
|
||||||
|
})
|
||||||
|
|
||||||
});
|
this.clearFields()
|
||||||
this.clearFields();
|
this.onClose()
|
||||||
this.onClose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFriend() {
|
removeFriend() {
|
||||||
@ -239,244 +236,60 @@ class AddFriendsModal extends connect(store)(LitElement) {
|
|||||||
mySelectedFeeds: this.mySelectedFeeds
|
mySelectedFeeds: this.mySelectedFeeds
|
||||||
},
|
},
|
||||||
true
|
true
|
||||||
);
|
)
|
||||||
this.clearFields();
|
|
||||||
this.onClose();
|
this.clearFields()
|
||||||
|
this.onClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
async updated(changedProperties) {
|
async updated(changedProperties) {
|
||||||
if (
|
if (changedProperties && changedProperties.has('editContent') && this.editContent) {
|
||||||
changedProperties &&
|
this.userSelected = { name: this.editContent.name ?? '' }
|
||||||
changedProperties.has('editContent') &&
|
this.notes = this.editContent.notes ?? ''
|
||||||
this.editContent
|
this.willFollow = this.editContent.willFollow ?? true
|
||||||
) {
|
this.alias = this.editContent.alias ?? ''
|
||||||
this.userSelected = {
|
|
||||||
name: this.editContent.name ?? '',
|
|
||||||
};
|
|
||||||
this.notes = this.editContent.notes ?? '';
|
|
||||||
this.willFollow = this.editContent.willFollow ?? true;
|
|
||||||
this.alias = this.editContent.alias ?? '';
|
|
||||||
this.requestUpdate()
|
this.requestUpdate()
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
changedProperties &&
|
if (changedProperties && changedProperties.has('isOpen') && this.isOpen) {
|
||||||
changedProperties.has('isOpen') && this.isOpen
|
|
||||||
) {
|
|
||||||
await this.getAvailableFeedSchemas()
|
await this.getAvailableFeedSchemas()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAvailableFeedSchemas() {
|
async getAvailableFeedSchemas() {
|
||||||
try {
|
try {
|
||||||
this.isLoadingSchemas= true
|
this.isLoadingSchemas = true
|
||||||
const url = `${this.nodeUrl}/arbitrary/resources/search?service=DOCUMENT&identifier=ui_schema_feed&prefix=true`;
|
const url = `${this.nodeUrl}/arbitrary/resources/search?service=DOCUMENT&identifier=ui_schema_feed&prefix=true`
|
||||||
const res = await fetch(url);
|
const res = await fetch(url)
|
||||||
const data = await res.json();
|
const data = await res.json()
|
||||||
|
|
||||||
if (data.error === 401) {
|
if (data.error === 401) {
|
||||||
this.availableFeeedSchemas = [];
|
this.availableFeeedSchemas = []
|
||||||
} else {
|
} else {
|
||||||
this.availableFeeedSchemas = data.filter(
|
this.availableFeeedSchemas = data.filter((item) => item.identifier === 'ui_schema_feed')
|
||||||
(item) => item.identifier === 'ui_schema_feed'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
this.userFoundModalOpen = true;
|
|
||||||
} catch (error) {} finally {
|
this.userFoundModalOpen = true
|
||||||
this.isLoadingSchemas= false
|
} catch (error) {
|
||||||
|
} finally {
|
||||||
|
this.isLoadingSchemas = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
// Standard functions
|
||||||
return html`
|
getApiKey() {
|
||||||
<div class="modal-overlay ${this.isOpen ? '' : 'hidden'}">
|
const coreNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
return coreNode.apiKey
|
||||||
|
}
|
||||||
|
|
||||||
<div class="modal-content">
|
isEmptyArray(arr) {
|
||||||
<div class="inner-content">
|
if (!arr) { return true }
|
||||||
<div style="text-align:center">
|
return arr.length === 0
|
||||||
<h1>
|
}
|
||||||
${this.editContent
|
|
||||||
? translate('friends.friend10')
|
|
||||||
: translate('friends.friend2')}
|
|
||||||
</h1>
|
|
||||||
<hr />
|
|
||||||
</div>
|
|
||||||
<p>${translate('friends.friend3')}</p>
|
|
||||||
<div class="checkbox-row">
|
|
||||||
<label
|
|
||||||
for="willFollow"
|
|
||||||
id="willFollowLabel"
|
|
||||||
style="color: var(--black);"
|
|
||||||
>
|
|
||||||
${translate('friends.friend5')}
|
|
||||||
</label>
|
|
||||||
<mwc-checkbox
|
|
||||||
style="margin-right: -15px;"
|
|
||||||
id="willFollow"
|
|
||||||
@change=${(e) => {
|
|
||||||
this.willFollow = e.target.checked;
|
|
||||||
}}
|
|
||||||
?checked=${this.willFollow}
|
|
||||||
></mwc-checkbox>
|
|
||||||
</div>
|
|
||||||
<div style="height:15px"></div>
|
|
||||||
<div style="display: flex;flex-direction: column;">
|
|
||||||
<label
|
|
||||||
for="name"
|
|
||||||
id="nameLabel"
|
|
||||||
style="color: var(--black);"
|
|
||||||
>
|
|
||||||
${translate('login.name')}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="name"
|
|
||||||
class="input"
|
|
||||||
?disabled=${true}
|
|
||||||
value=${this.userSelected
|
|
||||||
? this.userSelected.name
|
|
||||||
: ''}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div style="height:15px"></div>
|
|
||||||
<div style="display: flex;flex-direction: column;">
|
|
||||||
<label
|
|
||||||
for="alias"
|
|
||||||
id="aliasLabel"
|
|
||||||
style="color: var(--black);"
|
|
||||||
>
|
|
||||||
${translate('friends.friend6')}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="alias"
|
|
||||||
placeholder=${translate('friends.friend7')}
|
|
||||||
class="input"
|
|
||||||
.value=${this.alias}
|
|
||||||
@change=${(e) => {
|
|
||||||
this.alias = e.target.value
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div style="height:15px"></div>
|
|
||||||
<div style="margin-bottom:0;">
|
|
||||||
<textarea
|
|
||||||
class="input"
|
|
||||||
@change=${(e) => {
|
|
||||||
this.notes = e.target.value
|
|
||||||
}}
|
|
||||||
.value=${this.notes}
|
|
||||||
?disabled=${this.isLoading}
|
|
||||||
id="messageBoxAddFriend"
|
|
||||||
placeholder="${translate('friends.friend4')}"
|
|
||||||
rows="3"
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
<div style="height:15px"></div>
|
|
||||||
<h2>${translate('friends.friend15')}</h2>
|
|
||||||
<div style="margin-bottom:0;">
|
|
||||||
<p>${translate('friends.friend16')}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
${this.isLoadingSchemas ? html`
|
|
||||||
<div style="width:100%;display: flex; justify-content:center">
|
|
||||||
<paper-spinner-lite active></paper-spinner-lite>
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
${this.availableFeeedSchemas.map((schema) => {
|
|
||||||
const isAlreadySelected = this.mySelectedFeeds.find(
|
|
||||||
(item) => item.name === schema.name
|
|
||||||
);
|
|
||||||
let avatarImgApp;
|
|
||||||
const avatarUrl2 = `${this.nodeUrl}/arbitrary/THUMBNAIL/${schema.name}/qortal_avatar?async=true&apiKey=${this.myNode.apiKey}`;
|
|
||||||
avatarImgApp = html`<img
|
|
||||||
src="${avatarUrl2}"
|
|
||||||
style="max-width:100%; max-height:100%;"
|
|
||||||
onerror="this.onerror=null; this.src='/img/incognito.png';"
|
|
||||||
/>`;
|
|
||||||
return html`
|
|
||||||
<div
|
|
||||||
class="app-name"
|
|
||||||
style="background:${isAlreadySelected ? 'lightblue': ''}"
|
|
||||||
@click=${() => {
|
|
||||||
const copymySelectedFeeds = [
|
|
||||||
...this.mySelectedFeeds,
|
|
||||||
];
|
|
||||||
const findIndex =
|
|
||||||
copymySelectedFeeds.findIndex(
|
|
||||||
(item) =>
|
|
||||||
item.name === schema.name
|
|
||||||
);
|
|
||||||
if (findIndex === -1) {
|
|
||||||
if(this.mySelectedFeeds.length > 4) return
|
|
||||||
copymySelectedFeeds.push({
|
|
||||||
name: schema.name,
|
|
||||||
identifier: schema.identifier,
|
|
||||||
service: schema.service,
|
|
||||||
});
|
|
||||||
this.mySelectedFeeds =
|
|
||||||
copymySelectedFeeds;
|
|
||||||
} else {
|
|
||||||
this.mySelectedFeeds =
|
|
||||||
copymySelectedFeeds.filter(
|
|
||||||
(item) =>
|
|
||||||
item.name !==
|
|
||||||
schema.name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div class="avatar">${avatarImgApp}</div>
|
|
||||||
<span
|
|
||||||
style="color:${isAlreadySelected ? 'var(--white)': 'var(--black)'};font-size:16px"
|
|
||||||
>${schema.name}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style="display:flex;justify-content:space-between;align-items:center;margin-top:20px"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="modal-button-red"
|
|
||||||
?disabled="${this.isLoading}"
|
|
||||||
@click="${() => {
|
|
||||||
this.setIsOpen(false);
|
|
||||||
this.clearFields();
|
|
||||||
this.onClose();
|
|
||||||
}}"
|
|
||||||
>
|
|
||||||
${translate('general.close')}
|
|
||||||
</button>
|
|
||||||
${this.editContent
|
|
||||||
? html`
|
|
||||||
<button
|
|
||||||
?disabled="${this.isLoading}"
|
|
||||||
class="modal-button-red"
|
|
||||||
@click=${() => {
|
|
||||||
this.removeFriend();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
${translate('friends.friend14')}
|
|
||||||
</button>
|
|
||||||
`
|
|
||||||
: ''}
|
|
||||||
|
|
||||||
<button
|
round(number) {
|
||||||
?disabled="${this.isLoading}"
|
return (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8)
|
||||||
class="modal-button"
|
|
||||||
@click=${() => {
|
|
||||||
this.addFriend();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
${this.editContent
|
|
||||||
? translate('friends.friend10')
|
|
||||||
: translate('friends.friend2')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('add-friends-modal', AddFriendsModal);
|
window.customElements.define('add-friends-modal', AddFriendsModal)
|
@ -1,16 +1,17 @@
|
|||||||
import {css, html, LitElement} from 'lit';
|
import { html, LitElement } from 'lit'
|
||||||
import axios from 'axios';
|
import { connect } from 'pwa-helpers'
|
||||||
import '@material/mwc-menu';
|
import { store } from '../../store'
|
||||||
import '@material/mwc-list/mwc-list-item.js';
|
import { RequestQueueWithPromise } from '../../../../plugins/plugins/utils/classes'
|
||||||
import {RequestQueueWithPromise} from '../../../../plugins/plugins/utils/queue';
|
import { avatarComponentStyles } from '../../styles/core-css'
|
||||||
import '../../../../plugins/plugins/core/components/TimeAgo';
|
import axios from 'axios'
|
||||||
import {connect} from 'pwa-helpers';
|
import ShortUniqueId from 'short-unique-id'
|
||||||
import {store} from '../../store';
|
import '../../../../plugins/plugins/core/components/TimeAgo'
|
||||||
import ShortUniqueId from 'short-unique-id';
|
import '@material/mwc-menu'
|
||||||
|
import '@material/mwc-list/mwc-list-item.js'
|
||||||
|
|
||||||
const requestQueue = new RequestQueueWithPromise(3);
|
const requestQueue = new RequestQueueWithPromise(3)
|
||||||
const requestQueueRawData = new RequestQueueWithPromise(3);
|
const requestQueueRawData = new RequestQueueWithPromise(3)
|
||||||
const requestQueueStatus = new RequestQueueWithPromise(3);
|
const requestQueueStatus = new RequestQueueWithPromise(3)
|
||||||
|
|
||||||
export class AvatarComponent extends connect(store)(LitElement) {
|
export class AvatarComponent extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@ -18,284 +19,210 @@ export class AvatarComponent extends connect(store)(LitElement) {
|
|||||||
resource: { type: Object },
|
resource: { type: Object },
|
||||||
isReady: { type: Boolean },
|
isReady: { type: Boolean },
|
||||||
status: { type: Object },
|
status: { type: Object },
|
||||||
name: { type: String },
|
name: { type: String }
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [avatarComponentStyles]
|
||||||
* {
|
|
||||||
--mdc-theme-text-primary-on-background: var(--black);
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
:host {
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
max-height: 30vh;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.smallLoading,
|
|
||||||
.smallLoading:after {
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 2px;
|
|
||||||
height: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.defaultSize {
|
|
||||||
width: 100%;
|
|
||||||
height: 160px;
|
|
||||||
}
|
|
||||||
.parent-feed-item {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
background-color: var(--chat-bubble-bg);
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: center;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 12px 15px 4px 15px;
|
|
||||||
min-width: 150px;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
.avatar {
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
border-radius: 50%;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.avatarApp {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border-radius: 50%;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.feed-item-name {
|
|
||||||
user-select: none;
|
|
||||||
color: #03a9f4;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-name {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
mwc-menu {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super()
|
||||||
this.resource = {
|
this.resource = {
|
||||||
identifier: '',
|
identifier: '',
|
||||||
name: '',
|
name: '',
|
||||||
service: '',
|
service: ''
|
||||||
};
|
}
|
||||||
this.status = {
|
this.status = {
|
||||||
status: '',
|
status: ''
|
||||||
};
|
|
||||||
this.isReady = false;
|
|
||||||
this.nodeUrl = this.getNodeUrl();
|
|
||||||
this.myNode = this.getMyNode();
|
|
||||||
this.isFetching = false;
|
|
||||||
this.uid = new ShortUniqueId();
|
|
||||||
}
|
|
||||||
getNodeUrl() {
|
|
||||||
const myNode =
|
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
];
|
|
||||||
|
|
||||||
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
|
||||||
}
|
|
||||||
getMyNode() {
|
|
||||||
return window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
getApiKey() {
|
|
||||||
const myNode =
|
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
];
|
|
||||||
return myNode.apiKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchResource() {
|
|
||||||
try {
|
|
||||||
if (this.isFetching) return;
|
|
||||||
this.isFetching = true;
|
|
||||||
await axios.get(
|
|
||||||
`${this.nodeUrl}/arbitrary/resource/properties/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
|
|
||||||
);
|
|
||||||
this.isFetching = false;
|
|
||||||
} catch (error) {
|
|
||||||
this.isFetching = false;
|
|
||||||
}
|
}
|
||||||
}
|
this.isReady = false
|
||||||
|
this.nodeUrl = this.getNodeUrl()
|
||||||
async fetchVideoUrl() {
|
this.myNode = this.getMyNode()
|
||||||
await this.fetchResource();
|
this.isFetching = false
|
||||||
}
|
this.uid = new ShortUniqueId()
|
||||||
|
|
||||||
async getRawData() {
|
|
||||||
const url = `${this.nodeUrl}/arbitrary/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`;
|
|
||||||
return await requestQueueRawData.enqueue(() => {
|
|
||||||
return axios.get(url);
|
|
||||||
});
|
|
||||||
// const response2 = await fetch(url, {
|
|
||||||
// method: 'GET',
|
|
||||||
// headers: {
|
|
||||||
// 'Content-Type': 'application/json'
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
|
|
||||||
// const responseData2 = await response2.json()
|
|
||||||
// return responseData2
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDisplayWithPlaceholders(display, resource, rawdata) {
|
|
||||||
const pattern = /\$\$\{([a-zA-Z0-9_\.]+)\}\$\$/g;
|
|
||||||
|
|
||||||
for (const key in display) {
|
|
||||||
const value = display[key];
|
|
||||||
|
|
||||||
display[key] = value.replace(pattern, (match, p1) => {
|
|
||||||
if (p1.startsWith('rawdata.')) {
|
|
||||||
const dataKey = p1.split('.')[1];
|
|
||||||
if (rawdata[dataKey] === undefined) {
|
|
||||||
console.error('rawdata key not found:', dataKey);
|
|
||||||
}
|
|
||||||
return rawdata[dataKey] || match;
|
|
||||||
} else if (p1.startsWith('resource.')) {
|
|
||||||
const resourceKey = p1.split('.')[1];
|
|
||||||
if (resource[resourceKey] === undefined) {
|
|
||||||
console.error('resource key not found:', resourceKey);
|
|
||||||
}
|
|
||||||
return resource[resourceKey] || match;
|
|
||||||
}
|
|
||||||
return match;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchStatus() {
|
|
||||||
let isCalling = false;
|
|
||||||
let percentLoaded = 0;
|
|
||||||
let timer = 24;
|
|
||||||
const response = await requestQueueStatus.enqueue(() => {
|
|
||||||
return axios.get(
|
|
||||||
`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
if (response && response.data && response.data.status === 'READY') {
|
|
||||||
this.status = response.data;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const intervalId = setInterval(async () => {
|
|
||||||
if (isCalling) return;
|
|
||||||
isCalling = true;
|
|
||||||
|
|
||||||
const data = await requestQueue.enqueue(() => {
|
|
||||||
return axios.get(
|
|
||||||
`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
const res = data.data;
|
|
||||||
|
|
||||||
isCalling = false;
|
|
||||||
if (res.localChunkCount) {
|
|
||||||
if (res.percentLoaded) {
|
|
||||||
if (
|
|
||||||
res.percentLoaded === percentLoaded &&
|
|
||||||
res.percentLoaded !== 100
|
|
||||||
) {
|
|
||||||
timer = timer - 5;
|
|
||||||
} else {
|
|
||||||
timer = 24;
|
|
||||||
}
|
|
||||||
if (timer < 0) {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
}
|
|
||||||
percentLoaded = res.percentLoaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.status = res;
|
|
||||||
if (this.status.status === 'DOWNLOADED') {
|
|
||||||
await this.fetchResource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if progress is 100% and clear interval if true
|
|
||||||
if (res.status === 'READY') {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
this.status = res;
|
|
||||||
this.isReady = true;
|
|
||||||
}
|
|
||||||
}, 5000); // 1 second interval
|
|
||||||
}
|
|
||||||
|
|
||||||
async _fetchImage() {
|
|
||||||
try {
|
|
||||||
await this.fetchVideoUrl();
|
|
||||||
await this.fetchStatus();
|
|
||||||
} catch (error) {
|
|
||||||
/* empty */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated() {
|
|
||||||
this._fetchImage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div>
|
<div>
|
||||||
${this.status.status !== 'READY'
|
${this.status.status !== 'READY' ?
|
||||||
? html`
|
html`
|
||||||
<mwc-icon style="user-select:none;"
|
<mwc-icon style="user-select:none;">account_circle</mwc-icon>
|
||||||
>account_circle</mwc-icon
|
` : ''
|
||||||
>
|
}
|
||||||
`
|
${this.status.status === 'READY' ?
|
||||||
: ''}
|
html`
|
||||||
${this.status.status === 'READY'
|
<div style="height: 24px;width: 24px;overflow: hidden;">
|
||||||
? html`
|
<img
|
||||||
<div
|
src="${this.nodeUrl}/arbitrary/THUMBNAIL/${this.name}/qortal_avatar?async=true&apiKey=${this.myNode.apiKey}"
|
||||||
style="height: 24px;width: 24px;overflow: hidden;"
|
style="width:100%; height:100%;border-radius:50%"
|
||||||
>
|
onerror="this.onerror=null; this.src='/img/incognito.png';"
|
||||||
<img
|
/>
|
||||||
src="${this
|
</div>
|
||||||
.nodeUrl}/arbitrary/THUMBNAIL/${this
|
` : ''
|
||||||
.name}/qortal_avatar?async=true&apiKey=${this
|
}
|
||||||
.myNode.apiKey}"
|
|
||||||
style="width:100%; height:100%;border-radius:50%"
|
|
||||||
onerror="this.onerror=null; this.src='/img/incognito.png';"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ''}
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
this._fetchImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeUrl() {
|
||||||
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
|
}
|
||||||
|
|
||||||
|
getMyNode() {
|
||||||
|
return store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchResource() {
|
||||||
|
try {
|
||||||
|
if (this.isFetching) return
|
||||||
|
|
||||||
|
this.isFetching = true
|
||||||
|
|
||||||
|
await axios.get(
|
||||||
|
`${this.nodeUrl}/arbitrary/resource/properties/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
|
||||||
|
)
|
||||||
|
|
||||||
|
this.isFetching = false
|
||||||
|
} catch (error) {
|
||||||
|
this.isFetching = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchVideoUrl() {
|
||||||
|
await this.fetchResource()
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRawData() {
|
||||||
|
const url = `${this.nodeUrl}/arbitrary/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
|
||||||
|
|
||||||
|
return await requestQueueRawData.enqueue(() => {
|
||||||
|
return axios.get(url)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDisplayWithPlaceholders(display, resource, rawdata) {
|
||||||
|
const pattern = /\$\$\{([a-zA-Z0-9_\.]+)\}\$\$/g
|
||||||
|
|
||||||
|
for (const key in display) {
|
||||||
|
const value = display[key]
|
||||||
|
|
||||||
|
display[key] = value.replace(pattern, (match, p1) => {
|
||||||
|
if (p1.startsWith('rawdata.')) {
|
||||||
|
const dataKey = p1.split('.')[1]
|
||||||
|
|
||||||
|
if (rawdata[dataKey] === undefined) {
|
||||||
|
console.error('rawdata key not found:', dataKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawdata[dataKey] || match
|
||||||
|
} else if (p1.startsWith('resource.')) {
|
||||||
|
const resourceKey = p1.split('.')[1]
|
||||||
|
|
||||||
|
if (resource[resourceKey] === undefined) {
|
||||||
|
console.error('resource key not found:', resourceKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource[resourceKey] || match
|
||||||
|
}
|
||||||
|
|
||||||
|
return match
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchStatus() {
|
||||||
|
let isCalling = false
|
||||||
|
let percentLoaded = 0
|
||||||
|
let timer = 24
|
||||||
|
|
||||||
|
const response = await requestQueueStatus.enqueue(() => {
|
||||||
|
return axios.get(
|
||||||
|
`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response && response.data && response.data.status === 'READY') {
|
||||||
|
this.status = response.data
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const intervalId = setInterval(async () => {
|
||||||
|
if (isCalling) return
|
||||||
|
|
||||||
|
isCalling = true
|
||||||
|
|
||||||
|
const data = await requestQueue.enqueue(() => {
|
||||||
|
return axios.get(
|
||||||
|
`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = data.data
|
||||||
|
|
||||||
|
isCalling = false
|
||||||
|
|
||||||
|
if (res.localChunkCount) {
|
||||||
|
if (res.percentLoaded) {
|
||||||
|
if (res.percentLoaded === percentLoaded && res.percentLoaded !== 100) {
|
||||||
|
timer = timer - 5
|
||||||
|
} else {
|
||||||
|
timer = 24
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timer < 0) {
|
||||||
|
clearInterval(intervalId)
|
||||||
|
}
|
||||||
|
|
||||||
|
percentLoaded = res.percentLoaded
|
||||||
|
}
|
||||||
|
|
||||||
|
this.status = res
|
||||||
|
|
||||||
|
if (this.status.status === 'DOWNLOADED') {
|
||||||
|
await this.fetchResource()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if progress is 100% and clear interval if true
|
||||||
|
if (res.status === 'READY') {
|
||||||
|
clearInterval(intervalId)
|
||||||
|
this.status = res
|
||||||
|
this.isReady = true
|
||||||
|
}
|
||||||
|
}, 5000) // 5 second interval
|
||||||
|
}
|
||||||
|
|
||||||
|
async _fetchImage() {
|
||||||
|
try {
|
||||||
|
await this.fetchVideoUrl()
|
||||||
|
await this.fetchStatus()
|
||||||
|
} catch (error) {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard functions
|
||||||
|
getApiKey() {
|
||||||
|
const coreNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
return coreNode.apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmptyArray(arr) {
|
||||||
|
if (!arr) { return true }
|
||||||
|
return arr.length === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
round(number) {
|
||||||
|
return (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('avatar-component', AvatarComponent);
|
window.customElements.define('avatar-component', AvatarComponent)
|
@ -1,344 +1,214 @@
|
|||||||
import {css, html, LitElement} from 'lit';
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers';
|
import { connect } from 'pwa-helpers'
|
||||||
|
import { store } from '../../store'
|
||||||
import '@vaadin/item';
|
import { setNewTab } from '../../redux/app/app-actions'
|
||||||
import '@vaadin/list-box';
|
import { get } from '../../../translate'
|
||||||
import '@polymer/paper-icon-button/paper-icon-button.js';
|
import { beginnerChecklistStyles } from '../../styles/core-css'
|
||||||
import '@polymer/iron-icons/iron-icons.js';
|
import ShortUniqueId from 'short-unique-id'
|
||||||
import {store} from '../../store.js';
|
import '../notification-view/popover'
|
||||||
import {setNewTab} from '../../redux/app/app-actions.js';
|
import '../../../../plugins/plugins/core/components/TimeAgo'
|
||||||
import '@material/mwc-icon';
|
import '@material/mwc-icon'
|
||||||
import {get} from '../../../translate';
|
import '@polymer/paper-icon-button/paper-icon-button.js'
|
||||||
import '../../../../plugins/plugins/core/components/TimeAgo.js';
|
import '@polymer/iron-icons/iron-icons.js'
|
||||||
import '../notification-view/popover.js';
|
import '@vaadin/item'
|
||||||
import ShortUniqueId from 'short-unique-id';
|
import '@vaadin/list-box'
|
||||||
|
|
||||||
class BeginnerChecklist extends connect(store)(LitElement) {
|
class BeginnerChecklist extends connect(store)(LitElement) {
|
||||||
static properties = {
|
static get properties() {
|
||||||
notifications: { type: Array },
|
return {
|
||||||
showChecklist: { type: Boolean },
|
notifications: { type: Array },
|
||||||
theme: { type: String, reflect: true },
|
showChecklist: { type: Boolean },
|
||||||
isSynced: { type: Boolean },
|
isSynced: { type: Boolean },
|
||||||
hasName: { type: Boolean },
|
hasName: { type: Boolean },
|
||||||
hasTourFinished: { type: Boolean },
|
hasTourFinished: { type: Boolean },
|
||||||
};
|
theme: { type: String, reflect: true }
|
||||||
|
}
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.showChecklist = false;
|
|
||||||
this.initialFetch = false;
|
|
||||||
this.theme = localStorage.getItem('qortalTheme')
|
|
||||||
? localStorage.getItem('qortalTheme')
|
|
||||||
: 'light';
|
|
||||||
this.isSynced = false;
|
|
||||||
this.hasName = null;
|
|
||||||
this.nodeUrl = this.getNodeUrl();
|
|
||||||
this.myNode = this.getMyNode();
|
|
||||||
this.hasTourFinished = null;
|
|
||||||
this._controlTourFinished = this._controlTourFinished.bind(this);
|
|
||||||
this.uid = new ShortUniqueId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_controlTourFinished() {
|
static get styles() {
|
||||||
this.hasTourFinished = true;
|
return [beginnerChecklistStyles]
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.showChecklist = false
|
||||||
|
this.initialFetch = false
|
||||||
|
this.isSynced = false
|
||||||
|
this.hasName = null
|
||||||
|
this.nodeUrl = this.getNodeUrl()
|
||||||
|
this.myNode = this.getMyNode()
|
||||||
|
this.hasTourFinished = null
|
||||||
|
this._controlTourFinished = this._controlTourFinished.bind(this)
|
||||||
|
this.uid = new ShortUniqueId()
|
||||||
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return this.hasName === false || this.hasTourFinished === false ?
|
||||||
|
html`
|
||||||
|
<div class="layout">
|
||||||
|
<popover-component for="popover-checklist" message=${get('tour.tour16')}></popover-component>
|
||||||
|
<div id="popover-checklist" @click=${() => this._toggleChecklist()}>
|
||||||
|
<mwc-icon id="checklist-general-icon" style=${`color: ${!this.hasName ? 'red' : 'var(--black)'}; cursor:pointer;user-select:none`}>
|
||||||
|
checklist
|
||||||
|
</mwc-icon>
|
||||||
|
<vaadin-tooltip for="checklist-general-icon" position="bottom" hover-delay=${400} hide-delay=${1} text=${get('tour.tour16')}></vaadin-tooltip>
|
||||||
|
</div>
|
||||||
|
<div id="checklist-panel" class="popover-panel" style="visibility:${this.showChecklist ? 'visibile' : 'hidden'}" tabindex="0" @blur=${this.handleBlur}>
|
||||||
|
<div class="list">
|
||||||
|
<div class="task-list-item">
|
||||||
|
<p>Are you synced?</p>
|
||||||
|
${this.syncPercentage === 100 ?
|
||||||
|
html`
|
||||||
|
<mwc-icon id="checklist-general-icon" style="color: green; user-select:none">
|
||||||
|
task_alt
|
||||||
|
</mwc-icon>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<mwc-icon id="checklist-general-icon" style="color: red; user-select:none">
|
||||||
|
radio_button_unchecked
|
||||||
|
</mwc-icon>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="task-list-item"
|
||||||
|
style="cursor:pointer"
|
||||||
|
@click=${() => {
|
||||||
|
store.dispatch(
|
||||||
|
setNewTab({
|
||||||
|
url: `group-management`,
|
||||||
|
id: this.uid.rnd(),
|
||||||
|
myPlugObj: {
|
||||||
|
url: 'name-registration',
|
||||||
|
domain: 'core',
|
||||||
|
page: 'name-registration/index.html',
|
||||||
|
title: 'Name Registration',
|
||||||
|
icon: 'vaadin:user-check',
|
||||||
|
mwcicon: 'manage_accounts',
|
||||||
|
pluginNumber: 'plugin-qCmtXAQmtu',
|
||||||
|
menus: [],
|
||||||
|
parent: false
|
||||||
|
},
|
||||||
|
openExisting: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.handleBlur();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p>Do you have a name registered?</p>
|
||||||
|
${this.hasName ?
|
||||||
|
html`
|
||||||
|
<mwc-icon id="checklist-general-icon" style="color: green; user-select:none">
|
||||||
|
task_alt
|
||||||
|
</mwc-icon>
|
||||||
|
` : html`
|
||||||
|
<mwc-icon id="checklist-general-icon" style="color: red; user-select:none">
|
||||||
|
radio_button_unchecked
|
||||||
|
</mwc-icon>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
this.address = store.getState().app.selectedAddress.address;
|
this.address = store.getState().app.selectedAddress.address
|
||||||
this.hasTourFinished = JSON.parse(
|
this.hasTourFinished = JSON.parse(localStorage.getItem(`hasViewedTour-${this.address}`) || 'null')
|
||||||
localStorage.getItem(`hasViewedTour-${this.address}`) || 'null'
|
}
|
||||||
);
|
|
||||||
|
_controlTourFinished() {
|
||||||
|
this.hasTourFinished = true
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback()
|
||||||
window.addEventListener(
|
window.addEventListener('send-tour-finished', this._controlTourFinished)
|
||||||
'send-tour-finished',
|
|
||||||
this._controlTourFinished
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
window.removeEventListener(
|
window.removeEventListener('send-tour-finished', this._controlTourFinished)
|
||||||
'send-tour-finished',
|
super.disconnectedCallback()
|
||||||
this._controlTourFinished
|
|
||||||
);
|
|
||||||
|
|
||||||
super.disconnectedCallback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getNodeUrl() {
|
getNodeUrl() {
|
||||||
const myNode =
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
];
|
|
||||||
|
|
||||||
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getMyNode() {
|
getMyNode() {
|
||||||
return window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
return store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getName(recipient) {
|
async getName(recipient) {
|
||||||
try {
|
try {
|
||||||
if (!recipient) return '';
|
if (!recipient) return ''
|
||||||
const endpoint = `${this.nodeUrl}/names/address/${recipient}`;
|
|
||||||
const res = await fetch(endpoint);
|
|
||||||
const getNames = await res.json();
|
|
||||||
|
|
||||||
this.hasName = Array.isArray(getNames) && getNames.length > 0;
|
const endpoint = `${this.nodeUrl}/names/address/${recipient}`
|
||||||
|
const res = await fetch(endpoint)
|
||||||
|
const getNames = await res.json()
|
||||||
|
|
||||||
|
this.hasName = Array.isArray(getNames) && getNames.length > 0
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return '';
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stateChanged(state) {
|
stateChanged(state) {
|
||||||
if (
|
if (state.app.nodeStatus && state.app.nodeStatus.syncPercent !== this.syncPercentage) {
|
||||||
state.app.nodeStatus &&
|
this.syncPercentage = state.app.nodeStatus.syncPercent
|
||||||
state.app.nodeStatus.syncPercent !== this.syncPercentage
|
|
||||||
) {
|
|
||||||
this.syncPercentage = state.app.nodeStatus.syncPercent;
|
|
||||||
|
|
||||||
if (
|
if (!this.hasAttempted && state.app.selectedAddress && state.app.nodeStatus.syncPercent === 100) {
|
||||||
!this.hasAttempted &&
|
this.hasAttempted = true
|
||||||
state.app.selectedAddress &&
|
this.getName(state.app.selectedAddress.address)
|
||||||
state.app.nodeStatus.syncPercent === 100
|
|
||||||
) {
|
|
||||||
this.hasAttempted = true;
|
|
||||||
this.getName(state.app.selectedAddress.address);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
state.app.accountInfo &&
|
if (state.app.accountInfo &&
|
||||||
state.app.accountInfo.names.length &&
|
state.app.accountInfo.names.length && state.app.nodeStatus && state.app.nodeStatus.syncPercent === 100 &&
|
||||||
state.app.nodeStatus &&
|
this.hasName === false && this.hasAttempted && state.app.accountInfo && state.app.accountInfo.names &&
|
||||||
state.app.nodeStatus.syncPercent === 100 &&
|
|
||||||
this.hasName === false &&
|
|
||||||
this.hasAttempted &&
|
|
||||||
state.app.accountInfo &&
|
|
||||||
state.app.accountInfo.names &&
|
|
||||||
state.app.accountInfo.names.length > 0
|
state.app.accountInfo.names.length > 0
|
||||||
) {
|
) {
|
||||||
this.hasName = true;
|
this.hasName = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBlur() {
|
handleBlur() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!this.shadowRoot.contains(document.activeElement)) {
|
if (!this.shadowRoot.contains(document.activeElement)) {
|
||||||
this.showChecklist = false;
|
this.showChecklist = false
|
||||||
}
|
}
|
||||||
}, 0);
|
}, 0)
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return this.hasName === false || this.hasTourFinished === false
|
|
||||||
? html`
|
|
||||||
<div class="layout">
|
|
||||||
<popover-component
|
|
||||||
for="popover-checklist"
|
|
||||||
message=${get('tour.tour16')}
|
|
||||||
></popover-component>
|
|
||||||
<div
|
|
||||||
id="popover-checklist"
|
|
||||||
@click=${() => this._toggleChecklist()}
|
|
||||||
>
|
|
||||||
<mwc-icon
|
|
||||||
id="checklist-general-icon"
|
|
||||||
style=${`color: ${
|
|
||||||
!this.hasName ? 'red' : 'var(--black)'
|
|
||||||
}; cursor:pointer;user-select:none`}
|
|
||||||
>checklist</mwc-icon
|
|
||||||
>
|
|
||||||
<vaadin-tooltip
|
|
||||||
for="checklist-general-icon"
|
|
||||||
position="bottom"
|
|
||||||
hover-delay=${400}
|
|
||||||
hide-delay=${1}
|
|
||||||
text=${get('tour.tour16')}
|
|
||||||
>
|
|
||||||
</vaadin-tooltip>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
id="checklist-panel"
|
|
||||||
class="popover-panel"
|
|
||||||
style="visibility:${this.showChecklist
|
|
||||||
? 'visibile'
|
|
||||||
: 'hidden'}"
|
|
||||||
tabindex="0"
|
|
||||||
@blur=${this.handleBlur}
|
|
||||||
>
|
|
||||||
<div class="list">
|
|
||||||
<div class="task-list-item">
|
|
||||||
<p>Are you synced?</p>
|
|
||||||
${this.syncPercentage === 100
|
|
||||||
? html`
|
|
||||||
<mwc-icon
|
|
||||||
id="checklist-general-icon"
|
|
||||||
style="color: green; user-select:none"
|
|
||||||
>task_alt</mwc-icon
|
|
||||||
>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<mwc-icon
|
|
||||||
id="checklist-general-icon"
|
|
||||||
style="color: red; user-select:none"
|
|
||||||
>radio_button_unchecked</mwc-icon
|
|
||||||
>
|
|
||||||
`}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="task-list-item"
|
|
||||||
style="cursor:pointer"
|
|
||||||
@click=${() => {
|
|
||||||
store.dispatch(
|
|
||||||
setNewTab({
|
|
||||||
url: `group-management`,
|
|
||||||
id: this.uid.rnd(),
|
|
||||||
myPlugObj: {
|
|
||||||
url: 'name-registration',
|
|
||||||
domain: 'core',
|
|
||||||
page: 'name-registration/index.html',
|
|
||||||
title: 'Name Registration',
|
|
||||||
icon: 'vaadin:user-check',
|
|
||||||
mwcicon: 'manage_accounts',
|
|
||||||
pluginNumber:
|
|
||||||
'plugin-qCmtXAQmtu',
|
|
||||||
menus: [],
|
|
||||||
parent: false,
|
|
||||||
},
|
|
||||||
openExisting: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
this.handleBlur();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p>Do you have a name registered?</p>
|
|
||||||
${this.hasName
|
|
||||||
? html`
|
|
||||||
<mwc-icon
|
|
||||||
id="checklist-general-icon"
|
|
||||||
style="color: green; user-select:none"
|
|
||||||
>task_alt</mwc-icon
|
|
||||||
>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<mwc-icon
|
|
||||||
id="checklist-general-icon"
|
|
||||||
style="color: red; user-select:none"
|
|
||||||
>radio_button_unchecked</mwc-icon
|
|
||||||
>
|
|
||||||
`}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_toggleChecklist() {
|
_toggleChecklist() {
|
||||||
this.showChecklist = !this.showChecklist;
|
this.showChecklist = !this.showChecklist
|
||||||
|
|
||||||
if (this.showChecklist) {
|
if (this.showChecklist) {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
this.shadowRoot.getElementById('checklist-panel').focus();
|
this.shadowRoot.getElementById('checklist-panel').focus()
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = css`
|
// Standard functions
|
||||||
.layout {
|
getApiKey() {
|
||||||
display: flex;
|
const coreNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
flex-direction: column;
|
return coreNode.apiKey
|
||||||
align-items: center;
|
}
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.count {
|
isEmptyArray(arr) {
|
||||||
position: absolute;
|
if (!arr) { return true }
|
||||||
top: -5px;
|
return arr.length === 0
|
||||||
right: -5px;
|
}
|
||||||
font-size: 12px;
|
|
||||||
background-color: red;
|
|
||||||
color: white;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nocount {
|
round(number) {
|
||||||
display: none;
|
return (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8)
|
||||||
}
|
}
|
||||||
|
|
||||||
.popover-panel {
|
|
||||||
position: absolute;
|
|
||||||
width: 200px;
|
|
||||||
padding: 10px;
|
|
||||||
background-color: var(--white);
|
|
||||||
border: 1px solid var(--black);
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
||||||
top: 40px;
|
|
||||||
max-height: 350px;
|
|
||||||
overflow: auto;
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: #6a6c75 #a1a1a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-panel::-webkit-scrollbar {
|
|
||||||
width: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-panel::-webkit-scrollbar-track {
|
|
||||||
background: #a1a1a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-panel::-webkit-scrollbar-thumb {
|
|
||||||
background-color: #6a6c75;
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 3px solid #a1a1a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.task-list-item {
|
|
||||||
display: flex;
|
|
||||||
gap: 15px;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checklist-item {
|
|
||||||
padding: 5px;
|
|
||||||
border-bottom: 1px solid;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: 0.2s all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checklist-item:hover {
|
|
||||||
background: var(--nav-color-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 16px;
|
|
||||||
color: var(--black);
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('beginner-checklist', BeginnerChecklist);
|
window.customElements.define('beginner-checklist', BeginnerChecklist)
|
@ -1,91 +1,67 @@
|
|||||||
import {Sha256} from 'asmcrypto.js'
|
import { Sha256 } from 'asmcrypto.js'
|
||||||
|
|
||||||
|
function sbrk(size, heap) {
|
||||||
function sbrk(size, heap){
|
let brk = 512 * 1024 // stack top
|
||||||
let brk = 512 * 1024 // stack top
|
let old = brk
|
||||||
let old = brk
|
brk += size
|
||||||
brk += size
|
if (brk > heap.length) throw new Error('heap exhausted')
|
||||||
|
return old
|
||||||
if (brk > heap.length)
|
|
||||||
throw new Error('heap exhausted')
|
|
||||||
|
|
||||||
return old
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self.addEventListener('message', async e => {
|
self.addEventListener('message', async e => {
|
||||||
const response = await computePow(e.data.convertedBytes, e.data.path)
|
const response = await computePow(e.data.convertedBytes, e.data.path)
|
||||||
postMessage(response)
|
postMessage(response)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
|
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
|
||||||
const heap = new Uint8Array(memory.buffer)
|
const heap = new Uint8Array(memory.buffer)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const computePow = async (convertedBytes, path) => {
|
const computePow = async (convertedBytes, path) => {
|
||||||
|
let response = null
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
const _convertedBytesArray = Object.keys(convertedBytes).map(
|
||||||
|
function (key) {
|
||||||
|
return convertedBytes[key]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const convertedBytesArray = new Uint8Array(_convertedBytesArray)
|
||||||
|
const convertedBytesHash = new Sha256()
|
||||||
|
.process(convertedBytesArray)
|
||||||
|
.finish().result
|
||||||
|
const hashPtr = sbrk(32, heap)
|
||||||
|
const hashAry = new Uint8Array(
|
||||||
|
memory.buffer,
|
||||||
|
hashPtr,
|
||||||
|
32
|
||||||
|
)
|
||||||
|
hashAry.set(convertedBytesHash)
|
||||||
|
const difficulty = 14
|
||||||
|
const workBufferLength = 8 * 1024 * 1024
|
||||||
|
const workBufferPtr = sbrk(
|
||||||
|
workBufferLength,
|
||||||
|
heap
|
||||||
|
)
|
||||||
|
const importObject = {
|
||||||
|
env: {
|
||||||
|
memory: memory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function loadWebAssembly(filename, imports) {
|
||||||
|
return fetch(filename)
|
||||||
|
.then(response => response.arrayBuffer())
|
||||||
|
.then(buffer => WebAssembly.compile(buffer))
|
||||||
|
.then(module => {
|
||||||
|
return new WebAssembly.Instance(module, importObject)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loadWebAssembly(path)
|
||||||
|
.then(wasmModule => {
|
||||||
|
response = {
|
||||||
|
nonce: wasmModule.exports.compute2(hashPtr, workBufferPtr, workBufferLength, difficulty),
|
||||||
|
|
||||||
|
}
|
||||||
let response = null
|
resolve()
|
||||||
|
})
|
||||||
await new Promise((resolve, reject)=> {
|
})
|
||||||
|
return response
|
||||||
const _convertedBytesArray = Object.keys(convertedBytes).map(
|
|
||||||
function (key) {
|
|
||||||
return convertedBytes[key]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const convertedBytesArray = new Uint8Array(_convertedBytesArray)
|
|
||||||
const convertedBytesHash = new Sha256()
|
|
||||||
.process(convertedBytesArray)
|
|
||||||
.finish().result
|
|
||||||
const hashPtr = sbrk(32, heap)
|
|
||||||
const hashAry = new Uint8Array(
|
|
||||||
memory.buffer,
|
|
||||||
hashPtr,
|
|
||||||
32
|
|
||||||
)
|
|
||||||
|
|
||||||
hashAry.set(convertedBytesHash)
|
|
||||||
const difficulty = 14
|
|
||||||
const workBufferLength = 8 * 1024 * 1024
|
|
||||||
const workBufferPtr = sbrk(
|
|
||||||
workBufferLength,
|
|
||||||
heap
|
|
||||||
)
|
|
||||||
|
|
||||||
const importObject = {
|
|
||||||
env: {
|
|
||||||
memory: memory
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function loadWebAssembly(filename, imports) {
|
|
||||||
return fetch(filename)
|
|
||||||
.then(response => response.arrayBuffer())
|
|
||||||
.then(buffer => WebAssembly.compile(buffer))
|
|
||||||
.then(module => {
|
|
||||||
return new WebAssembly.Instance(module, importObject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
loadWebAssembly(path)
|
|
||||||
.then(wasmModule => {
|
|
||||||
response = {
|
|
||||||
nonce : wasmModule.exports.compute2(hashPtr, workBufferPtr, workBufferLength, difficulty),
|
|
||||||
|
|
||||||
}
|
|
||||||
resolve()
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
return response
|
|
||||||
}
|
}
|
@ -1,7 +1,8 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {store} from '../../store'
|
import { connect } from 'pwa-helpers'
|
||||||
import {connect} from 'pwa-helpers'
|
import { store } from '../../store'
|
||||||
import {translate} from '../../../translate'
|
import { translate } from '../../../translate'
|
||||||
|
import { coreSyncStatusStyles } from '../../styles/core-css'
|
||||||
|
|
||||||
class CoreSyncStatus extends connect(store)(LitElement) {
|
class CoreSyncStatus extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@ -12,6 +13,10 @@ class CoreSyncStatus extends connect(store)(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [coreSyncStatusStyles]
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.nodeInfos = []
|
this.nodeInfos = []
|
||||||
@ -19,69 +24,6 @@ class CoreSyncStatus extends connect(store)(LitElement) {
|
|||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
.lineHeight {
|
|
||||||
line-height: 33%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip {
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip .bottom {
|
|
||||||
min-width: 200px;
|
|
||||||
max-width: 250px;
|
|
||||||
top: 35px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, 0);
|
|
||||||
padding: 10px 10px;
|
|
||||||
color: var(--black);
|
|
||||||
background-color: var(--white);
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 13px;
|
|
||||||
border-radius: 8px;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 99999999;
|
|
||||||
box-sizing: border-box;
|
|
||||||
box-shadow: 0 1px 8px rgba(0,0,0,0.5);
|
|
||||||
border: 1px solid var(--black);
|
|
||||||
visibility: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.8s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip:hover .bottom {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip .bottom i {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 100%;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -12px;
|
|
||||||
width: 24px;
|
|
||||||
height: 12px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip .bottom i::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%,50%) rotate(45deg);
|
|
||||||
background-color: var(--white);
|
|
||||||
border: 1px solid var(--black);
|
|
||||||
box-shadow: 0 1px 8px rgba(0,0,0,0.5);
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div id="core-sync-status-id">
|
<div id="core-sync-status-id">
|
||||||
@ -136,7 +78,7 @@ class CoreSyncStatus extends connect(store)(LitElement) {
|
|||||||
<span><img src="/img/syncing.png" style="height: 24px; width: 24px; padding-top: 4px;"></span>
|
<span><img src="/img/syncing.png" style="height: 24px; width: 24px; padding-top: 4px;"></span>
|
||||||
<div class="bottom">
|
<div class="bottom">
|
||||||
<h3>${translate("walletprofile.wp3")}</h3>
|
<h3>${translate("walletprofile.wp3")}</h3>
|
||||||
<h4 class="lineHeight">${translate("appinfo.coreversion")}: <span style="color: #03a9f4">${this.coreInfos.buildVersion ? (this.coreInfos.buildVersion).substring(0,12) : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.coreversion")}: <span style="color: #03a9f4">${this.coreInfos.buildVersion ? (this.coreInfos.buildVersion).substring(0, 12) : ''}</span></h4>
|
||||||
<h4 class="lineHeight">${translate("appinfo.synchronizing")}... <span style="color: #03a9f4">${this.nodeInfos.syncPercent !== undefined ? this.nodeInfos.syncPercent + '%' : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.synchronizing")}... <span style="color: #03a9f4">${this.nodeInfos.syncPercent !== undefined ? this.nodeInfos.syncPercent + '%' : ''}</span></h4>
|
||||||
<h4 class="lineHeight">${translate("appinfo.blockheight")}: <span style="color: #03a9f4">${this.nodeInfos.height ? this.nodeInfos.height : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.blockheight")}: <span style="color: #03a9f4">${this.nodeInfos.height ? this.nodeInfos.height : ''}</span></h4>
|
||||||
<h4 class="lineHeight">${translate("appinfo.peers")}: <span style="color: #03a9f4">${this.nodeInfos.numberOfConnections ? this.nodeInfos.numberOfConnections : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.peers")}: <span style="color: #03a9f4">${this.nodeInfos.numberOfConnections ? this.nodeInfos.numberOfConnections : ''}</span></h4>
|
||||||
@ -150,7 +92,7 @@ class CoreSyncStatus extends connect(store)(LitElement) {
|
|||||||
<span><img src="/img/synced.png" style="height: 24px; width: 24px; padding-top: 4px;"></span>
|
<span><img src="/img/synced.png" style="height: 24px; width: 24px; padding-top: 4px;"></span>
|
||||||
<div class="bottom">
|
<div class="bottom">
|
||||||
<h3>${translate("walletprofile.wp3")}</h3>
|
<h3>${translate("walletprofile.wp3")}</h3>
|
||||||
<h4 class="lineHeight">${translate("appinfo.coreversion")}: <span style="color: #03a9f4">${this.coreInfos.buildVersion ? (this.coreInfos.buildVersion).substring(0,12) : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.coreversion")}: <span style="color: #03a9f4">${this.coreInfos.buildVersion ? (this.coreInfos.buildVersion).substring(0, 12) : ''}</span></h4>
|
||||||
<h4 class="lineHeight">${translate("walletprofile.wp4")} ${translate("walletprofile.wp2")}</h4>
|
<h4 class="lineHeight">${translate("walletprofile.wp4")} ${translate("walletprofile.wp2")}</h4>
|
||||||
<h4 class="lineHeight">${translate("appinfo.blockheight")}: <span style="color: #03a9f4">${this.nodeInfos.height ? this.nodeInfos.height : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.blockheight")}: <span style="color: #03a9f4">${this.nodeInfos.height ? this.nodeInfos.height : ''}</span></h4>
|
||||||
<h4 class="lineHeight">${translate("appinfo.peers")}: <span style="color: #03a9f4">${this.nodeInfos.numberOfConnections ? this.nodeInfos.numberOfConnections : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.peers")}: <span style="color: #03a9f4">${this.nodeInfos.numberOfConnections ? this.nodeInfos.numberOfConnections : ''}</span></h4>
|
||||||
@ -164,7 +106,7 @@ class CoreSyncStatus extends connect(store)(LitElement) {
|
|||||||
<span><img src="/img/synced.png" style="height: 24px; width: 24px; padding-top: 4px;"></span>
|
<span><img src="/img/synced.png" style="height: 24px; width: 24px; padding-top: 4px;"></span>
|
||||||
<div class="bottom">
|
<div class="bottom">
|
||||||
<h3>${translate("walletprofile.wp3")}</h3>
|
<h3>${translate("walletprofile.wp3")}</h3>
|
||||||
<h4 class="lineHeight">${translate("appinfo.coreversion")}: <span style="color: #03a9f4">${this.coreInfos.buildVersion ? (this.coreInfos.buildVersion).substring(0,12) : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.coreversion")}: <span style="color: #03a9f4">${this.coreInfos.buildVersion ? (this.coreInfos.buildVersion).substring(0, 12) : ''}</span></h4>
|
||||||
<h4 class="lineHeight">${translate("walletprofile.wp4")} ${translate("walletprofile.wp2")}</h4>
|
<h4 class="lineHeight">${translate("walletprofile.wp4")} ${translate("walletprofile.wp2")}</h4>
|
||||||
<h4 class="lineHeight">${translate("appinfo.blockheight")}: <span style="color: #03a9f4">${this.nodeInfos.height ? this.nodeInfos.height : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.blockheight")}: <span style="color: #03a9f4">${this.nodeInfos.height ? this.nodeInfos.height : ''}</span></h4>
|
||||||
<h4 class="lineHeight">${translate("appinfo.peers")}: <span style="color: #03a9f4">${this.nodeInfos.numberOfConnections ? this.nodeInfos.numberOfConnections : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.peers")}: <span style="color: #03a9f4">${this.nodeInfos.numberOfConnections ? this.nodeInfos.numberOfConnections : ''}</span></h4>
|
||||||
@ -178,7 +120,7 @@ class CoreSyncStatus extends connect(store)(LitElement) {
|
|||||||
<span><img src="/img/synced_minting.png" style="height: 24px; width: 24px; padding-top: 4px;"></span>
|
<span><img src="/img/synced_minting.png" style="height: 24px; width: 24px; padding-top: 4px;"></span>
|
||||||
<div class="bottom">
|
<div class="bottom">
|
||||||
<h3>${translate("walletprofile.wp3")}</h3>
|
<h3>${translate("walletprofile.wp3")}</h3>
|
||||||
<h4 class="lineHeight">${translate("appinfo.coreversion")}: <span style="color: #03a9f4">${this.coreInfos.buildVersion ? (this.coreInfos.buildVersion).substring(0,12) : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.coreversion")}: <span style="color: #03a9f4">${this.coreInfos.buildVersion ? (this.coreInfos.buildVersion).substring(0, 12) : ''}</span></h4>
|
||||||
<h4 class="lineHeight">${translate("walletprofile.wp4")} <span style="color: #03a9f4">( ${translate("walletprofile.wp1")} )</span></h4>
|
<h4 class="lineHeight">${translate("walletprofile.wp4")} <span style="color: #03a9f4">( ${translate("walletprofile.wp1")} )</span></h4>
|
||||||
<h4 class="lineHeight">${translate("appinfo.blockheight")}: <span style="color: #03a9f4">${this.nodeInfos.height ? this.nodeInfos.height : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.blockheight")}: <span style="color: #03a9f4">${this.nodeInfos.height ? this.nodeInfos.height : ''}</span></h4>
|
||||||
<h4 class="lineHeight">${translate("appinfo.peers")}: <span style="color: #03a9f4">${this.nodeInfos.numberOfConnections ? this.nodeInfos.numberOfConnections : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.peers")}: <span style="color: #03a9f4">${this.nodeInfos.numberOfConnections ? this.nodeInfos.numberOfConnections : ''}</span></h4>
|
||||||
@ -192,7 +134,7 @@ class CoreSyncStatus extends connect(store)(LitElement) {
|
|||||||
<span><img src="/img/synced_minting.png" style="height: 24px; width: 24px; padding-top: 4px;"></span>
|
<span><img src="/img/synced_minting.png" style="height: 24px; width: 24px; padding-top: 4px;"></span>
|
||||||
<div class="bottom">
|
<div class="bottom">
|
||||||
<h3>${translate("walletprofile.wp3")}</h3>
|
<h3>${translate("walletprofile.wp3")}</h3>
|
||||||
<h4 class="lineHeight">${translate("appinfo.coreversion")}: <span style="color: #03a9f4">${this.coreInfos.buildVersion ? (this.coreInfos.buildVersion).substring(0,12) : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.coreversion")}: <span style="color: #03a9f4">${this.coreInfos.buildVersion ? (this.coreInfos.buildVersion).substring(0, 12) : ''}</span></h4>
|
||||||
<h4 class="lineHeight">${translate("walletprofile.wp4")} <span style="color: #03a9f4">( ${translate("walletprofile.wp1")} )</span></h4>
|
<h4 class="lineHeight">${translate("walletprofile.wp4")} <span style="color: #03a9f4">( ${translate("walletprofile.wp1")} )</span></h4>
|
||||||
<h4 class="lineHeight">${translate("appinfo.blockheight")}: <span style="color: #03a9f4">${this.nodeInfos.height ? this.nodeInfos.height : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.blockheight")}: <span style="color: #03a9f4">${this.nodeInfos.height ? this.nodeInfos.height : ''}</span></h4>
|
||||||
<h4 class="lineHeight">${translate("appinfo.peers")}: <span style="color: #03a9f4">${this.nodeInfos.numberOfConnections ? this.nodeInfos.numberOfConnections : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.peers")}: <span style="color: #03a9f4">${this.nodeInfos.numberOfConnections ? this.nodeInfos.numberOfConnections : ''}</span></h4>
|
||||||
@ -206,7 +148,7 @@ class CoreSyncStatus extends connect(store)(LitElement) {
|
|||||||
<span><img src="/img/syncing.png" style="height: 24px; width: 24px; padding-top: 4px;"></span>
|
<span><img src="/img/syncing.png" style="height: 24px; width: 24px; padding-top: 4px;"></span>
|
||||||
<div class="bottom">
|
<div class="bottom">
|
||||||
<h3>${translate("walletprofile.wp3")}</h3>
|
<h3>${translate("walletprofile.wp3")}</h3>
|
||||||
<h4 class="lineHeight">${translate("appinfo.coreversion")}: <span style="color: #03a9f4">${this.coreInfos.buildVersion ? (this.coreInfos.buildVersion).substring(0,12) : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.coreversion")}: <span style="color: #03a9f4">${this.coreInfos.buildVersion ? (this.coreInfos.buildVersion).substring(0, 12) : ''}</span></h4>
|
||||||
<h4 class="lineHeight">${translate("appinfo.synchronizing")}... <span style="color: #03a9f4">${this.nodeInfos.syncPercent !== undefined ? this.nodeInfos.syncPercent + '%' : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.synchronizing")}... <span style="color: #03a9f4">${this.nodeInfos.syncPercent !== undefined ? this.nodeInfos.syncPercent + '%' : ''}</span></h4>
|
||||||
<h4 class="lineHeight">${translate("appinfo.blockheight")}: <span style="color: #03a9f4">${this.nodeInfos.height ? this.nodeInfos.height : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.blockheight")}: <span style="color: #03a9f4">${this.nodeInfos.height ? this.nodeInfos.height : ''}</span></h4>
|
||||||
<h4 class="lineHeight">${translate("appinfo.peers")}: <span style="color: #03a9f4">${this.nodeInfos.numberOfConnections ? this.nodeInfos.numberOfConnections : ''}</span></h4>
|
<h4 class="lineHeight">${translate("appinfo.peers")}: <span style="color: #03a9f4">${this.nodeInfos.numberOfConnections ? this.nodeInfos.numberOfConnections : ''}</span></h4>
|
||||||
@ -221,6 +163,20 @@ class CoreSyncStatus extends connect(store)(LitElement) {
|
|||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Standard functions
|
||||||
|
getApiKey() {
|
||||||
|
const coreNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
return coreNode.apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmptyArray(arr) {
|
||||||
|
if (!arr) { return true }
|
||||||
|
return arr.length === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
round(number) {
|
||||||
|
return (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('core-sync-status', CoreSyncStatus)
|
window.customElements.define('core-sync-status', CoreSyncStatus)
|
@ -1,366 +1,282 @@
|
|||||||
import {css, html, LitElement} from 'lit';
|
import { html, LitElement } from 'lit'
|
||||||
import {translate,} from '../../../translate'
|
import { connect } from 'pwa-helpers'
|
||||||
|
import { store } from '../../store'
|
||||||
|
import { setNewTab } from '../../redux/app/app-actions'
|
||||||
|
import { RequestQueueWithPromise } from '../../../../plugins/plugins/utils/classes'
|
||||||
|
import { translate, } from '../../../translate'
|
||||||
|
import { feedItemStyles } from '../../styles/core-css'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import '@material/mwc-menu';
|
import ShortUniqueId from 'short-unique-id'
|
||||||
import '@material/mwc-list/mwc-list-item.js'
|
|
||||||
import {RequestQueueWithPromise} from '../../../../plugins/plugins/utils/queue';
|
|
||||||
import '../../../../plugins/plugins/core/components/TimeAgo'
|
import '../../../../plugins/plugins/core/components/TimeAgo'
|
||||||
import {connect} from 'pwa-helpers';
|
import '@material/mwc-menu'
|
||||||
import {store} from '../../store';
|
import '@material/mwc-list/mwc-list-item.js'
|
||||||
import {setNewTab} from '../../redux/app/app-actions';
|
|
||||||
import ShortUniqueId from 'short-unique-id';
|
|
||||||
|
|
||||||
const requestQueue = new RequestQueueWithPromise(3);
|
|
||||||
const requestQueueRawData = new RequestQueueWithPromise(3);
|
|
||||||
const requestQueueStatus = new RequestQueueWithPromise(3);
|
|
||||||
|
|
||||||
|
const requestQueue = new RequestQueueWithPromise(3)
|
||||||
|
const requestQueueRawData = new RequestQueueWithPromise(3)
|
||||||
|
const requestQueueStatus = new RequestQueueWithPromise(3)
|
||||||
|
|
||||||
export class FeedItem extends connect(store)(LitElement) {
|
export class FeedItem extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
resource: { type: Object },
|
resource: { type: Object },
|
||||||
isReady: { type: Boolean},
|
isReady: { type: Boolean },
|
||||||
status: {type: Object},
|
status: { type: Object },
|
||||||
feedItem: {type: Object},
|
feedItem: { type: Object },
|
||||||
appName: {type: String},
|
appName: { type: String },
|
||||||
link: {type: String}
|
link: { type: String }
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
* {
|
|
||||||
--mdc-theme-text-primary-on-background: var(--black);
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
:host {
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
width:100%;
|
|
||||||
max-height:30vh;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.smallLoading,
|
|
||||||
.smallLoading:after {
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 2px;
|
|
||||||
height: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smallLoading {
|
|
||||||
border-width: 0.8em;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2)
|
|
||||||
rgba(3, 169, 244, 0.2) rgb(3, 169, 244);
|
|
||||||
font-size: 30px;
|
|
||||||
position: relative;
|
|
||||||
text-indent: -9999em;
|
|
||||||
transform: translateZ(0px);
|
|
||||||
animation: 1.1s linear 0s infinite normal none running loadingAnimation;
|
|
||||||
}
|
|
||||||
|
|
||||||
.defaultSize {
|
|
||||||
width: 100%;
|
|
||||||
height: 160px;
|
|
||||||
}
|
|
||||||
.parent-feed-item {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
background-color: var(--chat-bubble-bg);
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: center;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 12px 15px 4px 15px;
|
|
||||||
min-width: 150px;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
.avatar {
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
border-radius:50%;
|
|
||||||
overflow: hidden;
|
|
||||||
display:flex;
|
|
||||||
align-items:center;
|
|
||||||
}
|
|
||||||
.avatarApp {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border-radius:50%;
|
|
||||||
overflow: hidden;
|
|
||||||
display:flex;
|
|
||||||
align-items:center;
|
|
||||||
}
|
|
||||||
.feed-item-name {
|
|
||||||
user-select: none;
|
|
||||||
color: #03a9f4;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-name {
|
static get styles() {
|
||||||
display: flex;
|
return [feedItemStyles]
|
||||||
justify-content: space-between;
|
}
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
mwc-menu {
|
constructor() {
|
||||||
position: absolute;
|
super()
|
||||||
}
|
this.resource = {
|
||||||
|
identifier: "",
|
||||||
|
name: "",
|
||||||
|
service: ""
|
||||||
|
}
|
||||||
|
this.status = {
|
||||||
|
status: ''
|
||||||
|
}
|
||||||
|
this.isReady = false
|
||||||
|
this.nodeUrl = this.getNodeUrl()
|
||||||
|
this.myNode = this.getMyNode()
|
||||||
|
this.hasCalledWhenDownloaded = false
|
||||||
|
this.isFetching = false
|
||||||
|
this.uid = new ShortUniqueId()
|
||||||
|
this.observer = new IntersectionObserver(entries => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (entry.isIntersecting && this.status.status !== 'READY') {
|
||||||
|
this._fetchImage()
|
||||||
|
// Stop observing after the image has started loading
|
||||||
|
this.observer.unobserve(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.feedItem = null
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let avatarImg
|
||||||
|
const avatarUrl = `${this.nodeUrl}/arbitrary/THUMBNAIL/${this.resource.name}/qortal_avatar?async=true`
|
||||||
|
avatarImg = html`<img src="${avatarUrl}" style="width:100%; height:100%;" onerror="this.onerror=null; this.src='/img/incognito.png';" />`
|
||||||
|
|
||||||
|
let avatarImgApp
|
||||||
|
const avatarUrl2 = `${this.nodeUrl}/arbitrary/THUMBNAIL/${this.appName}/qortal_avatar?async=true`
|
||||||
|
avatarImgApp = html`<img src="${avatarUrl2}" style="width:100%; height:100%;" onerror="this.onerror=null; this.src='/img/incognito.png';" />`
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class=${[`image-container`, this.status.status !== 'READY' ? 'defaultSize' : '', this.status.status !== 'READY' ? 'hideImg' : '',].join(' ')}
|
||||||
|
style=" box-sizing: border-box;"
|
||||||
|
>
|
||||||
|
${this.status.status !== 'READY' ?
|
||||||
|
html`
|
||||||
|
<div style="display:flex;flex-direction:column;width:100%;height:100%;justify-content:center;align-items:center; box-sizing: border-box;">
|
||||||
|
<div class=${`smallLoading`}></div>
|
||||||
|
<p style="color: var(--black)">
|
||||||
|
${`${Math.round(this.status.percentLoaded || 0).toFixed(0)}% `}${translate('chatpage.cchange94')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
${this.status.status === 'READY' && this.feedItem ?
|
||||||
|
html`
|
||||||
|
<div class="parent-feed-item" style="position:relative" @click=${this.goToFeedLink}>
|
||||||
|
<div style="display:flex;gap:10px;margin-bottom:5px">
|
||||||
|
<div class="avatar">${avatarImg}</div>
|
||||||
|
<span class="feed-item-name">${this.resource.name}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>${this.feedItem.title}</p>
|
||||||
|
</div>
|
||||||
|
<div class="app-name">
|
||||||
|
<div class="avatarApp">${avatarImgApp}</div>
|
||||||
|
<message-time timestamp=${this.resource.created}></message-time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
@-webkit-keyframes loadingAnimation {
|
firstUpdated() {
|
||||||
0% {
|
this.observer.observe(this)
|
||||||
-webkit-transform: rotate(0deg);
|
}
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-webkit-transform: rotate(360deg);
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes loadingAnimation {
|
getNodeUrl() {
|
||||||
0% {
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
-webkit-transform: rotate(0deg);
|
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
transform: rotate(0deg);
|
}
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-webkit-transform: rotate(360deg);
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
getMyNode() {
|
||||||
super();
|
return store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
this.resource = {
|
}
|
||||||
identifier: "",
|
|
||||||
name: "",
|
|
||||||
service: ""
|
|
||||||
}
|
|
||||||
this.status = {
|
|
||||||
status: ''
|
|
||||||
}
|
|
||||||
this.isReady = false
|
|
||||||
this.nodeUrl = this.getNodeUrl()
|
|
||||||
this.myNode = this.getMyNode()
|
|
||||||
this.hasCalledWhenDownloaded = false
|
|
||||||
this.isFetching = false
|
|
||||||
this.uid = new ShortUniqueId()
|
|
||||||
|
|
||||||
this.observer = new IntersectionObserver(entries => {
|
async fetchResource() {
|
||||||
for (const entry of entries) {
|
try {
|
||||||
if (entry.isIntersecting && this.status.status !== 'READY') {
|
if (this.isFetching) return
|
||||||
this._fetchImage();
|
this.isFetching = true
|
||||||
// Stop observing after the image has started loading
|
await axios.get(`${this.nodeUrl}/arbitrary/resource/properties/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`)
|
||||||
this.observer.unobserve(this);
|
this.isFetching = false
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.feedItem = null
|
|
||||||
}
|
|
||||||
getNodeUrl(){
|
|
||||||
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
|
||||||
|
|
||||||
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
} catch (error) {
|
||||||
}
|
this.isFetching = false
|
||||||
getMyNode(){
|
}
|
||||||
return window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
}
|
||||||
}
|
|
||||||
|
|
||||||
getApiKey() {
|
async fetchVideoUrl() {
|
||||||
const myNode =
|
await this.fetchResource()
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
}
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
];
|
|
||||||
return myNode.apiKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchResource() {
|
async getRawData() {
|
||||||
try {
|
const url = `${this.nodeUrl}/arbitrary/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
|
||||||
if(this.isFetching) return
|
|
||||||
this.isFetching = true
|
|
||||||
await axios.get(`${this.nodeUrl}/arbitrary/resource/properties/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`)
|
|
||||||
this.isFetching = false
|
|
||||||
|
|
||||||
} catch (error) {
|
return await requestQueueRawData.enqueue(() => {
|
||||||
this.isFetching = false
|
return axios.get(url)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchVideoUrl() {
|
updateDisplayWithPlaceholders(display, resource, rawdata) {
|
||||||
|
const pattern = /\$\$\{([a-zA-Z0-9_\.]+)\}\$\$/g
|
||||||
|
|
||||||
await this.fetchResource()
|
for (const key in display) {
|
||||||
|
const value = display[key]
|
||||||
|
|
||||||
}
|
display[key] = value.replace(pattern, (match, p1) => {
|
||||||
|
if (p1.startsWith('rawdata.')) {
|
||||||
|
const dataKey = p1.split('.')[1]
|
||||||
|
if (rawdata[dataKey] === undefined) {
|
||||||
|
console.error("rawdata key not found:", dataKey)
|
||||||
|
}
|
||||||
|
return rawdata[dataKey] || match
|
||||||
|
} else if (p1.startsWith('resource.')) {
|
||||||
|
const resourceKey = p1.split('.')[1]
|
||||||
|
if (resource[resourceKey] === undefined) {
|
||||||
|
console.error("resource key not found:", resourceKey)
|
||||||
|
}
|
||||||
|
return resource[resourceKey] || match
|
||||||
|
}
|
||||||
|
return match
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getRawData(){
|
async fetchStatus() {
|
||||||
const url = `${this.nodeUrl}/arbitrary/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
|
let isCalling = false
|
||||||
return await requestQueueRawData.enqueue(()=> {
|
let percentLoaded = 0
|
||||||
return axios.get(url)
|
let timer = 24
|
||||||
})
|
|
||||||
// const response2 = await fetch(url, {
|
|
||||||
// method: 'GET',
|
|
||||||
// headers: {
|
|
||||||
// 'Content-Type': 'application/json'
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
|
|
||||||
// const responseData2 = await response2.json()
|
const response = await requestQueueStatus.enqueue(() => {
|
||||||
// return responseData2
|
return axios.get(`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`)
|
||||||
}
|
})
|
||||||
|
|
||||||
updateDisplayWithPlaceholders(display, resource, rawdata) {
|
if (response && response.data && response.data.status === 'READY') {
|
||||||
const pattern = /\$\$\{([a-zA-Z0-9_\.]+)\}\$\$/g;
|
const rawData = await this.getRawData()
|
||||||
|
|
||||||
for (const key in display) {
|
const object = {
|
||||||
const value = display[key];
|
...this.resource.schema.display
|
||||||
|
}
|
||||||
|
|
||||||
display[key] = value.replace(pattern, (match, p1) => {
|
this.updateDisplayWithPlaceholders(object, {}, rawData.data)
|
||||||
if (p1.startsWith('rawdata.')) {
|
this.feedItem = object
|
||||||
const dataKey = p1.split('.')[1];
|
this.status = response.data
|
||||||
if (rawdata[dataKey] === undefined) {
|
|
||||||
console.error("rawdata key not found:", dataKey);
|
|
||||||
}
|
|
||||||
return rawdata[dataKey] || match;
|
|
||||||
} else if (p1.startsWith('resource.')) {
|
|
||||||
const resourceKey = p1.split('.')[1];
|
|
||||||
if (resource[resourceKey] === undefined) {
|
|
||||||
console.error("resource key not found:", resourceKey);
|
|
||||||
}
|
|
||||||
return resource[resourceKey] || match;
|
|
||||||
}
|
|
||||||
return match;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const intervalId = setInterval(async () => {
|
||||||
|
if (isCalling) return
|
||||||
|
isCalling = true
|
||||||
|
|
||||||
|
const data = await requestQueue.enqueue(() => {
|
||||||
|
return axios.get(`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`)
|
||||||
|
})
|
||||||
|
|
||||||
async fetchStatus(){
|
const res = data.data
|
||||||
let isCalling = false
|
|
||||||
let percentLoaded = 0
|
|
||||||
let timer = 24
|
|
||||||
const response = await requestQueueStatus.enqueue(()=> {
|
|
||||||
return axios.get(`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`)
|
|
||||||
})
|
|
||||||
if(response && response.data && response.data.status === 'READY'){
|
|
||||||
const rawData = await this.getRawData()
|
|
||||||
const object = {
|
|
||||||
...this.resource.schema.display
|
|
||||||
}
|
|
||||||
this.updateDisplayWithPlaceholders(object, {},rawData.data)
|
|
||||||
this.feedItem = object
|
|
||||||
this.status = response.data
|
|
||||||
|
|
||||||
return
|
isCalling = false
|
||||||
}
|
if (res.localChunkCount) {
|
||||||
const intervalId = setInterval(async () => {
|
if (res.percentLoaded) {
|
||||||
if (isCalling) return
|
if (res.percentLoaded === percentLoaded && res.percentLoaded !== 100) {
|
||||||
isCalling = true
|
timer = timer - 5
|
||||||
|
} else {
|
||||||
|
timer = 24
|
||||||
|
}
|
||||||
|
if (timer < 0) {
|
||||||
|
timer = 24
|
||||||
|
isCalling = true
|
||||||
|
this.status = {
|
||||||
|
...res,
|
||||||
|
status: 'REFETCHING'
|
||||||
|
}
|
||||||
|
|
||||||
const data = await requestQueue.enqueue(() => {
|
setTimeout(() => {
|
||||||
return axios.get(`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`)
|
isCalling = false
|
||||||
});
|
this.fetchResource()
|
||||||
const res = data.data
|
}, 25000)
|
||||||
|
|
||||||
isCalling = false
|
return
|
||||||
if (res.localChunkCount) {
|
}
|
||||||
if (res.percentLoaded) {
|
percentLoaded = res.percentLoaded
|
||||||
if (
|
}
|
||||||
res.percentLoaded === percentLoaded &&
|
|
||||||
res.percentLoaded !== 100
|
|
||||||
) {
|
|
||||||
timer = timer - 5
|
|
||||||
} else {
|
|
||||||
timer = 24
|
|
||||||
}
|
|
||||||
if (timer < 0) {
|
|
||||||
timer = 24
|
|
||||||
isCalling = true
|
|
||||||
this.status = {
|
|
||||||
...res,
|
|
||||||
status: 'REFETCHING'
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
this.status = res
|
||||||
isCalling = false
|
|
||||||
this.fetchResource()
|
|
||||||
}, 25000)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
percentLoaded = res.percentLoaded
|
|
||||||
}
|
|
||||||
|
|
||||||
this.status = res
|
if (this.status.status === 'DOWNLOADED') {
|
||||||
if(this.status.status === 'DOWNLOADED'){
|
await this.fetchResource()
|
||||||
await this.fetchResource()
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// check if progress is 100% and clear interval if true
|
// check if progress is 100% and clear interval if true
|
||||||
if (res.status === 'READY') {
|
if (res.status === 'READY') {
|
||||||
const rawData = await this.getRawData()
|
const rawData = await this.getRawData()
|
||||||
const object = {
|
const object = {
|
||||||
...this.resource.schema.display
|
...this.resource.schema.display
|
||||||
}
|
}
|
||||||
this.updateDisplayWithPlaceholders(object, {},rawData.data)
|
this.updateDisplayWithPlaceholders(object, {}, rawData.data)
|
||||||
this.feedItem = object
|
this.feedItem = object
|
||||||
clearInterval(intervalId)
|
clearInterval(intervalId)
|
||||||
this.status = res
|
this.status = res
|
||||||
this.isReady = true
|
this.isReady = true
|
||||||
}
|
}
|
||||||
}, 5000) // 1 second interval
|
}, 5000) // 5 second interval
|
||||||
}
|
}
|
||||||
|
|
||||||
async _fetchImage() {
|
async _fetchImage() {
|
||||||
try {
|
try {
|
||||||
await this.fetchVideoUrl()
|
await this.fetchVideoUrl()
|
||||||
await this.fetchStatus()
|
await this.fetchStatus()
|
||||||
} catch (error) { /* empty */ }
|
} catch (error) { /* empty */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated(){
|
async goToFeedLink() {
|
||||||
this.observer.observe(this);
|
try {
|
||||||
|
let newQuery = this.link
|
||||||
|
if (newQuery.endsWith('/')) {
|
||||||
|
newQuery = newQuery.slice(0, -1)
|
||||||
|
}
|
||||||
|
const res = await this.extractComponents(newQuery)
|
||||||
|
if (!res) return
|
||||||
|
const { service, name, identifier, path } = res
|
||||||
|
let query = `?service=${service}`
|
||||||
|
if (name) {
|
||||||
|
query = query + `&name=${name}`
|
||||||
|
}
|
||||||
|
if (identifier) {
|
||||||
|
query = query + `&identifier=${identifier}`
|
||||||
|
}
|
||||||
|
if (path) {
|
||||||
|
query = query + `&path=${path}`
|
||||||
|
}
|
||||||
|
|
||||||
}
|
store.dispatch(setNewTab({
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async goToFeedLink(){
|
|
||||||
try {
|
|
||||||
let newQuery = this.link
|
|
||||||
if (newQuery.endsWith('/')) {
|
|
||||||
newQuery = newQuery.slice(0, -1)
|
|
||||||
}
|
|
||||||
const res = await this.extractComponents(newQuery)
|
|
||||||
if (!res) return
|
|
||||||
const { service, name, identifier, path } = res
|
|
||||||
let query = `?service=${service}`
|
|
||||||
if (name) {
|
|
||||||
query = query + `&name=${name}`
|
|
||||||
}
|
|
||||||
if (identifier) {
|
|
||||||
query = query + `&identifier=${identifier}`
|
|
||||||
}
|
|
||||||
if (path) {
|
|
||||||
query = query + `&path=${path}`
|
|
||||||
}
|
|
||||||
|
|
||||||
store.dispatch(setNewTab({
|
|
||||||
url: `qdn/browser/index.html${query}`,
|
url: `qdn/browser/index.html${query}`,
|
||||||
id: this.uid.rnd(),
|
id: this.uid.rnd(),
|
||||||
myPlugObj: {
|
myPlugObj: {
|
||||||
@ -373,137 +289,72 @@ getMyNode(){
|
|||||||
"menus": [],
|
"menus": [],
|
||||||
"parent": false
|
"parent": false
|
||||||
},
|
},
|
||||||
openExisting: true
|
openExisting: true
|
||||||
}))
|
}))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log({error})
|
console.log({ error })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async extractComponents(url) {
|
||||||
|
if (!url.startsWith("qortal://")) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
url = url.replace(/^(qortal\:\/\/)/, "")
|
||||||
|
|
||||||
async extractComponents(url) {
|
if (url.includes("/")) {
|
||||||
if (!url.startsWith("qortal://")) {
|
let parts = url.split("/")
|
||||||
return null
|
const service = parts[0].toUpperCase()
|
||||||
}
|
parts.shift()
|
||||||
|
const name = parts[0]
|
||||||
|
parts.shift()
|
||||||
|
let identifier
|
||||||
|
|
||||||
url = url.replace(/^(qortal\:\/\/)/, "")
|
if (parts.length > 0) {
|
||||||
if (url.includes("/")) {
|
identifier = parts[0] // Do not shift yet
|
||||||
let parts = url.split("/")
|
// Check if a resource exists with this service, name and identifier combination
|
||||||
const service = parts[0].toUpperCase()
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
parts.shift()
|
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
const name = parts[0]
|
const url = `${nodeUrl}/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${myNode.apiKey}}`
|
||||||
parts.shift()
|
|
||||||
let identifier
|
|
||||||
|
|
||||||
if (parts.length > 0) {
|
const res = await fetch(url);
|
||||||
identifier = parts[0] // Do not shift yet
|
const data = await res.json();
|
||||||
// Check if a resource exists with this service, name and identifier combination
|
if (data.totalChunkCount > 0) {
|
||||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
// Identifier exists, so don't include it in the path
|
||||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
parts.shift()
|
||||||
const url = `${nodeUrl}/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${myNode.apiKey}}`
|
}
|
||||||
|
else {
|
||||||
|
identifier = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const res = await fetch(url);
|
const path = parts.join("/")
|
||||||
const data = await res.json();
|
|
||||||
if (data.totalChunkCount > 0) {
|
|
||||||
// Identifier exists, so don't include it in the path
|
|
||||||
parts.shift()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
identifier = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const path = parts.join("/")
|
const components = {}
|
||||||
|
components["service"] = service
|
||||||
|
components["name"] = name
|
||||||
|
components["identifier"] = identifier
|
||||||
|
components["path"] = path
|
||||||
|
return components
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const components = {}
|
// Standard functions
|
||||||
components["service"] = service
|
getApiKey() {
|
||||||
components["name"] = name
|
const coreNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
components["identifier"] = identifier
|
return coreNode.apiKey
|
||||||
components["path"] = path
|
}
|
||||||
return components
|
|
||||||
}
|
isEmptyArray(arr) {
|
||||||
return null
|
if (!arr) { return true }
|
||||||
|
return arr.length === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
round(number) {
|
||||||
|
return (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.customElements.define('feed-item', FeedItem)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let avatarImg
|
|
||||||
const avatarUrl = `${this.nodeUrl}/arbitrary/THUMBNAIL/${this.resource.name}/qortal_avatar?async=true&apiKey=${this.myNode.apiKey}`;
|
|
||||||
avatarImg = html`<img
|
|
||||||
src="${avatarUrl}"
|
|
||||||
style="width:100%; height:100%;"
|
|
||||||
onerror="this.onerror=null; this.src='/img/incognito.png';"
|
|
||||||
/>`;
|
|
||||||
let avatarImgApp
|
|
||||||
const avatarUrl2 = `${this.nodeUrl}/arbitrary/THUMBNAIL/${this.appName}/qortal_avatar?async=true&apiKey=${this.myNode.apiKey}`;
|
|
||||||
avatarImgApp = html`<img
|
|
||||||
src="${avatarUrl2}"
|
|
||||||
style="width:100%; height:100%;"
|
|
||||||
onerror="this.onerror=null; this.src='/img/incognito.png';"
|
|
||||||
/>`;
|
|
||||||
return html`
|
|
||||||
<div
|
|
||||||
class=${[
|
|
||||||
`image-container`,
|
|
||||||
this.status.status !== 'READY'
|
|
||||||
? 'defaultSize'
|
|
||||||
: '',
|
|
||||||
this.status.status !== 'READY'
|
|
||||||
? 'hideImg'
|
|
||||||
: '',
|
|
||||||
].join(' ')}
|
|
||||||
style=" box-sizing: border-box;"
|
|
||||||
>
|
|
||||||
${
|
|
||||||
this.status.status !== 'READY'
|
|
||||||
? html`
|
|
||||||
<div
|
|
||||||
style="display:flex;flex-direction:column;width:100%;height:100%;justify-content:center;align-items:center; box-sizing: border-box;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class=${`smallLoading`}
|
|
||||||
></div>
|
|
||||||
<p style="color: var(--black)">${`${Math.round(this.status.percentLoaded || 0
|
|
||||||
).toFixed(0)}% `}${translate('chatpage.cchange94')}</p>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
${this.status.status === 'READY' && this.feedItem ? html`
|
|
||||||
<div class="parent-feed-item" style="position:relative" @click=${this.goToFeedLink}>
|
|
||||||
<div style="display:flex;gap:10px;margin-bottom:5px">
|
|
||||||
<div class="avatar">
|
|
||||||
${avatarImg}</div> <span class="feed-item-name">${this.resource.name}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p>${this.feedItem.title}</p>
|
|
||||||
</div>
|
|
||||||
<div class="app-name">
|
|
||||||
<div class="avatarApp">
|
|
||||||
${avatarImgApp}
|
|
||||||
</div>
|
|
||||||
<message-time
|
|
||||||
timestamp=${this
|
|
||||||
.resource
|
|
||||||
.created}
|
|
||||||
></message-time>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define('feed-item', FeedItem);
|
|
@ -1,146 +1,42 @@
|
|||||||
// popover-component.js
|
import { html, LitElement } from 'lit'
|
||||||
import {css, html, LitElement} from 'lit';
|
import { connect } from 'pwa-helpers'
|
||||||
import {createPopper} from '@popperjs/core';
|
import { store } from '../../store'
|
||||||
import '@material/mwc-icon';
|
import { createPopper } from '@popperjs/core'
|
||||||
import {translate} from '../../../translate'
|
import { setNewTab, setSideEffectAction } from '../../redux/app/app-actions'
|
||||||
import {store} from '../../store';
|
import { translate } from '../../../translate'
|
||||||
import {connect} from 'pwa-helpers';
|
import { friendItemActionsStyles } from '../../styles/core-css'
|
||||||
import {setNewTab, setSideEffectAction} from '../../redux/app/app-actions';
|
import ShortUniqueId from 'short-unique-id'
|
||||||
import ShortUniqueId from 'short-unique-id';
|
import '@material/mwc-icon'
|
||||||
|
|
||||||
export class FriendItemActions extends connect(store)(LitElement) {
|
export class FriendItemActions extends connect(store)(LitElement) {
|
||||||
static styles = css`
|
|
||||||
:host {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
background-color: var(--white);
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
padding: 8px;
|
|
||||||
z-index: 10;
|
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
||||||
color: var(--black);
|
|
||||||
max-width: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
float: right;
|
|
||||||
margin-left: 10px;
|
|
||||||
color: var(--black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.send-message-button {
|
|
||||||
font-family: Roboto, sans-serif;
|
|
||||||
letter-spacing: 0.3px;
|
|
||||||
font-weight: 300;
|
|
||||||
padding: 8px 5px;
|
|
||||||
border-radius: 3px;
|
|
||||||
text-align: center;
|
|
||||||
color: var(--mdc-theme-primary);
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.send-message-button:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: #03a8f485;
|
|
||||||
}
|
|
||||||
.action-parent {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div[tabindex='0']:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
for: { type: String, reflect: true },
|
for: { type: String, reflect: true },
|
||||||
message: { type: String },
|
message: { type: String },
|
||||||
openEditFriend: { attribute: false },
|
openEditFriend: { attribute: false },
|
||||||
name: { type: String },
|
name: { type: String },
|
||||||
closeSidePanel: { attribute: false, type: Object },
|
closeSidePanel: { attribute: false, type: Object }
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [friendItemActionsStyles]
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super()
|
||||||
this.message = '';
|
this.message = ''
|
||||||
this.nodeUrl = this.getNodeUrl();
|
this.nodeUrl = this.getNodeUrl()
|
||||||
this.uid = new ShortUniqueId();
|
this.uid = new ShortUniqueId()
|
||||||
this.getUserAddress = this.getUserAddress.bind(this);
|
this.getUserAddress = this.getUserAddress.bind(this)
|
||||||
}
|
|
||||||
getNodeUrl() {
|
|
||||||
const myNode =
|
|
||||||
store.getState().app.nodeConfig.knownNodes[
|
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
]
|
|
||||||
|
|
||||||
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated() {
|
|
||||||
// We'll defer the popper attachment to the openPopover() method to ensure target availability
|
|
||||||
}
|
|
||||||
|
|
||||||
attachToTarget(target) {
|
|
||||||
if (!this.popperInstance && target) {
|
|
||||||
this.popperInstance = createPopper(target, this, {
|
|
||||||
placement: 'bottom',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
openPopover(target) {
|
|
||||||
this.attachToTarget(target);
|
|
||||||
this.style.display = 'block';
|
|
||||||
setTimeout(() => {
|
|
||||||
this.shadowRoot.getElementById('parent-div').focus();
|
|
||||||
}, 50);
|
|
||||||
}
|
|
||||||
|
|
||||||
closePopover() {
|
|
||||||
this.style.display = 'none';
|
|
||||||
if (this.popperInstance) {
|
|
||||||
this.popperInstance.destroy();
|
|
||||||
this.popperInstance = null;
|
|
||||||
}
|
|
||||||
this.requestUpdate();
|
|
||||||
}
|
|
||||||
handleBlur() {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.closePopover();
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getUserAddress() {
|
|
||||||
try {
|
|
||||||
const url = `${this.nodeUrl}/names/${this.name}`;
|
|
||||||
const res = await fetch(url);
|
|
||||||
const result = await res.json();
|
|
||||||
if (result.error === 401) {
|
|
||||||
return '';
|
|
||||||
} else {
|
|
||||||
return result.owner;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div id="parent-div" tabindex="0" @blur=${this.handleBlur}>
|
<div id="parent-div" tabindex="0" @blur=${this.handleBlur}>
|
||||||
<span class="close-icon" @click="${this.closePopover}"
|
<span class="close-icon" @click="${this.closePopover}">
|
||||||
><mwc-icon style="color: var(--black)"
|
<mwc-icon style="color: var(--black)">close</mwc-icon>
|
||||||
>close</mwc-icon
|
</span>
|
||||||
></span
|
|
||||||
>
|
|
||||||
<div class="action-parent">
|
<div class="action-parent">
|
||||||
<div
|
<div
|
||||||
class="send-message-button"
|
class="send-message-button"
|
||||||
@ -164,16 +60,15 @@ export class FriendItemActions extends connect(store)(LitElement) {
|
|||||||
myPlugObj: {
|
myPlugObj: {
|
||||||
url: 'q-chat',
|
url: 'q-chat',
|
||||||
domain: 'core',
|
domain: 'core',
|
||||||
page: 'messaging/q-chat/index.html',
|
page: 'q-chat/index.html',
|
||||||
title: 'Q-Chat',
|
title: 'Q-Chat',
|
||||||
icon: 'vaadin:chat',
|
icon: 'vaadin:chat',
|
||||||
mwcicon: 'forum',
|
mwcicon: 'forum',
|
||||||
pluginNumber: 'plugin-qhsyOnpRhT',
|
pluginNumber: 'plugin-qhsyOnpRhT',
|
||||||
menus: [],
|
menus: [],
|
||||||
parent: false,
|
parent: false
|
||||||
},
|
},
|
||||||
|
openExisting: true
|
||||||
openExisting: true,
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
@ -181,8 +76,8 @@ export class FriendItemActions extends connect(store)(LitElement) {
|
|||||||
type: 'openPrivateChat',
|
type: 'openPrivateChat',
|
||||||
data: {
|
data: {
|
||||||
address,
|
address,
|
||||||
name: this.name,
|
name: this.name
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
this.closePopover();
|
this.closePopover();
|
||||||
@ -208,9 +103,9 @@ export class FriendItemActions extends connect(store)(LitElement) {
|
|||||||
icon: 'vaadin:mailbox',
|
icon: 'vaadin:mailbox',
|
||||||
mwcicon: 'mail_outline',
|
mwcicon: 'mail_outline',
|
||||||
menus: [],
|
menus: [],
|
||||||
parent: false,
|
parent: false
|
||||||
},
|
},
|
||||||
openExisting: true,
|
openExisting: true
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
this.closePopover();
|
this.closePopover();
|
||||||
@ -223,26 +118,89 @@ export class FriendItemActions extends connect(store)(LitElement) {
|
|||||||
<div
|
<div
|
||||||
class="send-message-button"
|
class="send-message-button"
|
||||||
@click="${() => {
|
@click="${() => {
|
||||||
const customEvent = new CustomEvent(
|
const customEvent = new CustomEvent('open-visiting-profile', { detail: this.name });
|
||||||
'open-visiting-profile',
|
|
||||||
{
|
|
||||||
detail: this.name,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
window.dispatchEvent(customEvent);
|
window.dispatchEvent(customEvent);
|
||||||
this.closePopover();
|
this.closePopover();
|
||||||
this.closeSidePanel();
|
this.closeSidePanel();
|
||||||
}}"
|
}}"
|
||||||
>
|
>
|
||||||
<mwc-icon style="color: var(--black)"
|
<mwc-icon style="color: var(--black)">person</mwc-icon>
|
||||||
>person</mwc-icon
|
|
||||||
>
|
|
||||||
${translate('profile.profile18')}
|
${translate('profile.profile18')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeUrl() {
|
||||||
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
|
}
|
||||||
|
|
||||||
|
attachToTarget(target) {
|
||||||
|
if (!this.popperInstance && target) {
|
||||||
|
this.popperInstance = createPopper(target, this, {
|
||||||
|
placement: 'bottom'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openPopover(target) {
|
||||||
|
this.attachToTarget(target)
|
||||||
|
this.style.display = 'block'
|
||||||
|
setTimeout(() => {
|
||||||
|
this.shadowRoot.getElementById('parent-div').focus()
|
||||||
|
}, 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
closePopover() {
|
||||||
|
this.style.display = 'none'
|
||||||
|
if (this.popperInstance) {
|
||||||
|
this.popperInstance.destroy()
|
||||||
|
this.popperInstance = null
|
||||||
|
}
|
||||||
|
this.requestUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleBlur() {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.closePopover()
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserAddress() {
|
||||||
|
try {
|
||||||
|
const url = `${this.nodeUrl}/names/${this.name}`
|
||||||
|
const res = await fetch(url)
|
||||||
|
const result = await res.json()
|
||||||
|
if (result.error === 401) {
|
||||||
|
return ''
|
||||||
|
} else {
|
||||||
|
return result.owner
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard functions
|
||||||
|
getApiKey() {
|
||||||
|
const coreNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
return coreNode.apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmptyArray(arr) {
|
||||||
|
if (!arr) { return true }
|
||||||
|
return arr.length === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
round(number) {
|
||||||
|
return (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('friend-item-actions', FriendItemActions);
|
window.customElements.define('friend-item-actions', FriendItemActions)
|
||||||
|
@ -1,476 +1,461 @@
|
|||||||
import {html, LitElement} from 'lit';
|
import { html, LitElement } from 'lit'
|
||||||
import '@material/mwc-icon';
|
import { store } from '../../store'
|
||||||
import './friends-view'
|
import { connect } from 'pwa-helpers'
|
||||||
import {friendsViewStyles} from './friends-view-css';
|
import { translate } from '../../../translate'
|
||||||
import {connect} from 'pwa-helpers';
|
import { friendsViewStyles } from '../../styles/core-css'
|
||||||
import {store} from '../../store';
|
|
||||||
import './feed-item'
|
import './feed-item'
|
||||||
import {translate} from '../../../translate'
|
import './friends-view'
|
||||||
|
import '@material/mwc-icon'
|
||||||
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
||||||
|
|
||||||
|
const perEndpointCount = 20
|
||||||
|
const totalDesiredCount = 100
|
||||||
|
const maxResultsInMemory = 300
|
||||||
|
|
||||||
const perEndpointCount = 20;
|
|
||||||
const totalDesiredCount = 100;
|
|
||||||
const maxResultsInMemory = 300;
|
|
||||||
class FriendsFeed extends connect(store)(LitElement) {
|
class FriendsFeed extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
feed: {type: Array},
|
feed: { type: Array },
|
||||||
setHasNewFeed: {attribute:false},
|
setHasNewFeed: { attribute: false },
|
||||||
isLoading: {type: Boolean},
|
isLoading: { type: Boolean },
|
||||||
hasFetched: {type: Boolean},
|
hasFetched: { type: Boolean },
|
||||||
mySelectedFeeds: {type: Array}
|
mySelectedFeeds: { type: Array }
|
||||||
};
|
}
|
||||||
}
|
|
||||||
constructor(){
|
|
||||||
super()
|
|
||||||
this.feed = []
|
|
||||||
this.feedToRender = []
|
|
||||||
this.nodeUrl = this.getNodeUrl();
|
|
||||||
this.myNode = this.getMyNode();
|
|
||||||
this.endpoints = []
|
|
||||||
this.endpointOffsets = [] // Initialize offsets for each endpoint to 0
|
|
||||||
|
|
||||||
this.loadAndMergeData = this.loadAndMergeData.bind(this)
|
|
||||||
this.hasInitialFetch = false
|
|
||||||
this.observerHandler = this.observerHandler.bind(this);
|
|
||||||
this.elementObserver = this.elementObserver.bind(this)
|
|
||||||
this.mySelectedFeeds = []
|
|
||||||
this.getSchemas = this.getSchemas.bind(this)
|
|
||||||
this.hasFetched = false
|
|
||||||
this._updateFeeds = this._updateFeeds.bind(this)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [friendsViewStyles];
|
return [friendsViewStyles]
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.feed = []
|
||||||
|
this.feedToRender = []
|
||||||
|
this.nodeUrl = this.getNodeUrl()
|
||||||
|
this.myNode = this.getMyNode()
|
||||||
|
this.endpoints = []
|
||||||
|
this.endpointOffsets = [] // Initialize offsets for each endpoint to 0
|
||||||
|
this.loadAndMergeData = this.loadAndMergeData.bind(this)
|
||||||
|
this.hasInitialFetch = false
|
||||||
|
this.observerHandler = this.observerHandler.bind(this)
|
||||||
|
this.elementObserver = this.elementObserver.bind(this)
|
||||||
|
this.mySelectedFeeds = []
|
||||||
|
this.getSchemas = this.getSchemas.bind(this)
|
||||||
|
this.hasFetched = false
|
||||||
|
this._updateFeeds = this._updateFeeds.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<div class="container">
|
||||||
|
<div id="viewElement" class="container-body" style=${"position: relative"}>
|
||||||
|
${this.isLoading ? html`
|
||||||
|
<div style="width:100%;display: flex; justify-content:center">
|
||||||
|
<paper-spinner-lite active></paper-spinner-lite>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
${this.hasFetched && !this.isLoading && this.feed.length === 0 ? html`
|
||||||
|
<div style="width:100%;display: flex; justify-content:center">
|
||||||
|
<p>${translate('friends.friend17')}</p>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
${this.feedToRender.map((item) => {
|
||||||
|
return html`
|
||||||
|
<feed-item
|
||||||
|
.resource=${item}
|
||||||
|
appName=${'Q-Blog'}
|
||||||
|
link=${item.link}
|
||||||
|
>
|
||||||
|
</feed-item>
|
||||||
|
`
|
||||||
|
})}
|
||||||
|
<div id="downObserver"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async firstUpdated() {
|
||||||
|
this.viewElement = this.shadowRoot.getElementById('viewElement')
|
||||||
|
this.downObserverElement = this.shadowRoot.getElementById('downObserver')
|
||||||
|
this.elementObserver()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await new Promise((res) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
res()
|
||||||
|
}, 5000)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.mySelectedFeeds.length === 0) {
|
||||||
|
await this.getEndpoints()
|
||||||
|
await this.loadAndMergeData()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getFeedOnInterval()
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getNodeUrl() {
|
getNodeUrl() {
|
||||||
const myNode =
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
store.getState().app.nodeConfig.knownNodes[
|
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
]
|
|
||||||
|
|
||||||
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
}
|
}
|
||||||
|
|
||||||
getMyNode() {
|
getMyNode() {
|
||||||
return store.getState().app.nodeConfig.knownNodes[
|
return store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateFeeds(event) {
|
_updateFeeds(event) {
|
||||||
this.mySelectedFeeds = event.detail
|
this.mySelectedFeeds = event.detail
|
||||||
this.reFetchFeedData()
|
this.reFetchFeedData()
|
||||||
this.requestUpdate()
|
this.requestUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback()
|
super.connectedCallback()
|
||||||
window.addEventListener('friends-my-selected-feeds-event', this._updateFeeds) }
|
window.addEventListener('friends-my-selected-feeds-event', this._updateFeeds)
|
||||||
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
window.removeEventListener('friends-my-selected-feeds-event', this._updateFeeds)
|
window.removeEventListener('friends-my-selected-feeds-event', this._updateFeeds)
|
||||||
super.disconnectedCallback()
|
super.disconnectedCallback()
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSchemas(){
|
async getSchemas() {
|
||||||
this.mySelectedFeeds = JSON.parse(localStorage.getItem('friends-my-selected-feeds') || "[]")
|
this.mySelectedFeeds = JSON.parse(localStorage.getItem('friends-my-selected-feeds') || "[]")
|
||||||
const schemas = this.mySelectedFeeds
|
const schemas = this.mySelectedFeeds
|
||||||
const getAllSchemas = (schemas || []).map(
|
|
||||||
async (schema) => {
|
|
||||||
try {
|
|
||||||
const url = `${this.nodeUrl}/arbitrary/${schema.service}/${schema.name}/${schema.identifier}`;
|
|
||||||
const res = await fetch(url)
|
|
||||||
const data = await res.json()
|
|
||||||
if(data.error) return false
|
|
||||||
return data
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const res = await Promise.all(getAllSchemas);
|
|
||||||
return res.filter((item)=> !!item)
|
|
||||||
}
|
|
||||||
|
|
||||||
getFeedOnInterval(){
|
const getAllSchemas = (schemas || []).map(
|
||||||
let interval = null;
|
async (schema) => {
|
||||||
let stop = false;
|
|
||||||
const getAnswer = async () => {
|
|
||||||
|
|
||||||
if (!stop) {
|
|
||||||
stop = true;
|
|
||||||
try {
|
try {
|
||||||
await this.reFetchFeedData()
|
const url = `${this.nodeUrl}/arbitrary/${schema.service}/${schema.name}/${schema.identifier}`
|
||||||
} catch (error) {}
|
const res = await fetch(url)
|
||||||
stop = false;
|
const data = await res.json()
|
||||||
|
if (data.error) return false
|
||||||
|
return data
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
)
|
||||||
interval = setInterval(getAnswer, 900000);
|
|
||||||
|
|
||||||
}
|
const res = await Promise.all(getAllSchemas)
|
||||||
|
return res.filter((item) => !!item)
|
||||||
async getEndpoints(){
|
|
||||||
const dynamicVars = {
|
|
||||||
|
|
||||||
}
|
|
||||||
const schemas = await this.getSchemas()
|
|
||||||
const friendList = JSON.parse(localStorage.getItem('friends-my-friend-list') || "[]")
|
|
||||||
const names = friendList.map(friend => `name=${friend.name}`).join('&');
|
|
||||||
if(names.length === 0){
|
|
||||||
this.endpoints= []
|
|
||||||
this.endpointOffsets = Array(this.endpoints.length).fill(0);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const baseurl = `${this.nodeUrl}/arbitrary/resources/search?reverse=true&mode=ALL&exactmatchnames=true&${names}`
|
|
||||||
let formEndpoints = []
|
|
||||||
schemas.forEach((schema)=> {
|
|
||||||
const feedData = schema.feed[0]
|
|
||||||
if(feedData){
|
|
||||||
const copyFeedData = {...feedData}
|
|
||||||
const fullUrl = constructUrl(baseurl, copyFeedData.search, dynamicVars);
|
|
||||||
if(fullUrl){
|
|
||||||
formEndpoints.push({
|
|
||||||
url: fullUrl, schemaName: schema.name, schema: copyFeedData
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
})
|
|
||||||
this.endpoints= formEndpoints
|
|
||||||
this.endpointOffsets = Array(this.endpoints.length).fill(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
async firstUpdated(){
|
|
||||||
this.viewElement = this.shadowRoot.getElementById('viewElement');
|
|
||||||
this.downObserverElement =
|
|
||||||
this.shadowRoot.getElementById('downObserver');
|
|
||||||
this.elementObserver();
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
await new Promise((res)=> {
|
|
||||||
setTimeout(() => {
|
|
||||||
res()
|
|
||||||
}, 5000);
|
|
||||||
})
|
|
||||||
if(this.mySelectedFeeds.length === 0){
|
|
||||||
await this.getEndpoints()
|
|
||||||
|
|
||||||
await this.loadAndMergeData();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getFeedOnInterval()
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getMoreFeed(){
|
getFeedOnInterval() {
|
||||||
if(!this.hasInitialFetch) return
|
let interval = null
|
||||||
if(this.feedToRender.length === this.feed.length ) return
|
let stop = false
|
||||||
this.feedToRender = this.feed.slice(0, this.feedToRender.length + 20)
|
|
||||||
this.requestUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
async refresh(){
|
const getAnswer = async () => {
|
||||||
try {
|
if (!stop) {
|
||||||
await this.getEndpoints()
|
stop = true
|
||||||
await this.reFetchFeedData()
|
try {
|
||||||
} catch (error) {
|
await this.reFetchFeedData()
|
||||||
|
} catch (error) { }
|
||||||
|
stop = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
interval = setInterval(getAnswer, 900000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getEndpoints() {
|
||||||
|
const dynamicVars = { }
|
||||||
|
const schemas = await this.getSchemas()
|
||||||
|
const friendList = JSON.parse(localStorage.getItem('friends-my-friend-list') || "[]")
|
||||||
|
const names = friendList.map(friend => `name=${friend.name}`).join('&')
|
||||||
|
|
||||||
|
if (names.length === 0) {
|
||||||
|
this.endpoints = []
|
||||||
|
this.endpointOffsets = Array(this.endpoints.length).fill(0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseurl = `${this.nodeUrl}/arbitrary/resources/search?reverse=true&mode=ALL&exactmatchnames=true&${names}`
|
||||||
|
let formEndpoints = []
|
||||||
|
|
||||||
|
schemas.forEach((schema) => {
|
||||||
|
const feedData = schema.feed[0]
|
||||||
|
if (feedData) {
|
||||||
|
const copyFeedData = { ...feedData }
|
||||||
|
const fullUrl = constructUrl(baseurl, copyFeedData.search, dynamicVars)
|
||||||
|
if (fullUrl) {
|
||||||
|
formEndpoints.push({
|
||||||
|
url: fullUrl, schemaName: schema.name, schema: copyFeedData
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.endpoints = formEndpoints
|
||||||
|
this.endpointOffsets = Array(this.endpoints.length).fill(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
getMoreFeed() {
|
||||||
|
if (!this.hasInitialFetch) return
|
||||||
|
if (this.feedToRender.length === this.feed.length) return
|
||||||
|
this.feedToRender = this.feed.slice(0, this.feedToRender.length + 20)
|
||||||
|
this.requestUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
async refresh() {
|
||||||
|
try {
|
||||||
|
await this.getEndpoints()
|
||||||
|
await this.reFetchFeedData()
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
elementObserver() {
|
elementObserver() {
|
||||||
const options = {
|
const options = {
|
||||||
rootMargin: '0px',
|
rootMargin: '0px',
|
||||||
threshold: 1,
|
threshold: 1
|
||||||
};
|
}
|
||||||
|
|
||||||
// identify an element to observe
|
// identify an element to observe
|
||||||
const elementToObserve = this.downObserverElement;
|
const elementToObserve = this.downObserverElement
|
||||||
|
|
||||||
// passing it a callback function
|
// passing it a callback function
|
||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
this.observerHandler,
|
this.observerHandler,
|
||||||
options
|
options
|
||||||
);
|
)
|
||||||
|
|
||||||
// call `observe()` on that MutationObserver instance,
|
// call `observe()` on that MutationObserver instance,
|
||||||
// passing it the element to observe, and the options object
|
// passing it the element to observe, and the options object
|
||||||
observer.observe(elementToObserve);
|
observer.observe(elementToObserve)
|
||||||
}
|
}
|
||||||
|
|
||||||
observerHandler(entries) {
|
observerHandler(entries) {
|
||||||
if (!entries[0].isIntersecting) {
|
if (!entries[0].isIntersecting) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (this.feedToRender.length < 20) {
|
if (this.feedToRender.length < 20) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
this.getMoreFeed();
|
this.getMoreFeed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchDataFromEndpoint(endpointIndex, count) {
|
async fetchDataFromEndpoint(endpointIndex, count) {
|
||||||
const offset = this.endpointOffsets[endpointIndex];
|
const offset = this.endpointOffsets[endpointIndex]
|
||||||
const url = `${this.endpoints[endpointIndex].url}&limit=${count}&offset=${offset}`;
|
const url = `${this.endpoints[endpointIndex].url}&limit=${count}&offset=${offset}`
|
||||||
const res = await fetch(url)
|
const res = await fetch(url)
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
return data.map((i)=> {
|
|
||||||
return {
|
|
||||||
...this.endpoints[endpointIndex],
|
|
||||||
...i
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
return data.map((i) => {
|
||||||
|
return {
|
||||||
|
...this.endpoints[endpointIndex],
|
||||||
|
...i
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async initialLoad() {
|
||||||
|
let results = []
|
||||||
|
let totalFetched = 0
|
||||||
|
let i = 0
|
||||||
|
let madeProgress = true
|
||||||
|
let exhaustedEndpoints = new Set()
|
||||||
|
|
||||||
async initialLoad() {
|
while (totalFetched < totalDesiredCount && madeProgress) {
|
||||||
let results = [];
|
madeProgress = false
|
||||||
let totalFetched = 0;
|
this.isLoading = true
|
||||||
let i = 0;
|
for (i = 0; i < this.endpoints.length; i++) {
|
||||||
let madeProgress = true;
|
if (exhaustedEndpoints.has(i)) {
|
||||||
let exhaustedEndpoints = new Set();
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
while (totalFetched < totalDesiredCount && madeProgress) {
|
const remainingCount = totalDesiredCount - totalFetched
|
||||||
madeProgress = false;
|
|
||||||
this.isLoading = true
|
|
||||||
for (i = 0; i < this.endpoints.length; i++) {
|
|
||||||
if (exhaustedEndpoints.has(i)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const remainingCount = totalDesiredCount - totalFetched;
|
// If we've already reached the desired count, break
|
||||||
|
if (remainingCount <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// If we've already reached the desired count, break
|
let fetchCount = Math.min(perEndpointCount, remainingCount)
|
||||||
if (remainingCount <= 0) {
|
let data = await this.fetchDataFromEndpoint(i, fetchCount)
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let fetchCount = Math.min(perEndpointCount, remainingCount);
|
// Increment the offset for this endpoint by the number of items fetched
|
||||||
let data = await this.fetchDataFromEndpoint(i, fetchCount);
|
this.endpointOffsets[i] += data.length
|
||||||
|
|
||||||
// Increment the offset for this endpoint by the number of items fetched
|
if (data.length > 0) {
|
||||||
this.endpointOffsets[i] += data.length;
|
madeProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
if (data.length > 0) {
|
if (data.length < fetchCount) {
|
||||||
madeProgress = true;
|
exhaustedEndpoints.add(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.length < fetchCount) {
|
results = results.concat(data)
|
||||||
exhaustedEndpoints.add(i);
|
totalFetched += data.length
|
||||||
}
|
}
|
||||||
|
|
||||||
results = results.concat(data);
|
if (exhaustedEndpoints.size === this.endpoints.length) {
|
||||||
totalFetched += data.length;
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (exhaustedEndpoints.size === this.endpoints.length) {
|
this.isLoading = false
|
||||||
break;
|
this.hasFetched = true
|
||||||
}
|
|
||||||
}
|
|
||||||
this.isLoading = false
|
|
||||||
this.hasFetched = true;
|
|
||||||
// Trim the results if somehow they are over the totalDesiredCount
|
|
||||||
return results.slice(0, totalDesiredCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Trim the results if somehow they are over the totalDesiredCount
|
||||||
|
return results.slice(0, totalDesiredCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
trimDataToLimit(data, limit) {
|
||||||
|
return data.slice(0, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeData(newData, existingData) {
|
||||||
|
const existingIds = new Set(existingData.map(item => item.identifier)) // Assume each item has a unique 'id'
|
||||||
|
const uniqueNewData = newData.filter(item => !existingIds.has(item.identifier))
|
||||||
|
return uniqueNewData.concat(existingData)
|
||||||
|
}
|
||||||
|
|
||||||
|
async addExtraData(data) {
|
||||||
|
let newData = []
|
||||||
|
for (let item of data) {
|
||||||
|
let newItem = {
|
||||||
|
...item,
|
||||||
|
schema: {
|
||||||
|
...item.schema,
|
||||||
|
customParams: { ...item.schema.customParams }
|
||||||
|
|
||||||
trimDataToLimit(data, limit) {
|
}
|
||||||
return data.slice(0, limit);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
mergeData(newData, existingData) {
|
let newResource = {
|
||||||
const existingIds = new Set(existingData.map(item => item.identifier)); // Assume each item has a unique 'id'
|
identifier: newItem.identifier,
|
||||||
const uniqueNewData = newData.filter(item => !existingIds.has(item.identifier));
|
service: newItem.service,
|
||||||
return uniqueNewData.concat(existingData);
|
name: newItem.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newItem.schema) {
|
||||||
|
const resource = newItem
|
||||||
|
|
||||||
async addExtraData(data){
|
let clickValue1 = newItem.schema.click;
|
||||||
let newData = []
|
|
||||||
for (let item of data) {
|
|
||||||
let newItem = {
|
|
||||||
...item,
|
|
||||||
schema: {
|
|
||||||
...item.schema,
|
|
||||||
customParams: {...item.schema.customParams}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let newResource = {
|
|
||||||
identifier: newItem.identifier,
|
|
||||||
service: newItem.service,
|
|
||||||
name: newItem.name
|
|
||||||
}
|
|
||||||
if(newItem.schema){
|
|
||||||
const resource = newItem
|
|
||||||
|
|
||||||
let clickValue1 = newItem.schema.click;
|
|
||||||
|
|
||||||
newItem.link = replacePlaceholders(clickValue1, resource, newItem.schema.customParams)
|
newItem.link = replacePlaceholders(clickValue1, resource, newItem.schema.customParams)
|
||||||
newData.push(newItem)
|
newData.push(newItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newData
|
return newData
|
||||||
|
}
|
||||||
|
|
||||||
}
|
async reFetchFeedData() {
|
||||||
async reFetchFeedData() {
|
// Resetting offsets to start fresh.
|
||||||
// Resetting offsets to start fresh.
|
this.endpointOffsets = Array(this.endpoints.length).fill(0)
|
||||||
this.endpointOffsets = Array(this.endpoints.length).fill(0);
|
await this.getEndpoints()
|
||||||
await this.getEndpoints()
|
const oldIdentifiers = new Set(this.feed.map(item => item.identifier))
|
||||||
const oldIdentifiers = new Set(this.feed.map(item => item.identifier));
|
const newData = await this.initialLoad()
|
||||||
const newData = await this.initialLoad();
|
|
||||||
|
|
||||||
// Filter out items that are already in the feed
|
// Filter out items that are already in the feed
|
||||||
const trulyNewData = newData.filter(item => !oldIdentifiers.has(item.identifier));
|
const trulyNewData = newData.filter(item => !oldIdentifiers.has(item.identifier))
|
||||||
|
|
||||||
if (trulyNewData.length > 0) {
|
if (trulyNewData.length > 0) {
|
||||||
// Adding extra data and merging with old data
|
// Adding extra data and merging with old data
|
||||||
const enhancedNewData = await this.addExtraData(trulyNewData);
|
const enhancedNewData = await this.addExtraData(trulyNewData)
|
||||||
|
|
||||||
// Merge new data with old data immutably
|
// Merge new data with old data immutably
|
||||||
this.feed = [...enhancedNewData, ...this.feed];
|
this.feed = [...enhancedNewData, ...this.feed]
|
||||||
this.feed = this.removeDuplicates(this.feed)
|
this.feed = this.removeDuplicates(this.feed)
|
||||||
this.feed.sort((a, b) => new Date(b.created) - new Date(a.created)); // Sort by timestamp, most recent first
|
this.feed.sort((a, b) => new Date(b.created) - new Date(a.created)) // Sort by timestamp, most recent first
|
||||||
this.feed = this.trimDataToLimit(this.feed, maxResultsInMemory); // Trim to the maximum allowed in memory
|
this.feed = this.trimDataToLimit(this.feed, maxResultsInMemory) // Trim to the maximum allowed in memory
|
||||||
this.feedToRender = this.feed.slice(0, 20);
|
this.feedToRender = this.feed.slice(0, 20)
|
||||||
this.hasInitialFetch = true;
|
this.hasInitialFetch = true
|
||||||
|
|
||||||
const created = trulyNewData[0].created;
|
const created = trulyNewData[0].created
|
||||||
let value = localStorage.getItem('lastSeenFeed');
|
let value = localStorage.getItem('lastSeenFeed')
|
||||||
if (((+value || 0) < created)) {
|
|
||||||
this.setHasNewFeed(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
removeDuplicates(array) {
|
|
||||||
const seenIds = new Set();
|
|
||||||
return array.filter(item => {
|
|
||||||
if (!seenIds.has(item.identifier)) {
|
|
||||||
seenIds.add(item.identifier);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async loadAndMergeData() {
|
|
||||||
let allData = this.feed
|
|
||||||
const newData = await this.initialLoad();
|
|
||||||
allData = await this.addExtraData(newData)
|
|
||||||
allData = this.mergeData(newData, allData);
|
|
||||||
allData.sort((a, b) => new Date(b.created) - new Date(a.created)); // Sort by timestamp, most recent first
|
|
||||||
allData = this.trimDataToLimit(allData, maxResultsInMemory); // Trim to the maximum allowed in memory
|
|
||||||
allData = this.removeDuplicates(allData)
|
|
||||||
this.feed = [...allData]
|
|
||||||
this.feedToRender = this.feed.slice(0,20)
|
|
||||||
this.hasInitialFetch = true
|
|
||||||
if(allData.length > 0){
|
|
||||||
const created = allData[0].created
|
|
||||||
let value = localStorage.getItem('lastSeenFeed')
|
|
||||||
if (((+value || 0) < created)) {
|
if (((+value || 0) < created)) {
|
||||||
this.setHasNewFeed(true)
|
this.setHasNewFeed(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`
|
|
||||||
<div class="container">
|
|
||||||
<div id="viewElement" class="container-body" style=${"position: relative"}>
|
|
||||||
${this.isLoading ? html`
|
|
||||||
<div style="width:100%;display: flex; justify-content:center">
|
|
||||||
<paper-spinner-lite active></paper-spinner-lite>
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
${this.hasFetched && !this.isLoading && this.feed.length === 0 ? html`
|
|
||||||
<div style="width:100%;display: flex; justify-content:center">
|
|
||||||
<p>${translate('friends.friend17')}</p>
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
${this.feedToRender.map((item) => {
|
|
||||||
return html`<feed-item
|
|
||||||
.resource=${item}
|
|
||||||
appName=${'Q-Blog'}
|
|
||||||
link=${item.link}
|
|
||||||
></feed-item>`;
|
|
||||||
})}
|
|
||||||
<div id="downObserver"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeDuplicates(array) {
|
||||||
|
const seenIds = new Set()
|
||||||
|
return array.filter(item => {
|
||||||
|
if (!seenIds.has(item.identifier)) {
|
||||||
|
seenIds.add(item.identifier)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadAndMergeData() {
|
||||||
|
let allData = this.feed
|
||||||
|
const newData = await this.initialLoad();
|
||||||
|
allData = await this.addExtraData(newData)
|
||||||
|
allData = this.mergeData(newData, allData);
|
||||||
|
allData.sort((a, b) => new Date(b.created) - new Date(a.created)); // Sort by timestamp, most recent first
|
||||||
|
allData = this.trimDataToLimit(allData, maxResultsInMemory); // Trim to the maximum allowed in memory
|
||||||
|
allData = this.removeDuplicates(allData)
|
||||||
|
this.feed = [...allData]
|
||||||
|
this.feedToRender = this.feed.slice(0, 20)
|
||||||
|
this.hasInitialFetch = true
|
||||||
|
if (allData.length > 0) {
|
||||||
|
const created = allData[0].created
|
||||||
|
let value = localStorage.getItem('lastSeenFeed')
|
||||||
|
if (((+value || 0) < created)) {
|
||||||
|
this.setHasNewFeed(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard functions
|
||||||
|
getApiKey() {
|
||||||
|
const coreNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
return coreNode.apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmptyArray(arr) {
|
||||||
|
if (!arr) { return true }
|
||||||
|
return arr.length === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
round(number) {
|
||||||
|
return (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('friends-feed', FriendsFeed);
|
window.customElements.define('friends-feed', FriendsFeed)
|
||||||
|
|
||||||
export function substituteDynamicVar(value, dynamicVars) {
|
export function substituteDynamicVar(value, dynamicVars) {
|
||||||
if (typeof value !== 'string') return value;
|
if (typeof value !== 'string') return value
|
||||||
|
const pattern = /\$\$\{([a-zA-Z0-9_]+)\}\$\$/g // Adjusted pattern to capture $${name}$$ with curly braces
|
||||||
const pattern = /\$\$\{([a-zA-Z0-9_]+)\}\$\$/g; // Adjusted pattern to capture $${name}$$ with curly braces
|
return value.replace(pattern, (match, p1) => {
|
||||||
|
return dynamicVars[p1] !== undefined ? dynamicVars[p1] : match
|
||||||
return value.replace(pattern, (match, p1) => {
|
})
|
||||||
return dynamicVars[p1] !== undefined ? dynamicVars[p1] : match;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function constructUrl(base, search, dynamicVars) {
|
export function constructUrl(base, search, dynamicVars) {
|
||||||
let queryStrings = [];
|
let queryStrings = []
|
||||||
|
for (const [key, value] of Object.entries(search)) {
|
||||||
for (const [key, value] of Object.entries(search)) {
|
const substitutedValue = substituteDynamicVar(value, dynamicVars)
|
||||||
const substitutedValue = substituteDynamicVar(value, dynamicVars);
|
queryStrings.push(`${key}=${encodeURIComponent(substitutedValue)}`)
|
||||||
queryStrings.push(`${key}=${encodeURIComponent(substitutedValue)}`);
|
}
|
||||||
}
|
return queryStrings.length > 0 ? `${base}&${queryStrings.join('&')}` : base
|
||||||
|
|
||||||
return queryStrings.length > 0 ? `${base}&${queryStrings.join('&')}` : base;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function replacePlaceholders(template, resource, customParams) {
|
export function replacePlaceholders(template, resource, customParams) {
|
||||||
const dataSource = { resource, customParams };
|
const dataSource = { resource, customParams }
|
||||||
|
return template.replace(/\$\$\{(.*?)\}\$\$/g, (match, p1) => {
|
||||||
return template.replace(/\$\$\{(.*?)\}\$\$/g, (match, p1) => {
|
const keys = p1.split('.')
|
||||||
const keys = p1.split('.');
|
let value = dataSource
|
||||||
let value = dataSource;
|
for (let key of keys) {
|
||||||
|
if (value[key] !== undefined) {
|
||||||
for (let key of keys) {
|
value = value[key]
|
||||||
if (value[key] !== undefined) {
|
} else {
|
||||||
value = value[key];
|
return match // Return placeholder unchanged
|
||||||
} else {
|
}
|
||||||
return match; // Return placeholder unchanged
|
}
|
||||||
}
|
return value
|
||||||
}
|
})
|
||||||
return value;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,68 +1,43 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
|
import { connect } from 'pwa-helpers'
|
||||||
|
import { store } from '../../store'
|
||||||
|
import { translate } from '../../../translate'
|
||||||
|
import { friendsSidePanelParentStyles } from '../../styles/core-css'
|
||||||
|
import './friends-side-panel'
|
||||||
import '@material/mwc-icon'
|
import '@material/mwc-icon'
|
||||||
import './friends-side-panel.js'
|
|
||||||
import '@vaadin/tooltip'
|
import '@vaadin/tooltip'
|
||||||
import {translate} from '../../../translate'
|
|
||||||
|
|
||||||
class FriendsSidePanelParent extends LitElement {
|
class FriendsSidePanelParent extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
isOpen: {type: Boolean},
|
isOpen: { type: Boolean },
|
||||||
hasNewFeed: {type: Boolean}
|
hasNewFeed: { type: Boolean }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [friendsSidePanelParentStyles]
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.isOpen = false
|
this.isOpen = false
|
||||||
this.hasNewFeed = false
|
this.hasNewFeed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 16px;
|
|
||||||
border-bottom: 1px solid #e0e0e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
.close {
|
|
||||||
visibility: hidden;
|
|
||||||
position: fixed;
|
|
||||||
z-index: -100;
|
|
||||||
right: -1000px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.parent-side-panel {
|
|
||||||
transform: translateX(100%); /* start from outside the right edge */
|
|
||||||
transition: transform 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
.parent-side-panel.open {
|
|
||||||
transform: translateX(0); /* slide in to its original position */
|
|
||||||
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
setHasNewFeed(val){
|
|
||||||
this.hasNewFeed = val
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<mwc-icon
|
<mwc-icon
|
||||||
id="friends-icon"
|
id="friends-icon"
|
||||||
@click=${()=> {
|
@click=${() => {
|
||||||
this.isOpen = !this.isOpen
|
this.isOpen = !this.isOpen
|
||||||
if(this.isOpen && this.hasNewFeed) {
|
if (this.isOpen && this.hasNewFeed) {
|
||||||
localStorage.setItem('lastSeenFeed', Date.now())
|
localStorage.setItem('lastSeenFeed', Date.now())
|
||||||
this.hasNewFeed = false
|
this.hasNewFeed = false
|
||||||
this.shadowRoot.querySelector("friends-side-panel").selected = 'feed'
|
this.shadowRoot.querySelector("friends-side-panel").selected = 'feed'
|
||||||
}
|
}
|
||||||
}} style="color: ${this.hasNewFeed ? 'green' : 'var(--black)'}; cursor:pointer;user-select:none"
|
}}
|
||||||
|
style="color: ${this.hasNewFeed ? 'green' : 'var(--black)'}; cursor:pointer;user-select:none"
|
||||||
>
|
>
|
||||||
group
|
group
|
||||||
</mwc-icon>
|
</mwc-icon>
|
||||||
@ -72,12 +47,33 @@ class FriendsSidePanelParent extends LitElement {
|
|||||||
hover-delay=${400}
|
hover-delay=${400}
|
||||||
hide-delay=${1}
|
hide-delay=${1}
|
||||||
text=${translate('friends.friend12')}
|
text=${translate('friends.friend12')}
|
||||||
>
|
></vaadin-tooltip>
|
||||||
</vaadin-tooltip>
|
<friends-side-panel .setHasNewFeed=${(val) => this.setHasNewFeed(val)} ?isOpen=${this.isOpen} .setIsOpen=${(val) => this.isOpen = val}></friends-side-panel>
|
||||||
<friends-side-panel .setHasNewFeed=${(val)=> this.setHasNewFeed(val)} ?isOpen=${this.isOpen} .setIsOpen=${(val)=> this.isOpen = val}></friends-side-panel>
|
|
||||||
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
setHasNewFeed(val) {
|
||||||
|
this.hasNewFeed = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard functions
|
||||||
|
getApiKey() {
|
||||||
|
const coreNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
return coreNode.apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmptyArray(arr) {
|
||||||
|
if (!arr) { return true }
|
||||||
|
return arr.length === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
round(number) {
|
||||||
|
return (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('friends-side-panel-parent', FriendsSidePanelParent)
|
window.customElements.define('friends-side-panel-parent', FriendsSidePanelParent)
|
@ -1,158 +1,94 @@
|
|||||||
import {css, html, LitElement} from 'lit';
|
import { html, LitElement } from 'lit'
|
||||||
import '@material/mwc-icon';
|
import { connect } from 'pwa-helpers'
|
||||||
|
import { store } from '../../store'
|
||||||
|
import { translate } from '../../../translate'
|
||||||
|
import { friendsSidePanelStyles } from '../../styles/core-css'
|
||||||
import './friends-view'
|
import './friends-view'
|
||||||
import './friends-feed'
|
import './friends-feed'
|
||||||
import {translate} from '../../../translate'
|
import '@material/mwc-icon'
|
||||||
|
|
||||||
class FriendsSidePanel extends LitElement {
|
class FriendsSidePanel extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
setIsOpen: { attribute: false},
|
setIsOpen: { attribute: false },
|
||||||
isOpen: {type: Boolean},
|
isOpen: { type: Boolean },
|
||||||
selected: {type: String},
|
selected: { type: String },
|
||||||
setHasNewFeed: {attribute: false},
|
setHasNewFeed: { attribute: false },
|
||||||
closeSidePanel: {attribute: false, type: Object},
|
closeSidePanel: { attribute: false, type: Object },
|
||||||
openSidePanel: {attribute: false, type: Object}
|
openSidePanel: { attribute: false, type: Object }
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(){
|
static get styles() {
|
||||||
|
return [friendsSidePanelStyles]
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.selected = 'friends'
|
this.selected = 'friends'
|
||||||
this.closeSidePanel = this.closeSidePanel.bind(this)
|
this.closeSidePanel = this.closeSidePanel.bind(this)
|
||||||
this.openSidePanel = this.openSidePanel.bind(this)
|
this.openSidePanel = this.openSidePanel.bind(this)
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
position: fixed;
|
|
||||||
top: 55px;
|
|
||||||
right: 0px;
|
|
||||||
width: 420px;
|
|
||||||
max-width: 95%;
|
|
||||||
height: calc(100vh - 55px);
|
|
||||||
background-color: var(--white);
|
|
||||||
border-left: 1px solid rgb(224, 224, 224);
|
|
||||||
z-index: 1;
|
|
||||||
transform: translateX(100%); /* start from outside the right edge */
|
|
||||||
transition: transform 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
:host([isOpen]) {
|
|
||||||
transform: unset; /* slide in to its original position */
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 16px;
|
|
||||||
border-bottom: 1px solid #e0e0e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 16px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-grow: 1;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
.content::-webkit-scrollbar-track {
|
|
||||||
background-color: whitesmoke;
|
|
||||||
border-radius: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content::-webkit-scrollbar {
|
|
||||||
width: 12px;
|
|
||||||
border-radius: 7px;
|
|
||||||
background-color: whitesmoke;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content::-webkit-scrollbar-thumb {
|
|
||||||
background-color: rgb(180, 176, 176);
|
|
||||||
border-radius: 7px;
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
.parent {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active {
|
|
||||||
font-size: 16px;
|
|
||||||
background: var(--black);
|
|
||||||
color: var(--white);
|
|
||||||
padding: 5px;
|
|
||||||
border-radius: 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.default {
|
|
||||||
font-size: 16px;
|
|
||||||
color: var(--black);
|
|
||||||
padding: 5px;
|
|
||||||
border-radius: 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.default-content {
|
|
||||||
visibility: hidden;
|
|
||||||
position: absolute;
|
|
||||||
z-index: -50;
|
|
||||||
}
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
refreshFeed(){
|
|
||||||
|
|
||||||
this.shadowRoot.querySelector('friends-feed').refresh()
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
closeSidePanel(){
|
|
||||||
this.setIsOpen(false)
|
|
||||||
}
|
|
||||||
openSidePanel(){
|
|
||||||
this.setIsOpen(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div class="parent">
|
<div class="parent">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div style="display:flex;align-items:center;gap:10px">
|
<div style="display:flex;align-items:center;gap:10px">
|
||||||
<span @click=${()=> this.selected = 'friends'} class="${this.selected === 'friends' ? 'active' : 'default'}">${translate('friends.friend12')}</span>
|
<span @click=${() => this.selected = 'friends'} class="${this.selected === 'friends' ? 'active' : 'default'}">${translate('friends.friend12')}</span>
|
||||||
<span @click=${()=> this.selected = 'feed'} class="${this.selected === 'feed' ? 'active' : 'default'}">${translate('friends.friend13')}</span>
|
<span @click=${() => this.selected = 'feed'} class="${this.selected === 'feed' ? 'active' : 'default'}">${translate('friends.friend13')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;gap:15px;align-items:center">
|
<div style="display:flex;gap:15px;align-items:center">
|
||||||
<mwc-icon @click=${()=> {
|
<mwc-icon @click=${() => { this.refreshFeed(); }} style="color: var(--black); cursor:pointer;">
|
||||||
this.refreshFeed()
|
refresh
|
||||||
}} style="color: var(--black); cursor:pointer;">refresh</mwc-icon>
|
</mwc-icon>
|
||||||
<mwc-icon style="cursor:pointer" @click=${()=> {
|
<mwc-icon style="cursor:pointer" @click=${() => { this.setIsOpen(false); }}>
|
||||||
this.setIsOpen(false)
|
close
|
||||||
}}>close</mwc-icon>
|
</mwc-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="${this.selected === 'friends' ? 'active-content' : 'default-content'}">
|
|
||||||
<friends-view .openSidePanel=${this.openSidePanel} .closeSidePanel=${this.closeSidePanel} .refreshFeed=${()=>this.refreshFeed()}></friends-view>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="${this.selected === 'feed' ? 'active-content' : 'default-content'}">
|
<div class="content">
|
||||||
<friends-feed .setHasNewFeed=${(val)=> this.setHasNewFeed(val)}></friends-feed>
|
<div class="${this.selected === 'friends' ? 'active-content' : 'default-content'}">
|
||||||
|
<friends-view .openSidePanel=${this.openSidePanel} .closeSidePanel=${this.closeSidePanel} .refreshFeed=${() => this.refreshFeed()}></friends-view>
|
||||||
|
</div>
|
||||||
|
<div class="${this.selected === 'feed' ? 'active-content' : 'default-content'}">
|
||||||
|
<friends-feed .setHasNewFeed=${(val) => this.setHasNewFeed(val)}></friends-feed>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
`
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshFeed() {
|
||||||
|
this.shadowRoot.querySelector('friends-feed').refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
closeSidePanel() {
|
||||||
|
this.setIsOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
openSidePanel() {
|
||||||
|
this.setIsOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard functions
|
||||||
|
getApiKey() {
|
||||||
|
const coreNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
return coreNode.apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmptyArray(arr) {
|
||||||
|
if (!arr) { return true }
|
||||||
|
return arr.length === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
round(number) {
|
||||||
|
return (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('friends-side-panel', FriendsSidePanel);
|
window.customElements.define('friends-side-panel', FriendsSidePanel)
|
@ -1,182 +0,0 @@
|
|||||||
import {css} from 'lit'
|
|
||||||
|
|
||||||
export const friendsViewStyles = css`
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.top-bar-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
height: 18px;
|
|
||||||
width: 18px;
|
|
||||||
transition: 0.2s all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-bar-icon:hover {
|
|
||||||
color: var(--black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-button {
|
|
||||||
font-family: Roboto, sans-serif;
|
|
||||||
font-size: 16px;
|
|
||||||
color: var(--mdc-theme-primary);
|
|
||||||
background-color: transparent;
|
|
||||||
padding: 8px 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
border: none;
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-row {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
height: 50px;
|
|
||||||
flex:0
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-body {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-grow: 1;
|
|
||||||
margin-top: 5px;
|
|
||||||
padding: 0px 6px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-body::-webkit-scrollbar-track {
|
|
||||||
background-color: whitesmoke;
|
|
||||||
border-radius: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-body::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
border-radius: 7px;
|
|
||||||
background-color: whitesmoke;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-body::-webkit-scrollbar-thumb {
|
|
||||||
background-color: rgb(180, 176, 176);
|
|
||||||
border-radius: 7px;
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-body::-webkit-scrollbar-thumb:hover {
|
|
||||||
background-color: rgb(148, 146, 146);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
color: var(--black);
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-right-panel-label {
|
|
||||||
font-family: Montserrat, sans-serif;
|
|
||||||
color: var(--group-header);
|
|
||||||
padding: 5px;
|
|
||||||
font-size: 13px;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.group-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-start;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.group-name {
|
|
||||||
font-family: Raleway, sans-serif;
|
|
||||||
font-size: 20px;
|
|
||||||
color: var(--chat-bubble-msg-color);
|
|
||||||
text-align: center;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.group-description {
|
|
||||||
font-family: Roboto, sans-serif;
|
|
||||||
color: var(--chat-bubble-msg-color);
|
|
||||||
letter-spacing: 0.3px;
|
|
||||||
font-weight: 300;
|
|
||||||
font-size: 14px;
|
|
||||||
margin-top: 15px;
|
|
||||||
word-break: break-word;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.group-subheader {
|
|
||||||
font-family: Montserrat, sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
color: var(--chat-bubble-msg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.group-data {
|
|
||||||
font-family: Roboto, sans-serif;
|
|
||||||
letter-spacing: 0.3px;
|
|
||||||
font-weight: 300;
|
|
||||||
font-size: 14px;
|
|
||||||
color: var(--chat-bubble-msg-color);
|
|
||||||
}
|
|
||||||
.search-results-div {
|
|
||||||
position: absolute;
|
|
||||||
top: 25px;
|
|
||||||
right: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name-input {
|
|
||||||
width: 100%;
|
|
||||||
outline: 0;
|
|
||||||
border-width: 0 0 2px;
|
|
||||||
border-color: var(--mdc-theme-primary);
|
|
||||||
background-color: transparent;
|
|
||||||
padding: 10px;
|
|
||||||
font-family: Roboto, sans-serif;
|
|
||||||
font-size: 15px;
|
|
||||||
color: var(--chat-bubble-msg-color);
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name-input::selection {
|
|
||||||
background-color: var(--mdc-theme-primary);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name-input::placeholder {
|
|
||||||
opacity: 0.9;
|
|
||||||
color: var(--black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-field {
|
|
||||||
width: 100%;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-icon {
|
|
||||||
position: absolute;
|
|
||||||
right: 3px;
|
|
||||||
color: var(--chat-bubble-msg-color);
|
|
||||||
transition: hover 0.3s ease-in-out;
|
|
||||||
background: none;
|
|
||||||
border-radius: 50%;
|
|
||||||
padding: 6px 3px;
|
|
||||||
font-size: 21px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-icon:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background: #d7d7d75c;
|
|
||||||
}
|
|
||||||
`
|
|
@ -1,22 +1,20 @@
|
|||||||
import {html, LitElement} from 'lit';
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers';
|
import { store } from '../../store'
|
||||||
|
import { connect } from 'pwa-helpers'
|
||||||
import '@material/mwc-button';
|
import { parentEpml } from '../show-plugin'
|
||||||
import '@material/mwc-dialog';
|
import { translate } from '../../../translate'
|
||||||
import '@polymer/paper-spinner/paper-spinner-lite.js';
|
import { friendsViewStyles } from '../../styles/core-css'
|
||||||
import '@polymer/paper-progress/paper-progress.js';
|
import './add-friends-modal'
|
||||||
import '@material/mwc-icon';
|
import './ChatSideNavHeads'
|
||||||
|
import '../../../../plugins/plugins/core/components/ChatSearchResults'
|
||||||
|
import '@material/mwc-button'
|
||||||
|
import '@material/mwc-dialog'
|
||||||
|
import '@material/mwc-icon'
|
||||||
|
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
||||||
|
import '@polymer/paper-progress/paper-progress.js'
|
||||||
import '@vaadin/icon'
|
import '@vaadin/icon'
|
||||||
import '@vaadin/icons'
|
import '@vaadin/icons'
|
||||||
import '@vaadin/button';
|
import '@vaadin/button'
|
||||||
import './ChatSideNavHeads';
|
|
||||||
import '../../../../plugins/plugins/core/components/ChatSearchResults'
|
|
||||||
import './add-friends-modal'
|
|
||||||
|
|
||||||
import {translate,} from '../../../translate'
|
|
||||||
import {store} from '../../store';
|
|
||||||
import {friendsViewStyles} from './friends-view-css';
|
|
||||||
import {parentEpml} from '../show-plugin';
|
|
||||||
|
|
||||||
class FriendsView extends connect(store)(LitElement) {
|
class FriendsView extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@ -29,94 +27,159 @@ class FriendsView extends connect(store)(LitElement) {
|
|||||||
setUserName: { attribute: false },
|
setUserName: { attribute: false },
|
||||||
friendList: { type: Array },
|
friendList: { type: Array },
|
||||||
userSelected: { type: Object },
|
userSelected: { type: Object },
|
||||||
isLoading: {type: Boolean},
|
isLoading: { type: Boolean },
|
||||||
userFoundModalOpen: {type: Boolean},
|
userFoundModalOpen: { type: Boolean },
|
||||||
userFound: { type: Array},
|
userFound: { type: Array },
|
||||||
isOpenAddFriendsModal: {type: Boolean},
|
isOpenAddFriendsModal: { type: Boolean },
|
||||||
editContent: {type: Object},
|
editContent: { type: Object },
|
||||||
mySelectedFeeds: {type: Array},
|
mySelectedFeeds: { type: Array },
|
||||||
refreshFeed: {attribute: false},
|
refreshFeed: { attribute: false },
|
||||||
closeSidePanel: {attribute: false, type: Object},
|
closeSidePanel: { attribute: false, type: Object },
|
||||||
openSidePanel: {attribute:false, type: Object}
|
openSidePanel: { attribute: false, type: Object }
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [friendsViewStyles];
|
return [friendsViewStyles]
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super()
|
||||||
this.error = false;
|
this.error = false
|
||||||
this.observerHandler = this.observerHandler.bind(this);
|
this.observerHandler = this.observerHandler.bind(this)
|
||||||
this.viewElement = '';
|
this.viewElement = ''
|
||||||
this.downObserverElement = '';
|
this.downObserverElement = ''
|
||||||
this.myAddress =
|
this.myAddress = store.getState().app.selectedAddress.address
|
||||||
window.parent.reduxStore.getState().app.selectedAddress.address;
|
this.errorMessage = ''
|
||||||
this.errorMessage = '';
|
this.successMessage = ''
|
||||||
this.successMessage = '';
|
this.friendList = []
|
||||||
this.friendList = [];
|
this.userSelected = {}
|
||||||
this.userSelected = {};
|
this.isLoading = false
|
||||||
this.isLoading = false;
|
|
||||||
this.userFoundModalOpen = false
|
this.userFoundModalOpen = false
|
||||||
this.userFound = [];
|
this.userFound = []
|
||||||
this.nodeUrl = this.getNodeUrl();
|
this.nodeUrl = this.getNodeUrl()
|
||||||
this.myNode = this.getMyNode();
|
this.myNode = this.getMyNode()
|
||||||
this.isOpenAddFriendsModal = false
|
this.isOpenAddFriendsModal = false
|
||||||
this.editContent = null
|
this.editContent = null
|
||||||
this.addToFriendList = this.addToFriendList.bind(this)
|
this.addToFriendList = this.addToFriendList.bind(this)
|
||||||
this._updateFriends = this._updateFriends.bind(this)
|
this._updateFriends = this._updateFriends.bind(this)
|
||||||
this._updateFeed = this._updateFeed.bind(this)
|
this._updateFeed = this._updateFeed.bind(this)
|
||||||
this._addFriend = this._addFriend.bind(this)
|
this._addFriend = this._addFriend.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<div class="container">
|
||||||
|
<div id="viewElement" class="container-body" style=${"position: relative"}>
|
||||||
|
<p class="group-name">My Friends</p>
|
||||||
|
<div class="search-field">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="name-input"
|
||||||
|
?disabled=${this.isLoading}
|
||||||
|
id="sendTo"
|
||||||
|
placeholder="${translate("friends.friend1")}"
|
||||||
|
value=${this.userSelected.name ? this.userSelected.name : ''}
|
||||||
|
@keypress=${(e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.userSearch()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<vaadin-icon
|
||||||
|
@click=${this.userSearch}
|
||||||
|
slot="icon"
|
||||||
|
icon="vaadin:search"
|
||||||
|
class="search-icon"
|
||||||
|
>
|
||||||
|
</vaadin-icon>
|
||||||
|
</div>
|
||||||
|
<div class="search-results-div">
|
||||||
|
<chat-search-results
|
||||||
|
.onClickFunc=${(result) => {
|
||||||
|
this.userSelected = result;
|
||||||
|
this.isOpenAddFriendsModal = true
|
||||||
|
this.userFound = [];
|
||||||
|
this.userFoundModalOpen = false;
|
||||||
|
}}
|
||||||
|
.closeFunc=${() => {
|
||||||
|
this.userFoundModalOpen = false;
|
||||||
|
this.userFound = [];
|
||||||
|
}}
|
||||||
|
.searchResults=${this.userFound}
|
||||||
|
?isOpen=${this.userFoundModalOpen}
|
||||||
|
?loading=${this.isLoading}
|
||||||
|
>
|
||||||
|
</chat-search-results>
|
||||||
|
</div>
|
||||||
|
${this.friendList.map((item) => {
|
||||||
|
return html`
|
||||||
|
<chat-side-nav-heads
|
||||||
|
activeChatHeadUrl=""
|
||||||
|
.setActiveChatHeadUrl=${(val) => { }}
|
||||||
|
.chatInfo=${item}
|
||||||
|
.openEditFriend=${(val) => this.openEditFriend(val)}
|
||||||
|
.closeSidePanel=${this.closeSidePanel}
|
||||||
|
>
|
||||||
|
</chat-side-nav-heads>
|
||||||
|
`
|
||||||
|
})}
|
||||||
|
<div id="downObserver"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<add-friends-modal
|
||||||
|
?isOpen=${this.isOpenAddFriendsModal}
|
||||||
|
.setIsOpen=${(val) => {
|
||||||
|
this.isOpenAddFriendsModal = val
|
||||||
|
}}
|
||||||
|
.userSelected=${this.userSelected}
|
||||||
|
.onSubmit=${(val, isRemove) => this.addToFriendList(val, isRemove)}
|
||||||
|
.editContent=${this.editContent}
|
||||||
|
.onClose=${() => this.onClose()}
|
||||||
|
.mySelectedFeeds=${this.mySelectedFeeds}
|
||||||
|
>
|
||||||
|
</add-friends-modal>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
this.viewElement = this.shadowRoot.getElementById('viewElement')
|
||||||
|
this.downObserverElement = this.shadowRoot.getElementById('downObserver')
|
||||||
|
this.elementObserver()
|
||||||
|
this.mySelectedFeeds = JSON.parse(localStorage.getItem('friends-my-selected-feeds') || "[]")
|
||||||
|
this.friendList = JSON.parse(localStorage.getItem('friends-my-friend-list') || "[]")
|
||||||
}
|
}
|
||||||
|
|
||||||
getNodeUrl() {
|
getNodeUrl() {
|
||||||
const myNode =
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
store.getState().app.nodeConfig.knownNodes[
|
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
]
|
|
||||||
|
|
||||||
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
}
|
}
|
||||||
|
|
||||||
getMyNode() {
|
getMyNode() {
|
||||||
return store.getState().app.nodeConfig.knownNodes[
|
return store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getMoreFriends() {}
|
getMoreFriends() { }
|
||||||
|
|
||||||
firstUpdated() {
|
|
||||||
this.viewElement = this.shadowRoot.getElementById('viewElement');
|
|
||||||
this.downObserverElement =
|
|
||||||
this.shadowRoot.getElementById('downObserver');
|
|
||||||
this.elementObserver();
|
|
||||||
this.mySelectedFeeds = JSON.parse(localStorage.getItem('friends-my-selected-feeds') || "[]")
|
|
||||||
this.friendList = JSON.parse(localStorage.getItem('friends-my-friend-list') || "[]")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateFriends(event) {
|
_updateFriends(event) {
|
||||||
this.friendList = event.detail
|
this.friendList = event.detail
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateFeed(event) {
|
_updateFeed(event) {
|
||||||
this.mySelectedFeeds = event.detail
|
this.mySelectedFeeds = event.detail
|
||||||
this.requestUpdate()
|
this.requestUpdate()
|
||||||
}
|
}
|
||||||
_addFriend(event){
|
|
||||||
const name = event.detail;
|
|
||||||
const findFriend = this.friendList.find((friend)=> friend.name === name)
|
|
||||||
if(findFriend){
|
|
||||||
this.editContent = {...findFriend, mySelectedFeeds: this.mySelectedFeeds}
|
|
||||||
this.userSelected = findFriend;
|
|
||||||
|
|
||||||
|
_addFriend(event) {
|
||||||
|
const name = event.detail;
|
||||||
|
const findFriend = this.friendList.find((friend) => friend.name === name)
|
||||||
|
if (findFriend) {
|
||||||
|
this.editContent = { ...findFriend, mySelectedFeeds: this.mySelectedFeeds }
|
||||||
|
this.userSelected = findFriend
|
||||||
} else {
|
} else {
|
||||||
this.userSelected = {
|
this.userSelected = {
|
||||||
name
|
name
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isOpenAddFriendsModal = true
|
this.isOpenAddFriendsModal = true
|
||||||
@ -134,7 +197,6 @@ class FriendsView extends connect(store)(LitElement) {
|
|||||||
window.removeEventListener('friends-my-friend-list-event', this._updateFriends)
|
window.removeEventListener('friends-my-friend-list-event', this._updateFriends)
|
||||||
window.removeEventListener('friends-my-selected-feeds-event', this._updateFeed)
|
window.removeEventListener('friends-my-selected-feeds-event', this._updateFeed)
|
||||||
window.removeEventListener('add-friend', this._addFriend)
|
window.removeEventListener('add-friend', this._addFriend)
|
||||||
|
|
||||||
super.disconnectedCallback()
|
super.disconnectedCallback()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,18 +204,21 @@ class FriendsView extends connect(store)(LitElement) {
|
|||||||
const options = {
|
const options = {
|
||||||
root: this.viewElement,
|
root: this.viewElement,
|
||||||
rootMargin: '0px',
|
rootMargin: '0px',
|
||||||
threshold: 1,
|
threshold: 1
|
||||||
};
|
}
|
||||||
|
|
||||||
// identify an element to observe
|
// identify an element to observe
|
||||||
const elementToObserve = this.downObserverElement;
|
const elementToObserve = this.downObserverElement
|
||||||
|
|
||||||
// passing it a callback function
|
// passing it a callback function
|
||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
this.observerHandler,
|
this.observerHandler,
|
||||||
options
|
options
|
||||||
);
|
)
|
||||||
|
|
||||||
// call `observe()` on that MutationObserver instance,
|
// call `observe()` on that MutationObserver instance,
|
||||||
// passing it the element to observe, and the options object
|
// passing it the element to observe, and the options object
|
||||||
observer.observe(elementToObserve);
|
observer.observe(elementToObserve)
|
||||||
}
|
}
|
||||||
|
|
||||||
observerHandler(entries) {
|
observerHandler(entries) {
|
||||||
@ -163,112 +228,121 @@ class FriendsView extends connect(store)(LitElement) {
|
|||||||
if (this.friendList.length < 20) {
|
if (this.friendList.length < 20) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.getMoreFriends();
|
|
||||||
|
this.getMoreFriends()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async userSearch() {
|
async userSearch() {
|
||||||
const nameValue = this.shadowRoot.getElementById('sendTo').value
|
const nameValue = this.shadowRoot.getElementById('sendTo').value
|
||||||
if(!nameValue) {
|
|
||||||
|
if (!nameValue) {
|
||||||
|
this.userFound = []
|
||||||
|
this.userFoundModalOpen = true
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = `${this.nodeUrl}/names/${nameValue}`
|
||||||
|
const res = await fetch(url)
|
||||||
|
const result = await res.json()
|
||||||
|
|
||||||
|
if (result.error === 401) {
|
||||||
this.userFound = []
|
this.userFound = []
|
||||||
this.userFoundModalOpen = true
|
} else {
|
||||||
return;
|
this.userFound = [
|
||||||
}
|
result
|
||||||
try {
|
]
|
||||||
const url = `${this.nodeUrl}/names/${nameValue}`
|
|
||||||
const res = await fetch(url)
|
|
||||||
const result = await res.json()
|
|
||||||
if (result.error === 401) {
|
|
||||||
this.userFound = []
|
|
||||||
} else {
|
|
||||||
this.userFound = [
|
|
||||||
result
|
|
||||||
];
|
|
||||||
}
|
|
||||||
this.userFoundModalOpen = true;
|
|
||||||
} catch (error) {
|
|
||||||
// let err4string = get("chatpage.cchange35");
|
|
||||||
// parentEpml.request('showSnackBar', `${err4string}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.userFoundModalOpen = true;
|
||||||
|
} catch (error) {
|
||||||
|
// let err4string = get("chatpage.cchange35")
|
||||||
|
// parentEpml.request('showSnackBar', `${err4string}`)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getApiKey() {
|
async myFollowName(name) {
|
||||||
const apiNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
let items = [
|
||||||
return apiNode.apiKey
|
name
|
||||||
}
|
]
|
||||||
|
|
||||||
async myFollowName(name) {
|
let namesJsonString = JSON.stringify({ "items": items })
|
||||||
let items = [
|
|
||||||
name
|
|
||||||
]
|
|
||||||
let namesJsonString = JSON.stringify({ "items": items })
|
|
||||||
|
|
||||||
return await parentEpml.request('apiCall', {
|
return await parentEpml.request('apiCall', {
|
||||||
url: `/lists/followedNames?apiKey=${this.getApiKey()}`,
|
url: `/lists/followedNames?apiKey=${this.getApiKey()}`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: `${namesJsonString}`
|
body: `${namesJsonString}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async unFollowName(name) {
|
async unFollowName(name) {
|
||||||
let items = [
|
let items = [
|
||||||
name
|
name
|
||||||
]
|
]
|
||||||
let namesJsonString = JSON.stringify({ "items": items })
|
|
||||||
|
|
||||||
return await parentEpml.request('apiCall', {
|
let namesJsonString = JSON.stringify({ "items": items })
|
||||||
url: `/lists/followedNames?apiKey=${this.getApiKey()}`,
|
|
||||||
method: 'DELETE',
|
return await parentEpml.request('apiCall', {
|
||||||
headers: {
|
url: `/lists/followedNames?apiKey=${this.getApiKey()}`,
|
||||||
'Content-Type': 'application/json'
|
method: 'DELETE',
|
||||||
},
|
headers: {
|
||||||
body: `${namesJsonString}`
|
'Content-Type': 'application/json'
|
||||||
})
|
},
|
||||||
}
|
body: `${namesJsonString}`
|
||||||
async addToFriendList(val, isRemove){
|
})
|
||||||
const copyVal = {...val}
|
}
|
||||||
|
|
||||||
|
async addToFriendList(val, isRemove) {
|
||||||
|
const copyVal = { ...val }
|
||||||
delete copyVal.mySelectedFeeds
|
delete copyVal.mySelectedFeeds
|
||||||
if(isRemove){
|
|
||||||
this.friendList = this.friendList.filter((item)=> item.name !== copyVal.name)
|
if (isRemove) {
|
||||||
}else if(this.editContent){
|
this.friendList = this.friendList.filter((item) => item.name !== copyVal.name)
|
||||||
const findFriend = this.friendList.findIndex(item=> item.name === copyVal.name)
|
} else if (this.editContent) {
|
||||||
if(findFriend !== -1){
|
const findFriend = this.friendList.findIndex(item => item.name === copyVal.name)
|
||||||
|
if (findFriend !== -1) {
|
||||||
const copyList = [...this.friendList]
|
const copyList = [...this.friendList]
|
||||||
copyList[findFriend] = copyVal
|
copyList[findFriend] = copyVal
|
||||||
this.friendList = copyList
|
this.friendList = copyList
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.friendList = [...this.friendList, copyVal]
|
this.friendList = [...this.friendList, copyVal]
|
||||||
}
|
}
|
||||||
if(!copyVal.willFollow || isRemove) {
|
|
||||||
|
if (!copyVal.willFollow || isRemove) {
|
||||||
await this.unFollowName(copyVal.name)
|
await this.unFollowName(copyVal.name)
|
||||||
} else if(copyVal.willFollow){
|
} else if (copyVal.willFollow) {
|
||||||
await this.myFollowName(copyVal.name)
|
await this.myFollowName(copyVal.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setMySelectedFeeds(val.mySelectedFeeds)
|
this.setMySelectedFeeds(val.mySelectedFeeds)
|
||||||
await new Promise((res)=> {
|
|
||||||
setTimeout(()=> {
|
await new Promise((res) => {
|
||||||
|
setTimeout(() => {
|
||||||
res()
|
res()
|
||||||
},50)
|
}, 50)
|
||||||
})
|
})
|
||||||
this.userSelected = {};
|
|
||||||
|
this.userSelected = {}
|
||||||
this.shadowRoot.getElementById('sendTo').value = ''
|
this.shadowRoot.getElementById('sendTo').value = ''
|
||||||
this.isLoading = false;
|
this.isLoading = false
|
||||||
this.isOpenAddFriendsModal = false
|
this.isOpenAddFriendsModal = false
|
||||||
this.editContent = null
|
this.editContent = null
|
||||||
this.setMyFriends(this.friendList)
|
this.setMyFriends(this.friendList)
|
||||||
if(!isRemove && this.friendList.length === 1){
|
|
||||||
|
if (!isRemove && this.friendList.length === 1) {
|
||||||
this.refreshFeed()
|
this.refreshFeed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setMyFriends(friendList){
|
|
||||||
|
setMyFriends(friendList) {
|
||||||
localStorage.setItem('friends-my-friend-list', JSON.stringify(friendList));
|
localStorage.setItem('friends-my-friend-list', JSON.stringify(friendList));
|
||||||
const tempSettingsData= JSON.parse(localStorage.getItem('temp-settings-data') || "{}")
|
const tempSettingsData = JSON.parse(localStorage.getItem('temp-settings-data') || "{}")
|
||||||
const newTemp = {
|
const newTemp = {
|
||||||
...tempSettingsData,
|
...tempSettingsData,
|
||||||
userLists: {
|
userLists: {
|
||||||
@ -277,18 +351,20 @@ class FriendsView extends connect(store)(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
localStorage.setItem('temp-settings-data', JSON.stringify(newTemp));
|
localStorage.setItem('temp-settings-data', JSON.stringify(newTemp))
|
||||||
|
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent('temp-settings-data-event', {
|
new CustomEvent('temp-settings-data-event', {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
composed: true
|
composed: true
|
||||||
}),
|
})
|
||||||
);
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
setMySelectedFeeds(mySelectedFeeds){
|
|
||||||
|
setMySelectedFeeds(mySelectedFeeds) {
|
||||||
this.mySelectedFeeds = mySelectedFeeds
|
this.mySelectedFeeds = mySelectedFeeds
|
||||||
const tempSettingsData= JSON.parse(localStorage.getItem('temp-settings-data') || "{}")
|
const tempSettingsData = JSON.parse(localStorage.getItem('temp-settings-data') || "{}")
|
||||||
|
|
||||||
const newTemp = {
|
const newTemp = {
|
||||||
...tempSettingsData,
|
...tempSettingsData,
|
||||||
friendsFeed: {
|
friendsFeed: {
|
||||||
@ -297,98 +373,37 @@ class FriendsView extends connect(store)(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
localStorage.setItem('temp-settings-data', JSON.stringify(newTemp));
|
localStorage.setItem('temp-settings-data', JSON.stringify(newTemp))
|
||||||
localStorage.setItem('friends-my-selected-feeds', JSON.stringify(mySelectedFeeds));
|
localStorage.setItem('friends-my-selected-feeds', JSON.stringify(mySelectedFeeds))
|
||||||
}
|
|
||||||
openEditFriend(val){
|
|
||||||
this.isOpenAddFriendsModal = true
|
|
||||||
this.userSelected = val
|
|
||||||
this.editContent = {...val, mySelectedFeeds: this.mySelectedFeeds}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose(){
|
openEditFriend(val) {
|
||||||
|
this.isOpenAddFriendsModal = true
|
||||||
|
this.userSelected = val
|
||||||
|
this.editContent = { ...val, mySelectedFeeds: this.mySelectedFeeds }
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose() {
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
this.isOpenAddFriendsModal = false
|
this.isOpenAddFriendsModal = false
|
||||||
this.editContent = null
|
this.editContent = null
|
||||||
this.userSelected = {}
|
this.userSelected = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
// Standard functions
|
||||||
return html`
|
getApiKey() {
|
||||||
<div class="container">
|
const coreNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
<div id="viewElement" class="container-body" style=${"position: relative"}>
|
return coreNode.apiKey
|
||||||
<p class="group-name">My Friends</p>
|
}
|
||||||
<div class="search-field">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="name-input"
|
|
||||||
?disabled=${this.isLoading}
|
|
||||||
id="sendTo"
|
|
||||||
placeholder="${translate("friends.friend1")}"
|
|
||||||
value=${this.userSelected.name ? this.userSelected.name: ''}
|
|
||||||
@keypress=${(e) => {
|
|
||||||
if(e.key === 'Enter'){
|
|
||||||
this.userSearch()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<vaadin-icon
|
isEmptyArray(arr) {
|
||||||
@click=${this.userSearch}
|
if (!arr) { return true }
|
||||||
slot="icon"
|
return arr.length === 0
|
||||||
icon="vaadin:search"
|
}
|
||||||
class="search-icon">
|
|
||||||
</vaadin-icon>
|
|
||||||
|
|
||||||
</div>
|
round(number) {
|
||||||
<div class="search-results-div">
|
return (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8)
|
||||||
<chat-search-results
|
|
||||||
.onClickFunc=${(result) => {
|
|
||||||
this.userSelected = result;
|
|
||||||
this.isOpenAddFriendsModal = true
|
|
||||||
|
|
||||||
this.userFound = [];
|
|
||||||
this.userFoundModalOpen = false;
|
|
||||||
}}
|
|
||||||
.closeFunc=${() => {
|
|
||||||
this.userFoundModalOpen = false;
|
|
||||||
this.userFound = [];
|
|
||||||
}}
|
|
||||||
.searchResults=${this.userFound}
|
|
||||||
?isOpen=${this.userFoundModalOpen}
|
|
||||||
?loading=${this.isLoading}>
|
|
||||||
</chat-search-results>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
${this.friendList.map((item) => {
|
|
||||||
return html`<chat-side-nav-heads
|
|
||||||
activeChatHeadUrl=""
|
|
||||||
.setActiveChatHeadUrl=${(val) => {
|
|
||||||
|
|
||||||
}}
|
|
||||||
.chatInfo=${item}
|
|
||||||
.openEditFriend=${(val)=> this.openEditFriend(val)}
|
|
||||||
.closeSidePanel=${this.closeSidePanel}
|
|
||||||
></chat-side-nav-heads>`;
|
|
||||||
})}
|
|
||||||
<div id="downObserver"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<add-friends-modal
|
|
||||||
?isOpen=${this.isOpenAddFriendsModal}
|
|
||||||
.setIsOpen=${(val)=> {
|
|
||||||
this.isOpenAddFriendsModal = val
|
|
||||||
}}
|
|
||||||
.userSelected=${this.userSelected}
|
|
||||||
.onSubmit=${(val, isRemove)=> this.addToFriendList(val, isRemove)}
|
|
||||||
.editContent=${this.editContent}
|
|
||||||
.onClose=${()=> this.onClose()}
|
|
||||||
.mySelectedFeeds=${this.mySelectedFeeds}
|
|
||||||
>
|
|
||||||
</add-friends-modal>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('friends-view', FriendsView);
|
window.customElements.define('friends-view', FriendsView)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,125 +1,88 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {registerTranslateConfig, translate, use} from '../../translate'
|
import { languageSelectorStyles } from '../styles/core-css'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { registerTranslateConfig, translate, use } from '../../translate'
|
||||||
|
|
||||||
registerTranslateConfig({
|
registerTranslateConfig({
|
||||||
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
|
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
|
||||||
})
|
})
|
||||||
|
|
||||||
const checkLanguage = localStorage.getItem('qortalLanguage')
|
const checkLanguage = localStorage.getItem('qortalLanguage')
|
||||||
|
|
||||||
if (checkLanguage === null || checkLanguage.length === 0) {
|
if (checkLanguage === null || checkLanguage.length === 0) {
|
||||||
localStorage.setItem('qortalLanguage', 'us')
|
localStorage.setItem('qortalLanguage', 'us')
|
||||||
use('us')
|
use('us')
|
||||||
} else {
|
} else {
|
||||||
use(checkLanguage)
|
use(checkLanguage)
|
||||||
}
|
}
|
||||||
|
|
||||||
class LanguageSelector extends LitElement {
|
class LanguageSelector extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
theme: { type: String, reflect: true }
|
theme: { type: String, reflect: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [languageSelectorStyles]
|
||||||
css`
|
}
|
||||||
select {
|
|
||||||
width: 175px;
|
|
||||||
height: 34px;
|
|
||||||
padding: 5px 0px 5px 5px;
|
|
||||||
font-size: 16px;
|
|
||||||
border: 1px solid var(--black);
|
|
||||||
border-radius: 3px;
|
|
||||||
color: var(--black);
|
|
||||||
background:
|
|
||||||
linear-gradient(45deg, transparent 50%, white 50%),
|
|
||||||
linear-gradient(135deg, white 50%, transparent 50%),
|
|
||||||
linear-gradient(to right, #03a9f4, #03a9f4);
|
|
||||||
background-position:
|
|
||||||
calc(100% - 17px) calc(0.5em + 4px),
|
|
||||||
calc(100% - 7px) calc(0.5em + 4px),
|
|
||||||
100% 0;
|
|
||||||
background-size:
|
|
||||||
10px 10px,
|
|
||||||
10px 10px,
|
|
||||||
2.2em 2.2em;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
-webkit-appearance:none;
|
|
||||||
-moz-appearance:none;
|
|
||||||
}
|
|
||||||
|
|
||||||
*:focus {
|
constructor() {
|
||||||
outline: none;
|
super()
|
||||||
}
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
|
}
|
||||||
|
|
||||||
select option {
|
render() {
|
||||||
color: var(--black);
|
return html`
|
||||||
background: var(--white);
|
<div style="display: inline;">
|
||||||
line-height: 34px;
|
<select id="languageSelect" @change="${this.changeLanguage}">
|
||||||
}
|
<option value="us">US - ${translate("selectmenu.english")}</option>
|
||||||
`
|
<option value="de">DE - ${translate("selectmenu.german")}</option>
|
||||||
]
|
<option value="es">ES - ${translate("selectmenu.spanish")}</option>
|
||||||
}
|
<option value="et">ET - ${translate("selectmenu.estonian")}</option>
|
||||||
|
<option value="fi">FI - ${translate("selectmenu.finnish")}</option>
|
||||||
|
<option value="fr">FR - ${translate("selectmenu.french")}</option>
|
||||||
|
<option value="hr">HR - ${translate("selectmenu.croatian")}</option>
|
||||||
|
<option value="hu">HU - ${translate("selectmenu.hungarian")}</option>
|
||||||
|
<option value="hindi">IN - ${translate("selectmenu.hindi")}</option>
|
||||||
|
<option value="it">IT - ${translate("selectmenu.italian")}</option>
|
||||||
|
<option value="jp">JP - ${translate("selectmenu.japanese")}</option>
|
||||||
|
<option value="ko">KO - ${translate("selectmenu.korean")}</option>
|
||||||
|
<option value="nl">NL - ${translate("selectmenu.dutch")}</option>
|
||||||
|
<option value="no">NO - ${translate("selectmenu.norwegian")}</option>
|
||||||
|
<option value="pl">PL - ${translate("selectmenu.polish")}</option>
|
||||||
|
<option value="pt">PT - ${translate("selectmenu.portuguese")}</option>
|
||||||
|
<option value="rs">RS - ${translate("selectmenu.serbian")}</option>
|
||||||
|
<option value="ro">RO - ${translate("selectmenu.romanian")}</option>
|
||||||
|
<option value="ru">RU - ${translate("selectmenu.russian")}</option>
|
||||||
|
<option value="zht">ZHT - ${translate("selectmenu.chinese2")}</option>
|
||||||
|
<option value="zhc">ZHC - ${translate("selectmenu.chinese1")}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
firstUpdated() {
|
||||||
super()
|
const myElement = this.shadowRoot.getElementById('languageSelect')
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
myElement.addEventListener('change', () => {
|
||||||
return html`
|
this.selectElement()
|
||||||
<div style="display: inline;">
|
})
|
||||||
<select id="languageSelect" @change="${this.changeLanguage}">
|
|
||||||
<option value="us">US - ${translate("selectmenu.english")}</option>
|
|
||||||
<option value="de">DE - ${translate("selectmenu.german")}</option>
|
|
||||||
<option value="es">ES - ${translate("selectmenu.spanish")}</option>
|
|
||||||
<option value="et">ET - ${translate("selectmenu.estonian")}</option>
|
|
||||||
<option value="fi">FI - ${translate("selectmenu.finnish")}</option>
|
|
||||||
<option value="fr">FR - ${translate("selectmenu.french")}</option>
|
|
||||||
<option value="hr">HR - ${translate("selectmenu.croatian")}</option>
|
|
||||||
<option value="hu">HU - ${translate("selectmenu.hungarian")}</option>
|
|
||||||
<option value="hindi">IN - ${translate("selectmenu.hindi")}</option>
|
|
||||||
<option value="it">IT - ${translate("selectmenu.italian")}</option>
|
|
||||||
<option value="jp">JP - ${translate("selectmenu.japanese")}</option>
|
|
||||||
<option value="ko">KO - ${translate("selectmenu.korean")}</option>
|
|
||||||
<option value="nl">NL - ${translate("selectmenu.dutch")}</option>
|
|
||||||
<option value="no">NO - ${translate("selectmenu.norwegian")}</option>
|
|
||||||
<option value="pl">PL - ${translate("selectmenu.polish")}</option>
|
|
||||||
<option value="pt">PT - ${translate("selectmenu.portuguese")}</option>
|
|
||||||
<option value="rs">RS - ${translate("selectmenu.serbian")}</option>
|
|
||||||
<option value="ro">RO - ${translate("selectmenu.romanian")}</option>
|
|
||||||
<option value="ru">RU - ${translate("selectmenu.russian")}</option>
|
|
||||||
<option value="zht">ZHT - ${translate("selectmenu.chinese2")}</option>
|
|
||||||
<option value="zhc">ZHC - ${translate("selectmenu.chinese1")}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated() {
|
this.selectElement()
|
||||||
const myElement = this.shadowRoot.getElementById('languageSelect')
|
}
|
||||||
|
|
||||||
myElement.addEventListener("change", () => {
|
selectElement() {
|
||||||
this.selectElement()
|
const selectedLanguage = localStorage.getItem('qortalLanguage')
|
||||||
})
|
let element = this.shadowRoot.getElementById('languageSelect')
|
||||||
|
element.value = selectedLanguage
|
||||||
|
}
|
||||||
|
|
||||||
this.selectElement()
|
changeLanguage(event) {
|
||||||
}
|
use(event.target.value)
|
||||||
|
localStorage.setItem('qortalLanguage', event.target.value)
|
||||||
selectElement() {
|
}
|
||||||
const selectedLanguage = localStorage.getItem('qortalLanguage')
|
|
||||||
let element = this.shadowRoot.getElementById('languageSelect')
|
|
||||||
element.value = selectedLanguage
|
|
||||||
}
|
|
||||||
|
|
||||||
changeLanguage(event) {
|
|
||||||
use(event.target.value)
|
|
||||||
localStorage.setItem('qortalLanguage', event.target.value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('language-selector', LanguageSelector)
|
window.customElements.define('language-selector', LanguageSelector)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,20 +1,7 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../../store.js'
|
import { store } from '../../store'
|
||||||
import {stateAwait} from '../../stateAwait.js'
|
import { stateAwait } from '../../stateAwait'
|
||||||
import {get} from '../../../translate'
|
|
||||||
|
|
||||||
import '@material/mwc-button'
|
|
||||||
import '@material/mwc-icon'
|
|
||||||
import '@material/mwc-fab'
|
|
||||||
import '@polymer/iron-pages'
|
|
||||||
import '@polymer/paper-icon-button/paper-icon-button.js'
|
|
||||||
import './welcome-page.js'
|
|
||||||
import './create-account-section.js'
|
|
||||||
import './login-section.js'
|
|
||||||
import '../qort-theme-toggle.js'
|
|
||||||
|
|
||||||
import settings from '../../functional-components/settings-page.js'
|
|
||||||
import {
|
import {
|
||||||
addAutoLoadImageChat,
|
addAutoLoadImageChat,
|
||||||
addChatLastSeen,
|
addChatLastSeen,
|
||||||
@ -32,352 +19,388 @@ import {
|
|||||||
setNewTab,
|
setNewTab,
|
||||||
setSideEffectAction,
|
setSideEffectAction,
|
||||||
setTabNotifications
|
setTabNotifications
|
||||||
} from '../../redux/app/app-actions.js'
|
} from '../../redux/app/app-actions'
|
||||||
|
import settings from '../../functional-components/settings-page'
|
||||||
|
import './welcome-page'
|
||||||
|
import './create-account-section'
|
||||||
|
import './login-section'
|
||||||
|
import '../qort-theme-toggle'
|
||||||
|
import '@material/mwc-button'
|
||||||
|
import '@material/mwc-icon'
|
||||||
|
import '@material/mwc-fab'
|
||||||
|
import '@polymer/iron-pages'
|
||||||
|
import '@polymer/paper-icon-button/paper-icon-button.js'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { get } from '../../../translate'
|
||||||
|
|
||||||
window.reduxStore = store
|
window.reduxStore = store
|
||||||
|
|
||||||
window.reduxAction = {
|
window.reduxAction = {
|
||||||
addAutoLoadImageChat: addAutoLoadImageChat,
|
addAutoLoadImageChat: addAutoLoadImageChat,
|
||||||
removeAutoLoadImageChat: removeAutoLoadImageChat,
|
removeAutoLoadImageChat: removeAutoLoadImageChat,
|
||||||
addChatLastSeen: addChatLastSeen,
|
addChatLastSeen: addChatLastSeen,
|
||||||
allowQAPPAutoAuth: allowQAPPAutoAuth,
|
allowQAPPAutoAuth: allowQAPPAutoAuth,
|
||||||
removeQAPPAutoAuth: removeQAPPAutoAuth,
|
removeQAPPAutoAuth: removeQAPPAutoAuth,
|
||||||
allowQAPPAutoLists: allowQAPPAutoLists,
|
allowQAPPAutoLists: allowQAPPAutoLists,
|
||||||
removeQAPPAutoLists: removeQAPPAutoLists,
|
removeQAPPAutoLists: removeQAPPAutoLists,
|
||||||
addTabInfo: addTabInfo,
|
addTabInfo: addTabInfo,
|
||||||
setTabNotifications: setTabNotifications,
|
setTabNotifications: setTabNotifications,
|
||||||
setNewTab: setNewTab,
|
setNewTab: setNewTab,
|
||||||
setNewNotification: setNewNotification,
|
setNewNotification: setNewNotification,
|
||||||
setSideEffectAction: setSideEffectAction,
|
setSideEffectAction: setSideEffectAction,
|
||||||
allowQAPPAutoFriendsList: allowQAPPAutoFriendsList,
|
allowQAPPAutoFriendsList: allowQAPPAutoFriendsList,
|
||||||
removeQAPPAutoFriendsList: removeQAPPAutoFriendsList,
|
removeQAPPAutoFriendsList: removeQAPPAutoFriendsList,
|
||||||
allowShowSyncIndicator: allowShowSyncIndicator,
|
allowShowSyncIndicator: allowShowSyncIndicator,
|
||||||
removeShowSyncIndicator: removeShowSyncIndicator
|
removeShowSyncIndicator: removeShowSyncIndicator
|
||||||
}
|
}
|
||||||
|
|
||||||
const animationDuration = 0.7 // Seconds
|
const animationDuration = 0.7 // Seconds
|
||||||
|
|
||||||
class LoginView extends connect(store)(LitElement) {
|
class LoginView extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
loggedIn: { type: Boolean },
|
loggedIn: { type: Boolean },
|
||||||
selectedPage: { type: String },
|
selectedPage: { type: String },
|
||||||
pages: { type: Object },
|
pages: { type: Object },
|
||||||
rippleIsOpen: { type: Boolean },
|
rippleIsOpen: { type: Boolean },
|
||||||
config: { type: Object },
|
config: { type: Object },
|
||||||
rippleLoadingMessage: { type: String },
|
rippleLoadingMessage: { type: String },
|
||||||
selectedPageElement: {},
|
selectedPageElement: {},
|
||||||
nodeConfig: { type: Object },
|
nodeConfig: { type: Object },
|
||||||
theme: { type: String, reflect: true }
|
theme: { type: String, reflect: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
constructor() {
|
||||||
return [
|
super()
|
||||||
css``
|
this.selectedPage = this.getPreSelectedPage()
|
||||||
]
|
this.selectedPageElement = {}
|
||||||
}
|
this.rippleIsOpen = false
|
||||||
|
this.pages = {
|
||||||
|
welcome: 0,
|
||||||
|
'create-account': 1,
|
||||||
|
login: 2
|
||||||
|
}
|
||||||
|
this.rippleLoadingMessage = 'Getting information'
|
||||||
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
|
}
|
||||||
|
|
||||||
getPreSelectedPage() {
|
render() {
|
||||||
return 'welcome'
|
return html`
|
||||||
}
|
<style>
|
||||||
|
|
||||||
constructor() {
|
canvas {
|
||||||
super()
|
display: block;
|
||||||
this.selectedPage = this.getPreSelectedPage()
|
vertical-align: bottom;
|
||||||
this.selectedPageElement = {}
|
}
|
||||||
this.rippleIsOpen = false
|
|
||||||
this.pages = {
|
|
||||||
welcome: 0,
|
|
||||||
'create-account': 1,
|
|
||||||
login: 2
|
|
||||||
}
|
|
||||||
this.rippleLoadingMessage = 'Getting information'
|
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated() {
|
.login-page {
|
||||||
|
background: var(--background);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-position: center;
|
||||||
|
height: var(--window-height);
|
||||||
|
width: 100vw;
|
||||||
|
max-width: 100vw;
|
||||||
|
max-height: var(--window-height);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
stateAwait(state => {
|
.login-card-container {
|
||||||
return 'primary' in state.config.styles.theme.colors
|
max-width: 1240px;
|
||||||
}).catch(e => console.error(e))
|
max-height: var(--window-height);
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
width: calc(100vw);
|
||||||
|
}
|
||||||
|
|
||||||
const loginContainerPages = this.shadowRoot.querySelector('#loginContainerPages')
|
.qortal-logo {
|
||||||
const loginCard = this.shadowRoot.querySelector('#login-card')
|
margin-left: auto;
|
||||||
const navigate = e => {
|
margin-right: auto;
|
||||||
this.selectPage(e.detail.page)
|
width: 200px;
|
||||||
}
|
max-width: 40%;
|
||||||
const updatedProperty = e => {
|
z-index: 1;
|
||||||
// ...
|
}
|
||||||
const selectedPageElement = this.selectedPageElement
|
|
||||||
this.selectedPageElement = {}
|
|
||||||
setTimeout(() => { this.selectedPageElement = selectedPageElement }, 1) // Yuck
|
|
||||||
}
|
|
||||||
loginContainerPages.addEventListener('selected-item-changed', () => {
|
|
||||||
|
|
||||||
if (!loginContainerPages.selectedItem) {
|
.login-card-center-container {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: var(--window-height);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: var(--window-height);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.selectedPageElement.removeEventListener) {
|
#loginContainerPages {
|
||||||
this.selectedPageElement.removeEventListener('navigate', navigate)
|
display: inline;
|
||||||
this.selectedPageElement.removeEventListener('updatedProperty', updatedProperty)
|
}
|
||||||
}
|
|
||||||
this.selectedPageElement = {}
|
|
||||||
loginCard.classList.remove('animated')
|
|
||||||
loginCard.className += ' animated'
|
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
|
||||||
|
|
||||||
this.selectedPageElement = loginContainerPages.selectedItem
|
#loginContainerPages [page] {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
this.selectedPageElement.addEventListener('navigate', navigate)
|
.login-card {
|
||||||
this.selectedPageElement.addEventListener('updatedProperty', updatedProperty)
|
min-width: 340px;
|
||||||
setTimeout(() => loginCard.classList.remove('animated'), animationDuration * 1000)
|
border-bottom: 2px solid var(--mdc-theme-primary);
|
||||||
}, 1)
|
border-top: 2px solid var(--mdc-theme-primary);
|
||||||
}
|
text-align: center;
|
||||||
})
|
z-index: 0;
|
||||||
}
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
.login-card p {
|
||||||
return html`
|
margin-top: 0;
|
||||||
<style>
|
font-size: 1rem;
|
||||||
canvas {
|
font-style: italic;
|
||||||
display: block;
|
}
|
||||||
vertical-align: bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-page {
|
.login-card h1 {
|
||||||
background: var(--background);
|
margin-bottom: 12px;
|
||||||
background-repeat: no-repeat;
|
font-size: 64px;
|
||||||
background-attachment: fixed;
|
}
|
||||||
background-position: center;
|
|
||||||
height: var(--window-height);
|
|
||||||
width:100vw;
|
|
||||||
max-width:100vw;
|
|
||||||
max-height:var(--window-height);
|
|
||||||
position:absolute;
|
|
||||||
top:0;
|
|
||||||
left:0;
|
|
||||||
z-index:1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-card-container {
|
.login-card h5 {
|
||||||
max-width:1240px;
|
margin-top: -16px;
|
||||||
max-height:var(--window-height);
|
margin-left: 100px;
|
||||||
margin-right: auto;
|
font-size: 14px;
|
||||||
margin-left: auto;
|
color: var(--black);
|
||||||
width: calc(100vw);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.qortal-logo {
|
.login-card h6 {
|
||||||
margin-left: auto;
|
font-size: 12px;
|
||||||
margin-right: auto;
|
color: var(--mdc-theme-primary);
|
||||||
width:200px;
|
}
|
||||||
max-width:40%;
|
|
||||||
z-index:1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-card-center-container {
|
.login-card iron-pages {
|
||||||
max-width:100%;
|
height: 100%;
|
||||||
max-height:var(--window-height);
|
margin-top: -16px;
|
||||||
display: flex;
|
}
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: var(--window-height);
|
|
||||||
overflow:hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#loginContainerPages {
|
.backButton {
|
||||||
display:inline;
|
padding-top: 18px;
|
||||||
}
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
#loginContainerPages [page] {
|
#login-pages-nav {
|
||||||
background: none;
|
text-align: left;
|
||||||
padding:0;
|
padding: 12px 0 8px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-card {
|
#nav-next {
|
||||||
min-width: 340px;
|
float: right;
|
||||||
border-bottom: 2px solid var(--mdc-theme-primary);
|
}
|
||||||
border-top: 2px solid var(--mdc-theme-primary);
|
|
||||||
text-align:center;
|
|
||||||
z-index:0;
|
|
||||||
padding:0;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-card p {
|
@media only screen and (min-width: ${getComputedStyle(document.body).getPropertyValue('--layout-breakpoint-tablet')}) {
|
||||||
margin-top: 0;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-card h1 {
|
/* Desktop/tablet */
|
||||||
margin-bottom:12px;
|
.login-card {
|
||||||
font-size:64px;
|
max-width: 460px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-card h5 {
|
#loginContainerPages [page] {
|
||||||
margin-top: -16px;
|
border-radius: 4px;
|
||||||
margin-left: 100px;
|
}
|
||||||
font-size: 14px;
|
|
||||||
color: var(--black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-card h6 {
|
#loginContainerPages [page="welcome"] {}
|
||||||
font-size: 12px;
|
}
|
||||||
color: var(--mdc-theme-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-card iron-pages {
|
@media only screen and (max-width: ${getComputedStyle(document.body).getPropertyValue('--layout-breakpoint-tablet')}) {
|
||||||
height:100%;
|
|
||||||
margin-top: -16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.backButton {
|
/* Mobile */
|
||||||
padding-top:18px;
|
.qortal-logo {
|
||||||
text-align:center;
|
display: none;
|
||||||
}
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
#login-pages-nav {
|
.login-card {
|
||||||
text-align: left;
|
width: 100%;
|
||||||
padding: 12px 0 8px 0;
|
margin: 0;
|
||||||
}
|
top: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
#nav-next {
|
.backButton {
|
||||||
float: right;
|
text-align: left;
|
||||||
}
|
padding-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: ${getComputedStyle(document.body).getPropertyValue('--layout-breakpoint-tablet')}) {
|
.login-card h5 {
|
||||||
/* Desktop/tablet */
|
margin-top: 0px;
|
||||||
.login-card {
|
margin-left: 0px;
|
||||||
max-width:460px;
|
font-size: 14px;
|
||||||
}
|
color: var(--black);
|
||||||
#loginContainerPages [page] {
|
}
|
||||||
border-radius: 4px;
|
}
|
||||||
}
|
|
||||||
#loginContainerPages [page="welcome"] {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: ${getComputedStyle(document.body).getPropertyValue('--layout-breakpoint-tablet')}) {
|
@keyframes fade {
|
||||||
/* Mobile */
|
from {
|
||||||
.qortal-logo {
|
opacity: 0;
|
||||||
display:none;
|
transform: translateX(-20%);
|
||||||
visibility:hidden;
|
}
|
||||||
}
|
|
||||||
.login-card {
|
|
||||||
width:100%;
|
|
||||||
margin:0;
|
|
||||||
top:0;
|
|
||||||
max-width:100%;
|
|
||||||
}
|
|
||||||
.backButton {
|
|
||||||
text-align: left;
|
|
||||||
padding-left:12px;
|
|
||||||
}
|
|
||||||
.login-card h5 {
|
|
||||||
margin-top: 0px;
|
|
||||||
margin-left: 0px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: var(--black);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fade {
|
to {
|
||||||
from {
|
opacity: 1;
|
||||||
opacity: 0;
|
transform: translateX(0);
|
||||||
transform: translateX(-20%);
|
}
|
||||||
}
|
}
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes grow-up {
|
@keyframes grow-up {
|
||||||
from {
|
from {
|
||||||
overflow:hidden;
|
overflow: hidden;
|
||||||
max-height:0;
|
max-height: 0;
|
||||||
}
|
}
|
||||||
to {
|
|
||||||
overflow:hidden;
|
|
||||||
max-height:var(--window-height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iron-pages .animated, .animated {
|
to {
|
||||||
animation-duration: ${animationDuration}s;
|
overflow: hidden;
|
||||||
animation-name: grow-up;
|
max-height: var(--window-height);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
div[page] > paper-icon-button {
|
iron-pages .animated,
|
||||||
margin:12px;
|
.animated {
|
||||||
}
|
animation-duration: ${animationDuration}s;
|
||||||
|
animation-name: grow-up;
|
||||||
|
}
|
||||||
|
|
||||||
.corner-box {
|
div[page]>paper-icon-button {
|
||||||
border-color: var(--mdc-theme-primary) !important;
|
margin: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[hidden] {
|
.corner-box {
|
||||||
visibility: hidden;
|
border-color: var(--mdc-theme-primary) !important;
|
||||||
display: none;
|
}
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<div class="login-page" ?hidden=${this.loggedIn}>
|
|
||||||
<mwc-fab icon="settings" style="position:fixed; right:24px; bottom:24px;" @click=${() => settings.show()}></mwc-fab>
|
|
||||||
<span style="position:fixed; left:24px; bottom:24px;"><qort-theme-toggle></qort-theme-toggle></span>
|
|
||||||
<div class="login-card-container">
|
|
||||||
<div class="login-card-center-container">
|
|
||||||
<div class="login-card" id="login-card">
|
|
||||||
<img class="qortal-logo" src="${this.config.coin.logo}">
|
|
||||||
<h5 ?hidden="${this.selectedPage != "welcome"}">UI: v${this.nodeConfig.version ? this.nodeConfig.version : ''}</h5>
|
|
||||||
${this.renderSelectedNodeOnStart()}
|
|
||||||
<iron-pages selected="${this.selectedPage}" attr-for-selected="page" id="loginContainerPages">
|
|
||||||
<welcome-page @next=${e => this.selectedPageElement.next(e)} page="welcome"></welcome-page>
|
|
||||||
<create-account-section @next=${e => this.selectedPageElement.next(e)} page="create-account"></create-account-section>
|
|
||||||
<login-section @next=${e => this.selectedPageElement.next(e)} page="login"></login-section>
|
|
||||||
</iron-pages>
|
|
||||||
<div id="login-pages-nav" ?hidden="${this.selectedPageElement.hideNav}">
|
|
||||||
<mwc-button @click=${e => this.selectedPageElement.back(e)} id="nav-back" ?hidden="${this.selectedPageElement.backHidden}" ?disabled="${this.selectedPageElement.backDisabled}">
|
|
||||||
<mwc-icon>keyboard_arrow_left</mwc-icon>${this.selectedPageElement.backText}
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button @click=${e => this.selectedPageElement.next(e)} id="nav-next" ?hidden="${this.selectedPageElement.nextHidden}" ?disabled="${this.selectedPageElement.nextDisabled}">
|
|
||||||
${this.selectedPageElement.nextText}<mwc-icon>keyboard_arrow_right</mwc-icon>
|
|
||||||
</mwc-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSelectedNodeOnStart() {
|
[hidden] {
|
||||||
const selectedNodeIndexOnStart = localStorage.getItem('mySelectedNode')
|
visibility: hidden;
|
||||||
const catchSavedNodes = JSON.parse(localStorage.getItem('myQortalNodes'))
|
display: none;
|
||||||
const selectedNodeOnStart = catchSavedNodes[selectedNodeIndexOnStart]
|
}
|
||||||
const selectedNameOnStart = `${selectedNodeOnStart.name}`
|
</style>
|
||||||
const selectedNodeUrlOnStart = `${selectedNodeOnStart.protocol + '://' + selectedNodeOnStart.domain +':' + selectedNodeOnStart.port}`
|
<div class="login-page" ?hidden=${this.loggedIn}>
|
||||||
|
<mwc-fab icon="settings" style="position:fixed; right:24px; bottom:24px;" @click=${() => settings.show()}></mwc-fab>
|
||||||
|
<span style="position:fixed; left:24px; bottom:24px;">
|
||||||
|
<qort-theme-toggle></qort-theme-toggle>
|
||||||
|
</span>
|
||||||
|
<div class="login-card-container">
|
||||||
|
<div class="login-card-center-container">
|
||||||
|
<div class="login-card" id="login-card">
|
||||||
|
<img class="qortal-logo" src="${this.config.coin.logo}">
|
||||||
|
<h5 ?hidden="${this.selectedPage != "welcome"}">UI: v${this.nodeConfig.version ? this.nodeConfig.version : ''}</h5>
|
||||||
|
${this.renderSelectedNodeOnStart()}
|
||||||
|
<iron-pages selected="${this.selectedPage}" attr-for-selected="page" id="loginContainerPages">
|
||||||
|
<welcome-page @next=${e => this.selectedPageElement.next(e)} page="welcome"></welcome-page>
|
||||||
|
<create-account-section @next=${e => this.selectedPageElement.next(e)} page="create-account"></create-account-section>
|
||||||
|
<login-section @next=${e => this.selectedPageElement.next(e)} page="login"></login-section>
|
||||||
|
</iron-pages>
|
||||||
|
<div id="login-pages-nav" ?hidden="${this.selectedPageElement.hideNav}">
|
||||||
|
<mwc-button
|
||||||
|
@click=${e => this.selectedPageElement.back(e)}
|
||||||
|
id="nav-back"
|
||||||
|
?hidden="${this.selectedPageElement.backHidden}"
|
||||||
|
?disabled="${this.selectedPageElement.backDisabled}"
|
||||||
|
>
|
||||||
|
<mwc-icon>keyboard_arrow_left</mwc-icon>
|
||||||
|
${this.selectedPageElement.backText}
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
@click=${e => this.selectedPageElement.next(e)}
|
||||||
|
id="nav-next"
|
||||||
|
?hidden="${this.selectedPageElement.nextHidden}"
|
||||||
|
?disabled="${this.selectedPageElement.nextDisabled}"
|
||||||
|
>
|
||||||
|
${this.selectedPageElement.nextText}
|
||||||
|
<mwc-icon>keyboard_arrow_right</mwc-icon>
|
||||||
|
</mwc-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
let connectString = get('settings.snack2')
|
firstUpdated() {
|
||||||
|
stateAwait(state => {
|
||||||
|
return 'primary' in state.config.styles.theme.colors
|
||||||
|
}).catch(e => console.error(e))
|
||||||
|
|
||||||
return html`<h6>${connectString} : ${selectedNameOnStart} ${selectedNodeUrlOnStart}</h6>`
|
const loginContainerPages = this.shadowRoot.querySelector('#loginContainerPages')
|
||||||
}
|
const loginCard = this.shadowRoot.querySelector('#login-card')
|
||||||
|
|
||||||
selectPage(newPage) {
|
const navigate = e => {
|
||||||
this.selectedPage = newPage
|
this.selectPage(e.detail.page)
|
||||||
}
|
}
|
||||||
|
|
||||||
stateChanged(state) {
|
const updatedProperty = e => {
|
||||||
if (this.loggedIn && !state.app.loggedIn) this.cleanup()
|
const selectedPageElement = this.selectedPageElement
|
||||||
this.loggedIn = state.app.loggedIn
|
|
||||||
this.config = state.config
|
|
||||||
this.nodeConfig = state.app.nodeConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup() {
|
this.selectedPageElement = {}
|
||||||
this.selectedPage = 'welcome'
|
|
||||||
}
|
setTimeout(() => { this.selectedPageElement = selectedPageElement }, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
loginContainerPages.addEventListener('selected-item-changed', () => {
|
||||||
|
if (!loginContainerPages.selectedItem) {
|
||||||
|
|
||||||
|
if (this.selectedPageElement.removeEventListener) {
|
||||||
|
this.selectedPageElement.removeEventListener('navigate', navigate)
|
||||||
|
this.selectedPageElement.removeEventListener('updatedProperty', updatedProperty)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectedPageElement = {}
|
||||||
|
|
||||||
|
loginCard.classList.remove('animated')
|
||||||
|
loginCard.className += ' animated'
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.selectedPageElement = loginContainerPages.selectedItem
|
||||||
|
this.selectedPageElement.addEventListener('navigate', navigate)
|
||||||
|
this.selectedPageElement.addEventListener('updatedProperty', updatedProperty)
|
||||||
|
|
||||||
|
setTimeout(() => loginCard.classList.remove('animated'), animationDuration * 1000)
|
||||||
|
}, 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getPreSelectedPage() {
|
||||||
|
return 'welcome'
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSelectedNodeOnStart() {
|
||||||
|
const selectedNodeIndexOnStart = localStorage.getItem('mySelectedNode')
|
||||||
|
const catchSavedNodes = JSON.parse(localStorage.getItem('myQortalNodes'))
|
||||||
|
const selectedNodeOnStart = catchSavedNodes[selectedNodeIndexOnStart]
|
||||||
|
const selectedNameOnStart = `${selectedNodeOnStart.name}`
|
||||||
|
const selectedNodeUrlOnStart = `${selectedNodeOnStart.protocol + '://' + selectedNodeOnStart.domain + ':' + selectedNodeOnStart.port}`
|
||||||
|
|
||||||
|
let connectString = get('settings.snack2')
|
||||||
|
|
||||||
|
return html`<h6>${connectString} : ${selectedNameOnStart} ${selectedNodeUrlOnStart}</h6>`
|
||||||
|
}
|
||||||
|
|
||||||
|
selectPage(newPage) {
|
||||||
|
this.selectedPage = newPage
|
||||||
|
}
|
||||||
|
|
||||||
|
stateChanged(state) {
|
||||||
|
if (this.loggedIn && !state.app.loggedIn) this.cleanup()
|
||||||
|
|
||||||
|
this.loggedIn = state.app.loggedIn
|
||||||
|
this.config = state.config
|
||||||
|
this.nodeConfig = state.app.nodeConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
this.selectedPage = 'welcome'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('login-view', LoginView)
|
window.customElements.define('login-view', LoginView)
|
@ -1,76 +1,44 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {translate} from '../../../translate'
|
import { welcomePageStyles } from '../../styles/core-css'
|
||||||
|
|
||||||
import '@material/mwc-button'
|
import '@material/mwc-button'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { translate } from '../../../translate'
|
||||||
|
|
||||||
class WelcomePage extends LitElement {
|
class WelcomePage extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
nextHidden: { type: Boolean, notify: true },
|
hideNav: { type: Boolean, notify: true },
|
||||||
nextEnabled: { type: Boolean, notify: true },
|
theme: { type: String, reflect: true }
|
||||||
nextText: { type: String, notify: true },
|
}
|
||||||
backHidden: { type: Boolean, notify: true },
|
}
|
||||||
backDisabled: { type: Boolean, notify: true },
|
|
||||||
backText: { type: String, notify: true },
|
|
||||||
hideNav: { type: Boolean, notify: true },
|
|
||||||
welcomeMessage: { type: String },
|
|
||||||
theme: { type: String, reflect: true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [welcomePageStyles]
|
||||||
* {
|
}
|
||||||
--mdc-theme-primary: var(--login-button);
|
|
||||||
--mdc-theme-secondary: var(--mdc-theme-primary);
|
|
||||||
--mdc-button-outline-color: var(--general-color-blue);
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-outline {
|
constructor() {
|
||||||
margin: 6px;
|
super()
|
||||||
width: 90%;
|
this.hideNav = true
|
||||||
max-width:90vw;
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
border-top: 0;
|
}
|
||||||
border-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welcome-page {
|
render() {
|
||||||
padding: 12px 0;
|
return html`
|
||||||
overflow: hidden;
|
<div class="welcome-page">
|
||||||
}
|
<mwc-button class="button-outline" @click=${() => this.navigate('login')} outlined>${translate("login.login")}</mwc-button>
|
||||||
`
|
<mwc-button class="button-outline" @click=${() => this.navigate('create-account')} outlined>${translate("login.createaccount")}</mwc-button>
|
||||||
}
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
navigate(page) {
|
||||||
super()
|
this.dispatchEvent(new CustomEvent('navigate', {
|
||||||
this.hideNav = true
|
detail: { page },
|
||||||
this.nextText = ''
|
bubbles: true,
|
||||||
this.welcomeMessage = 'Welcome to Qortal'
|
composed: true
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`
|
|
||||||
<div class="welcome-page">
|
|
||||||
<mwc-button class="button-outline" @click=${() => this.navigate('login')} outlined>${translate("login.login")}</mwc-button>
|
|
||||||
<mwc-button class="button-outline" @click=${() => this.navigate('create-account')} outlined>${translate("login.createaccount")}</mwc-button>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
back() {}
|
|
||||||
|
|
||||||
next() {}
|
|
||||||
|
|
||||||
navigate(page) {
|
|
||||||
this.dispatchEvent(new CustomEvent('navigate', {
|
|
||||||
detail: { page },
|
|
||||||
bubbles: true,
|
|
||||||
composed: true
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('welcome-page', WelcomePage)
|
window.customElements.define('welcome-page', WelcomePage)
|
@ -1,72 +1,61 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../../store.js'
|
import { store } from '../../store.js'
|
||||||
import {doLogout} from '../../redux/app/app-actions.js'
|
import { doLogout } from '../../redux/app/app-actions.js'
|
||||||
import {translate} from '../../../translate'
|
import { logoutViewStyles } from '../../styles/core-css'
|
||||||
|
|
||||||
import '@polymer/paper-dialog/paper-dialog.js'
|
|
||||||
import '@material/mwc-button'
|
import '@material/mwc-button'
|
||||||
|
import '@polymer/paper-dialog/paper-dialog.js'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { translate } from '../../../translate'
|
||||||
|
|
||||||
class LogoutView extends connect(store)(LitElement) {
|
class LogoutView extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
theme: { type: String, reflect: true }
|
theme: { type: String, reflect: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [logoutViewStyles]
|
||||||
* {
|
}
|
||||||
--mdc-theme-primary: rgb(3, 169, 244);
|
|
||||||
--mdc-theme-secondary: var(--mdc-theme-primary);
|
|
||||||
--mdc-theme-surface: var(--white);
|
|
||||||
--mdc-dialog-content-ink-color: var(--black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.decline {
|
constructor() {
|
||||||
--mdc-theme-primary: var(--mdc-theme-error)
|
super()
|
||||||
}
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
|
}
|
||||||
|
|
||||||
.buttons {
|
render() {
|
||||||
text-align:right;
|
return html`
|
||||||
}
|
<paper-dialog style="background: var(--white);" id="userLogoutDialog" modal>
|
||||||
`
|
<div style="text-align: center;">
|
||||||
}
|
<h2 style="color: var(--black);">Qortal UI</h2>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<h2 style="color: var(--black);">${translate("logout.confirmlogout")}</h2>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<mwc-button class='decline' @click=${() => this.decline()} dialog-dismiss>${translate("general.no")}</mwc-button>
|
||||||
|
<mwc-button class='confirm' @click=${e => this.confirm(e)} dialog-confirm autofocus>${translate("general.yes")}</mwc-button>
|
||||||
|
</div>
|
||||||
|
</paper-dialog>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
openLogout() {
|
||||||
super()
|
this.shadowRoot.getElementById('userLogoutDialog').open()
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
}
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
async confirm(e) {
|
||||||
return html`
|
store.dispatch(doLogout())
|
||||||
<paper-dialog style="background: var(--white);" id="userLogoutDialog" modal>
|
e.stopPropagation()
|
||||||
<div style="text-align: center;">
|
}
|
||||||
<h2 style="color: var(--black);">Qortal UI</h2>
|
|
||||||
<hr>
|
|
||||||
</div>
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<h2 style="color: var(--black);">${translate("logout.confirmlogout")}</h2>
|
|
||||||
</div>
|
|
||||||
<div class="buttons">
|
|
||||||
<mwc-button class='decline' @click=${e => this.decline(e)} dialog-dismiss>${translate("general.no")}</mwc-button>
|
|
||||||
<mwc-button class='confirm' @click=${e => this.confirm(e)} dialog-confirm autofocus>${translate("general.yes")}</mwc-button>
|
|
||||||
</div>
|
|
||||||
</paper-dialog>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
openLogout() {
|
decline() {
|
||||||
this.shadowRoot.getElementById('userLogoutDialog').open()
|
this.shadowRoot.getElementById('userLogoutDialog').close()
|
||||||
}
|
this.requestUpdate()
|
||||||
|
}
|
||||||
async confirm(e) {
|
|
||||||
store.dispatch(doLogout())
|
|
||||||
}
|
|
||||||
|
|
||||||
decline(e) {
|
|
||||||
this.shadowRoot.getElementById('userLogoutDialog').close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('logout-view', LogoutView)
|
window.customElements.define('logout-view', LogoutView)
|
@ -1,77 +1,66 @@
|
|||||||
import {html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {installRouter} from 'pwa-helpers/router.js'
|
import { connect } from 'pwa-helpers'
|
||||||
import {connect} from 'pwa-helpers'
|
import { store } from '../store'
|
||||||
import {store} from '../store.js'
|
import { installRouter } from 'pwa-helpers/router'
|
||||||
import {doNavigate} from '../redux/app/app-actions.js'
|
import { doNavigate } from '../redux/app/app-actions'
|
||||||
|
import { loadPlugins } from '../plugins/load-plugins'
|
||||||
import isElectron from 'is-electron'
|
import isElectron from 'is-electron'
|
||||||
import '../plugins/streams.js'
|
import './login-view/login-view'
|
||||||
|
import './app-view'
|
||||||
import {loadPlugins} from '../plugins/load-plugins.js'
|
import '../plugins/streams'
|
||||||
|
import '../styles/app-styles'
|
||||||
import '../styles/app-styles.js'
|
|
||||||
import './login-view/login-view.js'
|
|
||||||
import './app-view.js'
|
|
||||||
|
|
||||||
installRouter((location) => store.dispatch(doNavigate(location)))
|
installRouter((location) => store.dispatch(doNavigate(location)))
|
||||||
|
|
||||||
class MainApp extends connect(store)(LitElement) {
|
class MainApp extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
name: { type: String },
|
name: { type: String },
|
||||||
loggedIn: { type: Boolean }
|
loggedIn: { type: Boolean }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
render() {
|
||||||
return []
|
return html`${this.renderViews(this.loggedIn)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
connectedCallback() {
|
||||||
return html`${this.renderViews(this.loggedIn)}`
|
super.connectedCallback()
|
||||||
}
|
this.initial = 0
|
||||||
|
|
||||||
/**
|
if (!isElectron()) {
|
||||||
* Dynamic renderViews method to introduce conditional rendering of views based on user's logged in state.
|
} else {
|
||||||
* @param {Boolean} isLoggedIn
|
window.addEventListener('contextmenu', (event) => {
|
||||||
*/
|
event.preventDefault()
|
||||||
|
window.electronAPI.showMyMenu()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
renderViews(isLoggedIn) {
|
/**
|
||||||
if (isLoggedIn) {
|
* Dynamic renderViews method to introduce conditional rendering of views based on user's logged in state.
|
||||||
return html`
|
* @param {Boolean} isLoggedIn
|
||||||
<app-view></app-view>
|
*/
|
||||||
`
|
renderViews(isLoggedIn) {
|
||||||
} else {
|
if (isLoggedIn) {
|
||||||
return html`
|
return html`<app-view></app-view>`
|
||||||
<login-view></login-view>
|
} else {
|
||||||
`
|
return html`<login-view></login-view>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stateChanged(state) {
|
_loadPlugins() {
|
||||||
this.loggedIn = state.app.loggedIn
|
loadPlugins()
|
||||||
if (this.loggedIn === true && this.initial === 0) {
|
}
|
||||||
this.initial = this.initial + 1
|
|
||||||
this._loadPlugins()
|
|
||||||
}
|
|
||||||
document.title = state.config.coin.name
|
|
||||||
}
|
|
||||||
|
|
||||||
_loadPlugins() {
|
stateChanged(state) {
|
||||||
loadPlugins()
|
this.loggedIn = state.app.loggedIn
|
||||||
}
|
if (this.loggedIn === true && this.initial === 0) {
|
||||||
|
this.initial = this.initial + 1
|
||||||
connectedCallback() {
|
this._loadPlugins()
|
||||||
super.connectedCallback()
|
}
|
||||||
this.initial = 0
|
document.title = state.config.coin.name
|
||||||
|
}
|
||||||
if (!isElectron()) {
|
|
||||||
} else {
|
|
||||||
window.addEventListener('contextmenu', (event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
window.electronAPI.showMyMenu()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('main-app', MainApp)
|
window.customElements.define('main-app', MainApp)
|
@ -1,140 +1,114 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {registerTranslateConfig, translate, use} from '../../translate'
|
import { newSelectorStyles } from '../styles/core-css'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { registerTranslateConfig, translate, use } from '../../translate'
|
||||||
|
|
||||||
registerTranslateConfig({
|
registerTranslateConfig({
|
||||||
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
|
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
|
||||||
})
|
})
|
||||||
|
|
||||||
const checkLanguage = localStorage.getItem('qortalLanguage')
|
const checkLanguage = localStorage.getItem('qortalLanguage')
|
||||||
|
|
||||||
if (checkLanguage === null || checkLanguage.length === 0) {
|
if (checkLanguage === null || checkLanguage.length === 0) {
|
||||||
localStorage.setItem('qortalLanguage', 'us')
|
localStorage.setItem('qortalLanguage', 'us')
|
||||||
use('us')
|
use('us')
|
||||||
} else {
|
} else {
|
||||||
use(checkLanguage)
|
use(checkLanguage)
|
||||||
}
|
}
|
||||||
|
|
||||||
class NewSelector extends LitElement {
|
class NewSelector extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
theme: { type: String, reflect: true }
|
theme: { type: String, reflect: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [newSelectorStyles]
|
||||||
css`
|
}
|
||||||
select {
|
|
||||||
width: auto;
|
|
||||||
height: auto;
|
|
||||||
position: absolute;
|
|
||||||
top: 50px;
|
|
||||||
padding: 5px 5px 5px 5px;
|
|
||||||
font-size: 16px;
|
|
||||||
border: 1px solid var(--black);
|
|
||||||
border-radius: 3px;
|
|
||||||
color: var(--black);
|
|
||||||
background: var(--white);
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
*:focus {
|
constructor() {
|
||||||
outline: none;
|
super()
|
||||||
}
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
|
}
|
||||||
|
|
||||||
select option {
|
render() {
|
||||||
color: var(--black);
|
return html`
|
||||||
background: var(--white);
|
<div style="display: inline;">
|
||||||
line-height: 34px;
|
<span>
|
||||||
}
|
<img src="/img/${translate("selectmenu.languageflag")}-flag-round-icon-32.png" style="width: 24px; height: 24px; padding-top: 4px;" @click=${() => this.toggleMenu()}>
|
||||||
|
</span>
|
||||||
|
<select id="languageNew" style="display: none;" size="20" tabindex="0" @change="${this.changeLanguage}">
|
||||||
|
<option value="us">US - ${translate("selectmenu.english")}</option>
|
||||||
|
<option value="de">DE - ${translate("selectmenu.german")}</option>
|
||||||
|
<option value="es">ES - ${translate("selectmenu.spanish")}</option>
|
||||||
|
<option value="et">ET - ${translate("selectmenu.estonian")}</option>
|
||||||
|
<option value="fi">FI - ${translate("selectmenu.finnish")}</option>
|
||||||
|
<option value="fr">FR - ${translate("selectmenu.french")}</option>
|
||||||
|
<option value="hr">HR - ${translate("selectmenu.croatian")}</option>
|
||||||
|
<option value="hu">HU - ${translate("selectmenu.hungarian")}</option>
|
||||||
|
<option value="hindi">IN - ${translate("selectmenu.hindi")}</option>
|
||||||
|
<option value="it">IT - ${translate("selectmenu.italian")}</option>
|
||||||
|
<option value="jp">JP - ${translate("selectmenu.japanese")}</option>
|
||||||
|
<option value="ko">KO - ${translate("selectmenu.korean")}</option>
|
||||||
|
<option value="nl">NL - ${translate("selectmenu.dutch")}</option>
|
||||||
|
<option value="no">NO - ${translate("selectmenu.norwegian")}</option>
|
||||||
|
<option value="pl">PL - ${translate("selectmenu.polish")}</option>
|
||||||
|
<option value="pt">PT - ${translate("selectmenu.portuguese")}</option>
|
||||||
|
<option value="rs">RS - ${translate("selectmenu.serbian")}</option>
|
||||||
|
<option value="ro">RO - ${translate("selectmenu.romanian")}</option>
|
||||||
|
<option value="ru">RU - ${translate("selectmenu.russian")}</option>
|
||||||
|
<option value="zht">ZHT - ${translate("selectmenu.chinese2")}</option>
|
||||||
|
<option value="zhc">ZHC - ${translate("selectmenu.chinese1")}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
select option:hover {
|
`
|
||||||
color: var(--white);
|
}
|
||||||
background: var(--black);
|
|
||||||
line-height: 34px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
firstUpdated() {
|
||||||
super()
|
const myElement = this.shadowRoot.getElementById('languageNew')
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
myElement.addEventListener('change', () => {
|
||||||
return html`
|
this.selectElement()
|
||||||
<div style="display: inline;">
|
})
|
||||||
<span>
|
|
||||||
<img src="/img/${translate("selectmenu.languageflag")}-flag-round-icon-32.png" style="width: 24px; height: 24px; padding-top: 4px;" @click=${() => this.toggleMenu()}>
|
|
||||||
</span>
|
|
||||||
<select id="languageNew" style="display: none;" size="20" tabindex="0" @change="${this.changeLanguage}">
|
|
||||||
<option value="us">US - ${translate("selectmenu.english")}</option>
|
|
||||||
<option value="de">DE - ${translate("selectmenu.german")}</option>
|
|
||||||
<option value="es">ES - ${translate("selectmenu.spanish")}</option>
|
|
||||||
<option value="et">ET - ${translate("selectmenu.estonian")}</option>
|
|
||||||
<option value="fi">FI - ${translate("selectmenu.finnish")}</option>
|
|
||||||
<option value="fr">FR - ${translate("selectmenu.french")}</option>
|
|
||||||
<option value="hr">HR - ${translate("selectmenu.croatian")}</option>
|
|
||||||
<option value="hu">HU - ${translate("selectmenu.hungarian")}</option>
|
|
||||||
<option value="hindi">IN - ${translate("selectmenu.hindi")}</option>
|
|
||||||
<option value="it">IT - ${translate("selectmenu.italian")}</option>
|
|
||||||
<option value="jp">JP - ${translate("selectmenu.japanese")}</option>
|
|
||||||
<option value="ko">KO - ${translate("selectmenu.korean")}</option>
|
|
||||||
<option value="nl">NL - ${translate("selectmenu.dutch")}</option>
|
|
||||||
<option value="no">NO - ${translate("selectmenu.norwegian")}</option>
|
|
||||||
<option value="pl">PL - ${translate("selectmenu.polish")}</option>
|
|
||||||
<option value="pt">PT - ${translate("selectmenu.portuguese")}</option>
|
|
||||||
<option value="rs">RS - ${translate("selectmenu.serbian")}</option>
|
|
||||||
<option value="ro">RO - ${translate("selectmenu.romanian")}</option>
|
|
||||||
<option value="ru">RU - ${translate("selectmenu.russian")}</option>
|
|
||||||
<option value="zht">ZHT - ${translate("selectmenu.chinese2")}</option>
|
|
||||||
<option value="zhc">ZHC - ${translate("selectmenu.chinese1")}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
`
|
myElement.addEventListener('click', () => {
|
||||||
}
|
const element1 = localStorage.getItem('qortalLanguage')
|
||||||
|
const element2 = this.shadowRoot.getElementById('languageNew').value
|
||||||
|
|
||||||
firstUpdated() {
|
if (element1 === element2) {
|
||||||
const myElement = this.shadowRoot.getElementById('languageNew')
|
myElement.style.display = 'none'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
myElement.addEventListener("change", () => {
|
this.selectElement()
|
||||||
this.selectElement()
|
}
|
||||||
})
|
|
||||||
|
|
||||||
myElement.addEventListener("click", () => {
|
selectElement() {
|
||||||
const element1 = localStorage.getItem('qortalLanguage')
|
const selectedLanguage = localStorage.getItem('qortalLanguage')
|
||||||
const element2 = this.shadowRoot.getElementById('languageNew').value
|
|
||||||
if (element1 === element2) {
|
|
||||||
myElement.style.display = "none"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
this.selectElement()
|
let element = this.shadowRoot.getElementById('languageNew')
|
||||||
}
|
|
||||||
|
|
||||||
selectElement() {
|
element.value = selectedLanguage
|
||||||
const selectedLanguage = localStorage.getItem('qortalLanguage')
|
element.style.display = 'none'
|
||||||
let element = this.shadowRoot.getElementById('languageNew')
|
}
|
||||||
element.value = selectedLanguage
|
|
||||||
element.style.display = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
changeLanguage(event) {
|
changeLanguage(event) {
|
||||||
use(event.target.value)
|
use(event.target.value)
|
||||||
localStorage.setItem('qortalLanguage', event.target.value)
|
localStorage.setItem('qortalLanguage', event.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleMenu() {
|
toggleMenu() {
|
||||||
let mySwitchDisplay = this.shadowRoot.getElementById('languageNew')
|
let mySwitchDisplay = this.shadowRoot.getElementById('languageNew')
|
||||||
if(mySwitchDisplay.style.display == "none") {
|
|
||||||
mySwitchDisplay.style.display = "block"
|
if (mySwitchDisplay.style.display == 'none') {
|
||||||
} else {
|
mySwitchDisplay.style.display = 'block'
|
||||||
mySwitchDisplay.style.display = "none"
|
} else {
|
||||||
}
|
mySwitchDisplay.style.display = 'none'
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('new-selector', NewSelector)
|
window.customElements.define('new-selector', NewSelector)
|
@ -1,163 +1,71 @@
|
|||||||
import {css, html, LitElement} from 'lit';
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers';
|
import { repeat } from 'lit/directives/repeat.js'
|
||||||
|
import { connect } from 'pwa-helpers'
|
||||||
import '@vaadin/item';
|
import { store } from '../../store'
|
||||||
import '@vaadin/list-box';
|
import { setNewNotification } from '../../redux/app/app-actions'
|
||||||
import '@polymer/paper-icon-button/paper-icon-button.js';
|
import { notificationBellGeneralStyles, notificationItemTxStyles } from '../../styles/core-css'
|
||||||
import '@polymer/iron-icons/iron-icons.js';
|
import './popover.js'
|
||||||
import {store} from '../../store.js';
|
import '../../../../plugins/plugins/core/components/TimeAgo'
|
||||||
import {setNewNotification} from '../../redux/app/app-actions.js';
|
import '@material/mwc-icon'
|
||||||
import '@material/mwc-icon';
|
import '@polymer/paper-icon-button/paper-icon-button.js'
|
||||||
import {get, translate} from '../../../translate'
|
import '@polymer/iron-icons/iron-icons.js'
|
||||||
import {repeat} from 'lit/directives/repeat.js';
|
import '@vaadin/item'
|
||||||
import '../../../../plugins/plugins/core/components/TimeAgo.js';
|
import '@vaadin/list-box'
|
||||||
import './popover.js';
|
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { get, translate } from '../../../translate'
|
||||||
|
|
||||||
class NotificationBellGeneral extends connect(store)(LitElement) {
|
class NotificationBellGeneral extends connect(store)(LitElement) {
|
||||||
static properties = {
|
static get properties() {
|
||||||
notifications: { type: Array },
|
return {
|
||||||
showNotifications: { type: Boolean },
|
notifications: { type: Array },
|
||||||
notificationCount: { type: Boolean },
|
showNotifications: { type: Boolean },
|
||||||
theme: { type: String, reflect: true },
|
notificationCount: { type: Boolean },
|
||||||
currentNotification: { type: Object },
|
currentNotification: { type: Object },
|
||||||
};
|
theme: { type: String, reflect: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [notificationBellGeneralStyles]
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super()
|
||||||
this.notifications = [];
|
this.notifications = []
|
||||||
this.showNotifications = false;
|
this.showNotifications = false
|
||||||
this.notificationCount = false;
|
this.notificationCount = false
|
||||||
this.initialFetch = false;
|
this.initialFetch = false
|
||||||
this.theme = localStorage.getItem('qortalTheme')
|
this.currentNotification = null
|
||||||
? localStorage.getItem('qortalTheme')
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
: 'light';
|
|
||||||
this.currentNotification = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated() {
|
|
||||||
try {
|
|
||||||
let value = JSON.parse(localStorage.getItem('isFirstTimeUser'));
|
|
||||||
if (!value && value !== false) {
|
|
||||||
value = true;
|
|
||||||
}
|
|
||||||
this.isFirstTimeUser = value;
|
|
||||||
} catch (error) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
async stateChanged(state) {
|
|
||||||
if (state.app.newNotification) {
|
|
||||||
const newNotification = state.app.newNotification;
|
|
||||||
this.notifications = [newNotification, ...this.notifications];
|
|
||||||
store.dispatch(setNewNotification(null));
|
|
||||||
if (this.isFirstTimeUser) {
|
|
||||||
const target = this.shadowRoot.getElementById(
|
|
||||||
'popover-notification'
|
|
||||||
);
|
|
||||||
const popover =
|
|
||||||
this.shadowRoot.querySelector('popover-component');
|
|
||||||
if (popover) {
|
|
||||||
popover.openPopover(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
localStorage.setItem('isFirstTimeUser', JSON.stringify(false));
|
|
||||||
this.isFirstTimeUser = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleBlur() {
|
|
||||||
setTimeout(() => {
|
|
||||||
if (!this.shadowRoot.contains(document.activeElement)) {
|
|
||||||
this.showNotifications = false;
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
changeStatus(signature, statusTx) {
|
|
||||||
const copyNotifications = [...this.notifications];
|
|
||||||
const findNotification = this.notifications.findIndex(
|
|
||||||
(notification) => notification.reference.signature === signature
|
|
||||||
);
|
|
||||||
if (findNotification !== -1) {
|
|
||||||
copyNotifications[findNotification] = {
|
|
||||||
...copyNotifications[findNotification],
|
|
||||||
status: statusTx,
|
|
||||||
};
|
|
||||||
this.notifications = copyNotifications;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const hasOngoing = this.notifications.find(
|
const hasOngoing = this.notifications.find(
|
||||||
(notification) => notification.status !== 'confirmed'
|
(notification) => notification.status !== 'confirmed'
|
||||||
);
|
)
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<popover-component
|
<popover-component for="popover-notification" message=${get('notifications.explanation')}></popover-component>
|
||||||
for="popover-notification"
|
<div id="popover-notification" @click=${() => this._toggleNotifications()}>
|
||||||
message=${get('notifications.explanation')}
|
${hasOngoing ? html`
|
||||||
></popover-component>
|
<mwc-icon id="notification-general-icon" style="color: green;cursor:pointer;user-select:none">notifications</mwc-icon>
|
||||||
<div
|
<vaadin-tooltip for="notification-general-icon" position="bottom" hover-delay=${400} hide-delay=${1} text=${get('notifications.notify4')}></vaadin-tooltip>
|
||||||
id="popover-notification"
|
` : html`
|
||||||
@click=${() => this._toggleNotifications()}
|
<mwc-icon id="notification-general-icon" style="color: var(--black); cursor:pointer;user-select:none">notifications</mwc-icon>
|
||||||
>
|
<vaadin-tooltip for="notification-general-icon" position="bottom" hover-delay=${400} hide-delay=${1}text=${get('notifications.notify4')}></vaadin-tooltip>
|
||||||
${hasOngoing
|
`}
|
||||||
? html`
|
|
||||||
<mwc-icon id="notification-general-icon" style="color: green;cursor:pointer;user-select:none"
|
|
||||||
>notifications</mwc-icon
|
|
||||||
>
|
|
||||||
<vaadin-tooltip
|
|
||||||
for="notification-general-icon"
|
|
||||||
position="bottom"
|
|
||||||
hover-delay=${400}
|
|
||||||
hide-delay=${1}
|
|
||||||
text=${get('notifications.notify4')}>
|
|
||||||
</vaadin-tooltip>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<mwc-icon
|
|
||||||
id="notification-general-icon"
|
|
||||||
style="color: var(--black); cursor:pointer;user-select:none"
|
|
||||||
>notifications</mwc-icon
|
|
||||||
>
|
|
||||||
<vaadin-tooltip
|
|
||||||
for="notification-general-icon"
|
|
||||||
position="bottom"
|
|
||||||
hover-delay=${400}
|
|
||||||
hide-delay=${1}
|
|
||||||
text=${get('notifications.notify4')}>
|
|
||||||
</vaadin-tooltip>
|
|
||||||
`}
|
|
||||||
</div>
|
</div>
|
||||||
${hasOngoing
|
${hasOngoing ? html`
|
||||||
? html`
|
<span class="count" style="cursor:pointer" @click=${() => this._toggleNotifications()}>
|
||||||
<span
|
<mwc-icon style="color: var(--black);font-size:18px">pending</mwc-icon>
|
||||||
class="count"
|
</span>
|
||||||
style="cursor:pointer"
|
` : ''}
|
||||||
@click=${() => this._toggleNotifications()}
|
<div id="notification-panel" class="popover-panel" style="visibility:${this.showNotifications ? 'visibile' : 'hidden'}" tabindex="0" @blur=${this.handleBlur}>
|
||||||
>
|
|
||||||
<mwc-icon
|
|
||||||
style="color: var(--black);font-size:18px"
|
|
||||||
>pending</mwc-icon
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
`
|
|
||||||
: ''}
|
|
||||||
|
|
||||||
<div
|
|
||||||
id="notification-panel"
|
|
||||||
class="popover-panel"
|
|
||||||
style="visibility:${this.showNotifications
|
|
||||||
? 'visibile'
|
|
||||||
: 'hidden'}"
|
|
||||||
tabindex="0"
|
|
||||||
@blur=${this.handleBlur}
|
|
||||||
>
|
|
||||||
<div class="notifications-list">
|
<div class="notifications-list">
|
||||||
${this.notifications.length === 0 ? html`
|
${this.notifications.length === 0 ? html`
|
||||||
<p style="font-size: 16px; width: 100%; text-align:center;margin-top:20px;">${translate('notifications.notify3')}</p>
|
<p style="font-size: 16px; width: 100%; text-align:center;margin-top:20px;">${translate('notifications.notify3')}</p>
|
||||||
` : ''}
|
` : ''}
|
||||||
${repeat(
|
${repeat(
|
||||||
this.notifications,
|
this.notifications,
|
||||||
@ -166,18 +74,82 @@ class NotificationBellGeneral extends connect(store)(LitElement) {
|
|||||||
<notification-item-tx
|
<notification-item-tx
|
||||||
.changeStatus=${(val1, val2) =>
|
.changeStatus=${(val1, val2) =>
|
||||||
this.changeStatus(val1, val2)}
|
this.changeStatus(val1, val2)}
|
||||||
status=${notification.status}
|
status=${notification.status}
|
||||||
timestamp=${notification.timestamp}
|
timestamp=${notification.timestamp}
|
||||||
type=${notification.type}
|
type=${notification.type}
|
||||||
signature=${notification.reference
|
signature=${notification.reference
|
||||||
.signature}
|
.signature
|
||||||
|
}
|
||||||
></notification-item-tx>
|
></notification-item-tx>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
try {
|
||||||
|
let value = JSON.parse(localStorage.getItem('isFirstTimeUser'))
|
||||||
|
|
||||||
|
if (!value && value !== false) {
|
||||||
|
value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isFirstTimeUser = value
|
||||||
|
} catch (error) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
async stateChanged(state) {
|
||||||
|
if (state.app.newNotification) {
|
||||||
|
const newNotification = state.app.newNotification
|
||||||
|
|
||||||
|
this.notifications = [newNotification, ...this.notifications]
|
||||||
|
|
||||||
|
store.dispatch(setNewNotification(null))
|
||||||
|
|
||||||
|
if (this.isFirstTimeUser) {
|
||||||
|
const target = this.shadowRoot.getElementById(
|
||||||
|
'popover-notification'
|
||||||
|
)
|
||||||
|
|
||||||
|
const popover = this.shadowRoot.querySelector('popover-component')
|
||||||
|
|
||||||
|
if (popover) {
|
||||||
|
popover.openPopover(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem('isFirstTimeUser', JSON.stringify(false))
|
||||||
|
|
||||||
|
this.isFirstTimeUser = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleBlur() {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!this.shadowRoot.contains(document.activeElement)) {
|
||||||
|
this.showNotifications = false
|
||||||
|
}
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
changeStatus(signature, statusTx) {
|
||||||
|
const copyNotifications = [...this.notifications]
|
||||||
|
|
||||||
|
const findNotification = this.notifications.findIndex(
|
||||||
|
(notification) => notification.reference.signature === signature
|
||||||
|
)
|
||||||
|
|
||||||
|
if (findNotification !== -1) {
|
||||||
|
copyNotifications[findNotification] = {
|
||||||
|
...copyNotifications[findNotification],
|
||||||
|
status: statusTx
|
||||||
|
}
|
||||||
|
|
||||||
|
this.notifications = copyNotifications
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_toggleNotifications() {
|
_toggleNotifications() {
|
||||||
@ -188,153 +160,34 @@ class NotificationBellGeneral extends connect(store)(LitElement) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
.layout {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.count {
|
|
||||||
position: absolute;
|
|
||||||
top: -5px;
|
|
||||||
right: -5px;
|
|
||||||
font-size: 12px;
|
|
||||||
background-color: red;
|
|
||||||
color: white;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nocount {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-panel {
|
|
||||||
position: absolute;
|
|
||||||
width: 200px;
|
|
||||||
padding: 10px;
|
|
||||||
background-color: var(--white);
|
|
||||||
border: 1px solid var(--black);
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
||||||
top: 40px;
|
|
||||||
max-height: 350px;
|
|
||||||
overflow: auto;
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: #6a6c75 #a1a1a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-panel::-webkit-scrollbar {
|
|
||||||
width: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-panel::-webkit-scrollbar-track {
|
|
||||||
background: #a1a1a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-panel::-webkit-scrollbar-thumb {
|
|
||||||
background-color: #6a6c75;
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 3px solid #a1a1a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notifications-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-item {
|
|
||||||
padding: 5px;
|
|
||||||
border-bottom: 1px solid;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: 0.2s all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-item:hover {
|
|
||||||
background: var(--nav-color-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 14px;
|
|
||||||
color: var(--black);
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('notification-bell-general', NotificationBellGeneral);
|
window.customElements.define('notification-bell-general', NotificationBellGeneral)
|
||||||
|
|
||||||
class NotificationItemTx extends connect(store)(LitElement) {
|
class NotificationItemTx extends connect(store)(LitElement) {
|
||||||
static properties = {
|
static get properties() {
|
||||||
status: { type: String },
|
return {
|
||||||
type: { type: String },
|
status: { type: String },
|
||||||
timestamp: { type: Number },
|
type: { type: String },
|
||||||
signature: { type: String },
|
timestamp: { type: Number },
|
||||||
changeStatus: { attribute: false },
|
signature: { type: String },
|
||||||
};
|
changeStatus: { attribute: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [notificationItemTxStyles]
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super()
|
||||||
this.nodeUrl = this.getNodeUrl();
|
this.nodeUrl = this.getNodeUrl()
|
||||||
this.myNode = this.getMyNode();
|
this.myNode = this.getMyNode()
|
||||||
}
|
|
||||||
|
|
||||||
getNodeUrl() {
|
|
||||||
const myNode =
|
|
||||||
store.getState().app.nodeConfig.knownNodes[
|
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
]
|
|
||||||
|
|
||||||
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
|
||||||
}
|
|
||||||
getMyNode() {
|
|
||||||
return store.getState().app.nodeConfig.knownNodes[
|
|
||||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
async getStatus() {
|
|
||||||
let interval = null;
|
|
||||||
let stop = false;
|
|
||||||
const getAnswer = async () => {
|
|
||||||
const getTx = async (minterAddr) => {
|
|
||||||
const url = `${this.nodeUrl}/transactions/signature/${this.signature}`
|
|
||||||
const res = await fetch(url)
|
|
||||||
return await res.json()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stop) {
|
|
||||||
stop = true;
|
|
||||||
try {
|
|
||||||
const txTransaction = await getTx();
|
|
||||||
if (!txTransaction.error && txTransaction.signature && txTransaction.blockHeight) {
|
|
||||||
clearInterval(interval);
|
|
||||||
this.changeStatus(this.signature, 'confirmed');
|
|
||||||
}
|
|
||||||
} catch (error) {}
|
|
||||||
stop = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
interval = setInterval(getAnswer, 20000);
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated() {
|
|
||||||
this.getStatus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div class="notification-item" @click=${() => {}}>
|
<div class="notification-item" @click=${() => { }}>
|
||||||
<div>
|
<div>
|
||||||
<p style="margin-bottom:10px; font-weight:bold">
|
<p style="margin-bottom:10px; font-weight:bold">
|
||||||
${translate('transpage.tchange1')}
|
${translate('transpage.tchange1')}
|
||||||
@ -345,187 +198,66 @@ class NotificationItemTx extends connect(store)(LitElement) {
|
|||||||
${translate('walletpage.wchange35')}: ${this.type}
|
${translate('walletpage.wchange35')}: ${this.type}
|
||||||
</p>
|
</p>
|
||||||
<p style="margin-bottom:5px">
|
<p style="margin-bottom:5px">
|
||||||
${translate('tubespage.schange28')}:
|
${translate('tubespage.schange28')}: ${this.status === 'confirming' ? translate('notifications.notify1') : translate('notifications.notify2')}
|
||||||
${this.status === 'confirming'
|
|
||||||
? translate('notifications.notify1')
|
|
||||||
: translate('notifications.notify2')}
|
|
||||||
</p>
|
</p>
|
||||||
${this.status !== 'confirmed'
|
${this.status !== 'confirmed' ? html`<div class="centered"><div class="loader">Loading...</div></div>` : ''}
|
||||||
? html`
|
<div style="display:flex;justify-content:space-between;align-items:center">
|
||||||
<div class="centered">
|
<message-time timestamp=${this.timestamp} style="color:red;font-size:12px"></message-time>
|
||||||
<div class="loader">Loading...</div>
|
${this.status === 'confirmed' ? html`<mwc-icon style="color: green;">done</mwc-icon>` : ''}
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ''}
|
|
||||||
<div
|
|
||||||
style="display:flex;justify-content:space-between;align-items:center"
|
|
||||||
>
|
|
||||||
<message-time
|
|
||||||
timestamp=${this.timestamp}
|
|
||||||
style="color:red;font-size:12px"
|
|
||||||
></message-time>
|
|
||||||
${this.status === 'confirmed'
|
|
||||||
? html`
|
|
||||||
<mwc-icon style="color: green;"
|
|
||||||
>done</mwc-icon
|
|
||||||
>
|
|
||||||
`
|
|
||||||
: ''}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
this.getStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeUrl() {
|
||||||
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
|
}
|
||||||
|
|
||||||
|
getMyNode() {
|
||||||
|
return store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
}
|
||||||
|
|
||||||
|
async getStatus() {
|
||||||
|
let interval = null
|
||||||
|
let stop = false
|
||||||
|
|
||||||
|
const getAnswer = async () => {
|
||||||
|
const getTx = async (minterAddr) => {
|
||||||
|
const url = `${this.nodeUrl}/transactions/signature/${this.signature}`
|
||||||
|
const res = await fetch(url)
|
||||||
|
|
||||||
|
return await res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stop) {
|
||||||
|
stop = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const txTransaction = await getTx()
|
||||||
|
|
||||||
|
if (!txTransaction.error && txTransaction.signature && txTransaction.blockHeight) {
|
||||||
|
clearInterval(interval)
|
||||||
|
this.changeStatus(this.signature, 'confirmed')
|
||||||
|
}
|
||||||
|
} catch (error) { }
|
||||||
|
|
||||||
|
stop = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interval = setInterval(getAnswer, 20000)
|
||||||
}
|
}
|
||||||
|
|
||||||
_toggleNotifications() {
|
_toggleNotifications() {
|
||||||
if (this.notifications.length === 0) return;
|
if (this.notifications.length === 0) return
|
||||||
this.showNotifications = !this.showNotifications;
|
this.showNotifications = !this.showNotifications
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
.centered {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.layout {
|
|
||||||
width: 100px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.count {
|
|
||||||
position: absolute;
|
|
||||||
top: -5px;
|
|
||||||
right: -5px;
|
|
||||||
font-size: 12px;
|
|
||||||
background-color: red;
|
|
||||||
color: white;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nocount {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-panel {
|
|
||||||
position: absolute;
|
|
||||||
width: 200px;
|
|
||||||
padding: 10px;
|
|
||||||
background-color: var(--white);
|
|
||||||
border: 1px solid var(--black);
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
||||||
top: 40px;
|
|
||||||
max-height: 350px;
|
|
||||||
overflow: auto;
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: #6a6c75 #a1a1a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-panel::-webkit-scrollbar {
|
|
||||||
width: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-panel::-webkit-scrollbar-track {
|
|
||||||
background: #a1a1a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-panel::-webkit-scrollbar-thumb {
|
|
||||||
background-color: #6a6c75;
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 3px solid #a1a1a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notifications-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-item {
|
|
||||||
padding: 5px;
|
|
||||||
border-bottom: 1px solid;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-item:hover {
|
|
||||||
background: var(--nav-color-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 14px;
|
|
||||||
color: var(--black);
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loader,
|
|
||||||
.loader:before,
|
|
||||||
.loader:after {
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
-webkit-animation-fill-mode: both;
|
|
||||||
animation-fill-mode: both;
|
|
||||||
-webkit-animation: load7 1.8s infinite ease-in-out;
|
|
||||||
animation: load7 1.8s infinite ease-in-out;
|
|
||||||
}
|
|
||||||
.loader {
|
|
||||||
color: var(--black);
|
|
||||||
font-size: 5px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
position: relative;
|
|
||||||
text-indent: -9999em;
|
|
||||||
-webkit-transform: translateZ(0);
|
|
||||||
-ms-transform: translateZ(0);
|
|
||||||
transform: translateZ(0);
|
|
||||||
-webkit-animation-delay: -0.16s;
|
|
||||||
animation-delay: -0.16s;
|
|
||||||
}
|
|
||||||
.loader:before,
|
|
||||||
.loader:after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
.loader:before {
|
|
||||||
left: -3.5em;
|
|
||||||
-webkit-animation-delay: -0.32s;
|
|
||||||
animation-delay: -0.32s;
|
|
||||||
}
|
|
||||||
.loader:after {
|
|
||||||
left: 3.5em;
|
|
||||||
}
|
|
||||||
@-webkit-keyframes load7 {
|
|
||||||
0%,
|
|
||||||
80%,
|
|
||||||
100% {
|
|
||||||
box-shadow: 0 2.5em 0 -1.3em;
|
|
||||||
}
|
|
||||||
40% {
|
|
||||||
box-shadow: 0 2.5em 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes load7 {
|
|
||||||
0%,
|
|
||||||
80%,
|
|
||||||
100% {
|
|
||||||
box-shadow: 0 2.5em 0 -1.3em;
|
|
||||||
}
|
|
||||||
40% {
|
|
||||||
box-shadow: 0 2.5em 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('notification-item-tx', NotificationItemTx);
|
window.customElements.define('notification-item-tx', NotificationItemTx)
|
@ -1,329 +1,262 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { css, html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
|
import { store } from '../../store'
|
||||||
|
import { setNewTab } from '../../redux/app/app-actions'
|
||||||
|
import { routes } from '../../plugins/routes'
|
||||||
|
import { notificationBellStyles } from '../../styles/core-css'
|
||||||
|
import config from '../../notifications/config'
|
||||||
|
import '../../../../plugins/plugins/core/components/TimeAgo'
|
||||||
|
import '@material/mwc-icon'
|
||||||
|
import '@polymer/paper-icon-button/paper-icon-button'
|
||||||
|
import '@polymer/iron-icons/iron-icons.js'
|
||||||
import '@vaadin/item'
|
import '@vaadin/item'
|
||||||
import '@vaadin/list-box'
|
import '@vaadin/list-box'
|
||||||
import '@polymer/paper-icon-button/paper-icon-button.js'
|
|
||||||
import '@polymer/iron-icons/iron-icons.js'
|
|
||||||
import {store} from '../../store.js'
|
|
||||||
import {setNewTab} from '../../redux/app/app-actions.js'
|
|
||||||
import {routes} from '../../plugins/routes.js'
|
|
||||||
import '@material/mwc-icon';
|
|
||||||
|
|
||||||
import config from '../../notifications/config.js'
|
|
||||||
import '../../../../plugins/plugins/core/components/TimeAgo.js'
|
|
||||||
|
|
||||||
class NotificationBell extends connect(store)(LitElement) {
|
class NotificationBell extends connect(store)(LitElement) {
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
notifications: { type: Array },
|
||||||
|
showNotifications: { type: Boolean },
|
||||||
|
notificationCount: { type: Boolean },
|
||||||
|
theme: { type: String, reflect: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static properties = {
|
static get styles() {
|
||||||
notifications: { type: Array },
|
return [notificationBellStyles]
|
||||||
showNotifications: { type: Boolean },
|
}
|
||||||
notificationCount: { type: Boolean },
|
|
||||||
theme: { type: String, reflect: true },
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.notifications = []
|
this.notifications = []
|
||||||
this.showNotifications = false
|
this.showNotifications = false
|
||||||
this.notificationCount = false
|
this.notificationCount = false
|
||||||
this.initialFetch = false
|
this.initialFetch = false
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
render() {
|
||||||
this.getNotifications();
|
return html`
|
||||||
document.addEventListener('click', (event) => {
|
<div class="layout">
|
||||||
const path = event.composedPath()
|
${this.notificationCount ? html`
|
||||||
if (!path.includes(this)) {
|
<mwc-icon @click=${() => this._toggleNotifications()} id="notification-mail-icon" style="color: green;cursor:pointer;user-select:none">
|
||||||
this.showNotifications = false
|
mail
|
||||||
}
|
</mwc-icon>
|
||||||
})
|
<vaadin-tooltip
|
||||||
}
|
for="notification-mail-icon"
|
||||||
|
position="bottom"
|
||||||
|
hover-delay=${400}
|
||||||
|
hide-delay=${1}
|
||||||
|
text="Q-Mail">
|
||||||
|
</vaadin-tooltip>
|
||||||
|
` : html`
|
||||||
|
<mwc-icon @click=${() => this._openTabQmail()} id="notification-mail-icon" style="color: var(--black); cursor:pointer;user-select:none">
|
||||||
|
mail
|
||||||
|
</mwc-icon>
|
||||||
|
<vaadin-tooltip
|
||||||
|
for="notification-mail-icon"
|
||||||
|
position="bottom"
|
||||||
|
hover-delay=${400}
|
||||||
|
hide-delay=${1}
|
||||||
|
text="Q-Mail">
|
||||||
|
</vaadin-tooltip>
|
||||||
|
`}
|
||||||
|
${this.notificationCount ? html`
|
||||||
|
<span class="count">${this.notifications.length}</span>
|
||||||
|
` : ''}
|
||||||
|
<div class="popover-panel" ?hidden=${!this.showNotifications}>
|
||||||
|
<div class="notifications-list">
|
||||||
|
${this.notifications.map(notification => html`
|
||||||
|
<div
|
||||||
|
class="notification-item"
|
||||||
|
@click=${() => {
|
||||||
|
const query = `?service=APP&name=Q-Mail`
|
||||||
|
store.dispatch(setNewTab({
|
||||||
|
url: `qdn/browser/index.html${query}`,
|
||||||
|
id: 'q-mail-notification',
|
||||||
|
myPlugObj: {
|
||||||
|
"url": "myapp",
|
||||||
|
"domain": "core",
|
||||||
|
"page": `qdn/browser/index.html${query}`,
|
||||||
|
"title": "Q-Mail",
|
||||||
|
"icon": "vaadin:mailbox",
|
||||||
|
"mwcicon": "mail_outline",
|
||||||
|
"menus": [],
|
||||||
|
"parent": false
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
this.showNotifications = false
|
||||||
|
this.notifications = []
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<p>Q-Mail</p>
|
||||||
|
<message-time timestamp=${notification.created} style="color:red;font-size:12px"></message-time>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>${notification.name}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
getApiKey() {
|
firstUpdated() {
|
||||||
const apiNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
this.getNotifications()
|
||||||
|
|
||||||
|
document.addEventListener('click', (event) => {
|
||||||
|
const path = event.composedPath()
|
||||||
|
if (!path.includes(this)) {
|
||||||
|
this.showNotifications = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getApiKey() {
|
||||||
|
const apiNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
return apiNode.apiKey
|
return apiNode.apiKey
|
||||||
}
|
}
|
||||||
|
|
||||||
async getNotifications() {
|
async getNotifications() {
|
||||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
|
|
||||||
let interval = null
|
let interval = null
|
||||||
let stop = false
|
let stop = false
|
||||||
|
|
||||||
const getNewMail = async () => {
|
const getNewMail = async () => {
|
||||||
|
const getMail = async (recipientName, recipientAddress) => {
|
||||||
|
const query = `qortal_qmail_${recipientName.slice(
|
||||||
|
0,
|
||||||
|
20
|
||||||
|
)}_${recipientAddress.slice(-6)}_mail_`
|
||||||
|
|
||||||
const getMail = async (recipientName, recipientAddress) => {
|
const url = `${nodeUrl}/arbitrary/resources/search?service=MAIL_PRIVATE&query=${query}&limit=10&includemetadata=false&offset=0&reverse=true&excludeblocked=true`
|
||||||
const query = `qortal_qmail_${recipientName.slice(
|
|
||||||
0,
|
const response = await fetch(url, {
|
||||||
20
|
method: 'GET',
|
||||||
)}_${recipientAddress.slice(-6)}_mail_`
|
headers: {
|
||||||
const url = `${nodeUrl}/arbitrary/resources/search?service=MAIL_PRIVATE&query=${query}&limit=10&includemetadata=false&offset=0&reverse=true&excludeblocked=true`
|
'Content-Type': 'application/json'
|
||||||
const response = await fetch(url, {
|
}
|
||||||
method: 'GET',
|
})
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return await response.json()
|
return await response.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stop && !this.showNotifications) {
|
if (!stop && !this.showNotifications) {
|
||||||
stop = true
|
stop = true
|
||||||
try {
|
|
||||||
const address = window.parent.reduxStore.getState().app?.selectedAddress?.address;
|
|
||||||
const name = window.parent.reduxStore.getState().app?.accountInfo?.names[0]?.name
|
|
||||||
|
|
||||||
if (!name || !address) return
|
try {
|
||||||
const mailArray = await getMail(name, address)
|
const address = window.parent.reduxStore.getState().app?.selectedAddress?.address;
|
||||||
let notificationsToShow = []
|
const name = window.parent.reduxStore.getState().app?.accountInfo?.names[0]?.name
|
||||||
if (mailArray.length > 0) {
|
|
||||||
const lastVisited = localStorage.getItem("Q-Mail-last-visited")
|
|
||||||
|
|
||||||
if (lastVisited) {
|
if (!name || !address) return
|
||||||
mailArray.forEach((mail) => {
|
|
||||||
if (mail.created > lastVisited) notificationsToShow.push(mail)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
notificationsToShow = mailArray
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
const mailArray = await getMail(name, address)
|
||||||
if (!this.initialFetch && notificationsToShow.length > 0) {
|
|
||||||
const mail = notificationsToShow[0]
|
|
||||||
const urlPic = `${nodeUrl}/arbitrary/THUMBNAIL/${mail.name}/qortal_avatar?async=true&apiKey=${this.getApiKey()}`
|
|
||||||
await routes.showNotification({
|
|
||||||
data: {
|
|
||||||
title: "New Q-Mail",
|
|
||||||
type: "qapp",
|
|
||||||
sound: config.messageAlert,
|
|
||||||
url: "",
|
|
||||||
options: {
|
|
||||||
body: `You have an unread mail from ${mail.name}`,
|
|
||||||
icon: urlPic,
|
|
||||||
badge: urlPic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else if (notificationsToShow.length > 0) {
|
|
||||||
if (notificationsToShow[0].created > (this.notifications[0]?.created || 0)) {
|
|
||||||
const mail = notificationsToShow[0]
|
|
||||||
const urlPic = `${nodeUrl}/arbitrary/THUMBNAIL/${mail.name}/qortal_avatar?async=true&apiKey=${this.getApiKey()}`
|
|
||||||
await routes.showNotification({
|
|
||||||
data: {
|
|
||||||
title: "New Q-Mail",
|
|
||||||
type: "qapp",
|
|
||||||
sound: config.messageAlert,
|
|
||||||
url: "",
|
|
||||||
options: {
|
|
||||||
body: `You have an unread mail from ${mail.name}`,
|
|
||||||
icon: urlPic,
|
|
||||||
badge: urlPic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.notifications = notificationsToShow
|
|
||||||
|
|
||||||
this.notificationCount = this.notifications.length !== 0;
|
let notificationsToShow = []
|
||||||
|
|
||||||
if (!this.initialFetch) this.initialFetch = true
|
if (mailArray.length > 0) {
|
||||||
} catch (error) {
|
const lastVisited = localStorage.getItem("Q-Mail-last-visited")
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
stop = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
|
|
||||||
setTimeout(() => {
|
if (lastVisited) {
|
||||||
getNewMail()
|
mailArray.forEach((mail) => {
|
||||||
}, 5000)
|
if (mail.created > lastVisited) notificationsToShow.push(mail)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
notificationsToShow = mailArray
|
||||||
|
}
|
||||||
|
|
||||||
interval = setInterval(getNewMail, 60000)
|
}
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
if (!this.initialFetch && notificationsToShow.length > 0) {
|
||||||
return html`
|
const mail = notificationsToShow[0]
|
||||||
<div class="layout">
|
const urlPic = `${nodeUrl}/arbitrary/THUMBNAIL/${mail.name}/qortal_avatar?async=true}`
|
||||||
${this.notificationCount ? html`
|
|
||||||
<mwc-icon @click=${() => this._toggleNotifications()} id="notification-mail-icon" style="color: green;cursor:pointer;user-select:none"
|
|
||||||
>mail</mwc-icon
|
|
||||||
>
|
|
||||||
<vaadin-tooltip
|
|
||||||
for="notification-mail-icon"
|
|
||||||
position="bottom"
|
|
||||||
hover-delay=${400}
|
|
||||||
hide-delay=${1}
|
|
||||||
text="Q-Mail">
|
|
||||||
</vaadin-tooltip>
|
|
||||||
|
|
||||||
` : html`
|
await routes.showNotification({
|
||||||
<mwc-icon @click=${() => this._openTabQmail()} id="notification-mail-icon" style="color: var(--black); cursor:pointer;user-select:none"
|
data: {
|
||||||
>mail</mwc-icon
|
title: 'New Q-Mail',
|
||||||
>
|
type: 'qapp',
|
||||||
<vaadin-tooltip
|
sound: config.messageAlert,
|
||||||
for="notification-mail-icon"
|
url: '',
|
||||||
position="bottom"
|
options: {
|
||||||
hover-delay=${400}
|
body: `You have an unread mail from ${mail.name}`,
|
||||||
hide-delay=${1}
|
icon: urlPic,
|
||||||
text="Q-Mail">
|
badge: urlPic
|
||||||
</vaadin-tooltip>
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (notificationsToShow.length > 0) {
|
||||||
|
if (notificationsToShow[0].created > (this.notifications[0]?.created || 0)) {
|
||||||
|
const mail = notificationsToShow[0]
|
||||||
|
const urlPic = `${nodeUrl}/arbitrary/THUMBNAIL/${mail.name}/qortal_avatar?async=true}`
|
||||||
|
|
||||||
`}
|
await routes.showNotification({
|
||||||
|
data: {
|
||||||
|
title: 'New Q-Mail',
|
||||||
|
type: 'qapp',
|
||||||
|
sound: config.messageAlert,
|
||||||
|
url: '',
|
||||||
|
options: {
|
||||||
|
body: `You have an unread mail from ${mail.name}`,
|
||||||
|
icon: urlPic,
|
||||||
|
badge: urlPic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
${this.notificationCount ? html`
|
this.notifications = notificationsToShow
|
||||||
<span class="count">${this.notifications.length}</span>
|
|
||||||
` : ''}
|
|
||||||
|
|
||||||
<div class="popover-panel" ?hidden=${!this.showNotifications}>
|
this.notificationCount = this.notifications.length !== 0
|
||||||
<div class="notifications-list">
|
|
||||||
${this.notifications.map(notification => html`
|
|
||||||
<div class="notification-item" @click=${() => {
|
|
||||||
const query = `?service=APP&name=Q-Mail`
|
|
||||||
store.dispatch(setNewTab({
|
|
||||||
url: `qdn/browser/index.html${query}`,
|
|
||||||
id: 'q-mail-notification',
|
|
||||||
myPlugObj: {
|
|
||||||
"url": "myapp",
|
|
||||||
"domain": "core",
|
|
||||||
"page": `qdn/browser/index.html${query}`,
|
|
||||||
"title": "Q-Mail",
|
|
||||||
"icon": "vaadin:mailbox",
|
|
||||||
"mwcicon": "mail_outline",
|
|
||||||
"menus": [],
|
|
||||||
"parent": false
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
this.showNotifications = false
|
|
||||||
this.notifications = []
|
|
||||||
}}>
|
|
||||||
<div>
|
|
||||||
<p>Q-Mail</p>
|
|
||||||
<message-time timestamp=${notification.created} style="color:red;font-size:12px"></message-time>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p>${notification.name}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
_toggleNotifications() {
|
if (!this.initialFetch) this.initialFetch = true
|
||||||
if (this.notifications.length === 0) return
|
} catch (error) {
|
||||||
this.showNotifications = !this.showNotifications
|
console.error(error)
|
||||||
}
|
}
|
||||||
_openTabQmail() {
|
|
||||||
const query = `?service=APP&name=Q-Mail`
|
|
||||||
store.dispatch(setNewTab({
|
|
||||||
url: `qdn/browser/index.html${query}`,
|
|
||||||
id: 'q-mail-notification',
|
|
||||||
myPlugObj: {
|
|
||||||
"url": "myapp",
|
|
||||||
"domain": "core",
|
|
||||||
"page": `qdn/browser/index.html${query}`,
|
|
||||||
"title": "Q-Mail",
|
|
||||||
"icon": "vaadin:mailbox",
|
|
||||||
"mwcicon": "mail_outline",
|
|
||||||
"menus": [],
|
|
||||||
"parent": false
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = css`
|
stop = false
|
||||||
.layout {
|
}
|
||||||
display: flex;
|
}
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.count {
|
try {
|
||||||
position: absolute;
|
setTimeout(() => {
|
||||||
top: -5px;
|
getNewMail()
|
||||||
right: -5px;
|
}, 5000)
|
||||||
font-size: 12px;
|
|
||||||
background-color: red;
|
|
||||||
color: white;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nocount {
|
interval = setInterval(getNewMail, 60000)
|
||||||
display: none;
|
} catch (error) {
|
||||||
}
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.popover-panel {
|
_toggleNotifications() {
|
||||||
position: absolute;
|
if (this.notifications.length === 0) return
|
||||||
width: 200px;
|
this.showNotifications = !this.showNotifications
|
||||||
padding: 10px;
|
}
|
||||||
background-color: var(--white);
|
|
||||||
border: 1px solid var(--black);
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
||||||
top: 40px;
|
|
||||||
max-height: 350px;
|
|
||||||
overflow: auto;
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: #6a6c75 #a1a1a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-panel::-webkit-scrollbar {
|
_openTabQmail() {
|
||||||
width: 11px;
|
const query = `?service=APP&name=Q-Mail`
|
||||||
}
|
|
||||||
|
|
||||||
.popover-panel::-webkit-scrollbar-track {
|
store.dispatch(setNewTab({
|
||||||
background: #a1a1a1;
|
url: `qdn/browser/index.html${query}`,
|
||||||
}
|
id: 'q-mail-notification',
|
||||||
|
myPlugObj: {
|
||||||
.popover-panel::-webkit-scrollbar-thumb {
|
"url": "myapp",
|
||||||
background-color: #6a6c75;
|
"domain": "core",
|
||||||
border-radius: 6px;
|
"page": `qdn/browser/index.html${query}`,
|
||||||
border: 3px solid #a1a1a1;
|
"title": "Q-Mail",
|
||||||
}
|
"icon": "vaadin:mailbox",
|
||||||
|
"mwcicon": "mail_outline",
|
||||||
.notifications-list {
|
"menus": [],
|
||||||
display: flex;
|
"parent": false
|
||||||
flex-direction: column;
|
}
|
||||||
}
|
}))
|
||||||
|
}
|
||||||
.notification-item {
|
|
||||||
padding: 5px;
|
|
||||||
border-bottom: 1px solid;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: 0.2s all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-item:hover {
|
|
||||||
background: var(--nav-color-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 14px;
|
|
||||||
color: var(--black);
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('notification-bell', NotificationBell)
|
window.customElements.define('notification-bell', NotificationBell)
|
@ -1,76 +1,56 @@
|
|||||||
// popover-component.js
|
import { css, html, LitElement } from 'lit'
|
||||||
import {css, html, LitElement} from 'lit';
|
import { createPopper } from '@popperjs/core'
|
||||||
import {createPopper} from '@popperjs/core';
|
import { popoverComponentStyles } from '../../styles/core-css'
|
||||||
import '@material/mwc-icon'
|
import '@material/mwc-icon'
|
||||||
|
|
||||||
export class PopoverComponent extends LitElement {
|
export class PopoverComponent extends LitElement {
|
||||||
static styles = css`
|
static get properties() {
|
||||||
:host {
|
return {
|
||||||
display: none;
|
for: { type: String, reflect: true },
|
||||||
position: absolute;
|
message: { type: String }
|
||||||
background-color: var(--white);
|
}
|
||||||
border: 1px solid #ddd;
|
}
|
||||||
padding: 8px;
|
|
||||||
z-index: 10;
|
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
||||||
color: var(--black);
|
|
||||||
max-width: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-icon {
|
static get styles() {
|
||||||
cursor: pointer;
|
return [popoverComponentStyles]
|
||||||
float: right;
|
}
|
||||||
margin-left: 10px;
|
|
||||||
color: var(--black)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.message = ''
|
||||||
|
}
|
||||||
|
|
||||||
`;
|
render() {
|
||||||
|
return html`
|
||||||
|
<span class="close-icon" @click="${this.closePopover}"><mwc-icon style="color: var(--black)">close</mwc-icon></span>
|
||||||
|
<div><mwc-icon style="color: var(--black)">info</mwc-icon> ${this.message} <slot></slot></div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
static properties = {
|
attachToTarget(target) {
|
||||||
for: { type: String, reflect: true },
|
if (!this.popperInstance && target) {
|
||||||
message: { type: String }
|
this.popperInstance = createPopper(target, this, {
|
||||||
};
|
placement: 'bottom',
|
||||||
|
strategy: 'fixed'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
openPopover(target) {
|
||||||
super();
|
this.attachToTarget(target)
|
||||||
this.message = '';
|
this.style.display = 'block'
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
closePopover() {
|
||||||
// We'll defer the popper attachment to the openPopover() method to ensure target availability
|
this.style.display = 'none'
|
||||||
}
|
|
||||||
|
|
||||||
attachToTarget(target) {
|
if (this.popperInstance) {
|
||||||
if (!this.popperInstance && target) {
|
this.popperInstance.destroy()
|
||||||
this.popperInstance = createPopper(target, this, {
|
this.popperInstance = null
|
||||||
placement: 'bottom',
|
}
|
||||||
strategy: 'fixed'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
openPopover(target) {
|
this.requestUpdate()
|
||||||
this.attachToTarget(target);
|
}
|
||||||
this.style.display = 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
closePopover() {
|
|
||||||
this.style.display = 'none';
|
|
||||||
if (this.popperInstance) {
|
|
||||||
this.popperInstance.destroy();
|
|
||||||
this.popperInstance = null;
|
|
||||||
}
|
|
||||||
this.requestUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`
|
|
||||||
<span class="close-icon" @click="${this.closePopover}"><mwc-icon style="color: var(--black)">close</mwc-icon></span>
|
|
||||||
<div><mwc-icon style="color: var(--black)">info</mwc-icon> ${this.message} <slot></slot>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('popover-component', PopoverComponent);
|
window.customElements.define('popover-component', PopoverComponent)
|
@ -1,134 +1,62 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {svgMoon, svgSun} from '../../assets/js/svg.js'
|
import { svgMoon, svgSun } from '../../assets/js/svg'
|
||||||
|
import { qortThemeToggleStyles } from '../styles/core-css'
|
||||||
|
|
||||||
class QortThemeToggle extends LitElement {
|
class QortThemeToggle extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
theme: {
|
theme: { type: String, reflect: true }
|
||||||
type: String,
|
}
|
||||||
reflect: true
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super()
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light';
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = [
|
static get styles() {
|
||||||
css`
|
return [qortThemeToggleStyles]
|
||||||
:host {
|
}
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
width: 54px;
|
|
||||||
height: 32px;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
render() {
|
||||||
width: 32px;
|
return html`
|
||||||
height: 32px;
|
<input type="checkbox" @change=${() => this.toggleTheme()}/>
|
||||||
}
|
<div class="slider"></div>
|
||||||
|
<div class="icon">
|
||||||
|
<span class="sun">${svgSun}</span>
|
||||||
|
<span class="moon">${svgMoon}</span>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
input {
|
firstUpdated() {
|
||||||
cursor: pointer;
|
this.initTheme()
|
||||||
position: absolute;
|
}
|
||||||
z-index: 1;
|
|
||||||
opacity: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider {
|
|
||||||
position: absolute;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
height: 16px;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
background-color: var(--switchbackground);
|
|
||||||
border: 2px solid var(--switchborder);
|
|
||||||
border-radius: 1rem;
|
|
||||||
transition: all .4s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
display: inline-block;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
background: var(--switchbackground);
|
|
||||||
border: 2px solid var(--switchborder);
|
|
||||||
border-radius: 50%;
|
|
||||||
transition: transform 300ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([theme="light"]) .icon {
|
|
||||||
transform: translate(0, -50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
input:checked ~ .icon,
|
|
||||||
:host([theme="dark"]) .icon {
|
|
||||||
transform: translate(calc(100% - 12px), -50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.moon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.moon svg {
|
|
||||||
transform: scale(0.6);
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([theme="dark"]) .sun {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([theme="dark"]) .moon {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
];
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`
|
|
||||||
<input type="checkbox" @change=${() => this.toggleTheme()}/>
|
|
||||||
<div class="slider"></div>
|
|
||||||
<div class="icon">
|
|
||||||
<span class="sun">${svgSun}</span>
|
|
||||||
<span class="moon">${svgMoon}</span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated() {
|
|
||||||
this.initTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
toggleTheme() {
|
toggleTheme() {
|
||||||
if (this.theme === 'light') {
|
if (this.theme === 'light') {
|
||||||
this.theme = 'dark';
|
this.theme = 'dark'
|
||||||
} else {
|
} else {
|
||||||
this.theme = 'light';
|
this.theme = 'light'
|
||||||
}
|
}
|
||||||
this.dispatchEvent(
|
|
||||||
new CustomEvent('qort-theme-change', {
|
|
||||||
bubbles: true,
|
|
||||||
composed: true,
|
|
||||||
detail: this.theme,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
window.localStorage.setItem('qortalTheme', this.theme);
|
this.dispatchEvent(
|
||||||
this.initTheme();
|
new CustomEvent('qort-theme-change', {
|
||||||
}
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
detail: this.theme
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
initTheme() {
|
window.localStorage.setItem('qortalTheme', this.theme)
|
||||||
document.querySelector('html').setAttribute('theme', this.theme);
|
|
||||||
}
|
this.initTheme()
|
||||||
|
}
|
||||||
|
|
||||||
|
initTheme() {
|
||||||
|
document.querySelector('html').setAttribute('theme', this.theme)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('qort-theme-toggle', QortThemeToggle);
|
window.customElements.define('qort-theme-toggle', QortThemeToggle)
|
@ -1,12 +1,14 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {get, translate} from '../../translate'
|
import { searchModalStyles } from '../styles/core-css'
|
||||||
import snackbar from '../functional-components/snackbar.js'
|
import snackbar from '../functional-components/snackbar'
|
||||||
|
|
||||||
import '@polymer/paper-icon-button/paper-icon-button.js'
|
import '@polymer/paper-icon-button/paper-icon-button.js'
|
||||||
import '@polymer/iron-icons/iron-icons.js'
|
import '@polymer/iron-icons/iron-icons.js'
|
||||||
import '@polymer/paper-dialog/paper-dialog.js'
|
import '@polymer/paper-dialog/paper-dialog.js'
|
||||||
import '@vaadin/text-field'
|
import '@vaadin/text-field'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { get, translate } from '../../translate'
|
||||||
|
|
||||||
class SearchModal extends LitElement {
|
class SearchModal extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
@ -15,56 +17,16 @@ class SearchModal extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [searchModalStyles]
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.searchContentString = ''
|
this.searchContentString = ''
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
* {
|
|
||||||
--lumo-primary-text-color: rgb(0, 167, 245);
|
|
||||||
--lumo-primary-color-50pct: rgba(0, 167, 245, 0.5);
|
|
||||||
--lumo-primary-color-10pct: rgba(0, 167, 245, 0.1);
|
|
||||||
--lumo-primary-color: hsl(199, 100%, 48%);
|
|
||||||
--lumo-base-color: var(--white);
|
|
||||||
--lumo-body-text-color: var(--black);
|
|
||||||
--lumo-secondary-text-color: var(--sectxt);
|
|
||||||
--lumo-contrast-60pct: var(--vdicon);
|
|
||||||
--item-selected-color: var(--nav-selected-color);
|
|
||||||
--item-selected-color-text: var(--nav-selected-color-text);
|
|
||||||
--item-color-active: var(--nav-color-active);
|
|
||||||
--item-color-hover: var(--nav-color-hover);
|
|
||||||
--item-text-color: var(--nav-text-color);
|
|
||||||
--item-icon-color: var(--nav-icon-color);
|
|
||||||
--item-border-color: var(--nav-border-color);
|
|
||||||
--item-border-selected-color: var(--nav-border-selected-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-dialog.searchSettings {
|
|
||||||
min-width: 525px;
|
|
||||||
max-width: 525px;
|
|
||||||
min-height: auto;
|
|
||||||
max-height: 150px;
|
|
||||||
background-color: var(--white);
|
|
||||||
color: var(--black);
|
|
||||||
line-height: 1.6;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid var(--black);
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 15px;
|
|
||||||
box-shadow: 0px 10px 15px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search {
|
|
||||||
display: inline;
|
|
||||||
width: 50%;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div style="display: inline;">
|
<div style="display: inline;">
|
||||||
@ -92,6 +54,7 @@ class SearchModal extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
openSearch() {
|
openSearch() {
|
||||||
@ -110,19 +73,24 @@ class SearchModal extends LitElement {
|
|||||||
|
|
||||||
openUserInfo() {
|
openUserInfo() {
|
||||||
const checkvalue = this.shadowRoot.getElementById('searchContent').value
|
const checkvalue = this.shadowRoot.getElementById('searchContent').value
|
||||||
|
|
||||||
if (checkvalue.length < 3) {
|
if (checkvalue.length < 3) {
|
||||||
let snackbar1string = get("publishpage.pchange20")
|
let snackbar1string = get("publishpage.pchange20")
|
||||||
let snackbar2string = get("welcomepage.wcchange4")
|
let snackbar2string = get("welcomepage.wcchange4")
|
||||||
|
|
||||||
snackbar.add({
|
snackbar.add({
|
||||||
labelText: `${snackbar1string} ${snackbar2string}`,
|
labelText: `${snackbar1string} ${snackbar2string}`,
|
||||||
dismiss: true
|
dismiss: true
|
||||||
})
|
})
|
||||||
this.shadowRoot.getElementById('searchContent').value = this.searchContentString
|
|
||||||
|
|
||||||
|
this.shadowRoot.getElementById('searchContent').value = this.searchContentString
|
||||||
} else {
|
} else {
|
||||||
let sendInfoAddress = this.shadowRoot.getElementById('searchContent').value
|
let sendInfoAddress = this.shadowRoot.getElementById('searchContent').value
|
||||||
|
|
||||||
const infoDialog = document.getElementById('main-app').shadowRoot.querySelector('app-view').shadowRoot.querySelector('user-info-view')
|
const infoDialog = document.getElementById('main-app').shadowRoot.querySelector('app-view').shadowRoot.querySelector('user-info-view')
|
||||||
|
|
||||||
infoDialog.openUserInfo(sendInfoAddress)
|
infoDialog.openUserInfo(sendInfoAddress)
|
||||||
|
|
||||||
this.shadowRoot.getElementById('searchContent').value = this.searchContentString
|
this.shadowRoot.getElementById('searchContent').value = this.searchContentString
|
||||||
this.shadowRoot.getElementById('searchSettingsDialog').close()
|
this.shadowRoot.getElementById('searchSettingsDialog').close()
|
||||||
}
|
}
|
||||||
|
@ -1,138 +1,92 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../../store.js'
|
import { store } from '../../store'
|
||||||
import {get, translate} from '../../../translate'
|
import { accountViewStyles } from '../../styles/core-css'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { get, translate } from '../../../translate'
|
||||||
|
|
||||||
class AccountView extends connect(store)(LitElement) {
|
class AccountView extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
accountInfo: { type: Object },
|
accountInfo: { type: Object },
|
||||||
theme: { type: String, reflect: true },
|
switchAvatar: { type: String },
|
||||||
switchAvatar: { type: String }
|
theme: { type: String, reflect: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [accountViewStyles]
|
||||||
|
}
|
||||||
|
|
||||||
.sub-main {
|
constructor() {
|
||||||
position: relative;
|
super()
|
||||||
text-align: center;
|
this.accountInfo = store.getState().app.accountInfo
|
||||||
}
|
this.switchAvatar = ''
|
||||||
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
|
}
|
||||||
|
|
||||||
.center-box {
|
render() {
|
||||||
position: relative;
|
return html`
|
||||||
top: 45%;
|
<div class="sub-main">
|
||||||
left: 50%;
|
<div class="center-box">
|
||||||
transform: translate(-50%, 0%);
|
<div class="img-icon">${this.getAvatar()}</div>
|
||||||
text-align: center;
|
<span id="accountName">
|
||||||
}
|
${this.accountInfo.names.length !== 0 ? this.accountInfo.names[0].name : get("chatpage.cchange15")}
|
||||||
|
</span>
|
||||||
|
<div class="content-box">
|
||||||
|
<span class="title">${translate("settings.address")}: </span>
|
||||||
|
<span class="value">${store.getState().app.selectedAddress.address}</span>
|
||||||
|
<br/>
|
||||||
|
<span class="title">${translate("settings.publickey")}: </span>
|
||||||
|
<span class="value">${store.getState().app.selectedAddress.base58PublicKey}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
.img-icon {
|
firstUpdated() {
|
||||||
display: block;
|
this.getSwitchAvatar()
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-box {
|
setInterval(() => {
|
||||||
border: 1px solid #a1a1a1;
|
this.getSwitchAvatar()
|
||||||
padding: 10px 25px;
|
}, 10000)
|
||||||
text-align: left;
|
}
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
getAvatar() {
|
||||||
font-weight: 600;
|
if (this.switchAvatar === 'light') {
|
||||||
font-size: 15px;
|
if (this.accountInfo.names.length === 0) {
|
||||||
display: block;
|
return html`<img src="/img/noavatar_light.png" style="width:150px; height:150px; border-radius: 25%;">`
|
||||||
line-height: 32px;
|
} else {
|
||||||
opacity: 0.66;
|
const avatarName = this.accountInfo.names[0].name
|
||||||
}
|
const avatarNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
const avatarUrl = avatarNode.protocol + '://' + avatarNode.domain + ':' + avatarNode.port
|
||||||
|
const url = `${avatarUrl}/arbitrary/THUMBNAIL/${avatarName}/qortal_avatar?async=true`
|
||||||
|
|
||||||
.value {
|
return html`<img src="${url}" style="width:150px; height:150px; border-radius: 25%;" onerror="this.src='/img/noavatar_light.png';">`
|
||||||
font-size: 16px;
|
}
|
||||||
display: inline-block;
|
} else if (this.switchAvatar === 'dark') {
|
||||||
}
|
if (this.accountInfo.names.length === 0) {
|
||||||
|
return html`<img src="/img/noavatar_dark.png" style="width:150px; height:150px; border-radius: 25%;">`
|
||||||
|
} else {
|
||||||
|
const avatarName = this.accountInfo.names[0].name
|
||||||
|
const avatarNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
const avatarUrl = avatarNode.protocol + '://' + avatarNode.domain + ':' + avatarNode.port
|
||||||
|
const url = `${avatarUrl}/arbitrary/THUMBNAIL/${avatarName}/qortal_avatar?async=true`
|
||||||
|
|
||||||
#accountName {
|
return html`<img src="${url}" style="width:150px; height:150px; border-radius: 25%;" onerror="this.src='/img/noavatar_dark.png';">`
|
||||||
margin: 0;
|
}
|
||||||
font-size: 24px;
|
}
|
||||||
font-weight:500;
|
}
|
||||||
display: inline-block;
|
|
||||||
width:100%;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
getSwitchAvatar() {
|
||||||
super()
|
this.switchAvatar = localStorage.getItem('qortalTheme')
|
||||||
this.accountInfo = store.getState().app.accountInfo
|
}
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
|
||||||
this.switchAvatar = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
stateChanged(state) {
|
||||||
return html`
|
this.accountInfo = state.app.accountInfo
|
||||||
<div class="sub-main">
|
}
|
||||||
<div class="center-box">
|
|
||||||
<div class="img-icon">${this.getAvatar()}</div>
|
|
||||||
<span id="accountName">
|
|
||||||
${this.accountInfo.names.length !== 0 ? this.accountInfo.names[0].name : get("chatpage.cchange15")}
|
|
||||||
</span>
|
|
||||||
<div class="content-box">
|
|
||||||
<span class="title">${translate("settings.address")}: </span>
|
|
||||||
<span class="value">${store.getState().app.selectedAddress.address}</span>
|
|
||||||
<br/>
|
|
||||||
<span class="title">${translate("settings.publickey")}: </span>
|
|
||||||
<span class="value">${store.getState().app.selectedAddress.base58PublicKey}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated() {
|
|
||||||
this.getSwitchAvatar()
|
|
||||||
setInterval(() => {
|
|
||||||
this.getSwitchAvatar()
|
|
||||||
}, 2000)
|
|
||||||
}
|
|
||||||
|
|
||||||
getAvatar() {
|
|
||||||
if (this.switchAvatar === 'light') {
|
|
||||||
if (this.accountInfo.names.length === 0) {
|
|
||||||
return html`<img src="/img/noavatar_light.png" style="width:150px; height:150px; border-radius: 25%;">`
|
|
||||||
} else {
|
|
||||||
const avatarName = this.accountInfo.names[0].name
|
|
||||||
const avatarNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
|
||||||
const avatarUrl = avatarNode.protocol + '://' + avatarNode.domain + ':' + avatarNode.port
|
|
||||||
const url = `${avatarUrl}/arbitrary/THUMBNAIL/${avatarName}/qortal_avatar?async=true&apiKey=${this.getApiKey()}`
|
|
||||||
return html`<img src="${url}" style="width:150px; height:150px; border-radius: 25%;" onerror="this.src='/img/noavatar_light.png';">`
|
|
||||||
}
|
|
||||||
} else if (this.switchAvatar === 'dark') {
|
|
||||||
if (this.accountInfo.names.length === 0) {
|
|
||||||
return html`<img src="/img/noavatar_dark.png" style="width:150px; height:150px; border-radius: 25%;">`
|
|
||||||
} else {
|
|
||||||
const avatarName = this.accountInfo.names[0].name
|
|
||||||
const avatarNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
|
||||||
const avatarUrl = avatarNode.protocol + '://' + avatarNode.domain + ':' + avatarNode.port
|
|
||||||
const url = `${avatarUrl}/arbitrary/THUMBNAIL/${avatarName}/qortal_avatar?async=true&apiKey=${this.getApiKey()}`
|
|
||||||
return html`<img src="${url}" style="width:150px; height:150px; border-radius: 25%;" onerror="this.src='/img/noavatar_dark.png';">`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSwitchAvatar() {
|
|
||||||
this.switchAvatar = localStorage.getItem('qortalTheme')
|
|
||||||
}
|
|
||||||
|
|
||||||
getApiKey() {
|
|
||||||
const apiNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
|
||||||
return apiNode.apiKey
|
|
||||||
}
|
|
||||||
|
|
||||||
stateChanged(state) {
|
|
||||||
this.accountInfo = state.app.accountInfo
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('account-view', AccountView)
|
window.customElements.define('account-view', AccountView)
|
@ -1,597 +1,423 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../../store.js'
|
import { store } from '../../store'
|
||||||
import {Epml} from '../../epml.js'
|
import { Epml } from '../../epml'
|
||||||
import {addTradeBotRoutes} from '../../tradebot/addTradeBotRoutes.js'
|
import { addTradeBotRoutes } from '../../tradebot/addTradeBotRoutes'
|
||||||
import {get, translate} from '../../../translate'
|
import { exportKeysStyles } from '../../styles/core-css'
|
||||||
import snackbar from '../../functional-components/snackbar.js'
|
|
||||||
import FileSaver from 'file-saver'
|
import FileSaver from 'file-saver'
|
||||||
|
import snackbar from '../../functional-components/snackbar'
|
||||||
import '@material/mwc-dialog'
|
|
||||||
import '@material/mwc-button'
|
import '@material/mwc-button'
|
||||||
|
import '@material/mwc-dialog'
|
||||||
import '@material/mwc-icon'
|
import '@material/mwc-icon'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { get, translate } from '../../../translate'
|
||||||
|
|
||||||
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||||
|
|
||||||
class ExportKeys extends connect(store)(LitElement) {
|
class ExportKeys extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
theme: { type: String, reflect: true },
|
theme: { type: String, reflect: true },
|
||||||
backupErrorMessage: { type: String },
|
backupErrorMessage: { type: String },
|
||||||
btcPMK: { type: String },
|
btcPMK: { type: String },
|
||||||
ltcPMK: { type: String },
|
ltcPMK: { type: String },
|
||||||
dogePMK: { type: String },
|
dogePMK: { type: String },
|
||||||
dgbPMK: { type: String },
|
dgbPMK: { type: String },
|
||||||
rvnPMK: { type: String },
|
rvnPMK: { type: String },
|
||||||
arrrPMK: { type: String },
|
arrrPMK: { type: String },
|
||||||
btcWALLET: { type: String },
|
btcWALLET: { type: String },
|
||||||
ltcWALLET: { type: String },
|
ltcWALLET: { type: String },
|
||||||
dogeWALLET: { type: String },
|
dogeWALLET: { type: String },
|
||||||
dgbWALLET: { type: String },
|
dgbWALLET: { type: String },
|
||||||
rvnWALLET: { type: String },
|
rvnWALLET: { type: String },
|
||||||
arrrWALLET: { type: String },
|
arrrWALLET: { type: String },
|
||||||
btcName: { type: String },
|
btcName: { type: String },
|
||||||
ltcName: { type: String },
|
ltcName: { type: String },
|
||||||
dogeName: { type: String },
|
dogeName: { type: String },
|
||||||
dgbName: { type: String },
|
dgbName: { type: String },
|
||||||
rvnName: { type: String },
|
rvnName: { type: String },
|
||||||
arrrName: { type: String },
|
arrrName: { type: String },
|
||||||
btcShort: { type: String },
|
btcShort: { type: String },
|
||||||
ltcShort: { type: String },
|
ltcShort: { type: String },
|
||||||
dogeShort: { type: String },
|
dogeShort: { type: String },
|
||||||
dgbShort: { type: String },
|
dgbShort: { type: String },
|
||||||
rvnShort: { type: String },
|
rvnShort: { type: String },
|
||||||
arrrShort: { type: String },
|
arrrShort: { type: String },
|
||||||
enableArrr: { type: Boolean },
|
enableArrr: { type: Boolean },
|
||||||
dWalletAddress: { type: String },
|
dWalletAddress: { type: String },
|
||||||
dPrivateKey: { type: String },
|
dPrivateKey: { type: String },
|
||||||
dCoinName: { type: String },
|
dCoinName: { type: String },
|
||||||
dCoinShort: { type: String }
|
dCoinShort: { type: String }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [exportKeysStyles]
|
||||||
* {
|
}
|
||||||
--mdc-theme-primary: rgb(3, 169, 244);
|
|
||||||
--mdc-theme-surface: var(--white);
|
|
||||||
--mdc-dialog-content-ink-color: var(--black);
|
|
||||||
--mdc-dialog-min-width: 500px;
|
|
||||||
--mdc-dialog-max-width: 750px;
|
|
||||||
--lumo-primary-text-color: rgb(0, 167, 245);
|
|
||||||
--lumo-primary-color-50pct: rgba(0, 167, 245, 0.5);
|
|
||||||
--lumo-primary-color-10pct: rgba(0, 167, 245, 0.1);
|
|
||||||
--lumo-primary-color: hsl(199, 100%, 48%);
|
|
||||||
--lumo-base-color: var(--white);
|
|
||||||
--lumo-body-text-color: var(--black);
|
|
||||||
--lumo-secondary-text-color: var(--sectxt);
|
|
||||||
--lumo-contrast-60pct: var(--vdicon);
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-box {
|
constructor() {
|
||||||
position: relative;
|
super()
|
||||||
top: 45%;
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
left: 50%;
|
this.backupErrorMessage = ''
|
||||||
transform: translate(-50%, 0%);
|
this.btcPMK = store.getState().app.selectedAddress.btcWallet.derivedMasterPrivateKey
|
||||||
text-align: center;
|
this.btcWALLET = store.getState().app.selectedAddress.btcWallet.address
|
||||||
}
|
this.btcName = 'Bitcoin'
|
||||||
|
this.btcShort = 'btc'
|
||||||
|
this.ltcPMK = store.getState().app.selectedAddress.ltcWallet.derivedMasterPrivateKey
|
||||||
|
this.ltcWALLET = store.getState().app.selectedAddress.ltcWallet.address
|
||||||
|
this.ltcName = 'Litecoin'
|
||||||
|
this.ltcShort = 'ltc'
|
||||||
|
this.dogePMK = store.getState().app.selectedAddress.dogeWallet.derivedMasterPrivateKey
|
||||||
|
this.dogeWALLET = store.getState().app.selectedAddress.dogeWallet.address
|
||||||
|
this.dogeName = 'Dogecoin'
|
||||||
|
this.dogeShort = 'doge'
|
||||||
|
this.dgbPMK = store.getState().app.selectedAddress.dgbWallet.derivedMasterPrivateKey
|
||||||
|
this.dgbWALLET = store.getState().app.selectedAddress.dgbWallet.address
|
||||||
|
this.dgbName = 'Digibyte'
|
||||||
|
this.dgbShort = 'dgb'
|
||||||
|
this.rvnPMK = store.getState().app.selectedAddress.rvnWallet.derivedMasterPrivateKey
|
||||||
|
this.rvnWALLET = store.getState().app.selectedAddress.rvnWallet.address
|
||||||
|
this.rvnName = 'Ravencoin'
|
||||||
|
this.rvnShort = 'rvn'
|
||||||
|
this.arrrPMK = ''
|
||||||
|
this.arrrWALLET = ''
|
||||||
|
this.arrrName = 'Pirate Chain'
|
||||||
|
this.arrrShort = 'arrr'
|
||||||
|
this.enableArrr = false
|
||||||
|
this.dWalletAddress = ''
|
||||||
|
this.dPrivateKey = ''
|
||||||
|
this.dCoinName = ''
|
||||||
|
this.dCoinShort = 'btc'
|
||||||
|
}
|
||||||
|
|
||||||
.sub-main {
|
render() {
|
||||||
position: relative;
|
return html`
|
||||||
text-align: center;
|
<div style="position: relative;">
|
||||||
height: auto;
|
<div class="center-box">
|
||||||
width: 100%;
|
<p>
|
||||||
}
|
${translate("settings.exp4")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="sub-main">
|
||||||
|
<div class="center-box">
|
||||||
|
<div class="content-box">
|
||||||
|
<div style="display: flex; align-items: center; justify-content: center;">
|
||||||
|
<img src="/img/btc.png" style="width: 32px; height: 32px;"> ${this.btcWALLET}<br>
|
||||||
|
</div>
|
||||||
|
<div @click=${() => this.checkForPmkDownload(this.btcWALLET, this.btcPMK, this.btcName, this.btcShort)} class="export-button"> ${translate("settings.exp2")} </div>
|
||||||
|
</div>
|
||||||
|
<div class="content-box">
|
||||||
|
<div style="display: flex; align-items: center; justify-content: center;">
|
||||||
|
<img src="/img/ltc.png" style="width: 32px; height: 32px;"> ${this.ltcWALLET}<br>
|
||||||
|
</div>
|
||||||
|
<div @click=${() => this.checkForPmkDownload(this.ltcWALLET, this.ltcPMK, this.ltcName, this.ltcShort)} class="export-button"> ${translate("settings.exp2")} </div>
|
||||||
|
</div>
|
||||||
|
<div class="content-box">
|
||||||
|
<div style="display: flex; align-items: center; justify-content: center;">
|
||||||
|
<img src="/img/doge.png" style="width: 32px; height: 32px;"> ${this.dogeWALLET}<br>
|
||||||
|
</div>
|
||||||
|
<div @click=${() => this.checkForPmkDownload(this.dogeWALLET, this.dogePMK, this.dogeName, this.dogeShort)} class="export-button"> ${translate("settings.exp2")} </div>
|
||||||
|
</div>
|
||||||
|
<div class="content-box">
|
||||||
|
<div style="display: flex; align-items: center; justify-content: center;">
|
||||||
|
<img src="/img/dgb.png" style="width: 32px; height: 32px;"> ${this.dgbWALLET}<br>
|
||||||
|
</div>
|
||||||
|
<div @click=${() => this.checkForPmkDownload(this.dgbWALLET, this.dgbPMK, this.dgbName, this.dgbShort)} class="export-button"> ${translate("settings.exp2")} </div>
|
||||||
|
</div>
|
||||||
|
<div class="content-box">
|
||||||
|
<div style="display: flex; align-items: center; justify-content: center;">
|
||||||
|
<img src="/img/rvn.png" style="width: 32px; height: 32px;"> ${this.rvnWALLET}<br>
|
||||||
|
</div>
|
||||||
|
<div @click=${() => this.checkForPmkDownload(this.rvnWALLET, this.rvnPMK, this.rvnName, this.rvnShort)} class="export-button"> ${translate("settings.exp2")} </div>
|
||||||
|
</div>
|
||||||
|
<div class="content-box" style="display:${this.enableArrr ? 'block' : 'none'}">
|
||||||
|
<div style="display: flex; align-items: center; justify-content: center;">
|
||||||
|
<img src="/img/arrr.png" style="width: 32px; height: 32px;"> ${this.arrrWALLET}<br>
|
||||||
|
</div>
|
||||||
|
<div @click=${() => this.checkForPmkDownload(this.arrrWALLET, this.arrrPMK, this.arrrName, this.arrrShort)} class="export-button"> ${translate("settings.exp2")} </div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr style="margin-top: 20px;">
|
||||||
|
<div class="button-row">
|
||||||
|
<button class="repair-button" title="${translate('nodepage.nchange38')}" @click="${() => this.openRepairLTCDialog()}">${translate("nodepage.nchange38")}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<mwc-dialog id="savePkmDialog" scrimClickAction="" escapeKeyAction="">
|
||||||
|
<img src="/img/${this.dCoinShort}.png" style="width: 32px; height: 32px;">
|
||||||
|
<h3>${this.dCoinName} ${translate("settings.exp2")}</h3>
|
||||||
|
<hr>
|
||||||
|
<h4>${translate("settings.address")}: ${this.dWalletAddress}</h4>
|
||||||
|
<mwc-button
|
||||||
|
slot="primaryAction"
|
||||||
|
@click="${() => this.closeSavePkmDialog()}"
|
||||||
|
class="red"
|
||||||
|
>
|
||||||
|
${translate("general.close")}
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
slot="secondaryAction"
|
||||||
|
@click="${() => this.exportKey(this.dPrivateKey, this.dCoinName, this.dWalletAddress)}"
|
||||||
|
>
|
||||||
|
${translate("settings.exp3")}
|
||||||
|
</mwc-button>
|
||||||
|
</mwc-dialog>
|
||||||
|
<mwc-dialog id="arrrWalletNotSynced" scrimClickAction="" escapeKeyAction="">
|
||||||
|
<img src="/img/arrr.png" style="width: 32px; height: 32px;">
|
||||||
|
<h3>${translate("settings.arrr1")}</h3>
|
||||||
|
<hr>
|
||||||
|
<h4>${translate("settings.arrr2")}</h4>
|
||||||
|
<mwc-button
|
||||||
|
slot="primaryAction"
|
||||||
|
@click="${() => this.closeArrrWalletNotSynced()}"
|
||||||
|
class="red"
|
||||||
|
>
|
||||||
|
${translate("general.close")}
|
||||||
|
</mwc-button>
|
||||||
|
</mwc-dialog>
|
||||||
|
<mwc-dialog id="needCoreUpdate" scrimClickAction="" escapeKeyAction="">
|
||||||
|
<img src="/img/arrr.png" style="width: 32px; height: 32px;">
|
||||||
|
<h3>${translate("settings.arrr3")}</h3>
|
||||||
|
<hr>
|
||||||
|
<h4>${translate("settings.arrr4")}</h4>
|
||||||
|
<mwc-button
|
||||||
|
slot="primaryAction"
|
||||||
|
@click="${() => this.closeNeedCoreUpdate()}"
|
||||||
|
class="red"
|
||||||
|
>
|
||||||
|
${translate("general.close")}
|
||||||
|
</mwc-button>
|
||||||
|
</mwc-dialog>
|
||||||
|
<mwc-dialog id="repairLTCDialog" scrimClickAction="" escapeKeyAction="">
|
||||||
|
<img src="/img/ltc.png" style="width: 32px; height: 32px;">
|
||||||
|
<h3>${translate("nodepage.nchange38")}</h3>
|
||||||
|
<hr>
|
||||||
|
<h4>${translate("nodepage.nchange39")}</h4>
|
||||||
|
<h4>${translate("nodepage.nchange40")}</h4>
|
||||||
|
<mwc-button slot="primaryAction" @click="${() => this.repairLtcWallet()}" class="green">
|
||||||
|
${translate("general.continue")}
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button slot="secondaryAction" @click="${() => this.closeRepairLTCDialog()}" class="red">
|
||||||
|
${translate("login.lp4")}
|
||||||
|
</mwc-button>
|
||||||
|
</mwc-dialog>
|
||||||
|
<mwc-dialog id="pleaseWaitDialog" scrimClickAction="" escapeKeyAction="">
|
||||||
|
<div class="lds-roller">
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
<h2>${translate("nodepage.nchange41")}</h2>
|
||||||
|
</mwc-dialog>
|
||||||
|
<mwc-dialog id="okDialog" scrimClickAction="" escapeKeyAction="">
|
||||||
|
<img src="/img/ltc.png" style="width: 32px; height: 32px;">
|
||||||
|
<h3>${translate("nodepage.nchange38")}</h3>
|
||||||
|
<hr>
|
||||||
|
<h3>${translate("nodepage.nchange42")}</h3>
|
||||||
|
</mwc-dialog>
|
||||||
|
<mwc-dialog id="errorDialog" scrimClickAction="" escapeKeyAction="">
|
||||||
|
<img src="/img/ltc.png" style="width: 32px; height: 32px;">
|
||||||
|
<h3>${translate("nodepage.nchange38")}</h3>
|
||||||
|
<hr>
|
||||||
|
<h3>${translate("nodepage.nchange43")}</h3>
|
||||||
|
</mwc-dialog>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
.content-box {
|
async firstUpdated() {
|
||||||
text-align: center;
|
addTradeBotRoutes(parentEpml)
|
||||||
display: inline-block;
|
parentEpml.imReady()
|
||||||
min-width: 400px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
margin-left: 10px;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.export-button {
|
await this.fetchArrrWalletAddress()
|
||||||
display: inline-flex;
|
await this.checkArrrWalletPrivateKey()
|
||||||
flex-direction: column;
|
}
|
||||||
justify-content: center;
|
|
||||||
align-content: center;
|
|
||||||
border: none;
|
|
||||||
border-radius: 20px;
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 10px;
|
|
||||||
color: white;
|
|
||||||
background: #03a9f4;
|
|
||||||
width: 75%;
|
|
||||||
font-size: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
height: 40px;
|
|
||||||
margin-top: 1rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: all .2s;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.red {
|
async fetchArrrWalletAddress() {
|
||||||
--mdc-theme-primary: #F44336;
|
let resAD = await parentEpml.request('apiCall', {
|
||||||
}
|
url: `/crosschain/arrr/walletaddress?apiKey=${this.getApiKey()}`,
|
||||||
|
method: 'POST',
|
||||||
|
body: `${store.getState().app.selectedAddress.arrrWallet.seed58}`
|
||||||
|
})
|
||||||
|
|
||||||
.green {
|
if (resAD != null && resAD.error != 1201) {
|
||||||
--mdc-theme-primary: #198754;
|
this.arrrWALLET = ''
|
||||||
}
|
this.enableArrr = true
|
||||||
|
this.arrrWALLET = resAD
|
||||||
|
} else {
|
||||||
|
this.arrrWALLET = ''
|
||||||
|
this.enableArrr = false
|
||||||
|
this.shadowRoot.querySelector('#arrrWalletNotSynced').show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.button-row {
|
async checkArrrWalletPrivateKey() {
|
||||||
position: relative;
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
display: flex;
|
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
align-items: center;
|
const privateKeyUrl = `${nodeUrl}/crosschain/arrr/walletprivatekey?apiKey=${this.getApiKey()}`
|
||||||
align-content: center;
|
|
||||||
font-family: Montserrat, sans-serif;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--black);
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repair-button {
|
await fetch(privateKeyUrl, {
|
||||||
height: 40px;
|
method: 'POST',
|
||||||
padding: 10px 10px;
|
body: `${store.getState().app.selectedAddress.arrrWallet.seed58}`
|
||||||
font-size: 16px;
|
}).then(res => {
|
||||||
font-weight: 500;
|
if (res.status === 404) {
|
||||||
background-color: #03a9f4;
|
this.arrrPMK = ''
|
||||||
color: white;
|
this.enableArrr = false
|
||||||
border: 1px solid transparent;
|
this.shadowRoot.querySelector('#needCoreUpdate').show()
|
||||||
border-radius: 20px;
|
} else {
|
||||||
text-decoration: none;
|
this.fetchArrrWalletPrivateKey()
|
||||||
text-transform: uppercase;
|
}
|
||||||
cursor: pointer;
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
.repair-button:hover {
|
async fetchArrrWalletPrivateKey() {
|
||||||
opacity: 0.8;
|
let resPK = await parentEpml.request('apiCall', {
|
||||||
cursor: pointer;
|
url: `/crosschain/arrr/walletprivatekey?apiKey=${this.getApiKey()}`,
|
||||||
}
|
method: 'POST',
|
||||||
|
body: `${store.getState().app.selectedAddress.arrrWallet.seed58}`
|
||||||
|
})
|
||||||
|
|
||||||
.lds-roller {
|
if (resPK != null && resPK.error != 1201) {
|
||||||
display: inline-block;
|
this.arrrPMK = ''
|
||||||
position: relative;
|
this.enableArrr = true
|
||||||
width: 80px;
|
this.arrrPMK = resPK
|
||||||
height: 80px;
|
} else {
|
||||||
}
|
this.arrrPMK = ''
|
||||||
|
this.enableArrr = false
|
||||||
|
this.shadowRoot.querySelector('#arrrWalletNotSynced').show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.lds-roller div {
|
closeArrrWalletNotSynced() {
|
||||||
animation: lds-roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
this.shadowRoot.querySelector('#arrrWalletNotSynced').close()
|
||||||
transform-origin: 40px 40px;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.lds-roller div:after {
|
closeNeedCoreUpdate() {
|
||||||
content: " ";
|
this.arrrPMK = ''
|
||||||
display: block;
|
this.enableArrr = false
|
||||||
position: absolute;
|
this.shadowRoot.querySelector('#needCoreUpdate').close()
|
||||||
width: 7px;
|
}
|
||||||
height: 7px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--black);
|
|
||||||
margin: -4px 0 0 -4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lds-roller div:nth-child(1) {
|
closeSavePkmDialog() {
|
||||||
animation-delay: -0.036s;
|
this.shadowRoot.querySelector('#savePkmDialog').close()
|
||||||
}
|
}
|
||||||
|
|
||||||
.lds-roller div:nth-child(1):after {
|
openRepairLTCDialog() {
|
||||||
top: 63px;
|
this.shadowRoot.querySelector('#repairLTCDialog').show()
|
||||||
left: 63px;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.lds-roller div:nth-child(2) {
|
closeRepairLTCDialog() {
|
||||||
animation-delay: -0.072s;
|
this.shadowRoot.querySelector('#repairLTCDialog').close()
|
||||||
}
|
}
|
||||||
|
|
||||||
.lds-roller div:nth-child(2):after {
|
async repairLtcWallet() {
|
||||||
top: 68px;
|
this.shadowRoot.querySelector('#repairLTCDialog').close()
|
||||||
left: 56px;
|
this.shadowRoot.querySelector('#pleaseWaitDialog').show()
|
||||||
}
|
|
||||||
|
|
||||||
.lds-roller div:nth-child(3) {
|
let resRepair = await parentEpml.request('apiCall', {
|
||||||
animation-delay: -0.108s;
|
url: `/crosschain/ltc/repair?apiKey=${this.getApiKey()}`,
|
||||||
}
|
method: 'POST',
|
||||||
|
body: `${store.getState().app.selectedAddress.ltcWallet.derivedMasterPrivateKey}`
|
||||||
|
})
|
||||||
|
|
||||||
.lds-roller div:nth-child(3):after {
|
if (resRepair != null && resRepair.error != 128) {
|
||||||
top: 71px;
|
this.shadowRoot.querySelector('#pleaseWaitDialog').close()
|
||||||
left: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lds-roller div:nth-child(4) {
|
await this.openOkDialog()
|
||||||
animation-delay: -0.144s;
|
} else {
|
||||||
}
|
this.shadowRoot.querySelector('#pleaseWaitDialog').close()
|
||||||
|
|
||||||
.lds-roller div:nth-child(4):after {
|
await this.openErrorDialog()
|
||||||
top: 72px;
|
}
|
||||||
left: 40px;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.lds-roller div:nth-child(5) {
|
async openOkDialog() {
|
||||||
animation-delay: -0.18s;
|
const okDelay = ms => new Promise(res => setTimeout(res, ms))
|
||||||
}
|
|
||||||
|
|
||||||
.lds-roller div:nth-child(5):after {
|
this.shadowRoot.querySelector('#okDialog').show()
|
||||||
top: 71px;
|
|
||||||
left: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lds-roller div:nth-child(6) {
|
await okDelay(3000)
|
||||||
animation-delay: -0.216s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lds-roller div:nth-child(6):after {
|
this.shadowRoot.querySelector('#okDialog').close()
|
||||||
top: 68px;
|
}
|
||||||
left: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lds-roller div:nth-child(7) {
|
async openErrorDialog() {
|
||||||
animation-delay: -0.252s;
|
const errorDelay = ms => new Promise(res => setTimeout(res, ms))
|
||||||
}
|
|
||||||
|
|
||||||
.lds-roller div:nth-child(7):after {
|
this.shadowRoot.querySelector('#errorDialog').show()
|
||||||
top: 63px;
|
|
||||||
left: 17px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lds-roller div:nth-child(8) {
|
await errorDelay(3000)
|
||||||
animation-delay: -0.288s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lds-roller div:nth-child(8):after {
|
this.shadowRoot.querySelector('#errorDialog').close()
|
||||||
top: 56px;
|
}
|
||||||
left: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes lds-roller {
|
checkForPmkDownload(wAddress, wPkm, wName, wShort) {
|
||||||
0% {
|
this.dWalletAddress = ''
|
||||||
transform: rotate(0deg);
|
this.dPrivateKey = ''
|
||||||
}
|
this.dCoinName = ''
|
||||||
100% {
|
this.dCoinShort = ''
|
||||||
transform: rotate(360deg);
|
this.dWalletAddress = wAddress
|
||||||
}
|
this.dPrivateKey = wPkm
|
||||||
}
|
this.dCoinName = wName
|
||||||
`
|
this.dCoinShort = wShort
|
||||||
}
|
this.shadowRoot.querySelector('#savePkmDialog').show()
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
async exportKey(cMasterKey, cName, cAddress) {
|
||||||
super()
|
let exportname = ''
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
|
||||||
this.backupErrorMessage = ''
|
|
||||||
this.btcPMK = store.getState().app.selectedAddress.btcWallet.derivedMasterPrivateKey
|
|
||||||
this.btcWALLET = store.getState().app.selectedAddress.btcWallet.address
|
|
||||||
this.btcName = 'Bitcoin'
|
|
||||||
this.btcShort = 'btc'
|
|
||||||
this.ltcPMK = store.getState().app.selectedAddress.ltcWallet.derivedMasterPrivateKey
|
|
||||||
this.ltcWALLET = store.getState().app.selectedAddress.ltcWallet.address
|
|
||||||
this.ltcName = 'Litecoin'
|
|
||||||
this.ltcShort = 'ltc'
|
|
||||||
this.dogePMK = store.getState().app.selectedAddress.dogeWallet.derivedMasterPrivateKey
|
|
||||||
this.dogeWALLET = store.getState().app.selectedAddress.dogeWallet.address
|
|
||||||
this.dogeName = 'Dogecoin'
|
|
||||||
this.dogeShort = 'doge'
|
|
||||||
this.dgbPMK = store.getState().app.selectedAddress.dgbWallet.derivedMasterPrivateKey
|
|
||||||
this.dgbWALLET = store.getState().app.selectedAddress.dgbWallet.address
|
|
||||||
this.dgbName = 'Digibyte'
|
|
||||||
this.dgbShort = 'dgb'
|
|
||||||
this.rvnPMK = store.getState().app.selectedAddress.rvnWallet.derivedMasterPrivateKey
|
|
||||||
this.rvnWALLET = store.getState().app.selectedAddress.rvnWallet.address
|
|
||||||
this.rvnName = 'Ravencoin'
|
|
||||||
this.rvnShort = 'rvn'
|
|
||||||
this.arrrPMK = ''
|
|
||||||
this.arrrWALLET = ''
|
|
||||||
this.arrrName = 'Pirate Chain'
|
|
||||||
this.arrrShort = 'arrr'
|
|
||||||
this.enableArrr = false
|
|
||||||
this.dWalletAddress = ''
|
|
||||||
this.dPrivateKey = ''
|
|
||||||
this.dCoinName = ''
|
|
||||||
this.dCoinShort = 'btc'
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
const myPrivateMasterKey = cMasterKey
|
||||||
return html`
|
const myCoinName = cName
|
||||||
<div style="position: relative;">
|
const myCoinAddress = cAddress
|
||||||
<div class="center-box">
|
const blob = new Blob([`${myPrivateMasterKey}`], { type: 'text/plain;charset=utf-8' })
|
||||||
<p>
|
|
||||||
${translate("settings.exp4")}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="sub-main">
|
|
||||||
<div class="center-box">
|
|
||||||
<div class="content-box">
|
|
||||||
<div style="display: flex; align-items: center; justify-content: center;">
|
|
||||||
<img src="/img/btc.png" style="width: 32px; height: 32px;"> ${this.btcWALLET}<br>
|
|
||||||
</div>
|
|
||||||
<div @click=${() => this.checkForPmkDownload(this.btcWALLET, this.btcPMK, this.btcName, this.btcShort)} class="export-button"> ${translate("settings.exp2")} </div>
|
|
||||||
</div>
|
|
||||||
<div class="content-box">
|
|
||||||
<div style="display: flex; align-items: center; justify-content: center;">
|
|
||||||
<img src="/img/ltc.png" style="width: 32px; height: 32px;"> ${this.ltcWALLET}<br>
|
|
||||||
</div>
|
|
||||||
<div @click=${() => this.checkForPmkDownload(this.ltcWALLET, this.ltcPMK, this.ltcName, this.ltcShort)} class="export-button"> ${translate("settings.exp2")} </div>
|
|
||||||
</div>
|
|
||||||
<div class="content-box">
|
|
||||||
<div style="display: flex; align-items: center; justify-content: center;">
|
|
||||||
<img src="/img/doge.png" style="width: 32px; height: 32px;"> ${this.dogeWALLET}<br>
|
|
||||||
</div>
|
|
||||||
<div @click=${() => this.checkForPmkDownload(this.dogeWALLET, this.dogePMK, this.dogeName, this.dogeShort)} class="export-button"> ${translate("settings.exp2")} </div>
|
|
||||||
</div>
|
|
||||||
<div class="content-box">
|
|
||||||
<div style="display: flex; align-items: center; justify-content: center;">
|
|
||||||
<img src="/img/dgb.png" style="width: 32px; height: 32px;"> ${this.dgbWALLET}<br>
|
|
||||||
</div>
|
|
||||||
<div @click=${() => this.checkForPmkDownload(this.dgbWALLET, this.dgbPMK, this.dgbName, this.dgbShort)} class="export-button"> ${translate("settings.exp2")} </div>
|
|
||||||
</div>
|
|
||||||
<div class="content-box">
|
|
||||||
<div style="display: flex; align-items: center; justify-content: center;">
|
|
||||||
<img src="/img/rvn.png" style="width: 32px; height: 32px;"> ${this.rvnWALLET}<br>
|
|
||||||
</div>
|
|
||||||
<div @click=${() => this.checkForPmkDownload(this.rvnWALLET, this.rvnPMK, this.rvnName, this.rvnShort)} class="export-button"> ${translate("settings.exp2")} </div>
|
|
||||||
</div>
|
|
||||||
<div class="content-box" style="display:${this.enableArrr ? 'block' : 'none'}">
|
|
||||||
<div style="display: flex; align-items: center; justify-content: center;">
|
|
||||||
<img src="/img/arrr.png" style="width: 32px; height: 32px;"> ${this.arrrWALLET}<br>
|
|
||||||
</div>
|
|
||||||
<div @click=${() => this.checkForPmkDownload(this.arrrWALLET, this.arrrPMK, this.arrrName, this.arrrShort)} class="export-button"> ${translate("settings.exp2")} </div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr style="margin-top: 20px;">
|
|
||||||
<div class="button-row">
|
|
||||||
<button class="repair-button" title="${translate('nodepage.nchange38')}" @click="${() => this.openRepairLTCDialog()}">${translate("nodepage.nchange38")}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<mwc-dialog id="savePkmDialog" scrimClickAction="" escapeKeyAction="">
|
|
||||||
<img src="/img/${this.dCoinShort}.png" style="width: 32px; height: 32px;">
|
|
||||||
<h3>${this.dCoinName} ${translate("settings.exp2")}</h3>
|
|
||||||
<hr>
|
|
||||||
<h4>${translate("settings.address")}: ${this.dWalletAddress}</h4>
|
|
||||||
<mwc-button
|
|
||||||
slot="primaryAction"
|
|
||||||
@click="${() => this.closeSavePkmDialog()}"
|
|
||||||
class="red"
|
|
||||||
>
|
|
||||||
${translate("general.close")}
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button
|
|
||||||
slot="secondaryAction"
|
|
||||||
@click="${() => this.exportKey(this.dPrivateKey, this.dCoinName, this.dWalletAddress)}"
|
|
||||||
>
|
|
||||||
${translate("settings.exp3")}
|
|
||||||
</mwc-button>
|
|
||||||
</mwc-dialog>
|
|
||||||
<mwc-dialog id="arrrWalletNotSynced" scrimClickAction="" escapeKeyAction="">
|
|
||||||
<img src="/img/arrr.png" style="width: 32px; height: 32px;">
|
|
||||||
<h3>${translate("settings.arrr1")}</h3>
|
|
||||||
<hr>
|
|
||||||
<h4>${translate("settings.arrr2")}</h4>
|
|
||||||
<mwc-button
|
|
||||||
slot="primaryAction"
|
|
||||||
@click="${() => this.closeArrrWalletNotSynced()}"
|
|
||||||
class="red"
|
|
||||||
>
|
|
||||||
${translate("general.close")}
|
|
||||||
</mwc-button>
|
|
||||||
</mwc-dialog>
|
|
||||||
<mwc-dialog id="needCoreUpdate" scrimClickAction="" escapeKeyAction="">
|
|
||||||
<img src="/img/arrr.png" style="width: 32px; height: 32px;">
|
|
||||||
<h3>${translate("settings.arrr3")}</h3>
|
|
||||||
<hr>
|
|
||||||
<h4>${translate("settings.arrr4")}</h4>
|
|
||||||
<mwc-button
|
|
||||||
slot="primaryAction"
|
|
||||||
@click="${() => this.closeNeedCoreUpdate()}"
|
|
||||||
class="red"
|
|
||||||
>
|
|
||||||
${translate("general.close")}
|
|
||||||
</mwc-button>
|
|
||||||
</mwc-dialog>
|
|
||||||
<mwc-dialog id="repairLTCDialog" scrimClickAction="" escapeKeyAction="">
|
|
||||||
<img src="/img/ltc.png" style="width: 32px; height: 32px;">
|
|
||||||
<h3>${translate("nodepage.nchange38")}</h3>
|
|
||||||
<hr>
|
|
||||||
<h4>${translate("nodepage.nchange39")}</h4>
|
|
||||||
<h4>${translate("nodepage.nchange40")}</h4>
|
|
||||||
<mwc-button slot="primaryAction" @click="${() => this.repairLtcWallet()}" class="green">
|
|
||||||
${translate("general.continue")}
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button slot="secondaryAction" @click="${() => this.closeRepairLTCDialog()}" class="red">
|
|
||||||
${translate("login.lp4")}
|
|
||||||
</mwc-button>
|
|
||||||
</mwc-dialog>
|
|
||||||
<mwc-dialog id="pleaseWaitDialog" scrimClickAction="" escapeKeyAction="">
|
|
||||||
<div class="lds-roller"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div>
|
|
||||||
<h2>${translate("nodepage.nchange41")}</h2>
|
|
||||||
</mwc-dialog>
|
|
||||||
<mwc-dialog id="okDialog" scrimClickAction="" escapeKeyAction="">
|
|
||||||
<img src="/img/ltc.png" style="width: 32px; height: 32px;">
|
|
||||||
<h3>${translate("nodepage.nchange38")}</h3>
|
|
||||||
<hr>
|
|
||||||
<h3>${translate("nodepage.nchange42")}</h3>
|
|
||||||
</mwc-dialog>
|
|
||||||
<mwc-dialog id="errorDialog" scrimClickAction="" escapeKeyAction="">
|
|
||||||
<img src="/img/ltc.png" style="width: 32px; height: 32px;">
|
|
||||||
<h3>${translate("nodepage.nchange38")}</h3>
|
|
||||||
<hr>
|
|
||||||
<h3>${translate("nodepage.nchange43")}</h3>
|
|
||||||
</mwc-dialog>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
async firstUpdated() {
|
exportname = 'Private_Master_Key_' + myCoinName + '_' + myCoinAddress + '.txt'
|
||||||
addTradeBotRoutes(parentEpml)
|
|
||||||
parentEpml.imReady()
|
|
||||||
await this.fetchArrrWalletAddress()
|
|
||||||
await this.checkArrrWalletPrivateKey()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchArrrWalletAddress() {
|
await this.saveFileToDisk(blob, exportname)
|
||||||
let resAD = await parentEpml.request('apiCall', {
|
}
|
||||||
url: `/crosschain/arrr/walletaddress?apiKey=${this.getApiKey()}`,
|
|
||||||
method: 'POST',
|
|
||||||
body: `${store.getState().app.selectedAddress.arrrWallet.seed58}`
|
|
||||||
})
|
|
||||||
|
|
||||||
if (resAD != null && resAD.error != 1201) {
|
async saveFileToDisk(blob, fileName) {
|
||||||
this.arrrWALLET = ''
|
try {
|
||||||
this.enableArrr = true
|
const fileHandle = await self.showSaveFilePicker({
|
||||||
this.arrrWALLET = resAD
|
suggestedName: fileName,
|
||||||
} else {
|
types: [{
|
||||||
this.arrrWALLET = ''
|
description: "File"
|
||||||
this.enableArrr = false
|
}]
|
||||||
this.shadowRoot.querySelector('#arrrWalletNotSynced').show()
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkArrrWalletPrivateKey() {
|
const writeFile = async (fileHandle, contents) => {
|
||||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
const writable = await fileHandle.createWritable()
|
||||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
|
||||||
const privateKeyUrl = `${nodeUrl}/crosschain/arrr/walletprivatekey?apiKey=${this.getApiKey()}`
|
|
||||||
|
|
||||||
await fetch(privateKeyUrl, {
|
await writable.write(contents)
|
||||||
method: 'POST',
|
await writable.close()
|
||||||
body: `${store.getState().app.selectedAddress.arrrWallet.seed58}`
|
}
|
||||||
}).then(res => {
|
|
||||||
if (res.status === 404) {
|
|
||||||
this.arrrPMK = ''
|
|
||||||
this.enableArrr = false
|
|
||||||
this.shadowRoot.querySelector('#needCoreUpdate').show()
|
|
||||||
} else {
|
|
||||||
this.fetchArrrWalletPrivateKey()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchArrrWalletPrivateKey() {
|
writeFile(fileHandle, blob).then(() => console.log("FILE SAVED"))
|
||||||
let resPK = await parentEpml.request('apiCall', {
|
|
||||||
url: `/crosschain/arrr/walletprivatekey?apiKey=${this.getApiKey()}`,
|
|
||||||
method: 'POST',
|
|
||||||
body: `${store.getState().app.selectedAddress.arrrWallet.seed58}`
|
|
||||||
})
|
|
||||||
|
|
||||||
if (resPK != null && resPK.error != 1201) {
|
let snack4string = get("general.save")
|
||||||
this.arrrPMK = ''
|
|
||||||
this.enableArrr = true
|
|
||||||
this.arrrPMK = resPK
|
|
||||||
} else {
|
|
||||||
this.arrrPMK = ''
|
|
||||||
this.enableArrr = false
|
|
||||||
this.shadowRoot.querySelector('#arrrWalletNotSynced').show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closeArrrWalletNotSynced() {
|
snackbar.add({
|
||||||
this.shadowRoot.querySelector('#arrrWalletNotSynced').close()
|
labelText: `${snack4string} ${fileName} ✅`,
|
||||||
}
|
dismiss: true
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
if (error.name === 'AbortError') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
closeNeedCoreUpdate() {
|
FileSaver.saveAs(blob, fileName)
|
||||||
this.arrrPMK = ''
|
}
|
||||||
this.enableArrr = false
|
}
|
||||||
this.shadowRoot.querySelector('#needCoreUpdate').close()
|
|
||||||
}
|
|
||||||
|
|
||||||
closeSavePkmDialog() {
|
getApiKey() {
|
||||||
this.shadowRoot.querySelector('#savePkmDialog').close()
|
const apiNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
}
|
|
||||||
|
|
||||||
openRepairLTCDialog() {
|
|
||||||
this.shadowRoot.querySelector('#repairLTCDialog').show()
|
|
||||||
}
|
|
||||||
|
|
||||||
closeRepairLTCDialog() {
|
|
||||||
this.shadowRoot.querySelector('#repairLTCDialog').close()
|
|
||||||
}
|
|
||||||
|
|
||||||
async repairLtcWallet() {
|
|
||||||
this.shadowRoot.querySelector('#repairLTCDialog').close()
|
|
||||||
this.shadowRoot.querySelector('#pleaseWaitDialog').show()
|
|
||||||
let resRepair = await parentEpml.request('apiCall', {
|
|
||||||
url: `/crosschain/ltc/repair?apiKey=${this.getApiKey()}`,
|
|
||||||
method: 'POST',
|
|
||||||
body: `${store.getState().app.selectedAddress.ltcWallet.derivedMasterPrivateKey}`
|
|
||||||
})
|
|
||||||
|
|
||||||
if (resRepair != null && resRepair.error != 128) {
|
|
||||||
this.shadowRoot.querySelector('#pleaseWaitDialog').close()
|
|
||||||
await this.openOkDialog()
|
|
||||||
} else {
|
|
||||||
this.shadowRoot.querySelector('#pleaseWaitDialog').close()
|
|
||||||
await this.openErrorDialog()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async openOkDialog() {
|
|
||||||
const okDelay = ms => new Promise(res => setTimeout(res, ms))
|
|
||||||
this.shadowRoot.querySelector('#okDialog').show()
|
|
||||||
await okDelay(3000)
|
|
||||||
this.shadowRoot.querySelector('#okDialog').close()
|
|
||||||
}
|
|
||||||
|
|
||||||
async openErrorDialog() {
|
|
||||||
const errorDelay = ms => new Promise(res => setTimeout(res, ms))
|
|
||||||
this.shadowRoot.querySelector('#errorDialog').show()
|
|
||||||
await errorDelay(3000)
|
|
||||||
this.shadowRoot.querySelector('#errorDialog').close()
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForPmkDownload(wAddress, wPkm, wName, wShort) {
|
|
||||||
this.dWalletAddress = ''
|
|
||||||
this.dPrivateKey = ''
|
|
||||||
this.dCoinName = ''
|
|
||||||
this.dCoinShort = ''
|
|
||||||
this.dWalletAddress = wAddress
|
|
||||||
this.dPrivateKey = wPkm
|
|
||||||
this.dCoinName = wName
|
|
||||||
this.dCoinShort = wShort
|
|
||||||
this.shadowRoot.querySelector('#savePkmDialog').show()
|
|
||||||
}
|
|
||||||
|
|
||||||
async exportKey(cMasterKey, cName, cAddress) {
|
|
||||||
let exportname = ""
|
|
||||||
const myPrivateMasterKey = cMasterKey
|
|
||||||
const myCoinName = cName
|
|
||||||
const myCoinAddress = cAddress
|
|
||||||
const blob = new Blob([`${myPrivateMasterKey}`], { type: 'text/plain;charset=utf-8' })
|
|
||||||
exportname = "Private_Master_Key_" + myCoinName + "_" + myCoinAddress + ".txt"
|
|
||||||
await this.saveFileToDisk(blob, exportname)
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveFileToDisk(blob, fileName) {
|
|
||||||
try {
|
|
||||||
const fileHandle = await self.showSaveFilePicker({
|
|
||||||
suggestedName: fileName,
|
|
||||||
types: [{
|
|
||||||
description: "File",
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
const writeFile = async (fileHandle, contents) => {
|
|
||||||
const writable = await fileHandle.createWritable()
|
|
||||||
await writable.write(contents)
|
|
||||||
await writable.close()
|
|
||||||
}
|
|
||||||
writeFile(fileHandle, blob).then(() => console.log("FILE SAVED"))
|
|
||||||
let snack4string = get("general.save")
|
|
||||||
snackbar.add({
|
|
||||||
labelText: `${snack4string} ${fileName} ✅`,
|
|
||||||
dismiss: true
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
if (error.name === 'AbortError') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
FileSaver.saveAs(blob, fileName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getApiKey() {
|
|
||||||
const apiNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
|
||||||
return apiNode.apiKey
|
return apiNode.apiKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('export-keys', ExportKeys)
|
window.customElements.define('export-keys', ExportKeys)
|
@ -1,258 +1,160 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../../store.js'
|
import { store } from '../../store'
|
||||||
import {allowShowSyncIndicator, removeShowSyncIndicator} from '../../redux/app/app-actions.js'
|
import { allowShowSyncIndicator, removeShowSyncIndicator } from '../../redux/app/app-actions'
|
||||||
import {doSetQChatNotificationConfig} from '../../redux/user/user-actions.js'
|
import { doSetQChatNotificationConfig } from '../../redux/user/user-actions'
|
||||||
import {translate} from '../../../translate'
|
import { notificationsViewStyles } from '../../styles/core-css'
|
||||||
import isElectron from 'is-electron'
|
import isElectron from 'is-electron'
|
||||||
|
|
||||||
import '@material/mwc-checkbox'
|
import '@material/mwc-checkbox'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { translate } from '../../../translate'
|
||||||
|
|
||||||
class NotificationsView extends connect(store)(LitElement) {
|
class NotificationsView extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
notificationConfig: { type: Object },
|
notificationConfig: { type: Object },
|
||||||
q_chatConfig: { type: Object },
|
q_chatConfig: { type: Object },
|
||||||
theme: { type: String, reflect: true },
|
theme: { type: String, reflect: true },
|
||||||
appNotificationList: { type: Array }
|
appNotificationList: { type: Array }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
static get styles() {
|
||||||
super()
|
return [notificationsViewStyles]
|
||||||
this.notificationConfig = {}
|
}
|
||||||
this.q_chatConfig = {}
|
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
|
||||||
this.appNotificationList = [] // Fetch the list of apps from local storage
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated() {
|
constructor() {
|
||||||
this.appNotificationList = this.getAppsFromStorage()
|
super()
|
||||||
}
|
this.notificationConfig = {}
|
||||||
|
this.q_chatConfig = {}
|
||||||
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
|
this.appNotificationList = []
|
||||||
|
}
|
||||||
|
|
||||||
static get styles() {
|
firstUpdated() {
|
||||||
return css`
|
this.appNotificationList = this.getAppsFromStorage()
|
||||||
.sub-main {
|
}
|
||||||
position: relative;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-box {
|
render() {
|
||||||
display: block;
|
return html`
|
||||||
position: relative;
|
<div class="sub-main">
|
||||||
top: 45%;
|
<div class="notification-box">
|
||||||
left: 50%;
|
<div class="content-box">
|
||||||
transform: translate(-50%, 0%);
|
<h4>Q-Chat ${translate("settings.notifications")}</h4>
|
||||||
text-align: center;
|
<div style="line-height: 3rem;">
|
||||||
}
|
<mwc-checkbox id="qChatPlaySound" @click=${e => this.setQChatNotificationConfig({ type: 'PLAY_SOUND', value: e.target.checked })} ?checked=${this.q_chatConfig.playSound}></mwc-checkbox>
|
||||||
|
<label
|
||||||
|
for="qChatPlaySound"
|
||||||
|
@click=${() => this.shadowRoot.getElementById('qChatPlaySound').click()}
|
||||||
|
>
|
||||||
|
${translate("settings.playsound")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div style="line-height: 3rem;">
|
||||||
|
<mwc-checkbox id="qChatShowNotification" @click=${e => this.setQChatNotificationConfig({ type: 'SHOW_NOTIFICATION', value: e.target.checked })} ?checked=${this.q_chatConfig.showNotification}></mwc-checkbox>
|
||||||
|
<label
|
||||||
|
for="qChatShowNotification"
|
||||||
|
@click=${() => this.shadowRoot.getElementById('qChatShowNotification').click()}
|
||||||
|
>
|
||||||
|
${translate("settings.shownotifications")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-box">
|
||||||
|
<h4>${translate("settings.qappNotification1")}</h4>
|
||||||
|
${this.appNotificationList.map((app) => html`
|
||||||
|
<div style="display: flex; justify-content: space-between; margin-top: 10px;">
|
||||||
|
${app}
|
||||||
|
<button class="remove-button" @click=${() => this.removeApp(app)}>Remove</button>
|
||||||
|
</div>
|
||||||
|
`)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox-row">
|
||||||
|
<label for="syncIndicator" id="syncIndicatorLabel" style="color: var(--black);">
|
||||||
|
${translate("settings.sync_indicator")}
|
||||||
|
</label>
|
||||||
|
<mwc-checkbox style="margin-right: -15px;" id="syncIndicator" @click=${(e) => this.checkForSyncMessages(e)} ?checked=${store.getState().app.showSyncIndicator}></mwc-checkbox>
|
||||||
|
</div>
|
||||||
|
${this.renderSetCoreButton()}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
@media(min-width: 1400px) {
|
getAppsFromStorage() {
|
||||||
.notification-box {
|
const address = store.getState().app.selectedAddress.address
|
||||||
display: grid;
|
const id = `appNotificationList-${address}`
|
||||||
grid-template-columns: 1fr 1fr;
|
const data = localStorage.getItem(id)
|
||||||
grid-gap: 30px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-row {
|
return data ? Object.keys(JSON.parse(data)) : []
|
||||||
position: relative;
|
}
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
align-content: center;
|
|
||||||
font-family: Montserrat, sans-serif;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-box {
|
removeApp(appName) {
|
||||||
border: 1px solid #a1a1a1;
|
// Remove the app from local storage
|
||||||
padding: 10px 25px;
|
this.removeAppFromStorage(appName)
|
||||||
text-align: left;
|
|
||||||
display: inline-block;
|
|
||||||
min-width: 350px;
|
|
||||||
min-height: 150px;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
// Update the apps list in the component
|
||||||
margin-bottom: 0;
|
this.appNotificationList = this.appNotificationList.filter(app => app !== appName)
|
||||||
}
|
}
|
||||||
|
|
||||||
mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::before {
|
removeAppFromStorage(appName) {
|
||||||
background-color:var(--mdc-theme-primary)
|
// Your method to remove the app from local storage
|
||||||
}
|
const address = store.getState().app.selectedAddress.address
|
||||||
|
const id = `appNotificationList-${address}`
|
||||||
|
const data = JSON.parse(localStorage.getItem(id) || '{}')
|
||||||
|
|
||||||
label:hover {
|
delete data[appName]
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
localStorage.setItem(id, JSON.stringify(data));
|
||||||
font-weight: 600;
|
}
|
||||||
font-size: 15px;
|
|
||||||
display: block;
|
|
||||||
line-height: 32px;
|
|
||||||
opacity: 0.66;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
renderSetCoreButton() {
|
||||||
font-size: 16px;
|
if (!isElectron()) {
|
||||||
display: inline-block;
|
return html``
|
||||||
}
|
} else {
|
||||||
|
return html`
|
||||||
|
<div style="max-width: 500px; display: flex; justify-content: center; margin: auto;">
|
||||||
|
<div @click=${() => this.checkCoreSettings()} class="q-button">${translate("settings.core")}</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.q-button {
|
checkCoreSettings() {
|
||||||
display: inline-flex;
|
window.electronAPI.setStartCore()
|
||||||
flex-direction: column;
|
}
|
||||||
justify-content: center;
|
|
||||||
align-content: center;
|
|
||||||
border: none;
|
|
||||||
border-radius: 20px;
|
|
||||||
padding-left: 25px;
|
|
||||||
padding-right: 25px;
|
|
||||||
color: white;
|
|
||||||
background: #03a9f4;
|
|
||||||
width: 50%;
|
|
||||||
font-size: 17px;
|
|
||||||
cursor: pointer;
|
|
||||||
height: 50px;
|
|
||||||
margin-top: 1rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: all .2s;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.remove-button {
|
checkForSyncMessages(e) {
|
||||||
font-family: Roboto, sans-serif;
|
if (e.target.checked) {
|
||||||
font-size: 16px;
|
store.dispatch(removeShowSyncIndicator(false))
|
||||||
color: var(--mdc-theme-primary);
|
} else {
|
||||||
background-color: transparent;
|
store.dispatch(allowShowSyncIndicator(true))
|
||||||
padding: 8px 10px;
|
}
|
||||||
border-radius: 5px;
|
}
|
||||||
border: none;
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
stateChanged(state) {
|
||||||
return html`
|
this.notificationConfig = state.user.notifications
|
||||||
<div class="sub-main">
|
this.q_chatConfig = this.notificationConfig.q_chat
|
||||||
<div class="notification-box">
|
}
|
||||||
<div class="content-box">
|
|
||||||
<h4> Q-Chat ${translate("settings.notifications")} </h4>
|
|
||||||
|
|
||||||
<div style="line-height: 3rem;">
|
setQChatNotificationConfig(valueObject) {
|
||||||
<mwc-checkbox id="qChatPlaySound" @click=${e => this.setQChatNotificationConfig({ type: 'PLAY_SOUND', value: e.target.checked })} ?checked=${this.q_chatConfig.playSound}></mwc-checkbox>
|
if (valueObject.type === 'PLAY_SOUND') {
|
||||||
<label
|
let data = {
|
||||||
for="qChatPlaySound"
|
playSound: !valueObject.value,
|
||||||
@click=${() => this.shadowRoot.getElementById('qChatPlaySound').click()}
|
showNotification: this.q_chatConfig.showNotification
|
||||||
>
|
}
|
||||||
${translate("settings.playsound")}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="line-height: 3rem;">
|
store.dispatch(doSetQChatNotificationConfig(data))
|
||||||
<mwc-checkbox id="qChatShowNotification" @click=${e => this.setQChatNotificationConfig({ type: 'SHOW_NOTIFICATION', value: e.target.checked })} ?checked=${this.q_chatConfig.showNotification}></mwc-checkbox>
|
} if (valueObject.type === 'SHOW_NOTIFICATION') {
|
||||||
<label
|
let data = {
|
||||||
for="qChatShowNotification"
|
playSound: this.q_chatConfig.playSound,
|
||||||
@click=${() => this.shadowRoot.getElementById('qChatShowNotification').click()}
|
showNotification: !valueObject.value
|
||||||
>
|
}
|
||||||
${translate("settings.shownotifications")}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="content-box">
|
|
||||||
<h4>${translate("settings.qappNotification1")}</h4>
|
|
||||||
${this.appNotificationList.map((app) => html`
|
|
||||||
<div style="display: flex; justify-content: space-between; margin-top: 10px;">
|
|
||||||
${app}
|
|
||||||
<button class="remove-button" @click=${() => this.removeApp(app)}>Remove</button>
|
|
||||||
</div>
|
|
||||||
`)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox-row">
|
|
||||||
<label for="syncIndicator" id="syncIndicatorLabel" style="color: var(--black);">
|
|
||||||
${translate("settings.sync_indicator")}
|
|
||||||
</label>
|
|
||||||
<mwc-checkbox style="margin-right: -15px;" id="syncIndicator" @click=${(e) => this.checkForSyncMessages(e)} ?checked=${store.getState().app.showSyncIndicator}></mwc-checkbox>
|
|
||||||
</div>
|
|
||||||
${this.renderSetCoreButton()}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
getAppsFromStorage() {
|
store.dispatch(doSetQChatNotificationConfig(data))
|
||||||
// Your method to fetch the list of apps from local storage
|
}
|
||||||
// Example:
|
}
|
||||||
const address = store.getState().app.selectedAddress.address
|
|
||||||
const id = `appNotificationList-${address}`
|
|
||||||
const data = localStorage.getItem(id)
|
|
||||||
return data ? Object.keys(JSON.parse(data)) : []
|
|
||||||
}
|
|
||||||
|
|
||||||
removeApp(appName) {
|
|
||||||
// Remove the app from local storage
|
|
||||||
this.removeAppFromStorage(appName);
|
|
||||||
// Update the apps list in the component
|
|
||||||
this.appNotificationList = this.appNotificationList.filter(app => app !== appName);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeAppFromStorage(appName) {
|
|
||||||
// Your method to remove the app from local storage
|
|
||||||
const address= store.getState().app.selectedAddress.address
|
|
||||||
const id = `appNotificationList-${address}`;
|
|
||||||
const data = JSON.parse(localStorage.getItem(id) || '{}');
|
|
||||||
delete data[appName];
|
|
||||||
localStorage.setItem(id, JSON.stringify(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSetCoreButton() {
|
|
||||||
if (!isElectron()) {
|
|
||||||
return html``
|
|
||||||
} else {
|
|
||||||
return html`
|
|
||||||
<div style="max-width: 500px; display: flex; justify-content: center; margin: auto;">
|
|
||||||
<div @click=${() => this.checkCoreSettings()} class="q-button"> ${translate("settings.core")} </div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkCoreSettings() {
|
|
||||||
window.electronAPI.setStartCore()
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForSyncMessages(e) {
|
|
||||||
if (e.target.checked) {
|
|
||||||
store.dispatch(removeShowSyncIndicator(false))
|
|
||||||
} else {
|
|
||||||
store.dispatch(allowShowSyncIndicator(true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stateChanged(state) {
|
|
||||||
this.notificationConfig = state.user.notifications
|
|
||||||
this.q_chatConfig = this.notificationConfig.q_chat
|
|
||||||
}
|
|
||||||
|
|
||||||
setQChatNotificationConfig(valueObject) {
|
|
||||||
if (valueObject.type === 'PLAY_SOUND') {
|
|
||||||
let data = {
|
|
||||||
playSound: !valueObject.value,
|
|
||||||
showNotification: this.q_chatConfig.showNotification
|
|
||||||
}
|
|
||||||
store.dispatch(doSetQChatNotificationConfig(data))
|
|
||||||
} if (valueObject.type === 'SHOW_NOTIFICATION') {
|
|
||||||
|
|
||||||
let data = {
|
|
||||||
playSound: this.q_chatConfig.playSound,
|
|
||||||
showNotification: !valueObject.value
|
|
||||||
}
|
|
||||||
store.dispatch(doSetQChatNotificationConfig(data))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('notifications-view', NotificationsView)
|
window.customElements.define('notifications-view', NotificationsView)
|
@ -1,140 +1,91 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../../store.js'
|
import { store } from '../../store'
|
||||||
import {translate} from '../../../translate'
|
import { qrLoginViewStyles } from '../../styles/core-css'
|
||||||
|
import '../../../../plugins/plugins/core/components/QortalQrcodeGenerator'
|
||||||
import '@material/mwc-textfield'
|
|
||||||
import '@material/mwc-icon'
|
import '@material/mwc-icon'
|
||||||
|
import '@material/mwc-textfield'
|
||||||
import '@vaadin/password-field/vaadin-password-field.js'
|
import '@vaadin/password-field/vaadin-password-field.js'
|
||||||
import '../../../../plugins/plugins/core/components/QortalQrcodeGenerator.js'
|
|
||||||
|
// Multi language support
|
||||||
|
import { translate } from '../../../translate'
|
||||||
|
|
||||||
class QRLoginView extends connect(store)(LitElement) {
|
class QRLoginView extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
theme: { type: String, reflect: true },
|
theme: { type: String, reflect: true },
|
||||||
savedWalletDataJson: { type: String },
|
savedWalletDataJson: { type: String },
|
||||||
translateDescriptionKey: { type: String }, // Description text
|
translateDescriptionKey: { type: String },
|
||||||
translateButtonKey: { type: String }, // Button text
|
translateButtonKey: { type: String }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [qrLoginViewStyles]
|
||||||
* {
|
}
|
||||||
--lumo-primary-text-color: rgb(0, 167, 245);
|
|
||||||
--lumo-primary-color-50pct: rgba(0, 167, 245, 0.5);
|
|
||||||
--lumo-primary-color-10pct: rgba(0, 167, 245, 0.1);
|
|
||||||
--lumo-primary-color: hsl(199, 100%, 48%);
|
|
||||||
--lumo-base-color: var(--white);
|
|
||||||
--lumo-body-text-color: var(--black);
|
|
||||||
--lumo-secondary-text-color: var(--sectxt);
|
|
||||||
--lumo-contrast-60pct: var(--vdicon);
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-box {
|
constructor() {
|
||||||
position: relative;
|
super()
|
||||||
top: 45%;
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
left: 50%;
|
this.translateDescriptionKey = 'settings.qr_login_description_' + (this.isWalletStored() ? '1' : '2')
|
||||||
transform: translate(-50%, 0%);
|
this.translateButtonKey = 'settings.qr_login_button_' + (this.isWalletStored() ? '1' : '2')
|
||||||
text-align: center;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.q-button {
|
render() {
|
||||||
display: inline-flex;
|
return html`
|
||||||
flex-direction: column;
|
<div style="position: relative;" >
|
||||||
justify-content: center;
|
<div class="center-box">
|
||||||
align-content: center;
|
<p>
|
||||||
border: none;
|
${translate(this.translateDescriptionKey)}
|
||||||
border-radius: 20px;
|
</p>
|
||||||
padding-left: 25px;
|
<div style="max-width: 500px; justify-content: center; margin: auto; display: ${this.isWalletStored() ? 'none' : 'flex'}">
|
||||||
padding-right: 25px;
|
<mwc-icon style="padding: 10px; padding-left:0; padding-top: 42px;">password</mwc-icon>
|
||||||
color: white;
|
<vaadin-password-field id="newWalletPassword" style="width: 100%; color: var(--black);" label="${translate("settings.password")}" autofocus></vaadin-password-field>
|
||||||
background: #03a9f4;
|
</div>
|
||||||
width: 50%;
|
<div style="max-width: 600px; display: flex; justify-content: center; margin: auto;">
|
||||||
font-size: 17px;
|
<div id="qr-toggle-button" @click=${() => this.showQRCode()} class="q-button outlined"> ${translate(this.translateButtonKey)} </div>
|
||||||
cursor: pointer;
|
</div>
|
||||||
height: 50px;
|
<div id="login-qr-code" style="display: none;">
|
||||||
margin-top: 1rem;
|
<qortal-qrcode-generator id="login-qr-code" data="${this.savedWalletDataJson}" mode="octet" format="html" auto></qortal-qrcode-generator>
|
||||||
text-transform: uppercase;
|
</div>
|
||||||
text-decoration: none;
|
</div>
|
||||||
transition: all .2s;
|
</div>
|
||||||
position: relative;
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
.q-button.outlined {
|
isWalletStored() {
|
||||||
background: unset;
|
const state = store.getState()
|
||||||
border: 1px solid #03a9f4;
|
const address0 = state.app.wallet._addresses[0].address
|
||||||
}
|
const savedWalletData = state.user.storedWallets && state.user.storedWallets[address0]
|
||||||
|
|
||||||
:host([theme="light"]) .q-button.outlined {
|
return !!savedWalletData
|
||||||
color: #03a9f4;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#qr-toggle-button {
|
async setSavedWalletDataJson() {
|
||||||
margin-left: 12px;
|
const state = store.getState()
|
||||||
}
|
|
||||||
|
|
||||||
#login-qr-code {
|
let data
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
if (this.isWalletStored()) {
|
||||||
super()
|
const address0 = state.app.wallet._addresses[0].address
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
|
||||||
this.translateDescriptionKey = 'settings.qr_login_description_' + (this.isWalletStored() ? '1' : '2')
|
|
||||||
this.translateButtonKey = 'settings.qr_login_button_' + (this.isWalletStored() ? '1' : '2')
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
data = state.user.storedWallets[address0]
|
||||||
return html`
|
} else {
|
||||||
<div style="position: relative;" >
|
const password = this.shadowRoot.getElementById('newWalletPassword').value
|
||||||
<div class="center-box">
|
|
||||||
<p>
|
|
||||||
${translate(this.translateDescriptionKey)}
|
|
||||||
</p>
|
|
||||||
<div style="max-width: 500px; justify-content: center; margin: auto; display: ${this.isWalletStored() ? 'none' : 'flex' }">
|
|
||||||
<mwc-icon style="padding: 10px; padding-left:0; padding-top: 42px;">password</mwc-icon>
|
|
||||||
<vaadin-password-field id="newWalletPassword" style="width: 100%; color: var(--black);" label="${translate("settings.password")}" autofocus></vaadin-password-field>
|
|
||||||
</div>
|
|
||||||
<div style="max-width: 600px; display: flex; justify-content: center; margin: auto;">
|
|
||||||
<div id="qr-toggle-button" @click=${() => this.showQRCode()} class="q-button outlined"> ${translate(this.translateButtonKey)} </div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="login-qr-code" style="display: none;">
|
data = await state.app.wallet.generateSaveWalletData(password, state.config.crypto.kdfThreads, () => { })
|
||||||
<qortal-qrcode-generator id="login-qr-code" data="${this.savedWalletDataJson}" mode="octet" format="html" auto></qortal-qrcode-generator>
|
}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
isWalletStored() {
|
this.savedWalletDataJson = JSON.stringify(data)
|
||||||
const state = store.getState()
|
}
|
||||||
const address0 = state.app.wallet._addresses[0].address
|
|
||||||
const savedWalletData = state.user.storedWallets && state.user.storedWallets[address0]
|
|
||||||
return !!savedWalletData
|
|
||||||
}
|
|
||||||
|
|
||||||
async setSavedWalletDataJson() {
|
async showQRCode() {
|
||||||
const state = store.getState()
|
await this.setSavedWalletDataJson()
|
||||||
let data
|
|
||||||
if (this.isWalletStored()) { // if the wallet is stored, we use the existing encrypted backup
|
|
||||||
const address0 = state.app.wallet._addresses[0].address
|
|
||||||
data = state.user.storedWallets[address0]
|
|
||||||
} else { // if the wallet is not stored, we generate new `saveWalletData` backup encrypted with the new password
|
|
||||||
const password = this.shadowRoot.getElementById('newWalletPassword').value
|
|
||||||
data = await state.app.wallet.generateSaveWalletData(password, state.config.crypto.kdfThreads, () => { })
|
|
||||||
}
|
|
||||||
this.savedWalletDataJson = JSON.stringify(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async showQRCode() {
|
let el = this.shadowRoot.getElementById('login-qr-code')
|
||||||
await this.setSavedWalletDataJson()
|
|
||||||
let el = this.shadowRoot.getElementById('login-qr-code')
|
el.style.display = 'flex'
|
||||||
el.style.display = 'flex'
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('qr-login-view', QRLoginView)
|
window.customElements.define('qr-login-view', QRLoginView)
|
@ -1,6 +1,6 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../../store.js'
|
import { store } from '../../store'
|
||||||
import {
|
import {
|
||||||
allowQAPPAutoAuth,
|
allowQAPPAutoAuth,
|
||||||
allowQAPPAutoFriendsList,
|
allowQAPPAutoFriendsList,
|
||||||
@ -9,262 +9,194 @@ import {
|
|||||||
removeQAPPAutoFriendsList,
|
removeQAPPAutoFriendsList,
|
||||||
removeQAPPAutoLists,
|
removeQAPPAutoLists,
|
||||||
setIsOpenDevDialog
|
setIsOpenDevDialog
|
||||||
} from '../../redux/app/app-actions.js'
|
} from '../../redux/app/app-actions'
|
||||||
import {get, translate} from '../../../translate'
|
import { securityViewStyles } from '../../styles/core-css'
|
||||||
import snackbar from '../../functional-components/snackbar.js'
|
|
||||||
import FileSaver from 'file-saver'
|
import FileSaver from 'file-saver'
|
||||||
|
import snackbar from '../../functional-components/snackbar'
|
||||||
import '@material/mwc-checkbox'
|
import '@material/mwc-checkbox'
|
||||||
import '@material/mwc-textfield'
|
|
||||||
import '@material/mwc-icon'
|
import '@material/mwc-icon'
|
||||||
|
import '@material/mwc-textfield'
|
||||||
import '@vaadin/password-field/vaadin-password-field.js'
|
import '@vaadin/password-field/vaadin-password-field.js'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { get, translate } from '../../../translate'
|
||||||
|
|
||||||
class SecurityView extends connect(store)(LitElement) {
|
class SecurityView extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
theme: { type: String, reflect: true },
|
theme: { type: String, reflect: true },
|
||||||
backupErrorMessage: { type: String },
|
backupErrorMessage: { type: String },
|
||||||
closeSettings: {attribute: false}
|
closeSettings: { attribute: false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [securityViewStyles]
|
||||||
* {
|
}
|
||||||
--lumo-primary-text-color: rgb(0, 167, 245);
|
|
||||||
--lumo-primary-color-50pct: rgba(0, 167, 245, 0.5);
|
|
||||||
--lumo-primary-color-10pct: rgba(0, 167, 245, 0.1);
|
|
||||||
--lumo-primary-color: hsl(199, 100%, 48%);
|
|
||||||
--lumo-base-color: var(--white);
|
|
||||||
--lumo-body-text-color: var(--black);
|
|
||||||
--lumo-secondary-text-color: var(--sectxt);
|
|
||||||
--lumo-contrast-60pct: var(--vdicon);
|
|
||||||
--mdc-checkbox-unchecked-color: var(--black);
|
|
||||||
--mdc-theme-on-surface: var(--black);
|
|
||||||
--mdc-checkbox-disabled-color: var(--black);
|
|
||||||
--mdc-checkbox-ink-color: var(--black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-box {
|
constructor() {
|
||||||
position: relative;
|
super()
|
||||||
top: 45%;
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
left: 50%;
|
this.backupErrorMessage = ''
|
||||||
transform: translate(-50%, 0%);
|
}
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-row {
|
render() {
|
||||||
position: relative;
|
return html`
|
||||||
display: flex;
|
<div style="position: relative;" >
|
||||||
align-items: center;
|
<div class="center-box">
|
||||||
align-content: center;
|
<p>
|
||||||
font-family: Montserrat, sans-serif;
|
${translate("settings.choose")}
|
||||||
font-weight: 600;
|
</p>
|
||||||
color: var(--black);
|
<div style="max-width: 500px; display: flex; justify-content: center; margin: auto;">
|
||||||
}
|
<mwc-icon style="padding: 10px; padding-left:0; padding-top: 42px;">password</mwc-icon>
|
||||||
|
<vaadin-password-field
|
||||||
|
style="width: 100%; color: var(--black);"
|
||||||
|
label="${translate("settings.password")}"
|
||||||
|
id="downloadBackupPassword"
|
||||||
|
helper-text="${translate("login.passwordhint")}"
|
||||||
|
autofocus
|
||||||
|
></vaadin-password-field>
|
||||||
|
</div>
|
||||||
|
<div style="max-width: 500px; display: flex; justify-content: center; margin: auto;">
|
||||||
|
<mwc-icon style="padding: 10px; padding-left:0; padding-top: 42px;">password</mwc-icon>
|
||||||
|
<vaadin-password-field
|
||||||
|
style="width: 100%; color: var(--black);"
|
||||||
|
label="${translate("login.confirmpass")}"
|
||||||
|
id="rePassword"
|
||||||
|
></vaadin-password-field>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center; color: var(--mdc-theme-error); text-transform: uppercase; font-size: 15px;">
|
||||||
|
${this.backupErrorMessage}
|
||||||
|
</div>
|
||||||
|
<div style="max-width: 500px; display: flex; justify-content: center; margin: auto;">
|
||||||
|
<div @click=${() => this.checkForDownload()} class="q-button"> ${translate("settings.download")} </div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr style="margin-top: 20px;">
|
||||||
|
<div class="checkbox-row">
|
||||||
|
<label for="authButton" id="authButtonLabel" style="color: var(--black);">
|
||||||
|
${get('browserpage.bchange26')}
|
||||||
|
</label>
|
||||||
|
<mwc-checkbox style="margin-right: -15px;" id="authButton" @click=${(e) => this.checkForAuth(e)} ?checked=${store.getState().app.qAPPAutoAuth}></mwc-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox-row">
|
||||||
|
<label for="authButton" id="authButtonLabel" style="color: var(--black);">
|
||||||
|
${get('browserpage.bchange39')}
|
||||||
|
</label>
|
||||||
|
<mwc-checkbox style="margin-right: -15px;" id="authButton" @click=${(e) => this.checkForLists(e)} ?checked=${store.getState().app.qAPPAutoLists}></mwc-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox-row">
|
||||||
|
<label for="authButton" id="authButtonLabel" style="color: var(--black);">
|
||||||
|
${get('browserpage.bchange53')}
|
||||||
|
</label>
|
||||||
|
<mwc-checkbox style="margin-right: -15px;" id="authButton" @click=${(e) => this.checkForFriends(e)} ?checked=${store.getState().app.qAPPFriendsList}></mwc-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox-row">
|
||||||
|
<button class="add-dev-button" title="${translate('tabmenu.tm18')}" @click=${this.openDevDialog}>
|
||||||
|
${translate('tabmenu.tm38')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
.q-button {
|
checkForAuth(e) {
|
||||||
display: inline-flex;
|
if (e.target.checked) {
|
||||||
flex-direction: column;
|
store.dispatch(removeQAPPAutoAuth(false))
|
||||||
justify-content: center;
|
} else {
|
||||||
align-content: center;
|
store.dispatch(allowQAPPAutoAuth(true))
|
||||||
border: none;
|
}
|
||||||
border-radius: 20px;
|
}
|
||||||
padding-left: 25px;
|
|
||||||
padding-right: 25px;
|
|
||||||
color: white;
|
|
||||||
background: #03a9f4;
|
|
||||||
width: 50%;
|
|
||||||
font-size: 17px;
|
|
||||||
cursor: pointer;
|
|
||||||
height: 50px;
|
|
||||||
margin-top: 1rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: all .2s;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-dev-button {
|
checkForLists(e) {
|
||||||
margin-top: 4px;
|
if (e.target.checked) {
|
||||||
max-height: 28px;
|
store.dispatch(removeQAPPAutoLists(false))
|
||||||
padding: 5px 5px;
|
} else {
|
||||||
font-size: 14px;
|
store.dispatch(allowQAPPAutoLists(true))
|
||||||
background-color: #03a9f4;
|
}
|
||||||
color: white;
|
}
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: 3px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-dev-button:hover {
|
checkForFriends(e) {
|
||||||
opacity: 0.8;
|
if (e.target.checked) {
|
||||||
cursor: pointer;
|
store.dispatch(removeQAPPAutoFriendsList(false))
|
||||||
}
|
} else {
|
||||||
`
|
store.dispatch(allowQAPPAutoFriendsList(true))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
checkForDownload() {
|
||||||
super()
|
const checkPass = this.shadowRoot.getElementById('downloadBackupPassword').value
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
const rePass = this.shadowRoot.getElementById('rePassword').value
|
||||||
this.backupErrorMessage = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
if (checkPass === '') {
|
||||||
return html`
|
this.backupErrorMessage = get("login.pleaseenter")
|
||||||
<div style="position: relative;" >
|
} else if (checkPass.length < 5) {
|
||||||
<div class="center-box">
|
this.backupErrorMessage = get("login.lessthen8-2")
|
||||||
<p>
|
} else if (checkPass != rePass) {
|
||||||
${translate("settings.choose")}
|
this.backupErrorMessage = get("login.notmatch")
|
||||||
</p>
|
} else {
|
||||||
<div style="max-width: 500px; display: flex; justify-content: center; margin: auto;">
|
this.downloadBackup()
|
||||||
<mwc-icon style="padding: 10px; padding-left:0; padding-top: 42px;">password</mwc-icon>
|
}
|
||||||
<vaadin-password-field
|
}
|
||||||
style="width: 100%; color: var(--black);"
|
|
||||||
label="${translate("settings.password")}"
|
|
||||||
id="downloadBackupPassword"
|
|
||||||
helper-text="${translate("login.passwordhint")}"
|
|
||||||
autofocus
|
|
||||||
>
|
|
||||||
</vaadin-password-field>
|
|
||||||
</div>
|
|
||||||
<div style="max-width: 500px; display: flex; justify-content: center; margin: auto;">
|
|
||||||
<mwc-icon style="padding: 10px; padding-left:0; padding-top: 42px;">password</mwc-icon>
|
|
||||||
<vaadin-password-field
|
|
||||||
style="width: 100%; color: var(--black);"
|
|
||||||
label="${translate("login.confirmpass")}"
|
|
||||||
id="rePassword"
|
|
||||||
>
|
|
||||||
</vaadin-password-field>
|
|
||||||
</div>
|
|
||||||
<div style="text-align: center; color: var(--mdc-theme-error); text-transform: uppercase; font-size: 15px;">
|
|
||||||
${this.backupErrorMessage}
|
|
||||||
</div>
|
|
||||||
<div style="max-width: 500px; display: flex; justify-content: center; margin: auto;">
|
|
||||||
<div @click=${() => this.checkForDownload()} class="q-button"> ${translate("settings.download")} </div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr style="margin-top: 20px;">
|
|
||||||
<div class="checkbox-row">
|
|
||||||
<label for="authButton" id="authButtonLabel" style="color: var(--black);">
|
|
||||||
${get('browserpage.bchange26')}
|
|
||||||
</label>
|
|
||||||
<mwc-checkbox style="margin-right: -15px;" id="authButton" @click=${(e) => this.checkForAuth(e)} ?checked=${store.getState().app.qAPPAutoAuth}></mwc-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox-row">
|
|
||||||
<label for="authButton" id="authButtonLabel" style="color: var(--black);">
|
|
||||||
${get('browserpage.bchange39')}
|
|
||||||
</label>
|
|
||||||
<mwc-checkbox style="margin-right: -15px;" id="authButton" @click=${(e) => this.checkForLists(e)} ?checked=${store.getState().app.qAPPAutoLists}></mwc-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox-row">
|
|
||||||
<label for="authButton" id="authButtonLabel" style="color: var(--black);">
|
|
||||||
${get('browserpage.bchange53')}
|
|
||||||
</label>
|
|
||||||
<mwc-checkbox style="margin-right: -15px;" id="authButton" @click=${(e) => this.checkForFriends(e)} ?checked=${store.getState().app.qAPPFriendsList}></mwc-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox-row">
|
|
||||||
<button
|
|
||||||
class="add-dev-button"
|
|
||||||
title="${translate('tabmenu.tm18')}"
|
|
||||||
@click=${this.openDevDialog}
|
|
||||||
>
|
|
||||||
${translate('tabmenu.tm38')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
stateChanged(state) {
|
openDevDialog() {
|
||||||
}
|
this.closeSettings()
|
||||||
|
store.dispatch(setIsOpenDevDialog(true))
|
||||||
|
}
|
||||||
|
|
||||||
checkForAuth(e) {
|
async downloadBackup() {
|
||||||
if (e.target.checked) {
|
let backupname = ''
|
||||||
store.dispatch(removeQAPPAutoAuth(false))
|
|
||||||
} else {
|
|
||||||
store.dispatch(allowQAPPAutoAuth(true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForLists(e) {
|
this.backupErrorMessage = ''
|
||||||
if (e.target.checked) {
|
|
||||||
store.dispatch(removeQAPPAutoLists(false))
|
|
||||||
} else {
|
|
||||||
store.dispatch(allowQAPPAutoLists(true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForFriends(e) {
|
const state = store.getState()
|
||||||
if (e.target.checked) {
|
const password = this.shadowRoot.getElementById('downloadBackupPassword').value
|
||||||
store.dispatch(removeQAPPAutoFriendsList(false))
|
const data = await state.app.wallet.generateSaveWalletData(password, state.config.crypto.kdfThreads, () => { })
|
||||||
} else {
|
const dataString = JSON.stringify(data)
|
||||||
store.dispatch(allowQAPPAutoFriendsList(true))
|
const blob = new Blob([dataString], { type: 'text/plain;charset=utf-8' })
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForDownload() {
|
backupname = 'qortal_backup_' + state.app.selectedAddress.address + '.json'
|
||||||
const checkPass = this.shadowRoot.getElementById('downloadBackupPassword').value
|
|
||||||
const rePass = this.shadowRoot.getElementById('rePassword').value
|
|
||||||
|
|
||||||
if (checkPass === '') {
|
await this.saveFileToDisk(blob, backupname)
|
||||||
this.backupErrorMessage = get("login.pleaseenter")
|
}
|
||||||
} else if (checkPass.length < 5) {
|
|
||||||
this.backupErrorMessage = get("login.lessthen8-2")
|
|
||||||
} else if (checkPass != rePass) {
|
|
||||||
this.backupErrorMessage = get("login.notmatch")
|
|
||||||
} else {
|
|
||||||
this.downloadBackup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
openDevDialog() {
|
async saveFileToDisk(blob, fileName) {
|
||||||
this.closeSettings()
|
try {
|
||||||
store.dispatch(setIsOpenDevDialog(true))
|
const fileHandle = await self.showSaveFilePicker({
|
||||||
}
|
suggestedName: fileName,
|
||||||
|
types: [{
|
||||||
|
description: "File"
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
async downloadBackup() {
|
const writeFile = async (fileHandle, contents) => {
|
||||||
let backupname = ''
|
const writable = await fileHandle.createWritable()
|
||||||
this.backupErrorMessage = ''
|
|
||||||
const state = store.getState()
|
|
||||||
const password = this.shadowRoot.getElementById('downloadBackupPassword').value
|
|
||||||
const data = await state.app.wallet.generateSaveWalletData(password, state.config.crypto.kdfThreads, () => { })
|
|
||||||
const dataString = JSON.stringify(data)
|
|
||||||
const blob = new Blob([dataString], { type: 'text/plain;charset=utf-8' })
|
|
||||||
backupname = "qortal_backup_" + state.app.selectedAddress.address + ".json"
|
|
||||||
await this.saveFileToDisk(blob, backupname)
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveFileToDisk(blob, fileName) {
|
await writable.write(contents)
|
||||||
try {
|
await writable.close()
|
||||||
const fileHandle = await self.showSaveFilePicker({
|
}
|
||||||
suggestedName: fileName,
|
|
||||||
types: [{
|
writeFile(fileHandle, blob).then(() => console.log("FILE SAVED"))
|
||||||
description: "File",
|
|
||||||
}]
|
let snack4string = get("general.save")
|
||||||
})
|
|
||||||
const writeFile = async (fileHandle, contents) => {
|
snackbar.add({
|
||||||
const writable = await fileHandle.createWritable()
|
labelText: `${snack4string} ${fileName} ✅`,
|
||||||
await writable.write(contents)
|
dismiss: true
|
||||||
await writable.close()
|
})
|
||||||
}
|
|
||||||
writeFile(fileHandle, blob).then(() => console.log("FILE SAVED"))
|
this.shadowRoot.getElementById('downloadBackupPassword').value = ''
|
||||||
let snack4string = get("general.save")
|
this.shadowRoot.getElementById('rePassword').value = ''
|
||||||
snackbar.add({
|
} catch (error) {
|
||||||
labelText: `${snack4string} ${fileName} ✅`,
|
if (error.name === 'AbortError') {
|
||||||
dismiss: true
|
return
|
||||||
})
|
}
|
||||||
this.shadowRoot.getElementById('downloadBackupPassword').value = ''
|
|
||||||
this.shadowRoot.getElementById('rePassword').value = ''
|
FileSaver.saveAs(blob, fileName)
|
||||||
} catch (error) {
|
this.shadowRoot.getElementById('downloadBackupPassword').value = ''
|
||||||
if (error.name === 'AbortError') {
|
this.shadowRoot.getElementById('rePassword').value = ''
|
||||||
return
|
}
|
||||||
}
|
}
|
||||||
FileSaver.saveAs(blob, fileName)
|
|
||||||
this.shadowRoot.getElementById('downloadBackupPassword').value = ''
|
|
||||||
this.shadowRoot.getElementById('rePassword').value = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('security-view', SecurityView)
|
window.customElements.define('security-view', SecurityView)
|
@ -1,309 +1,128 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../../store.js'
|
import { store } from '../../store'
|
||||||
import {translate} from '../../../translate'
|
import { userSettingsStyles } from '../../styles/core-css'
|
||||||
|
import './account-view'
|
||||||
import '@polymer/paper-dialog/paper-dialog.js'
|
import './export-keys'
|
||||||
|
import './notifications-view'
|
||||||
|
import './qr-login-view'
|
||||||
|
import './security-view'
|
||||||
import '@material/mwc-button'
|
import '@material/mwc-button'
|
||||||
|
import '@polymer/paper-dialog/paper-dialog.js'
|
||||||
|
|
||||||
import './account-view.js'
|
// Multi language support
|
||||||
import './security-view.js'
|
import { translate } from '../../../translate'
|
||||||
import './notifications-view.js'
|
|
||||||
import './qr-login-view.js'
|
|
||||||
import './export-keys.js'
|
|
||||||
|
|
||||||
class UserSettings extends connect(store)(LitElement) {
|
class UserSettings extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
loggedIn: { type: Boolean },
|
loggedIn: { type: Boolean },
|
||||||
pages: { type: Array },
|
pages: { type: Array },
|
||||||
selectedView: { type: Object },
|
selectedView: { type: Object },
|
||||||
theme: { type: String, reflect: true }
|
theme: { type: String, reflect: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [userSettingsStyles]
|
||||||
:host {
|
}
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100vw;
|
|
||||||
height: 100%;
|
|
||||||
max-height: 100vh;
|
|
||||||
background-color: var(--white);
|
|
||||||
color: var(--black);
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.decline {
|
constructor() {
|
||||||
--mdc-theme-primary: var(--mdc-theme-error)
|
super()
|
||||||
}
|
this.selectedView = { id: 'info', name: 'General Account Info' }
|
||||||
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
|
}
|
||||||
|
|
||||||
paper-dialog.userSettings {
|
render() {
|
||||||
width: 100%;
|
return html`
|
||||||
max-width: 100vw;
|
<paper-dialog id="userSettingsDialog" class="userSettings" modal>
|
||||||
height: 100%;
|
<div class="actions">
|
||||||
max-height: 100vh;
|
<h2></h2>
|
||||||
background-color: var(--white);
|
<mwc-icon class="close-icon" @click=${() => this.closeSettings()} title="Close Settings" >highlight_off</mwc-icon>
|
||||||
color: var(--black);
|
</div>
|
||||||
line-height: 1.6;
|
<div class="container">
|
||||||
overflow-y: auto;
|
<div class="wrapper">
|
||||||
}
|
<div class="leftBar" style="display: table; width: 100%;">
|
||||||
|
<div class="slug">Qortal UI ${translate("settings.settings")}</div>
|
||||||
|
<ul>
|
||||||
|
<li @click=${() => this.setSettingsView('info')} ><a class=${this.selectedView.id === 'info' ? 'active' : ''} href="javascript:void(0)">${translate("settings.account")}</a></li>
|
||||||
|
<li @click=${() => this.setSettingsView('security')} ><a class=${this.selectedView.id === 'security' ? 'active' : ''} href="javascript:void(0)">${translate("settings.security")}</a></li>
|
||||||
|
<li @click=${() => this.setSettingsView('export')} ><a class=${this.selectedView.id === 'export' ? 'active' : ''} href="javascript:void(0)">${translate("settings.exp1")}</a></li>
|
||||||
|
<li @click=${() => this.setSettingsView('qr-login')} ><a class=${this.selectedView.id === 'qr-login' ? 'active' : ''} href="javascript:void(0)">${translate("settings.qr_login_menu_item")}</a></li>
|
||||||
|
<li @click=${() => this.setSettingsView('notification')} ><a class=${this.selectedView.id === 'notification' ? 'active' : ''} href="javascript:void(0)">${translate("settings.notifications")}</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="mainPage">
|
||||||
|
<h1>${this.renderHeaderViews()}</h1>
|
||||||
|
<hr>
|
||||||
|
${html`${this.renderSettingViews(this.selectedView)}`}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</paper-dialog>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
.actions {
|
stateChanged(state) {
|
||||||
display:flex;
|
this.loggedIn = state.app.loggedIn
|
||||||
justify-content: space-between;
|
}
|
||||||
padding: 0 4em;
|
|
||||||
margin: 15px 0 -2px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-icon {
|
renderSettingViews(selectedView) {
|
||||||
font-size: 36px;
|
if (selectedView.id === 'info') {
|
||||||
}
|
return html`<account-view></account-view>`
|
||||||
|
} else if (selectedView.id === 'security') {
|
||||||
|
return html`<security-view .closeSettings=${() => this.closeSettings()}></security-view>`
|
||||||
|
} else if (selectedView.id === 'export') {
|
||||||
|
return html`<export-keys></export-keys>`
|
||||||
|
} else if (selectedView.id === 'notification') {
|
||||||
|
return html`<notifications-view></notifications-view>`
|
||||||
|
} else if (selectedView.id === 'qr-login') {
|
||||||
|
return html`<qr-login-view></qr-login-view>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.close-icon:hover {
|
renderHeaderViews() {
|
||||||
cursor: pointer;
|
if (this.selectedView.id === 'info') {
|
||||||
opacity: .6;
|
return html`${translate("settings.generalinfo")}`
|
||||||
}
|
} else if (this.selectedView.id === 'security') {
|
||||||
|
return html`${translate("settings.accountsecurity")}`
|
||||||
|
} else if (this.selectedView.id === 'export') {
|
||||||
|
return html`${translate("settings.exp1")}`
|
||||||
|
} else if (this.selectedView.id === 'notification') {
|
||||||
|
return html`UI ${translate("settings.notifications")}`
|
||||||
|
} else if (this.selectedView.id === 'qr-login') {
|
||||||
|
return html`${translate("settings.qr_login_menu_item")}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.buttons {
|
setSettingsView(pageId) {
|
||||||
text-align:right;
|
if (pageId === 'info') {
|
||||||
}
|
return this.selectedView = { id: 'info', name: 'General Account Info' }
|
||||||
|
} else if (pageId === 'security') {
|
||||||
|
return this.selectedView = { id: 'security', name: 'Account Security' }
|
||||||
|
} else if (pageId === 'export') {
|
||||||
|
return this.selectedView = { id: 'export', name: 'Export Master Keys' }
|
||||||
|
} else if (pageId === 'notification') {
|
||||||
|
return this.selectedView = { id: 'notification', name: 'UI Notifications' }
|
||||||
|
} else if (pageId === 'qr-login') {
|
||||||
|
return this.selectedView = { id: 'qr-login', name: 'QR Login' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
openSettings() {
|
||||||
max-width: 90vw;
|
if (this.loggedIn) {
|
||||||
margin-left: auto;
|
this.shadowRoot.getElementById('userSettingsDialog').open()
|
||||||
margin-right: auto;
|
}
|
||||||
margin-top: 20px;
|
}
|
||||||
padding: .6em;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
closeSettings() {
|
||||||
list-style: none;
|
this.shadowRoot.getElementById('userSettingsDialog').close()
|
||||||
padding: 0;
|
this.cleanUp()
|
||||||
margin-bottom: 0;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.leftBar {
|
cleanUp() {
|
||||||
background-color: var(--white);
|
this.selectedView = { id: 'info', name: 'General Account Info' }
|
||||||
color: var(--black);
|
}
|
||||||
border: 1px solid var(--border);
|
|
||||||
padding: 20px 0 0 0;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftBar img {
|
|
||||||
margin: 0 auto;
|
|
||||||
width: 75%;
|
|
||||||
height: 75%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftBar .slug {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 20px;
|
|
||||||
color: var(--black);
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftBar ul li {
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftBar ul li:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftBar ul li a {
|
|
||||||
color: var(--black);
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 400;
|
|
||||||
text-decoration: none;
|
|
||||||
padding: .9em;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftBar ul li a i {
|
|
||||||
margin-right: 8px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftBar ul li a:hover {
|
|
||||||
background-color: var(--menuhover);
|
|
||||||
color: #515151;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftBar ul li:active {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftBar ul li a.active {
|
|
||||||
color: #515151;
|
|
||||||
background-color: var(--menuactive);
|
|
||||||
border-left: 2px solid #515151;
|
|
||||||
margin-left: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mainPage {
|
|
||||||
background-color: var(--white);
|
|
||||||
color: var(--black);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
padding: 20px 0 10px 0;
|
|
||||||
border-radius: 5px;
|
|
||||||
font-size: 16px;
|
|
||||||
text-align: center;
|
|
||||||
min-height: 460px;
|
|
||||||
height: auto;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@media(max-width:700px) {
|
|
||||||
.mainPage {
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media(min-width:765px) {
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
display:flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0 4em;
|
|
||||||
margin: 15px 0 -25px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
padding: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 3fr;
|
|
||||||
grid-gap: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper > .mainPage {
|
|
||||||
padding: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftBar {
|
|
||||||
text-align: left;
|
|
||||||
max-height: 403px;
|
|
||||||
max-width: 400px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mainPage {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.selectedView = { id: 'info', name: 'General Account Info' }
|
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`
|
|
||||||
<paper-dialog id="userSettingsDialog" class="userSettings" modal>
|
|
||||||
<div class="actions">
|
|
||||||
<h2></h2>
|
|
||||||
<mwc-icon class="close-icon" @click=${ () => this.closeSettings()} title="Close Settings" >highlight_off</mwc-icon>
|
|
||||||
</div>
|
|
||||||
<div class="container">
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="leftBar" style="display: table; width: 100%;">
|
|
||||||
<div class="slug">Qortal UI ${translate("settings.settings")}</div>
|
|
||||||
<ul>
|
|
||||||
<li @click=${ () => this.setSettingsView('info')} ><a class=${this.selectedView.id === 'info' ? 'active' : ''} href="javascript:void(0)">${translate("settings.account")}</a></li>
|
|
||||||
<li @click=${ () => this.setSettingsView('security')} ><a class=${this.selectedView.id === 'security' ? 'active' : ''} href="javascript:void(0)">${translate("settings.security")}</a></li>
|
|
||||||
<li @click=${ () => this.setSettingsView('export')} ><a class=${this.selectedView.id === 'export' ? 'active' : ''} href="javascript:void(0)">${translate("settings.exp1") }</a></li>
|
|
||||||
<li @click=${ () => this.setSettingsView('qr-login')} ><a class=${this.selectedView.id === 'qr-login' ? 'active' : ''} href="javascript:void(0)">${translate("settings.qr_login_menu_item") }</a></li>
|
|
||||||
<li @click=${ () => this.setSettingsView('notification')} ><a class=${this.selectedView.id === 'notification' ? 'active' : ''} href="javascript:void(0)">${translate("settings.notifications")}</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="mainPage">
|
|
||||||
<h1>${this.renderHeaderViews()}</h1>
|
|
||||||
<hr>
|
|
||||||
${html`${this.renderSettingViews(this.selectedView)}`}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</paper-dialog>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
stateChanged(state) {
|
|
||||||
this.loggedIn = state.app.loggedIn
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSettingViews(selectedView) {
|
|
||||||
if (selectedView.id === 'info') {
|
|
||||||
return html`<account-view></account-view>`
|
|
||||||
} else if (selectedView.id === 'security') {
|
|
||||||
return html`<security-view .closeSettings=${()=> this.closeSettings()}></security-view>`
|
|
||||||
} else if (selectedView.id === 'export') {
|
|
||||||
return html`<export-keys></export-keys>`
|
|
||||||
} else if (selectedView.id === 'notification') {
|
|
||||||
return html`<notifications-view></notifications-view>`
|
|
||||||
} else if (selectedView.id === 'qr-login') {
|
|
||||||
return html`<qr-login-view></qr-login-view>`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderHeaderViews() {
|
|
||||||
if (this.selectedView.id === 'info') {
|
|
||||||
return html`${translate("settings.generalinfo")}`
|
|
||||||
} else if (this.selectedView.id === 'security') {
|
|
||||||
return html`${translate("settings.accountsecurity")}`
|
|
||||||
} else if (this.selectedView.id === 'export') {
|
|
||||||
return html`${translate("settings.exp1")}`
|
|
||||||
} else if (this.selectedView.id === 'notification') {
|
|
||||||
return html`UI ${translate("settings.notifications")}`
|
|
||||||
} else if (this.selectedView.id === 'qr-login') {
|
|
||||||
return html`${translate("settings.qr_login_menu_item")}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setSettingsView(pageId) {
|
|
||||||
if (pageId === 'info') {
|
|
||||||
return this.selectedView = { id: 'info', name: 'General Account Info' }
|
|
||||||
} else if (pageId === 'security') {
|
|
||||||
return this.selectedView = { id: 'security', name: 'Account Security' }
|
|
||||||
} else if (pageId === 'export') {
|
|
||||||
return this.selectedView = { id: 'export', name: 'Export Master Keys' }
|
|
||||||
} else if (pageId === 'notification') {
|
|
||||||
return this.selectedView = { id: 'notification', name: 'UI Notifications' }
|
|
||||||
} else if (pageId === 'qr-login') {
|
|
||||||
return this.selectedView = { id: 'qr-login', name: 'QR Login' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
openSettings() {
|
|
||||||
if (this.loggedIn) {
|
|
||||||
this.shadowRoot.getElementById('userSettingsDialog').open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closeSettings() {
|
|
||||||
this.shadowRoot.getElementById('userSettingsDialog').close()
|
|
||||||
this.cleanUp()
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanUp() {
|
|
||||||
this.selectedView = { id: 'info', name: 'General Account Info' }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('user-settings', UserSettings)
|
window.customElements.define('user-settings', UserSettings)
|
File diff suppressed because it is too large
Load Diff
@ -1,19 +1,20 @@
|
|||||||
import {css, html, LitElement} from 'lit';
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers';
|
import { asyncReplace } from 'lit/directives/async-replace.js'
|
||||||
import {store} from '../store.js';
|
import { connect } from 'pwa-helpers'
|
||||||
import {get, translate} from '../../translate'
|
import { store } from '../store.js'
|
||||||
import {asyncReplace} from 'lit/directives/async-replace.js';
|
import { routes } from '../plugins/routes'
|
||||||
|
import { startMintingStyles } from '../styles/core-css'
|
||||||
import '../functional-components/my-button.js';
|
import '../functional-components/my-button'
|
||||||
import {routes} from '../plugins/routes.js';
|
|
||||||
import "@material/mwc-button"
|
import "@material/mwc-button"
|
||||||
import '@material/mwc-dialog'
|
import '@material/mwc-dialog'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { get, translate } from '../../translate'
|
||||||
|
|
||||||
async function* countDown(count, callback) {
|
async function* countDown(count, callback) {
|
||||||
while (count > 0) {
|
while (count > 0) {
|
||||||
yield count--;
|
yield count--
|
||||||
await new Promise((r) => setTimeout(r, 1000));
|
await new Promise((r) => setTimeout(r, 1000))
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
callback()
|
callback()
|
||||||
}
|
}
|
||||||
@ -30,187 +31,56 @@ class StartMinting extends connect(store)(LitElement) {
|
|||||||
status: { type: Number },
|
status: { type: Number },
|
||||||
timer: { type: Number },
|
timer: { type: Number },
|
||||||
privateRewardShareKey: { type: String }
|
privateRewardShareKey: { type: String }
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [startMintingStyles]
|
||||||
css`
|
|
||||||
p, h1 {
|
|
||||||
color: var(--black)
|
|
||||||
}
|
|
||||||
.dialogCustom {
|
|
||||||
position: fixed;
|
|
||||||
z-index: 10000;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
top: 0px;
|
|
||||||
bottom: 0px;
|
|
||||||
left: 0px;
|
|
||||||
width: 100vw;
|
|
||||||
}
|
|
||||||
.dialogCustomInner {
|
|
||||||
width: 300px;
|
|
||||||
min-height: 400px;
|
|
||||||
background-color: var(--white);
|
|
||||||
box-shadow: var(--mdc-dialog-box-shadow, 0px 11px 15px -7px rgba(0, 0, 0, 0.2), 0px 24px 38px 3px rgba(0, 0, 0, 0.14), 0px 9px 46px 8px rgba(0, 0, 0, 0.12));
|
|
||||||
padding: 20px 24px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.dialogCustomInner ul {
|
|
||||||
padding-left: 0px
|
|
||||||
}
|
|
||||||
.dialogCustomInner li {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.start-minting-wrapper {
|
|
||||||
position: absolute;
|
|
||||||
transform: translate(50%, 20px);
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
.dialog-header h1 {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.modalFooter {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
.hide {
|
|
||||||
visibility: hidden
|
|
||||||
}
|
|
||||||
.inactiveText {
|
|
||||||
opacity: .60
|
|
||||||
}
|
|
||||||
.column {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.smallLoading,
|
|
||||||
.smallLoading:after {
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 2px;
|
|
||||||
height: 2px;
|
|
||||||
}
|
|
||||||
.smallLoading {
|
|
||||||
border-width: 0.6em;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2)
|
|
||||||
rgba(3, 169, 244, 0.2) rgb(3, 169, 244);
|
|
||||||
font-size: 10px;
|
|
||||||
position: relative;
|
|
||||||
text-indent: -9999em;
|
|
||||||
transform: translateZ(0px);
|
|
||||||
animation: 1.1s linear 0s infinite normal none running loadingAnimation;
|
|
||||||
}
|
|
||||||
@-webkit-keyframes loadingAnimation {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: rotate(0deg);
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-webkit-transform: rotate(360deg);
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes loadingAnimation {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: rotate(0deg);
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-webkit-transform: rotate(360deg);
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.word-break {
|
|
||||||
word-break:break-all;
|
|
||||||
}
|
|
||||||
.dialog-container {
|
|
||||||
width: 300px;
|
|
||||||
min-height: 300px;
|
|
||||||
max-height: 75vh;
|
|
||||||
padding: 5px;
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.between {
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
.no-width {
|
|
||||||
width: auto
|
|
||||||
}
|
|
||||||
.between p {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
color: var(--black);
|
|
||||||
}
|
|
||||||
.marginLoader {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
.marginRight {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
.warning{
|
|
||||||
display: flex;
|
|
||||||
flex-grow: 1
|
|
||||||
}
|
|
||||||
.message-error {
|
|
||||||
color: var(--error);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super()
|
||||||
this.addressInfo = {};
|
this.addressInfo = {}
|
||||||
this.mintingAccountData = [];
|
this.mintingAccountData = []
|
||||||
this.errorMsg = '';
|
this.errorMsg = ''
|
||||||
this.openDialogRewardShare = false;
|
this.openDialogRewardShare = false
|
||||||
this.status = 0;
|
this.status = 0
|
||||||
this.privateRewardShareKey = "";
|
this.privateRewardShareKey = ''
|
||||||
this.address = this.getAddress();
|
this.address = this.getAddress()
|
||||||
this.nonce = this.getNonce();
|
this.nonce = this.getNonce()
|
||||||
this.base58PublicKey = this.getBase58PublicKey()
|
this.base58PublicKey = this.getBase58PublicKey()
|
||||||
}
|
}
|
||||||
getBase58PublicKey(){
|
|
||||||
const appState = window.parent.reduxStore.getState().app;
|
|
||||||
const selectedAddress = appState && appState.selectedAddress;
|
|
||||||
const base58PublicKey = selectedAddress && selectedAddress.base58PublicKey;
|
|
||||||
return base58PublicKey || ""
|
|
||||||
}
|
|
||||||
|
|
||||||
getAddress(){
|
|
||||||
const appState = window.parent.reduxStore.getState().app;
|
|
||||||
const selectedAddress = appState && appState.selectedAddress;
|
|
||||||
const address = selectedAddress && selectedAddress.address;
|
|
||||||
return address || ""
|
|
||||||
|
|
||||||
}
|
|
||||||
getNonce(){
|
|
||||||
const appState = window.parent.reduxStore.getState().app;
|
|
||||||
const selectedAddress = appState && appState.selectedAddress;
|
|
||||||
const nonce = selectedAddress && selectedAddress.nonce;
|
|
||||||
return nonce || ""
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html` ${this.renderStartMintingButton()} `;
|
return html`${this.renderStartMintingButton()}`
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
this.getMintingAcccounts();
|
this.getMintingAcccounts()
|
||||||
|
}
|
||||||
|
|
||||||
|
getBase58PublicKey() {
|
||||||
|
const appState = window.parent.reduxStore.getState().app
|
||||||
|
const selectedAddress = appState && appState.selectedAddress
|
||||||
|
const base58PublicKey = selectedAddress && selectedAddress.base58PublicKey
|
||||||
|
|
||||||
|
return base58PublicKey || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
getAddress() {
|
||||||
|
const appState = window.parent.reduxStore.getState().app
|
||||||
|
const selectedAddress = appState && appState.selectedAddress
|
||||||
|
const address = selectedAddress && selectedAddress.address
|
||||||
|
|
||||||
|
return address || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
getNonce() {
|
||||||
|
const appState = window.parent.reduxStore.getState().app
|
||||||
|
const selectedAddress = appState && appState.selectedAddress
|
||||||
|
const nonce = selectedAddress && selectedAddress.nonce
|
||||||
|
|
||||||
|
return nonce || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
renderErrorMsg1() {
|
renderErrorMsg1() {
|
||||||
@ -230,124 +100,130 @@ const nonce = selectedAddress && selectedAddress.nonce;
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getMintingAcccounts() {
|
async getMintingAcccounts() {
|
||||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node];
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
const url = `${nodeUrl}/admin/mintingaccounts`;
|
const url = `${nodeUrl}/admin/mintingaccounts`
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(url);
|
const res = await fetch(url)
|
||||||
this.mintingAccountData = await res.json();
|
|
||||||
|
this.mintingAccountData = await res.json()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.errorMsg = this.renderErrorMsg1();
|
this.errorMsg = this.renderErrorMsg1()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async changeStatus(value){
|
async changeStatus(value) {
|
||||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node];
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
this.status = value
|
|
||||||
const address = this.address
|
const address = this.address
|
||||||
|
|
||||||
|
this.status = value
|
||||||
|
|
||||||
// Check to see if a sponsorship key on a newly-level 1 minter exists. If it does, remove it.
|
// Check to see if a sponsorship key on a newly-level 1 minter exists. If it does, remove it.
|
||||||
const findMintingAccountFromOtherUser = this.mintingAccountData.find((ma) => ma.recipientAccount === address && ma.mintingAccount !== address);
|
const findMintingAccountFromOtherUser = this.mintingAccountData.find((ma) => ma.recipientAccount === address && ma.mintingAccount !== address)
|
||||||
|
|
||||||
const removeMintingAccount = async (publicKey) => {
|
const removeMintingAccount = async (publicKey) => {
|
||||||
const url = `${nodeUrl}/admin/mintingaccounts?apiKey=${myNode.apiKey}`;
|
const url = `${nodeUrl}/admin/mintingaccounts?apiKey=${myNode.apiKey}`
|
||||||
return await fetch(url, {
|
return await fetch(url, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
body: publicKey,
|
body: publicKey
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const addMintingAccount = async (sponsorshipKeyValue) => {
|
const addMintingAccount = async (sponsorshipKeyValue) => {
|
||||||
const url = `${nodeUrl}/admin/mintingaccounts?apiKey=${myNode.apiKey}`;
|
const url = `${nodeUrl}/admin/mintingaccounts?apiKey=${myNode.apiKey}`
|
||||||
|
|
||||||
return await fetch(url, {
|
return await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: sponsorshipKeyValue,
|
body: sponsorshipKeyValue
|
||||||
});
|
})
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
findMintingAccountFromOtherUser &&
|
|
||||||
findMintingAccountFromOtherUser.publicKey &&
|
|
||||||
findMintingAccountFromOtherUser.publicKey[0]
|
|
||||||
) {
|
|
||||||
await removeMintingAccount(
|
|
||||||
findMintingAccountFromOtherUser.publicKey[0]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
this.errorMsg = this.renderErrorMsg2();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await addMintingAccount(this.privateRewardShareKey);
|
if (findMintingAccountFromOtherUser && findMintingAccountFromOtherUser.publicKey && findMintingAccountFromOtherUser.publicKey[0]) {
|
||||||
await routes.showSnackBar({
|
await removeMintingAccount(
|
||||||
data: translate('becomeMinterPage.bchange19'),
|
findMintingAccountFromOtherUser.publicKey[0]
|
||||||
});
|
)
|
||||||
this.status = 5;
|
}
|
||||||
await this.getMintingAcccounts();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.errorMsg = this.renderErrorMsg3();
|
this.errorMsg = this.renderErrorMsg2()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await addMintingAccount(this.privateRewardShareKey)
|
||||||
|
|
||||||
|
await routes.showSnackBar({
|
||||||
|
data: translate('becomeMinterPage.bchange19')
|
||||||
|
})
|
||||||
|
|
||||||
|
this.status = 5
|
||||||
|
|
||||||
|
await this.getMintingAcccounts()
|
||||||
|
} catch (error) {
|
||||||
|
this.errorMsg = this.renderErrorMsg3()
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async confirmRelationship() {
|
async confirmRelationship() {
|
||||||
const myNode =
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
store.getState().app.nodeConfig.knownNodes[
|
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
store.getState().app.nodeConfig.node
|
|
||||||
];
|
|
||||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
|
||||||
|
|
||||||
let interval = null
|
let interval = null
|
||||||
let stop = false
|
let stop = false
|
||||||
|
|
||||||
this.status = 2
|
this.status = 2
|
||||||
|
|
||||||
const getAnswer = async () => {
|
const getAnswer = async () => {
|
||||||
const rewardShares = async (minterAddr) => {
|
const rewardShares = async (minterAddr) => {
|
||||||
const url = `${nodeUrl}/addresses/rewardshares?minters=${minterAddr}&recipients=${minterAddr}`;
|
const url = `${nodeUrl}/addresses/rewardshares?minters=${minterAddr}&recipients=${minterAddr}`
|
||||||
const res = await fetch(url);
|
const res = await fetch(url)
|
||||||
return await res.json();
|
|
||||||
};
|
return await res.json()
|
||||||
|
}
|
||||||
|
|
||||||
if (!stop) {
|
if (!stop) {
|
||||||
stop = true;
|
stop = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const address = this.address
|
const address = this.address
|
||||||
const myRewardShareArray = await rewardShares(address);
|
const myRewardShareArray = await rewardShares(address)
|
||||||
|
|
||||||
if (myRewardShareArray.length > 0) {
|
if (myRewardShareArray.length > 0) {
|
||||||
clearInterval(interval)
|
clearInterval(interval)
|
||||||
this.status = 3
|
|
||||||
this.timer = countDown(180, () => this.changeStatus(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
this.status = 3
|
||||||
}
|
this.timer = countDown(180, () => this.changeStatus(4))
|
||||||
|
}
|
||||||
|
} catch (error) { }
|
||||||
|
|
||||||
stop = false
|
stop = false
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
interval = setInterval(getAnswer, 5000);
|
|
||||||
|
interval = setInterval(getAnswer, 5000)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderStartMintingButton() {
|
renderStartMintingButton() {
|
||||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node];
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
const mintingAccountData = this.mintingAccountData;
|
const mintingAccountData = this.mintingAccountData
|
||||||
const addressInfo = window.parent.reduxStore.getState().app.accountInfo.addressInfo
|
const addressInfo = window.parent.reduxStore.getState().app.accountInfo.addressInfo
|
||||||
const address = this.address
|
const address = this.address
|
||||||
const nonce = this.nonce
|
const nonce = this.nonce
|
||||||
const publicAddress = this.base58PublicKey
|
const publicAddress = this.base58PublicKey
|
||||||
const findMintingAccount = mintingAccountData.find((ma) => ma.mintingAccount === address);
|
const findMintingAccount = mintingAccountData.find((ma) => ma.mintingAccount === address)
|
||||||
const isMinterButKeyMintingKeyNotAssigned = addressInfo && addressInfo.error !== 124 && addressInfo.level >= 1 && !findMintingAccount;
|
const isMinterButKeyMintingKeyNotAssigned = addressInfo && addressInfo.error !== 124 && addressInfo.level >= 1 && !findMintingAccount
|
||||||
|
|
||||||
const makeTransactionRequest = async (lastRef) => {
|
const makeTransactionRequest = async (lastRef) => {
|
||||||
let mylastRef = lastRef;
|
let mylastRef = lastRef
|
||||||
let rewarddialog1 = get('transactions.rewarddialog1');
|
let rewarddialog1 = get('transactions.rewarddialog1')
|
||||||
let rewarddialog2 = get('transactions.rewarddialog2');
|
let rewarddialog2 = get('transactions.rewarddialog2')
|
||||||
let rewarddialog3 = get('transactions.rewarddialog3');
|
let rewarddialog3 = get('transactions.rewarddialog3')
|
||||||
let rewarddialog4 = get('transactions.rewarddialog4');
|
let rewarddialog4 = get('transactions.rewarddialog4')
|
||||||
|
|
||||||
return await routes.transaction({
|
return await routes.transaction({
|
||||||
data: {
|
data: {
|
||||||
@ -360,170 +236,150 @@ const nonce = selectedAddress && selectedAddress.nonce;
|
|||||||
rewarddialog1: rewarddialog1,
|
rewarddialog1: rewarddialog1,
|
||||||
rewarddialog2: rewarddialog2,
|
rewarddialog2: rewarddialog2,
|
||||||
rewarddialog3: rewarddialog3,
|
rewarddialog3: rewarddialog3,
|
||||||
rewarddialog4: rewarddialog4,
|
rewarddialog4: rewarddialog4
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
disableModal: true,
|
disableModal: true
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const getTxnRequestResponse = (txnResponse) => {
|
const getTxnRequestResponse = (txnResponse) => {
|
||||||
let err6string = get('rewardsharepage.rchange21');
|
let err6string = get('rewardsharepage.rchange21')
|
||||||
|
|
||||||
if (txnResponse && txnResponse.extraData && txnResponse.extraData.rewardSharePrivateKey &&
|
if (txnResponse && txnResponse.extraData && txnResponse.extraData.rewardSharePrivateKey &&
|
||||||
txnResponse.data && (txnResponse.data.message && (txnResponse.data.message.includes('multiple') || txnResponse.data.message.includes('SELF_SHARE_EXISTS')))) {
|
txnResponse.data && (txnResponse.data.message && (txnResponse.data.message.includes('multiple') || txnResponse.data.message.includes('SELF_SHARE_EXISTS')))) {
|
||||||
return err6string;
|
return err6string
|
||||||
}
|
}
|
||||||
|
|
||||||
if (txnResponse.success === false && txnResponse.message) {
|
if (txnResponse.success === false && txnResponse.message) {
|
||||||
throw txnResponse;
|
throw txnResponse
|
||||||
} else if (txnResponse.success === true && txnResponse.data && !txnResponse.data.error) {
|
} else if (txnResponse.success === true && txnResponse.data && !txnResponse.data.error) {
|
||||||
return err6string;
|
return err6string
|
||||||
} else {
|
} else {
|
||||||
throw txnResponse;
|
throw txnResponse
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
const createSponsorshipKey = async () => {
|
const createSponsorshipKey = async () => {
|
||||||
this.status = 1;
|
this.status = 1
|
||||||
let lastRef = await getLastRef();
|
|
||||||
let myTransaction = await makeTransactionRequest(lastRef);
|
let lastRef = await getLastRef()
|
||||||
getTxnRequestResponse(myTransaction);
|
let myTransaction = await makeTransactionRequest(lastRef)
|
||||||
|
|
||||||
|
getTxnRequestResponse(myTransaction)
|
||||||
|
|
||||||
if (myTransaction && myTransaction.extraData) {
|
if (myTransaction && myTransaction.extraData) {
|
||||||
return myTransaction.extraData.rewardSharePrivateKey;
|
return myTransaction.extraData.rewardSharePrivateKey
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
const getLastRef = async () => {
|
const getLastRef = async () => {
|
||||||
const url = `${nodeUrl}/addresses/lastreference/${address}`;
|
const url = `${nodeUrl}/addresses/lastreference/${address}`
|
||||||
const res = await fetch(url);
|
const res = await fetch(url)
|
||||||
return await res.text();
|
|
||||||
};
|
return await res.text()
|
||||||
|
}
|
||||||
|
|
||||||
const startMinting = async () => {
|
const startMinting = async () => {
|
||||||
this.openDialogRewardShare = true
|
this.openDialogRewardShare = true
|
||||||
this.errorMsg = '';
|
this.errorMsg = ''
|
||||||
|
|
||||||
const address = this.address
|
const address = this.address
|
||||||
|
|
||||||
const findMintingAccountsFromUser = this.mintingAccountData.filter((ma) => ma.recipientAccount === address && ma.mintingAccount === address);
|
const findMintingAccountsFromUser = this.mintingAccountData.filter((ma) => ma.recipientAccount === address && ma.mintingAccount === address)
|
||||||
|
|
||||||
if(findMintingAccountsFromUser.length > 2){
|
if (findMintingAccountsFromUser.length > 2) {
|
||||||
this.errorMsg = translate("startminting.smchange10")
|
this.errorMsg = translate("startminting.smchange10")
|
||||||
return;
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.privateRewardShareKey = await createSponsorshipKey();
|
this.privateRewardShareKey = await createSponsorshipKey()
|
||||||
|
|
||||||
await this.confirmRelationship(publicAddress)
|
await this.confirmRelationship(publicAddress)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log({ error })
|
console.log({ error })
|
||||||
this.errorMsg = (error && error.data && error.data.message) ? error.data.message : this.renderErrorMsg4();
|
|
||||||
|
|
||||||
|
this.errorMsg = (error && error.data && error.data.message) ? error.data.message : this.renderErrorMsg4()
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${isMinterButKeyMintingKeyNotAssigned ? html`
|
${isMinterButKeyMintingKeyNotAssigned ? html`
|
||||||
<div class="start-minting-wrapper">
|
<div class="start-minting-wrapper">
|
||||||
<my-button label="${translate('becomeMinterPage.bchange18')}"
|
<my-button
|
||||||
|
label="${translate('becomeMinterPage.bchange18')}"
|
||||||
?isLoading=${false}
|
?isLoading=${false}
|
||||||
.onClick=${async () => {
|
.onClick=${async () => {
|
||||||
await startMinting();
|
await startMinting();
|
||||||
if (this.errorMsg) {
|
if (this.errorMsg) {
|
||||||
await routes.showSnackBar({
|
await routes.showSnackBar({
|
||||||
data: this.errorMsg,
|
data: this.errorMsg
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
></my-button>
|
||||||
</my-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dialog for tracking the progress of starting minting -->
|
<!-- Dialog for tracking the progress of starting minting -->
|
||||||
|
|
||||||
${this.openDialogRewardShare ? html`
|
${this.openDialogRewardShare ? html`
|
||||||
<div class="dialogCustom">
|
<div class="dialogCustom">
|
||||||
<div class="dialogCustomInner">
|
<div class="dialogCustomInner">
|
||||||
<div class="dialog-header" >
|
<div class="dialog-header" >
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<h1>In progress</h1>
|
<h1>In progress</h1>
|
||||||
<div class=${`smallLoading marginLoader ${this.status > 3 && 'hide'}`}></div>
|
<div class=${`smallLoading marginLoader ${this.status > 3 && 'hide'}`}></div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<div class="dialog-container">
|
||||||
</div>
|
<ul>
|
||||||
<div class="dialog-container">
|
<li class="row between">
|
||||||
<ul>
|
<p>1. ${translate("startminting.smchange5")}</p>
|
||||||
<li class="row between">
|
<div class=${`smallLoading marginLoader ${this.status !== 1 && 'hide'}`}></div>
|
||||||
<p>
|
</li>
|
||||||
1. ${translate("startminting.smchange5")}
|
<li class=${`row between ${this.status < 2 && 'inactiveText'}`}>
|
||||||
</p>
|
<p>2. ${translate("startminting.smchange6")}</p>
|
||||||
<div class=${`smallLoading marginLoader ${this.status !== 1 && 'hide'}`}></div>
|
<div class=${`smallLoading marginLoader ${this.status !== 2 && 'hide'}`}></div>
|
||||||
</li>
|
</li>
|
||||||
|
<li class=${`row between ${this.status < 3 && 'inactiveText'}`}>
|
||||||
<li class=${`row between ${this.status < 2 && 'inactiveText'}`}>
|
<p>3. ${translate("startminting.smchange7")}</p>
|
||||||
<p>
|
<div class="row no-width">
|
||||||
2. ${translate("startminting.smchange6")}
|
<div class=${`smallLoading marginLoader marginRight ${this.status !== 3 && 'hide'}`} ></div>
|
||||||
</p>
|
<p>${asyncReplace(this.timer)}</p>
|
||||||
<div class=${`smallLoading marginLoader ${this.status !== 2 && 'hide'}`}></div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
<li class=${`row between ${this.status < 4 && 'inactiveText'}`}>
|
||||||
<li class=${`row between ${this.status < 3 && 'inactiveText'}`}>
|
<p>4. ${translate("startminting.smchange8")}</p>
|
||||||
<p>
|
<div class=${`smallLoading marginLoader ${this.status !== 4 && 'hide'}`}></div>
|
||||||
3. ${translate("startminting.smchange7")}
|
</li>
|
||||||
</p>
|
<li class=${`row between ${this.status < 5 && 'inactiveText'}`}>
|
||||||
<div class="row no-width">
|
<p>5. ${translate("startminting.smchange9")}</p>
|
||||||
<div class=${`smallLoading marginLoader marginRight ${this.status !== 3 && 'hide'}`} ></div> <p>${asyncReplace(this.timer)}</p>
|
</li>
|
||||||
</div>
|
</ul>
|
||||||
</li>
|
<div class="warning column">
|
||||||
|
<p>Warning: do not close the Qortal UI until completion!</p>
|
||||||
<li class=${`row between ${this.status < 4 && 'inactiveText'}`}>
|
<p class="message-error">${this.errorMsg}</p>
|
||||||
<p>
|
</div>
|
||||||
4. ${translate("startminting.smchange8")}
|
</div>
|
||||||
</p>
|
<div class="modalFooter">
|
||||||
<div class=${`smallLoading marginLoader ${this.status !== 4 && 'hide'}`}></div>
|
${this.errorMsg || this.status === 5 ? html`
|
||||||
</li>
|
<mwc-button slot="primaryAction" @click=${() => { this.openDialogRewardShare = false; this.errorMsg = '';}} class="red">
|
||||||
|
${translate("general.close")}
|
||||||
<li class=${`row between ${this.status < 5 && 'inactiveText'}`}>
|
</mwc-button>
|
||||||
<p>
|
` : ''}
|
||||||
5. ${translate("startminting.smchange9")}
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="warning column">
|
|
||||||
<p>
|
|
||||||
Warning: do not close the Qortal UI until completion!
|
|
||||||
</p>
|
|
||||||
<p class="message-error">${this.errorMsg}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modalFooter">
|
|
||||||
${this.errorMsg || this.status === 5 ? html`
|
|
||||||
<mwc-button
|
|
||||||
slot="primaryAction"
|
|
||||||
@click=${() => {
|
|
||||||
this.openDialogRewardShare = false
|
|
||||||
this.errorMsg = ''
|
|
||||||
}}
|
|
||||||
class="red"
|
|
||||||
>
|
|
||||||
${translate("general.close")}
|
|
||||||
</mwc-button>
|
|
||||||
` : '' }
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
` : ''}
|
||||||
</div>
|
|
||||||
|
|
||||||
` : ""}
|
|
||||||
` : ''}
|
` : ''}
|
||||||
`;
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
stateChanged(state) {
|
stateChanged(state) {
|
||||||
this.addressInfo = state.app.accountInfo.addressInfo;
|
this.addressInfo = state.app.accountInfo.addressInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('start-minting', StartMinting);
|
window.customElements.define('start-minting', StartMinting)
|
@ -1,9 +1,12 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {translate} from '../../translate'
|
import { themeToggleStyles } from '../styles/core-css'
|
||||||
import '@polymer/paper-icon-button/paper-icon-button.js'
|
import '@polymer/paper-icon-button/paper-icon-button.js'
|
||||||
import '@polymer/iron-icons/image-icons.js'
|
import '@polymer/iron-icons/image-icons.js'
|
||||||
import '@polymer/iron-icons/iron-icons.js'
|
import '@polymer/iron-icons/iron-icons.js'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { translate } from '../../translate'
|
||||||
|
|
||||||
class ThemeToggle extends LitElement {
|
class ThemeToggle extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
@ -11,58 +14,15 @@ class ThemeToggle extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [themeToggleStyles]
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = [
|
|
||||||
css`
|
|
||||||
* {
|
|
||||||
--mdc-theme-primary: rgb(3, 169, 244);
|
|
||||||
--mdc-theme-secondary: var(--mdc-theme-primary);
|
|
||||||
--mdc-theme-error: rgb(255, 89, 89);
|
|
||||||
--lumo-primary-text-color: rgb(0, 167, 245);
|
|
||||||
--lumo-primary-color-50pct: rgba(0, 167, 245, 0.5);
|
|
||||||
--lumo-primary-color-10pct: rgba(0, 167, 245, 0.1);
|
|
||||||
--lumo-primary-color: hsl(199, 100%, 48%);
|
|
||||||
--lumo-base-color: var(--white);
|
|
||||||
--lumo-body-text-color: var(--black);
|
|
||||||
--lumo-secondary-text-color: var(--sectxt);
|
|
||||||
--lumo-contrast-60pct: var(--vdicon);
|
|
||||||
--item-selected-color: var(--nav-selected-color);
|
|
||||||
--item-selected-color-text: var(--nav-selected-color-text);
|
|
||||||
--item-color-active: var(--nav-color-active);
|
|
||||||
--item-color-hover: var(--nav-color-hover);
|
|
||||||
--item-text-color: var(--nav-text-color);
|
|
||||||
--item-icon-color: var(--nav-icon-color);
|
|
||||||
--item-border-color: var(--nav-border-color);
|
|
||||||
--item-border-selected-color: var(--nav-border-selected-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-icon-button {
|
|
||||||
-ms-transform: rotate(120deg);
|
|
||||||
transform: rotate(120deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([theme="light"]) .light-mode {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([theme="light"]) .dark-mode {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([theme="dark"]) .light-mode {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([theme="dark"]) .dark-mode {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
]
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div style="display: inline;">
|
<div style="display: inline;">
|
||||||
@ -87,16 +47,17 @@ class ThemeToggle extends LitElement {
|
|||||||
this.dispatchEvent(new CustomEvent('qort-theme-change', {
|
this.dispatchEvent(new CustomEvent('qort-theme-change', {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
composed: true,
|
composed: true,
|
||||||
detail: this.theme,
|
detail: this.theme
|
||||||
}))
|
}))
|
||||||
|
|
||||||
window.localStorage.setItem('qortalTheme', this.theme)
|
window.localStorage.setItem('qortalTheme', this.theme)
|
||||||
|
|
||||||
this.initTheme()
|
this.initTheme()
|
||||||
}
|
}
|
||||||
|
|
||||||
initTheme() {
|
initTheme() {
|
||||||
document.querySelector('html').setAttribute('theme', this.theme);
|
document.querySelector('html').setAttribute('theme', this.theme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('theme-toggle', ThemeToggle);
|
window.customElements.define('theme-toggle', ThemeToggle)
|
File diff suppressed because it is too large
Load Diff
@ -1,124 +1,84 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../store.js'
|
import { store } from '../store'
|
||||||
import {translate} from '../../translate'
|
import { walletProfileStyles } from '../styles/core-css'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { translate } from '../../translate'
|
||||||
|
|
||||||
class WalletProfile extends connect(store)(LitElement) {
|
class WalletProfile extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
wallet: { type: Object },
|
wallet: { type: Object },
|
||||||
nodeConfig: { type: Object },
|
nodeConfig: { type: Object },
|
||||||
accountInfo: { type: Object },
|
accountInfo: { type: Object },
|
||||||
theme: { type: String, reflect: true }
|
theme: { type: String, reflect: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [walletProfileStyles]
|
||||||
#profileInMenu {
|
}
|
||||||
padding: 12px;
|
|
||||||
border-top: var(--border);
|
|
||||||
background: var(--sidetopbar);
|
|
||||||
color: var(--black);
|
|
||||||
}
|
|
||||||
|
|
||||||
#accountName {
|
constructor() {
|
||||||
margin: 0;
|
super()
|
||||||
font-size: 18px;
|
this.wallet = {}
|
||||||
font-weight: 500;
|
this.nodeConfig = {}
|
||||||
width: 100%;
|
this.accountInfo = {
|
||||||
padding-bottom: 8px;
|
names: [],
|
||||||
display: flex;
|
addressInfo: {}
|
||||||
}
|
}
|
||||||
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
|
}
|
||||||
|
|
||||||
#blocksMinted {
|
render() {
|
||||||
margin:0;
|
return html`
|
||||||
margin-top: 0;
|
<div id="profileInMenu">
|
||||||
font-size: 12px;
|
<div style="padding: 8px 0;">
|
||||||
color: #03a9f4;
|
<div id="accountName">
|
||||||
}
|
<div id="child inline-block-child" class="full-info-logo">${this.getAvatar()}</div>
|
||||||
|
|
||||||
|
<div id="inline-block-child">
|
||||||
|
<div>
|
||||||
|
${this.accountInfo.names.length !== 0 ? this.accountInfo.names[0].name : ''}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
${this.accountInfo.addressInfo ? html`
|
||||||
|
<span style="margin-bottom: 8px; display: inline-block; font-size: 14px;">
|
||||||
|
${translate("walletprofile.minterlevel")} - <span style="color: #03a9f4;">${this.accountInfo.addressInfo.level} ${this.accountInfo.addressInfo.flags === 1 ? html`<strong>(F)</strong>` : ''}
|
||||||
|
</span>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
<p id="blocksMinted">${translate("walletprofile.blocksminted")} - ${this.accountInfo.addressInfo.blocksMinted + this.accountInfo.addressInfo.blocksMintedAdjustment}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p id="address">${this.wallet.addresses[0].address}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
#address {
|
firstUpdated() {
|
||||||
white-space: nowrap;
|
// ...
|
||||||
overflow: hidden;
|
}
|
||||||
text-overflow: ellipsis;
|
|
||||||
margin:0;
|
|
||||||
margin-top: 8px;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.round-fullinfo {
|
getAvatar() {
|
||||||
position: relative;
|
if (this.accountInfo.names.length === 0) {
|
||||||
width: 68px;
|
return html`<img class="round-fullinfo" src="/img/incognito.png">`
|
||||||
height: 68px;
|
} else {
|
||||||
border-radius: 50%;
|
const avatarNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
}
|
const avatarUrl = avatarNode.protocol + '://' + avatarNode.domain + ':' + avatarNode.port
|
||||||
|
const url = `${avatarUrl}/arbitrary/THUMBNAIL/${this.accountInfo.names[0].name}/qortal_avatar?async=true`
|
||||||
|
|
||||||
.full-info-logo {
|
return html`<img class="round-fullinfo" src="${url}" onerror="this.src='/img/incognito.png';" />`
|
||||||
width: 68px;
|
}
|
||||||
height: 68px;
|
}
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inline-block-child {
|
stateChanged(state) {
|
||||||
flex: 1;
|
this.wallet = state.app.wallet
|
||||||
}
|
this.nodeConfig = state.app.nodeConfig
|
||||||
`
|
this.accountInfo = state.app.accountInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.wallet = {}
|
|
||||||
this.nodeConfig = {}
|
|
||||||
this.accountInfo = {
|
|
||||||
names: [],
|
|
||||||
addressInfo: {}
|
|
||||||
}
|
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`
|
|
||||||
<div id="profileInMenu">
|
|
||||||
<div style="padding: 8px 0;">
|
|
||||||
<div id="accountName">
|
|
||||||
<div id="child inline-block-child" class="full-info-logo">${this.getAvatar()}</div>
|
|
||||||
|
|
||||||
<div id="inline-block-child">
|
|
||||||
<div>${this.accountInfo.names.length !== 0 ? this.accountInfo.names[0].name : ''}</div>
|
|
||||||
<div>${this.accountInfo.addressInfo ? html`<span style="margin-bottom: 8px; display: inline-block; font-size: 14px;">${translate("walletprofile.minterlevel")} - <span style="color: #03a9f4;">${this.accountInfo.addressInfo.level} ${this.accountInfo.addressInfo.flags === 1 ? html`<strong>(F)</strong>` : ''}</span>` : ''}</div>
|
|
||||||
<p id="blocksMinted">${translate("walletprofile.blocksminted")} - ${this.accountInfo.addressInfo.blocksMinted + this.accountInfo.addressInfo.blocksMintedAdjustment}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p id="address">${this.wallet.addresses[0].address}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated() {}
|
|
||||||
|
|
||||||
getAvatar() {
|
|
||||||
if (this.accountInfo.names.length === 0) {
|
|
||||||
return html`<img class="round-fullinfo" src="/img/incognito.png">`
|
|
||||||
} else {
|
|
||||||
const avatarNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
|
||||||
const avatarUrl = avatarNode.protocol + '://' + avatarNode.domain + ':' + avatarNode.port
|
|
||||||
const url = `${avatarUrl}/arbitrary/THUMBNAIL/${this.accountInfo.names[0].name}/qortal_avatar?async=true&apiKey=${this.getApiKey()}`
|
|
||||||
return html`<img class="round-fullinfo" src="${url}" onerror="this.src='/img/incognito.png';" />`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getApiKey() {
|
|
||||||
const apiNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
|
||||||
return apiNode.apiKey
|
|
||||||
}
|
|
||||||
|
|
||||||
stateChanged(state) {
|
|
||||||
this.wallet = state.app.wallet
|
|
||||||
this.nodeConfig = state.app.nodeConfig
|
|
||||||
this.accountInfo = state.app.accountInfo
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('wallet-profile', WalletProfile)
|
window.customElements.define('wallet-profile', WalletProfile)
|
@ -1,55 +1,62 @@
|
|||||||
'use strict'
|
|
||||||
const utils = {
|
const utils = {
|
||||||
int32ToBytes (word) {
|
int32ToBytes(word) {
|
||||||
var byteArray = []
|
var byteArray = []
|
||||||
for (var b = 0; b < 32; b += 8) {
|
for (var b = 0; b < 32; b += 8) {
|
||||||
byteArray.push((word >>> (24 - b % 32)) & 0xFF)
|
byteArray.push((word >>> (24 - b % 32)) & 0xFF)
|
||||||
}
|
}
|
||||||
return byteArray
|
return byteArray
|
||||||
},
|
},
|
||||||
|
|
||||||
stringtoUTF8Array (message) {
|
stringtoUTF8Array(message) {
|
||||||
if (typeof message === 'string') {
|
if (typeof message === 'string') {
|
||||||
var s = unescape(encodeURIComponent(message)) // UTF-8
|
var s = unescape(encodeURIComponent(message)) // UTF-8
|
||||||
message = new Uint8Array(s.length)
|
message = new Uint8Array(s.length)
|
||||||
for (var i = 0; i < s.length; i++) {
|
for (var i = 0; i < s.length; i++) {
|
||||||
message[i] = s.charCodeAt(i) & 0xff
|
message[i] = s.charCodeAt(i) & 0xff
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return message
|
|
||||||
},
|
|
||||||
// ...buffers then buffers.foreach and append to buffer1
|
|
||||||
appendBuffer (buffer1, buffer2) {
|
|
||||||
buffer1 = new Uint8Array(buffer1)
|
|
||||||
buffer2 = new Uint8Array(buffer2)
|
|
||||||
const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength)
|
|
||||||
tmp.set(buffer1, 0)
|
|
||||||
tmp.set(buffer2, buffer1.byteLength)
|
|
||||||
return tmp
|
|
||||||
},
|
|
||||||
|
|
||||||
int64ToBytes (int64) {
|
return message
|
||||||
// we want to represent the input as a 8-bytes array
|
},
|
||||||
var byteArray = [0, 0, 0, 0, 0, 0, 0, 0]
|
|
||||||
|
|
||||||
for (var index = 0; index < byteArray.length; index++) {
|
// ...buffers then buffers.foreach and append to buffer1
|
||||||
var byte = int64 & 0xff
|
appendBuffer(buffer1, buffer2) {
|
||||||
byteArray[byteArray.length - index - 1] = byte
|
buffer1 = new Uint8Array(buffer1)
|
||||||
int64 = (int64 - byte) / 256
|
buffer2 = new Uint8Array(buffer2)
|
||||||
}
|
|
||||||
|
|
||||||
return byteArray
|
const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength)
|
||||||
},
|
|
||||||
|
|
||||||
equal (buf1, buf2) {
|
tmp.set(buffer1, 0)
|
||||||
if (buf1.byteLength != buf2.byteLength) return false
|
tmp.set(buffer2, buffer1.byteLength)
|
||||||
var dv1 = new Uint8Array(buf1)
|
|
||||||
var dv2 = new Uint8Array(buf2)
|
return tmp
|
||||||
for (var i = 0; i != buf1.byteLength; i++) {
|
},
|
||||||
if (dv1[i] != dv2[i]) return false
|
|
||||||
}
|
int64ToBytes(int64) {
|
||||||
return true
|
// we want to represent the input as a 8-bytes array
|
||||||
}
|
var byteArray = [0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
|
||||||
|
for (var index = 0; index < byteArray.length; index++) {
|
||||||
|
var byte = int64 & 0xff
|
||||||
|
byteArray[byteArray.length - index - 1] = byte
|
||||||
|
int64 = (int64 - byte) / 256
|
||||||
|
}
|
||||||
|
|
||||||
|
return byteArray
|
||||||
|
},
|
||||||
|
|
||||||
|
equal(buf1, buf2) {
|
||||||
|
if (buf1.byteLength != buf2.byteLength) return false
|
||||||
|
|
||||||
|
var dv1 = new Uint8Array(buf1)
|
||||||
|
var dv2 = new Uint8Array(buf2)
|
||||||
|
|
||||||
|
for (var i = 0; i != buf1.byteLength; i++) {
|
||||||
|
if (dv1[i] != dv2[i]) return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default utils
|
export default utils
|
@ -1,115 +0,0 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
|
||||||
|
|
||||||
import '@material/mwc-button'
|
|
||||||
import '@material/mwc-icon'
|
|
||||||
|
|
||||||
import {translate} from '../../translate'
|
|
||||||
|
|
||||||
class FragFileInput extends LitElement {
|
|
||||||
static get properties () {
|
|
||||||
return {
|
|
||||||
accept: { type: String },
|
|
||||||
readAs: { type: String }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles () {
|
|
||||||
return css`
|
|
||||||
#drop-area {
|
|
||||||
border: 2px dashed #ccc;
|
|
||||||
font-family: "Roboto", sans-serif;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#trigger:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#drop-area.highlight {
|
|
||||||
border-color: var(--mdc-theme-primary, #000);
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#fileInput {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor () {
|
|
||||||
super()
|
|
||||||
this.readAs = this.readAs || 'Text'
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
return html`
|
|
||||||
<div id="drop-area">
|
|
||||||
<slot name="info-text"></slot>
|
|
||||||
<div style="line-height: 40px; text-align: center;">
|
|
||||||
<slot id="trigger" name="inputTrigger" @click=${() => this.shadowRoot.getElementById('fileInput').click()} style="dispay:inline;">
|
|
||||||
<mwc-button><mwc-icon>cloud_upload</mwc-icon><span style="color: var(--black);"> ${translate("fragfile.selectfile")}</span></mwc-button>
|
|
||||||
</slot><br>
|
|
||||||
<span style="text-align: center; padding-top: 4px; color: var(--black);">${translate("fragfile.dragfile")}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<input type="file" id="fileInput" accept="${this.accept}" @change="${e => this.readFile(e.target.files[0])}">
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
readFile (file) {
|
|
||||||
const fr = new FileReader()
|
|
||||||
fr.onload = () => {
|
|
||||||
this.dispatchEvent(new CustomEvent('file-read-success', {
|
|
||||||
detail: { result: fr.result },
|
|
||||||
bubbles: true,
|
|
||||||
composed: true
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
fr['readAs' + this.readAs](file)
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated () {
|
|
||||||
this._dropArea = this.shadowRoot.getElementById('drop-area')
|
|
||||||
|
|
||||||
const preventDefaults = e => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
|
|
||||||
;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
|
||||||
this._dropArea.addEventListener(eventName, preventDefaults, false)
|
|
||||||
})
|
|
||||||
|
|
||||||
const highlight = e => {
|
|
||||||
this._dropArea.classList.add('highlight')
|
|
||||||
}
|
|
||||||
|
|
||||||
const unhighlight = e => {
|
|
||||||
this._dropArea.classList.remove('highlight')
|
|
||||||
}
|
|
||||||
|
|
||||||
;['dragenter', 'dragover'].forEach(eventName => {
|
|
||||||
this._dropArea.addEventListener(eventName, highlight, false)
|
|
||||||
})
|
|
||||||
|
|
||||||
;['dragleave', 'drop'].forEach(eventName => {
|
|
||||||
this._dropArea.addEventListener(eventName, unhighlight, false)
|
|
||||||
})
|
|
||||||
|
|
||||||
this._dropArea.addEventListener('drop', e => {
|
|
||||||
const dt = e.dataTransfer
|
|
||||||
const file = dt.files[0]
|
|
||||||
|
|
||||||
this.readFile(file)
|
|
||||||
}, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.customElements.define('frag-file-input', FragFileInput)
|
|
@ -1,4 +1,15 @@
|
|||||||
export const defaultQappsTabs = [
|
export const defaultQappsTabs = [
|
||||||
|
{
|
||||||
|
"url": "myapp",
|
||||||
|
"domain": "core",
|
||||||
|
"page": "qdn/browser/index.html?name=Q-Support&service=APP",
|
||||||
|
"title": "Q-Support",
|
||||||
|
"icon": "vaadin:external-browser",
|
||||||
|
"mwcicon": "apps",
|
||||||
|
"pluginNumber": "plugin-04tlGdLkkd",
|
||||||
|
"menus": [],
|
||||||
|
"parent": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "myapp",
|
"url": "myapp",
|
||||||
"domain": "core",
|
"domain": "core",
|
||||||
|
@ -1,103 +1,80 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { css, html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../store.js'
|
import { store } from '../store'
|
||||||
import {get, translate} from '../../translate'
|
import { listenForRequest } from '../transactionRequest'
|
||||||
|
import { confirmTransactionDialogStyles } from '../styles/core-css'
|
||||||
import {listenForRequest} from '../transactionRequest.js'
|
|
||||||
|
|
||||||
import '@polymer/paper-dialog/paper-dialog.js'
|
|
||||||
import '@material/mwc-button'
|
import '@material/mwc-button'
|
||||||
|
import '@polymer/paper-dialog/paper-dialog.js'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { get, translate } from '../../translate'
|
||||||
|
|
||||||
class ConfirmTransactionDialog extends connect(store)(LitElement) {
|
class ConfirmTransactionDialog extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
txInfo: { type: Object },
|
txInfo: { type: Object },
|
||||||
theme: { type: String, reflect: true }
|
theme: { type: String, reflect: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [confirmTransactionDialogStyles]
|
||||||
* {
|
}
|
||||||
--mdc-theme-primary: rgb(3, 169, 244);
|
|
||||||
--mdc-theme-secondary: var(--mdc-theme-primary);
|
|
||||||
--mdc-theme-surface: var(--white);
|
|
||||||
--mdc-dialog-content-ink-color: var(--black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.decline {
|
constructor() {
|
||||||
--mdc-theme-primary: var(--mdc-theme-error)
|
super()
|
||||||
}
|
this.transaction = {
|
||||||
|
template: html`Awaiting transaction info`
|
||||||
|
}
|
||||||
|
this.txInfo = html``
|
||||||
|
listenForRequest(args => this.requestTransaction(args))
|
||||||
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
|
}
|
||||||
|
|
||||||
#txInfo {
|
render() {
|
||||||
text-align: left;
|
return html`
|
||||||
max-width: 520px;
|
<paper-dialog style="background: var(--white);" id="confirmDialog" modal>
|
||||||
color: var(--black);
|
<h2 style="color: var(--black);">${translate("transpage.tchange1")}</h2>
|
||||||
}
|
<div id="txInfo">
|
||||||
|
${this.txInfo}
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<mwc-button class='decline' @click=${e => this.decline(e)} dialog-dismiss>${translate("transpage.tchange2")}</mwc-button>
|
||||||
|
<mwc-button class='confirm' @click=${e => this.confirm(e)} dialog-confirm autofocus>${translate("transpage.tchange3")}</mwc-button>
|
||||||
|
</div>
|
||||||
|
</paper-dialog>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
.buttons {
|
firstUpdated() {
|
||||||
text-align:right;
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
table td, th{
|
requestTransaction(transaction) {
|
||||||
padding:4px;
|
this.shadowRoot.getElementById('confirmDialog').open()
|
||||||
text-align:left;
|
this.transaction = transaction
|
||||||
font-size:14px;
|
this.txInfo = transaction.render(html)
|
||||||
color: var(--black);
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
return new Promise((resolve, reject) => {
|
||||||
super()
|
this._resolve = resolve
|
||||||
this.transaction = {
|
this._reject = reject
|
||||||
template: html`Awaiting transaction info`
|
})
|
||||||
}
|
}
|
||||||
this.txInfo = html``
|
|
||||||
listenForRequest(args => this.requestTransaction(args))
|
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light';
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
confirm(e) {
|
||||||
return html`
|
this._resolve({
|
||||||
<paper-dialog style="background: var(--white);" id="confirmDialog" modal>
|
success: true
|
||||||
<h2 style="color: var(--black);">${translate("transpage.tchange1")}</h2>
|
})
|
||||||
<div id="txInfo">
|
}
|
||||||
${this.txInfo}
|
|
||||||
</div>
|
|
||||||
<div class="buttons">
|
|
||||||
<mwc-button class='decline' @click=${e => this.decline(e)} dialog-dismiss>${translate("transpage.tchange2")}</mwc-button>
|
|
||||||
<mwc-button class='confirm' @click=${e => this.confirm(e)} dialog-confirm autofocus>${translate("transpage.tchange3")}</mwc-button>
|
|
||||||
</div>
|
|
||||||
</paper-dialog>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
stateChanged(state) {
|
decline(e) {
|
||||||
this.loggedIn = state.app.loggedIn
|
const rejecterror = get("transactions.declined")
|
||||||
}
|
this._reject(new Error(rejecterror))
|
||||||
|
}
|
||||||
|
|
||||||
requestTransaction(transaction) {
|
stateChanged(state) {
|
||||||
this.shadowRoot.getElementById('confirmDialog').open()
|
this.loggedIn = state.app.loggedIn
|
||||||
this.transaction = transaction
|
}
|
||||||
this.txInfo = transaction.render(html)
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this._resolve = resolve
|
|
||||||
this._reject = reject
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
confirm(e) {
|
|
||||||
this._resolve({
|
|
||||||
success: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
decline(e) {
|
|
||||||
const rejecterror = get("transactions.declined")
|
|
||||||
this._reject(new Error(rejecterror))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('confirm-transaction-dialog', ConfirmTransactionDialog)
|
window.customElements.define('confirm-transaction-dialog', ConfirmTransactionDialog)
|
||||||
|
93
core/src/functional-components/frag-file-input.js
Normal file
93
core/src/functional-components/frag-file-input.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { html, LitElement } from 'lit'
|
||||||
|
import { fragFileInputStyles } from '../styles/core-css'
|
||||||
|
import '@material/mwc-button'
|
||||||
|
import '@material/mwc-icon'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { translate } from '../../translate'
|
||||||
|
|
||||||
|
class FragFileInput extends LitElement {
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
accept: { type: String },
|
||||||
|
readAs: { type: String }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [fragFileInputStyles]
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.readAs = this.readAs || 'Text'
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<div id="drop-area">
|
||||||
|
<slot name="info-text"></slot>
|
||||||
|
<div style="line-height: 40px; text-align: center;">
|
||||||
|
<slot id="trigger" name="inputTrigger" @click=${() => this.shadowRoot.getElementById('fileInput').click()} style="dispay:inline;">
|
||||||
|
<mwc-button><mwc-icon>cloud_upload</mwc-icon><span style="color: var(--black);"> ${translate("fragfile.selectfile")}</span></mwc-button>
|
||||||
|
</slot>
|
||||||
|
<br>
|
||||||
|
<span style="text-align: center; padding-top: 4px; color: var(--black);">${translate("fragfile.dragfile")}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="file" id="fileInput" accept="${this.accept}" @change="${e => this.readFile(e.target.files[0])}">
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
this._dropArea = this.shadowRoot.getElementById('drop-area')
|
||||||
|
|
||||||
|
const preventDefaults = e => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
|
;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||||
|
this._dropArea.addEventListener(eventName, preventDefaults, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
const highlight = e => {
|
||||||
|
this._dropArea.classList.add('highlight')
|
||||||
|
}
|
||||||
|
|
||||||
|
const unhighlight = e => {
|
||||||
|
this._dropArea.classList.remove('highlight')
|
||||||
|
}
|
||||||
|
|
||||||
|
;['dragenter', 'dragover'].forEach(eventName => {
|
||||||
|
this._dropArea.addEventListener(eventName, highlight, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
;['dragleave', 'drop'].forEach(eventName => {
|
||||||
|
this._dropArea.addEventListener(eventName, unhighlight, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
this._dropArea.addEventListener('drop', e => {
|
||||||
|
const dt = e.dataTransfer
|
||||||
|
const file = dt.files[0]
|
||||||
|
|
||||||
|
this.readFile(file)
|
||||||
|
}, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
readFile(file) {
|
||||||
|
const fr = new FileReader()
|
||||||
|
|
||||||
|
fr.onload = () => {
|
||||||
|
this.dispatchEvent(new CustomEvent('file-read-success', {
|
||||||
|
detail: { result: fr.result },
|
||||||
|
bubbles: true,
|
||||||
|
composed: true
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fr['readAs' + this.readAs](file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.customElements.define('frag-file-input', FragFileInput)
|
@ -1,188 +1,118 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
|
import { loadingRippleStyles } from '../styles/core-css'
|
||||||
|
|
||||||
const TRANSITION_EVENT_NAMES = ['transitionend', 'webkitTransitionEnd', 'oTransitionEnd', 'MSTransitionEnd']
|
const TRANSITION_EVENT_NAMES = ['transitionend', 'webkitTransitionEnd', 'oTransitionEnd', 'MSTransitionEnd']
|
||||||
|
|
||||||
let rippleElement
|
let rippleElement
|
||||||
|
|
||||||
class LoadingRipple extends LitElement {
|
class LoadingRipple extends LitElement {
|
||||||
static get properties () {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
welcomeMessage: {
|
welcomeMessage: { type: String, attribute: 'welcome-message', reflectToAttribute: true },
|
||||||
type: String,
|
loadingMessage: { type: String, attribute: 'loading-message', reflectToAttribute: true}
|
||||||
attribute: 'welcome-message',
|
}
|
||||||
reflectToAttribute: true
|
}
|
||||||
},
|
|
||||||
loadingMessage: {
|
|
||||||
type: String,
|
|
||||||
attribute: 'loading-message',
|
|
||||||
reflectToAttribute: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor () {
|
static get styles() {
|
||||||
super()
|
return [loadingRippleStyles]
|
||||||
this.welcomeMessage = ''
|
}
|
||||||
this.loadingMessage = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles () {
|
constructor() {
|
||||||
return css`
|
super()
|
||||||
* {
|
this.welcomeMessage = ''
|
||||||
--mdc-theme-primary: rgb(3, 169, 244);
|
this.loadingMessage = ''
|
||||||
--mdc-theme-secondary: var(--mdc-theme-primary);
|
}
|
||||||
--paper-spinner-color: var(--mdc-theme-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
#rippleWrapper{
|
render() {
|
||||||
position:fixed;
|
return html`
|
||||||
top:0;
|
<div id="rippleWrapper">
|
||||||
left:0;
|
<div id="ripple">
|
||||||
bottom:0;
|
<div id="rippleShader"></div>
|
||||||
right:0;
|
<div id="rippleContentWrapper">
|
||||||
height:0;
|
<div id="rippleContent">
|
||||||
width:0;
|
<h1 style="color: var(--black);">${this.welcomeMessage}</h1>
|
||||||
z-index:999;
|
<paper-spinner-lite active></paper-spinner-lite>
|
||||||
overflow: visible;
|
<p style="color: var(--black);">${this.loadingMessage}</p>
|
||||||
--ripple-activating-transition: transform 0.3s cubic-bezier(0.6, 0.0, 1, 1), opacity 0.3s cubic-bezier(0.6, 0.0, 1, 1);
|
</div>
|
||||||
--ripple-disable-transition: opacity 0.5s ease;
|
</div>
|
||||||
}
|
</div>
|
||||||
#ripple {
|
</div>
|
||||||
border-radius:50%;
|
`
|
||||||
border-width:0;
|
}
|
||||||
margin-left:-100vmax;
|
|
||||||
margin-top: -100vmax;
|
|
||||||
height:200vmax;
|
|
||||||
width:200vmax;
|
|
||||||
overflow:hidden;
|
|
||||||
background: var(--black);
|
|
||||||
transform: scale(0);
|
|
||||||
overflow:hidden;
|
|
||||||
}
|
|
||||||
#ripple.error {
|
|
||||||
transition: var(--ripple-activating-transition);
|
|
||||||
background: var(--mdc-theme-error)
|
|
||||||
}
|
|
||||||
#rippleShader {
|
|
||||||
background: var(--white);
|
|
||||||
opacity:0;
|
|
||||||
height:100%;
|
|
||||||
width:100%;
|
|
||||||
}
|
|
||||||
#ripple.activating{
|
|
||||||
transition: var(--ripple-activating-transition);
|
|
||||||
transform: scale(1)
|
|
||||||
}
|
|
||||||
.activating #rippleShader {
|
|
||||||
transition: var(--ripple-activating-transition);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
#ripple.disabling{
|
|
||||||
transition: var(--ripple-disable-transition);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
#rippleContentWrapper {
|
|
||||||
position: absolute;
|
|
||||||
top:100vmax;
|
|
||||||
left:100vmax;
|
|
||||||
height:var(--window-height);
|
|
||||||
width:100vw;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
#rippleContent {
|
|
||||||
opacity: 0;
|
|
||||||
text-align:center;
|
|
||||||
}
|
|
||||||
.activating-done #rippleContent {
|
|
||||||
opacity: 1;
|
|
||||||
transition: var(--ripple-activating-transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
`
|
firstUpdated() {
|
||||||
}
|
this._rippleWrapper = this.shadowRoot.getElementById('rippleWrapper')
|
||||||
|
this._ripple = this.shadowRoot.getElementById('ripple')
|
||||||
|
this._rippleContentWrapper = this.shadowRoot.getElementById('rippleContentWrapper')
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
open(origin) {
|
||||||
return html`
|
this._rippleWrapper.style.top = origin.y + 'px'
|
||||||
<div id="rippleWrapper">
|
this._rippleWrapper.style.left = origin.x + 'px'
|
||||||
<div id="ripple">
|
this._rippleContentWrapper.style.marginTop = -origin.y + 'px'
|
||||||
<div id="rippleShader"></div>
|
this._rippleContentWrapper.style.marginLeft = -origin.x + 'px'
|
||||||
<div id="rippleContentWrapper">
|
|
||||||
<div id="rippleContent">
|
|
||||||
<h1 style="color: var(--black);">${this.welcomeMessage}</h1>
|
|
||||||
<paper-spinner-lite active></paper-spinner-lite>
|
|
||||||
<p style="color: var(--black);">${this.loadingMessage}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated () {
|
return new Promise((resolve, reject) => {
|
||||||
this._rippleWrapper = this.shadowRoot.getElementById('rippleWrapper')
|
this._ripple.classList.add('activating')
|
||||||
this._ripple = this.shadowRoot.getElementById('ripple')
|
|
||||||
this._rippleContentWrapper = this.shadowRoot.getElementById('rippleContentWrapper')
|
|
||||||
}
|
|
||||||
|
|
||||||
// duh
|
let isOpened = false
|
||||||
open (origin) {
|
|
||||||
this._rippleWrapper.style.top = origin.y + 'px'
|
|
||||||
this._rippleWrapper.style.left = origin.x + 'px'
|
|
||||||
this._rippleContentWrapper.style.marginTop = -origin.y + 'px'
|
|
||||||
this._rippleContentWrapper.style.marginLeft = -origin.x + 'px'
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
const doneOpeningEvent = () => {
|
||||||
this._ripple.classList.add('activating')
|
if (isOpened) return
|
||||||
let isOpened = false
|
|
||||||
const doneOpeningEvent = () => {
|
|
||||||
if (isOpened) return
|
|
||||||
// Clear events
|
|
||||||
TRANSITION_EVENT_NAMES.forEach(name => this._ripple.removeEventListener(name, doneOpeningEvent))
|
|
||||||
this._ripple.classList.add('activating-done')
|
|
||||||
isOpened = true
|
|
||||||
resolve()
|
|
||||||
}
|
|
||||||
TRANSITION_EVENT_NAMES.forEach(name => this._ripple.addEventListener(name, doneOpeningEvent))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fades out
|
// Clear events
|
||||||
fade () {
|
TRANSITION_EVENT_NAMES.forEach(name => this._ripple.removeEventListener(name, doneOpeningEvent))
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// CAN'T FADE OUT CAUSE THE STUPID THING GETS KILLED CAUSE OF STATE.APP.LOGGEEDIN
|
|
||||||
// let rippleClosed = false
|
|
||||||
this._ripple.classList.remove('activating')
|
|
||||||
this._ripple.classList.remove('activating-done')
|
|
||||||
this._ripple.classList.remove('disabling')
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// un-ripples...
|
this._ripple.classList.add('activating-done')
|
||||||
close () {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let rippleClosed = false
|
|
||||||
this._ripple.classList.add('error')
|
|
||||||
this._ripple.classList.remove('activating')
|
|
||||||
this._ripple.classList.remove('activating-done')
|
|
||||||
const rippleClosedEvent = () => {
|
|
||||||
if (rippleClosed) return
|
|
||||||
rippleClosed = true
|
|
||||||
TRANSITION_EVENT_NAMES.forEach(name => this._ripple.removeEventListener(name, rippleClosedEvent))
|
|
||||||
// Reset the ripple
|
|
||||||
this._ripple.classList.remove('error')
|
|
||||||
this.rippleIsOpen = false
|
|
||||||
resolve()
|
|
||||||
}
|
|
||||||
TRANSITION_EVENT_NAMES.forEach(name => this._ripple.addEventListener(name, rippleClosedEvent))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
stateChanged (state) {
|
isOpened = true
|
||||||
// this.loggedIn = state.app.loggedIn
|
|
||||||
}
|
resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
TRANSITION_EVENT_NAMES.forEach(name => this._ripple.addEventListener(name, doneOpeningEvent))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fade() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this._ripple.classList.remove('activating')
|
||||||
|
this._ripple.classList.remove('activating-done')
|
||||||
|
this._ripple.classList.remove('disabling')
|
||||||
|
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let rippleClosed = false
|
||||||
|
|
||||||
|
this._ripple.classList.add('error')
|
||||||
|
this._ripple.classList.remove('activating')
|
||||||
|
this._ripple.classList.remove('activating-done')
|
||||||
|
|
||||||
|
const rippleClosedEvent = () => {
|
||||||
|
if (rippleClosed) return
|
||||||
|
|
||||||
|
rippleClosed = true
|
||||||
|
|
||||||
|
TRANSITION_EVENT_NAMES.forEach(name => this._ripple.removeEventListener(name, rippleClosedEvent))
|
||||||
|
|
||||||
|
// Reset the ripple
|
||||||
|
this._ripple.classList.remove('error')
|
||||||
|
this.rippleIsOpen = false
|
||||||
|
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
TRANSITION_EVENT_NAMES.forEach(name => this._ripple.addEventListener(name, rippleClosedEvent))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
stateChanged(state) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('loading-ripple', LoadingRipple)
|
window.customElements.define('loading-ripple', LoadingRipple)
|
||||||
@ -192,10 +122,9 @@ rippleNode.id = 'ripple-node'
|
|||||||
rippleNode.loadingMessage = ''
|
rippleNode.loadingMessage = ''
|
||||||
rippleElement = document.body.appendChild(rippleNode)
|
rippleElement = document.body.appendChild(rippleNode)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const ripple = document.getElementById('ripple-node')
|
const ripple = document.getElementById('ripple-node')
|
||||||
const mainApp = document.getElementById('main-app')
|
const mainApp = document.getElementById('main-app')
|
||||||
const shadow = mainApp.shadowRoot
|
const shadow = mainApp.shadowRoot
|
||||||
// console.log(shadow)
|
rippleElement = shadow.appendChild(ripple)
|
||||||
rippleElement = shadow.appendChild(ripple)
|
|
||||||
}, 500) // Should just keep checking for the main-app and it's shadow and then append once it's there
|
}, 500) // Should just keep checking for the main-app and it's shadow and then append once it's there
|
||||||
export default rippleElement
|
export default rippleElement
|
@ -1,47 +1,39 @@
|
|||||||
import {css, html, LitElement} from 'lit';
|
import { html, LitElement } from 'lit'
|
||||||
import '@vaadin/button';
|
import { myButtonStyles } from '../styles/core-css'
|
||||||
import '@polymer/paper-spinner/paper-spinner-lite.js';
|
import '@vaadin/button'
|
||||||
|
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
||||||
|
|
||||||
export class MyButton extends LitElement {
|
export class MyButton extends LitElement {
|
||||||
static properties = {
|
static get properties() {
|
||||||
onClick: { type: Function },
|
return {
|
||||||
isLoading: { type: Boolean },
|
onClick: { type: Function },
|
||||||
label: { type: String },
|
isLoading: { type: Boolean },
|
||||||
};
|
label: { type: String }
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
vaadin-button {
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
min-width: 80px;
|
|
||||||
background-color: #03a9f4;
|
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
vaadin-button:hover {
|
static get styles() {
|
||||||
opacity: 0.8;
|
return [myButtonStyles]
|
||||||
}
|
}
|
||||||
`;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super()
|
||||||
this.onClick = () => {};
|
this.onClick = () => { }
|
||||||
this.isLoading = false;
|
this.isLoading = false
|
||||||
this.label = '';
|
this.label = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<vaadin-button
|
<vaadin-button ?disabled="${this.isLoading}" @click="${this.onClick}">
|
||||||
?disabled="${this.isLoading}"
|
${this.isLoading === false ? html`${this.label}` : html`<paper-spinner-lite active></paper-spinner-lite>`}
|
||||||
@click="${this.onClick}"
|
|
||||||
>
|
|
||||||
${this.isLoading === false
|
|
||||||
? html`${this.label}`
|
|
||||||
: html`<paper-spinner-lite active></paper-spinner-lite>`}
|
|
||||||
</vaadin-button>
|
</vaadin-button>
|
||||||
`;
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
// ...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define('my-button', MyButton);
|
|
||||||
|
window.customElements.define('my-button', MyButton)
|
@ -1,122 +1,105 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../store.js'
|
import { store } from '../store'
|
||||||
import {testApiKey} from '../apiKeyUtils.js'
|
import { testApiKey } from '../apiKeyUtils'
|
||||||
import {get, translate} from '../../translate'
|
import { mykeyPageStyles } from '../styles/core-css'
|
||||||
|
import snackbar from './snackbar'
|
||||||
import '@material/mwc-dialog'
|
|
||||||
import '@material/mwc-button'
|
import '@material/mwc-button'
|
||||||
import '@material/mwc-select'
|
import '@material/mwc-dialog'
|
||||||
import '@material/mwc-textfield'
|
|
||||||
import '@material/mwc-icon'
|
import '@material/mwc-icon'
|
||||||
|
import '@material/mwc-textfield'
|
||||||
|
|
||||||
import snackbar from './snackbar.js'
|
// Multi language support
|
||||||
|
import { get, translate } from '../../translate'
|
||||||
|
|
||||||
let mykeyDialog
|
let mykeyDialog
|
||||||
|
|
||||||
class MykeyPage extends connect(store)(LitElement) {
|
class MykeyPage extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
nodeConfig: { type: Object },
|
nodeConfig: { type: Object },
|
||||||
theme: { type: String, reflect: true }
|
theme: { type: String, reflect: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [mykeyPageStyles]
|
||||||
* {
|
}
|
||||||
--mdc-theme-primary: rgb(3, 169, 244);
|
|
||||||
--mdc-theme-secondary: var(--mdc-theme-primary);
|
|
||||||
--paper-input-container-focus-color: var(--mdc-theme-primary);
|
|
||||||
--mdc-theme-surface: var(--white);
|
|
||||||
--mdc-dialog-heading-ink-color: var(--black);
|
|
||||||
--mdc-dialog-content-ink-color: var(--black);
|
|
||||||
--lumo-primary-text-color: rgb(0, 167, 245);
|
|
||||||
--lumo-primary-color-50pct: rgba(0, 167, 245, 0.5);
|
|
||||||
--lumo-primary-color-10pct: rgba(0, 167, 245, 0.1);
|
|
||||||
--lumo-primary-color: hsl(199, 100%, 48%);
|
|
||||||
--lumo-base-color: var(--white);
|
|
||||||
--lumo-body-text-color: var(--black);
|
|
||||||
--_lumo-grid-border-color: var(--border);
|
|
||||||
--_lumo-grid-secondary-border-color: var(--border2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.red {
|
constructor() {
|
||||||
--mdc-theme-primary: red;
|
super()
|
||||||
}
|
this.nodeConfig = {}
|
||||||
`
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
render() {
|
||||||
super()
|
return html`
|
||||||
this.nodeConfig = {}
|
<mwc-dialog id="mykeyDialog" heading="${translate("apipage.achange1")}" opened=false>
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light';
|
<div style="min-height:200px; min-width: 300px; box-sizing: border-box; position: relative;">
|
||||||
}
|
<mwc-textfield icon="fingerprint" id="mykeyInput" style="width:100%;" label="${translate("apipage.achange2")}"></mwc-textfield>
|
||||||
|
<p style="margin-top: 45px;">${translate("apipage.achange3")}</p>
|
||||||
|
</div>
|
||||||
|
<mwc-button slot="secondaryAction" dialogAction="close" class="red">
|
||||||
|
${translate("apipage.achange4")}
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button slot="primaryAction" @click="${this.addMykey}">
|
||||||
|
${translate("apipage.achange5")}
|
||||||
|
</mwc-button>
|
||||||
|
</mwc-dialog>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
firstUpdated() {
|
||||||
return html`
|
// ...
|
||||||
<mwc-dialog id="mykeyDialog" heading="${translate("apipage.achange1")}" opened=false>
|
}
|
||||||
<div style="min-height:200px; min-width: 300px; box-sizing: border-box; position: relative;">
|
|
||||||
<mwc-textfield icon="fingerprint" id="mykeyInput" style="width:100%;" label="${translate("apipage.achange2")}"></mwc-textfield>
|
|
||||||
<p style="margin-top: 45px;">${translate("apipage.achange3")}</p>
|
|
||||||
</div>
|
|
||||||
<mwc-button
|
|
||||||
slot="secondaryAction"
|
|
||||||
dialogAction="close"
|
|
||||||
class="red"
|
|
||||||
>
|
|
||||||
${translate("apipage.achange4")}
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button
|
|
||||||
slot="primaryAction"
|
|
||||||
@click="${this.addMykey}"
|
|
||||||
>
|
|
||||||
${translate("apipage.achange5")}
|
|
||||||
</mwc-button>
|
|
||||||
</mwc-dialog>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
stateChanged(state) {
|
show() {
|
||||||
this.config = state.config
|
this.shadowRoot.getElementById('mykeyDialog').show()
|
||||||
this.nodeConfig = state.app.nodeConfig
|
}
|
||||||
}
|
|
||||||
|
|
||||||
show() {
|
async addMykey() {
|
||||||
this.shadowRoot.getElementById('mykeyDialog').show()
|
const mykeyInput = this.shadowRoot.getElementById('mykeyInput').value
|
||||||
}
|
|
||||||
|
|
||||||
async addMykey() {
|
let selectedNode = this.nodeConfig.knownNodes[this.nodeConfig.node]
|
||||||
const mykeyInput = this.shadowRoot.getElementById('mykeyInput').value
|
let testResult = await testApiKey(mykeyInput)
|
||||||
let selectedNode = this.nodeConfig.knownNodes[this.nodeConfig.node];
|
|
||||||
let testResult = await testApiKey(mykeyInput);
|
|
||||||
|
|
||||||
if (testResult === true) {
|
if (testResult === true) {
|
||||||
selectedNode.apiKey = mykeyInput;
|
selectedNode.apiKey = mykeyInput
|
||||||
this.nodeConfig.knownNodes[this.nodeConfig.node] = selectedNode;
|
|
||||||
localStorage.setItem('myQortalNodes', JSON.stringify(this.nodeConfig.knownNodes));
|
this.nodeConfig.knownNodes[this.nodeConfig.node] = selectedNode
|
||||||
let snackbar1 = get("apipage.achange6")
|
|
||||||
snackbar.add({
|
localStorage.setItem('myQortalNodes', JSON.stringify(this.nodeConfig.knownNodes))
|
||||||
labelText: `${snackbar1}`,
|
|
||||||
dismiss: true
|
let snackbar1 = get("apipage.achange6")
|
||||||
})
|
|
||||||
this.shadowRoot.getElementById('mykeyInput').value = ''
|
snackbar.add({
|
||||||
this.shadowRoot.querySelector('#mykeyDialog').close()
|
labelText: `${snackbar1}`,
|
||||||
} else {
|
dismiss: true
|
||||||
let snackbar2 = get("apipage.achange7")
|
})
|
||||||
snackbar.add({
|
|
||||||
labelText: `${snackbar2}`,
|
this.shadowRoot.getElementById('mykeyInput').value = ''
|
||||||
dismiss: true
|
this.shadowRoot.querySelector('#mykeyDialog').close()
|
||||||
})
|
} else {
|
||||||
this.shadowRoot.getElementById('mykeyInput').value = ''
|
let snackbar2 = get("apipage.achange7")
|
||||||
this.shadowRoot.querySelector('#mykeyDialog').close()
|
|
||||||
}
|
snackbar.add({
|
||||||
}
|
labelText: `${snackbar2}`,
|
||||||
|
dismiss: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.shadowRoot.getElementById('mykeyInput').value = ''
|
||||||
|
this.shadowRoot.querySelector('#mykeyDialog').close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stateChanged(state) {
|
||||||
|
this.config = state.config
|
||||||
|
this.nodeConfig = state.app.nodeConfig
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('mykey-page', MykeyPage)
|
window.customElements.define('mykey-page', MykeyPage)
|
||||||
|
|
||||||
const mykey = document.createElement('mykey-page')
|
const mykey = document.createElement('mykey-page')
|
||||||
mykeyDialog = document.body.appendChild(mykey)
|
mykeyDialog = document.body.appendChild(mykey)
|
||||||
|
|
||||||
export default mykeyDialog
|
export default mykeyDialog
|
@ -1,38 +1,18 @@
|
|||||||
// Author: irontiga <irontiga@gmail.com>
|
// Author: irontiga <irontiga@gmail.com>
|
||||||
|
|
||||||
'use strict'
|
import { html, LitElement } from 'lit'
|
||||||
import {html, LitElement} from 'lit'
|
import * as WORDLISTS from './wordlists'
|
||||||
import * as WORDLISTS from './wordlists.js'
|
|
||||||
|
|
||||||
class RandomSentenceGenerator extends LitElement {
|
class RandomSentenceGenerator extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
template: {
|
template: { type: String, attribute: 'template' },
|
||||||
type: String,
|
parsedString: { type: String },
|
||||||
attribute: 'template'
|
fetchedWordlistCount: { type: Number, value: 0 },
|
||||||
},
|
capitalize: { type: Boolean },
|
||||||
parsedString: {
|
partsOfSpeechMap: { type: Object },
|
||||||
type: String
|
templateEntropy: { type: Number, reflect: true, attribute: 'template-entropy' },
|
||||||
},
|
maxWordLength: { type: Number, attribute: 'max-word-length' }
|
||||||
fetchedWordlistCount: {
|
|
||||||
type: Number,
|
|
||||||
value: 0
|
|
||||||
},
|
|
||||||
capitalize: {
|
|
||||||
type: Boolean
|
|
||||||
},
|
|
||||||
partsOfSpeechMap: {
|
|
||||||
type: Object
|
|
||||||
},
|
|
||||||
templateEntropy: {
|
|
||||||
type: Number,
|
|
||||||
reflect: true,
|
|
||||||
attribute: 'template-entropy'
|
|
||||||
},
|
|
||||||
maxWordLength: {
|
|
||||||
type: Number,
|
|
||||||
attribute: 'max-word-length'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,25 +37,42 @@ class RandomSentenceGenerator extends LitElement {
|
|||||||
this._wordlists = WORDLISTS
|
this._wordlists = WORDLISTS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
${this.parsedString}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
updated(changedProperties) {
|
updated(changedProperties) {
|
||||||
let regen = false
|
let regen = false
|
||||||
|
|
||||||
if (changedProperties.has('template')) {
|
if (changedProperties.has('template')) {
|
||||||
regen = true
|
regen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changedProperties.has('maxWordLength')) {
|
if (changedProperties.has('maxWordLength')) {
|
||||||
console.dir(this.maxWordLength)
|
console.dir(this.maxWordLength)
|
||||||
|
|
||||||
if (this.maxWordLength) {
|
if (this.maxWordLength) {
|
||||||
const wl = { ...this._wordlists }
|
const wl = { ...this._wordlists }
|
||||||
|
|
||||||
for (const partOfSpeech in this._wordlists) {
|
for (const partOfSpeech in this._wordlists) {
|
||||||
console.log(this._wordlists[partOfSpeech])
|
console.log(this._wordlists[partOfSpeech])
|
||||||
if (Array.isArray(this._wordlists[partOfSpeech])) {
|
if (Array.isArray(this._wordlists[partOfSpeech])) {
|
||||||
wl[partOfSpeech] = this._wordlists[partOfSpeech].filter(word => word.length <= this.maxWordLength)
|
wl[partOfSpeech] = this._wordlists[partOfSpeech].filter(word => word.length <= this.maxWordLength)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._wordlists = wl
|
this._wordlists = wl
|
||||||
}
|
}
|
||||||
|
|
||||||
regen = true
|
regen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (regen) this.generate()
|
if (regen) this.generate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,13 +80,16 @@ class RandomSentenceGenerator extends LitElement {
|
|||||||
if (entropy > 1074) {
|
if (entropy > 1074) {
|
||||||
throw new Error('Javascript can not handle that much entropy!')
|
throw new Error('Javascript can not handle that much entropy!')
|
||||||
}
|
}
|
||||||
|
|
||||||
let randNum = 0
|
let randNum = 0
|
||||||
|
|
||||||
const crypto = window.crypto || window.msCrypto
|
const crypto = window.crypto || window.msCrypto
|
||||||
|
|
||||||
if (crypto) {
|
if (crypto) {
|
||||||
const entropy256 = Math.ceil(entropy / 8)
|
const entropy256 = Math.ceil(entropy / 8)
|
||||||
|
|
||||||
let buffer = new Uint8Array(entropy256)
|
let buffer = new Uint8Array(entropy256)
|
||||||
|
|
||||||
crypto.getRandomValues(buffer)
|
crypto.getRandomValues(buffer)
|
||||||
|
|
||||||
randNum = buffer.reduce((num, value) => {
|
randNum = buffer.reduce((num, value) => {
|
||||||
@ -97,8 +97,10 @@ class RandomSentenceGenerator extends LitElement {
|
|||||||
}, 1) / Math.pow(256, entropy256)
|
}, 1) / Math.pow(256, entropy256)
|
||||||
} else {
|
} else {
|
||||||
console.warn('Secure RNG not found. Using Math.random')
|
console.warn('Secure RNG not found. Using Math.random')
|
||||||
|
|
||||||
randNum = Math.random()
|
randNum = Math.random()
|
||||||
}
|
}
|
||||||
|
|
||||||
return randNum
|
return randNum
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +129,9 @@ class RandomSentenceGenerator extends LitElement {
|
|||||||
|
|
||||||
parse(template) {
|
parse(template) {
|
||||||
const split = template.split(/[\s]/g)
|
const split = template.split(/[\s]/g)
|
||||||
|
|
||||||
let entropy = 1
|
let entropy = 1
|
||||||
|
|
||||||
const final = split.map(word => {
|
const final = split.map(word => {
|
||||||
const lower = word.toLowerCase()
|
const lower = word.toLowerCase()
|
||||||
|
|
||||||
@ -139,22 +143,20 @@ class RandomSentenceGenerator extends LitElement {
|
|||||||
const replacement = this.getWord(partOfSpeech)
|
const replacement = this.getWord(partOfSpeech)
|
||||||
word = replacement.word + word.slice(partOfSpeech.length) // Append the rest of the "word" (punctuation)
|
word = replacement.word + word.slice(partOfSpeech.length) // Append the rest of the "word" (punctuation)
|
||||||
entropy = entropy * replacement.entropy
|
entropy = entropy * replacement.entropy
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return word
|
return word
|
||||||
})
|
})
|
||||||
this.templateEntropy = Math.floor(Math.log(entropy) / Math.log(8))
|
|
||||||
return final.join(' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
this.templateEntropy = Math.floor(Math.log(entropy) / Math.log(8))
|
||||||
return html`
|
|
||||||
${this.parsedString}
|
return final.join(' ')
|
||||||
`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('random-sentence-generator', RandomSentenceGenerator)
|
window.customElements.define('random-sentence-generator', RandomSentenceGenerator)
|
||||||
|
|
||||||
export default RandomSentenceGenerator
|
export default RandomSentenceGenerator
|
@ -1,19 +1,21 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {connect} from 'pwa-helpers'
|
import { connect } from 'pwa-helpers'
|
||||||
import {store} from '../store.js'
|
import { store } from '../store'
|
||||||
import {doAddNode, doEditNode, doLoadNodeConfig, doRemoveNode, doSetNode} from '../redux/app/app-actions.js'
|
import { doAddNode, doEditNode, doLoadNodeConfig, doRemoveNode, doSetNode } from '../redux/app/app-actions'
|
||||||
import {get, registerTranslateConfig, translate, use} from '../../translate'
|
import { settingsPageStyles } from '../styles/core-css'
|
||||||
import snackbar from './snackbar.js'
|
|
||||||
import '../components/language-selector.js'
|
|
||||||
import '../custom-elements/frag-file-input.js'
|
|
||||||
import FileSaver from 'file-saver'
|
import FileSaver from 'file-saver'
|
||||||
|
import snackbar from './snackbar'
|
||||||
import '@material/mwc-dialog'
|
import './frag-file-input'
|
||||||
|
import '../components/language-selector'
|
||||||
import '@material/mwc-button'
|
import '@material/mwc-button'
|
||||||
import '@material/mwc-select'
|
import '@material/mwc-dialog'
|
||||||
import '@material/mwc-textfield'
|
|
||||||
import '@material/mwc-icon'
|
import '@material/mwc-icon'
|
||||||
import '@material/mwc-list/mwc-list-item.js'
|
import '@material/mwc-list/mwc-list-item.js'
|
||||||
|
import '@material/mwc-select'
|
||||||
|
import '@material/mwc-textfield'
|
||||||
|
|
||||||
|
// Multi language support
|
||||||
|
import { get, registerTranslateConfig, translate, use } from '../../translate'
|
||||||
|
|
||||||
registerTranslateConfig({
|
registerTranslateConfig({
|
||||||
loader: (lang) => fetch(`/language/${lang}.json`).then((res) => res.json())
|
loader: (lang) => fetch(`/language/${lang}.json`).then((res) => res.json())
|
||||||
@ -35,132 +37,23 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
return {
|
return {
|
||||||
lastSelected: { type: Number },
|
lastSelected: { type: Number },
|
||||||
nodeConfig: { type: Object },
|
nodeConfig: { type: Object },
|
||||||
theme: { type: String, reflect: true },
|
|
||||||
isBeingEdited: { type: Boolean },
|
isBeingEdited: { type: Boolean },
|
||||||
dropdownOpen: { type: Boolean }
|
dropdownOpen: { type: Boolean },
|
||||||
|
theme: { type: String, reflect: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [settingsPageStyles]
|
||||||
* {
|
|
||||||
--mdc-theme-primary: var(--login-button);
|
|
||||||
--mdc-theme-secondary: var(--mdc-theme-primary);
|
|
||||||
--mdc-dialog-content-ink-color: var(--black);
|
|
||||||
--mdc-theme-surface: var(--white);
|
|
||||||
--mdc-theme-text-primary-on-background: var(--black);
|
|
||||||
--mdc-dialog-min-width: 300px;
|
|
||||||
--mdc-dialog-max-width: 650px;
|
|
||||||
--mdc-dialog-max-height: 700px;
|
|
||||||
--mdc-list-item-text-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main {
|
|
||||||
width: 210px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.globe {
|
|
||||||
color: var(--black);
|
|
||||||
--mdc-icon-size: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.name {
|
|
||||||
display: inline-block;
|
|
||||||
width: 150px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--general-color-blue);
|
|
||||||
border: 1px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.red {
|
|
||||||
--mdc-theme-primary: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonred {
|
|
||||||
color: #f44336;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttongreen {
|
|
||||||
color: #03c851;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonBlue {
|
|
||||||
color: var(--general-color-blue);
|
|
||||||
}
|
|
||||||
|
|
||||||
.floatleft {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.floatright {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-parent {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#customSelect {
|
|
||||||
position: relative;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
cursor: pointer;
|
|
||||||
background: var(--plugback);
|
|
||||||
}
|
|
||||||
|
|
||||||
#customSelect .selected {
|
|
||||||
padding: 10px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
#customSelect ul {
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
left: 0;
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
display: none;
|
|
||||||
background: var(--plugback);
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
#customSelect ul.open {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#customSelect ul li {
|
|
||||||
padding: 10px;
|
|
||||||
transition: 0.2s all;
|
|
||||||
}
|
|
||||||
|
|
||||||
#customSelect ul li:hover {
|
|
||||||
background-color: var(--graylight);
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-left-side {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
|
||||||
this.nodeConfig = {}
|
this.nodeConfig = {}
|
||||||
this.isBeingEdited = false
|
this.isBeingEdited = false
|
||||||
this.isBeingEditedIndex = null
|
this.isBeingEditedIndex = null
|
||||||
this.dropdownOpen = false
|
this.dropdownOpen = false
|
||||||
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -176,68 +69,65 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
<div class="selected">
|
<div class="selected">
|
||||||
<div class="selected-left-side">
|
<div class="selected-left-side">
|
||||||
<mwc-icon style="margin-right: 10px">link</mwc-icon>
|
<mwc-icon style="margin-right: 10px">link</mwc-icon>
|
||||||
${this.selectedItem ? html
|
${this.selectedItem ?
|
||||||
`
|
html `
|
||||||
<div>
|
<div>
|
||||||
<span class="name">${this.selectedItem.name}</span>
|
<span class="name">${this.selectedItem.name}</span>
|
||||||
<span>${this.selectedItem.protocol + '://' + this.selectedItem.domain + ':' + this.selectedItem.port}</span>
|
<span>${this.selectedItem.protocol + '://' + this.selectedItem.domain + ':' + this.selectedItem.port}</span>
|
||||||
</div>
|
</div>
|
||||||
` : html`${translate('settings.selectnode')}`
|
` : html`
|
||||||
}
|
${translate('settings.selectnode')}
|
||||||
</div>
|
`
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<mwc-icon>expand_more</mwc-icon>
|
<mwc-icon>expand_more</mwc-icon>
|
||||||
</div>
|
</div>
|
||||||
<ul class="${this.dropdownOpen ? 'open' : ''}">
|
<ul class="${this.dropdownOpen ? 'open' : ''}">
|
||||||
${this.nodeConfig.knownNodes.map(
|
${this.nodeConfig.knownNodes.map((n, index) => html`
|
||||||
(n, index) => html`
|
<li @click="${(e) => this.handleSelection(e, n, index)}">
|
||||||
<li @click="${(e) => this.handleSelection(e, n, index)}">
|
<div class="list-parent">
|
||||||
<div class="list-parent">
|
<div>
|
||||||
<div>
|
<span class="name">${n.name}</span>
|
||||||
<span class="name">${n.name}</span>
|
<span>${n.protocol + '://' + n.domain + ':' + n.port}</span>
|
||||||
<span>${n.protocol + '://' + n.domain + ':' + n.port}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<mwc-button
|
|
||||||
outlined
|
|
||||||
@click="${(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
const currentValues = this.nodeConfig.knownNodes[index]
|
|
||||||
const dialog = this.shadowRoot.querySelector('#addNodeDialog')
|
|
||||||
|
|
||||||
// Set the value for mwc-textfield elements
|
|
||||||
dialog.querySelector('#nameInput').value = currentValues.name
|
|
||||||
dialog.querySelector('#domainInput').value = currentValues.domain
|
|
||||||
dialog.querySelector('#portInput').value = currentValues.port
|
|
||||||
|
|
||||||
// Set the selected value for mwc-select
|
|
||||||
const protocolList = dialog.querySelector('#protocolList')
|
|
||||||
protocolList.value = currentValues.protocol
|
|
||||||
this.isBeingEdited = true
|
|
||||||
this.isBeingEditedIndex = index
|
|
||||||
this.shadowRoot.querySelector('#addNodeDialog').show()
|
|
||||||
}}"
|
|
||||||
>
|
|
||||||
<mwc-icon class="buttonBlue">edit</mwc-icon>
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button outlined @click="${(e) => this.removeNode(e, index)}">
|
|
||||||
<mwc-icon class="buttonred">remove</mwc-icon>
|
|
||||||
</mwc-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
<div>
|
||||||
`
|
<mwc-button
|
||||||
)}
|
outlined
|
||||||
|
@click="${(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
const currentValues = this.nodeConfig.knownNodes[index]
|
||||||
|
const dialog = this.shadowRoot.querySelector('#addNodeDialog')
|
||||||
|
|
||||||
|
// Set the value for mwc-textfield elements
|
||||||
|
dialog.querySelector('#nameInput').value = currentValues.name
|
||||||
|
dialog.querySelector('#domainInput').value = currentValues.domain
|
||||||
|
dialog.querySelector('#portInput').value = currentValues.port
|
||||||
|
|
||||||
|
// Set the selected value for mwc-select
|
||||||
|
const protocolList = dialog.querySelector('#protocolList')
|
||||||
|
protocolList.value = currentValues.protocol
|
||||||
|
this.isBeingEdited = true
|
||||||
|
this.isBeingEditedIndex = index
|
||||||
|
this.shadowRoot.querySelector('#addNodeDialog').show()
|
||||||
|
}}"
|
||||||
|
>
|
||||||
|
<mwc-icon class="buttonBlue">edit</mwc-icon>
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button outlined @click="${(e) => this.removeNode(e, index)}">
|
||||||
|
<mwc-icon class="buttonred">remove</mwc-icon>
|
||||||
|
</mwc-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
`)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<p style="margin-top: 30px; text-align: center;">
|
<p style="margin-top: 30px; text-align: center;">
|
||||||
${translate('settings.nodehint')}
|
${translate('settings.nodehint')}
|
||||||
</p>
|
</p>
|
||||||
<center>
|
<center>
|
||||||
<mwc-button
|
<mwc-button outlined @click="${() => this.shadowRoot.querySelector('#addNodeDialog').show()}">
|
||||||
outlined
|
<mwc-icon class="buttongreen">add</mwc-icon>
|
||||||
@click="${() => this.shadowRoot.querySelector('#addNodeDialog').show()}"
|
|
||||||
><mwc-icon class="buttongreen">add</mwc-icon
|
|
||||||
>
|
|
||||||
${translate('settings.addcustomnode')}
|
${translate('settings.addcustomnode')}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</center>
|
</center>
|
||||||
@ -261,8 +151,7 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
<br />
|
<br />
|
||||||
<center>
|
<center>
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<mwc-icon class="globe">language</mwc-icon
|
<mwc-icon class="globe">language</mwc-icon> <language-selector></language-selector>
|
||||||
> <language-selector></language-selector>
|
|
||||||
</div>
|
</div>
|
||||||
</center>
|
</center>
|
||||||
</div>
|
</div>
|
||||||
@ -270,7 +159,6 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
${translate('general.close')}
|
${translate('general.close')}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</mwc-dialog>
|
</mwc-dialog>
|
||||||
|
|
||||||
<mwc-dialog id="addNodeDialog">
|
<mwc-dialog id="addNodeDialog">
|
||||||
<div style="text-align: center;">
|
<div style="text-align: center;">
|
||||||
<h2>${translate('settings.addcustomnode')}</h2>
|
<h2>${translate('settings.addcustomnode')}</h2>
|
||||||
@ -293,7 +181,6 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
${translate('settings.addandsave')}
|
${translate('settings.addandsave')}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</mwc-dialog>
|
</mwc-dialog>
|
||||||
|
|
||||||
<mwc-dialog id="importQortalNodesListDialog">
|
<mwc-dialog id="importQortalNodesListDialog">
|
||||||
<div style="text-align:center">
|
<div style="text-align:center">
|
||||||
<h2>${translate('settings.import')}</h2>
|
<h2>${translate('settings.import')}</h2>
|
||||||
@ -326,6 +213,7 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
|
|
||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
const checkNode = localStorage.getItem('mySelectedNode')
|
const checkNode = localStorage.getItem('mySelectedNode')
|
||||||
|
|
||||||
if (checkNode === null || checkNode.length === 0) {
|
if (checkNode === null || checkNode.length === 0) {
|
||||||
localStorage.setItem('mySelectedNode', 0)
|
localStorage.setItem('mySelectedNode', 0)
|
||||||
} else {
|
} else {
|
||||||
@ -364,13 +252,36 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
this.dropdownOpen = false
|
this.dropdownOpen = false
|
||||||
this.requestUpdate()
|
this.requestUpdate()
|
||||||
this.nodeSelected(index)
|
this.nodeSelected(index)
|
||||||
|
|
||||||
localStorage.setItem('mySelectedNode', index)
|
localStorage.setItem('mySelectedNode', index)
|
||||||
|
|
||||||
const selectedNodeIndexOnNewStart = localStorage.getItem('mySelectedNode')
|
const selectedNodeIndexOnNewStart = localStorage.getItem('mySelectedNode')
|
||||||
const catchSavedNodes = JSON.parse(localStorage.getItem('myQortalNodes'))
|
const catchSavedNodes = JSON.parse(localStorage.getItem('myQortalNodes'))
|
||||||
const selectedNodeOnNewStart = catchSavedNodes[selectedNodeIndexOnNewStart]
|
const selectedNodeOnNewStart = catchSavedNodes[selectedNodeIndexOnNewStart]
|
||||||
const selectedNameOnNewStart = `${selectedNodeOnNewStart.name}`
|
const selectedNameOnNewStart = `${selectedNodeOnNewStart.name}`
|
||||||
const selectedNodeUrlOnNewStart = `${selectedNodeOnNewStart.protocol + '://' + selectedNodeOnNewStart.domain +':' + selectedNodeOnNewStart.port}`
|
const selectedNodeUrlOnNewStart = `${selectedNodeOnNewStart.protocol + '://' + selectedNodeOnNewStart.domain + ':' + selectedNodeOnNewStart.port}`
|
||||||
|
|
||||||
|
let snack2string = get('settings.snack2')
|
||||||
|
|
||||||
|
snackbar.add({
|
||||||
|
labelText: `${snack2string} : ${selectedNameOnNewStart} ${selectedNodeUrlOnNewStart}`,
|
||||||
|
dismiss: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAddNodeSelection(node, index) {
|
||||||
|
this.selectedItem = node
|
||||||
|
this.dropdownOpen = false
|
||||||
|
this.requestUpdate()
|
||||||
|
this.nodeSelected(index)
|
||||||
|
|
||||||
|
localStorage.setItem('mySelectedNode', index)
|
||||||
|
|
||||||
|
const selectedNodeIndexOnNewStart = localStorage.getItem('mySelectedNode')
|
||||||
|
const catchSavedNodes = JSON.parse(localStorage.getItem('myQortalNodes'))
|
||||||
|
const selectedNodeOnNewStart = catchSavedNodes[selectedNodeIndexOnNewStart]
|
||||||
|
const selectedNameOnNewStart = `${selectedNodeOnNewStart.name}`
|
||||||
|
const selectedNodeUrlOnNewStart = `${selectedNodeOnNewStart.protocol + '://' + selectedNodeOnNewStart.domain + ':' + selectedNodeOnNewStart.port}`
|
||||||
|
|
||||||
let snack2string = get('settings.snack2')
|
let snack2string = get('settings.snack2')
|
||||||
|
|
||||||
@ -408,10 +319,13 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var renewNodes = []
|
var renewNodes = []
|
||||||
|
|
||||||
renewNodes.push(obj1, obj2)
|
renewNodes.push(obj1, obj2)
|
||||||
|
|
||||||
localStorage.setItem('myQortalNodes', JSON.stringify(renewNodes))
|
localStorage.setItem('myQortalNodes', JSON.stringify(renewNodes))
|
||||||
|
|
||||||
let snack1string = get('settings.snack1')
|
let snack1string = get('settings.snack1')
|
||||||
|
|
||||||
snackbar.add({
|
snackbar.add({
|
||||||
labelText: `${snack1string}`,
|
labelText: `${snack1string}`,
|
||||||
dismiss: true
|
dismiss: true
|
||||||
@ -426,9 +340,9 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
nodeSelected(selectedNodeIndex) {
|
nodeSelected(selectedNodeIndex) {
|
||||||
const selectedNode = this.nodeConfig.knownNodes[selectedNodeIndex]
|
const selectedNode = this.nodeConfig.knownNodes[selectedNodeIndex]
|
||||||
const selectedName = `${selectedNode.name}`
|
const selectedName = `${selectedNode.name}`
|
||||||
const selectedNodeUrl = `${selectedNode.protocol + '://' + selectedNode.domain +':' + selectedNode.port}`
|
const selectedNodeUrl = `${selectedNode.protocol + '://' + selectedNode.domain + ':' + selectedNode.port}`
|
||||||
|
|
||||||
const index = parseInt(selectedNodeIndex)
|
const index = parseInt(selectedNodeIndex)
|
||||||
|
|
||||||
if (isNaN(index)) return
|
if (isNaN(index)) return
|
||||||
|
|
||||||
store.dispatch(doSetNode(selectedNodeIndex))
|
store.dispatch(doSetNode(selectedNodeIndex))
|
||||||
@ -462,13 +376,23 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
store.dispatch(doAddNode(nodeObject))
|
store.dispatch(doAddNode(nodeObject))
|
||||||
|
|
||||||
const haveNodes = JSON.parse(localStorage.getItem('myQortalNodes'))
|
const haveNodes = JSON.parse(localStorage.getItem('myQortalNodes'))
|
||||||
|
const indexLength = this.nodeConfig.knownNodes.length
|
||||||
|
|
||||||
|
let choosedIndex
|
||||||
|
let choosedNode
|
||||||
|
|
||||||
|
choosedIndex = indexLength -1
|
||||||
|
choosedNode = this.nodeConfig.knownNodes[this.nodeConfig.knownNodes.length - 1]
|
||||||
|
|
||||||
if (haveNodes === null || haveNodes.length === 0) {
|
if (haveNodes === null || haveNodes.length === 0) {
|
||||||
var savedNodes = []
|
var savedNodes = []
|
||||||
|
|
||||||
savedNodes.push(nodeObject)
|
savedNodes.push(nodeObject)
|
||||||
|
|
||||||
localStorage.setItem('myQortalNodes', JSON.stringify(savedNodes))
|
localStorage.setItem('myQortalNodes', JSON.stringify(savedNodes))
|
||||||
|
|
||||||
let snack3string = get('settings.snack3')
|
let snack3string = get('settings.snack3')
|
||||||
|
|
||||||
snackbar.add({
|
snackbar.add({
|
||||||
labelText: `${snack3string}`,
|
labelText: `${snack3string}`,
|
||||||
dismiss: true
|
dismiss: true
|
||||||
@ -478,14 +402,16 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
this.shadowRoot.getElementById('protocolList').value = ''
|
this.shadowRoot.getElementById('protocolList').value = ''
|
||||||
this.shadowRoot.getElementById('domainInput').value = ''
|
this.shadowRoot.getElementById('domainInput').value = ''
|
||||||
this.shadowRoot.getElementById('portInput').value = ''
|
this.shadowRoot.getElementById('portInput').value = ''
|
||||||
|
|
||||||
this.shadowRoot.querySelector('#addNodeDialog').close()
|
this.shadowRoot.querySelector('#addNodeDialog').close()
|
||||||
} else {
|
} else {
|
||||||
var stored = JSON.parse(localStorage.getItem('myQortalNodes'))
|
var stored = JSON.parse(localStorage.getItem('myQortalNodes'))
|
||||||
|
|
||||||
stored.push(nodeObject)
|
stored.push(nodeObject)
|
||||||
|
|
||||||
localStorage.setItem('myQortalNodes', JSON.stringify(stored))
|
localStorage.setItem('myQortalNodes', JSON.stringify(stored))
|
||||||
|
|
||||||
let snack3string = get('settings.snack3');
|
let snack3string = get('settings.snack3')
|
||||||
|
|
||||||
snackbar.add({
|
snackbar.add({
|
||||||
labelText: `${snack3string}`,
|
labelText: `${snack3string}`,
|
||||||
dismiss: true
|
dismiss: true
|
||||||
@ -495,9 +421,10 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
this.shadowRoot.getElementById('protocolList').value = ''
|
this.shadowRoot.getElementById('protocolList').value = ''
|
||||||
this.shadowRoot.getElementById('domainInput').value = ''
|
this.shadowRoot.getElementById('domainInput').value = ''
|
||||||
this.shadowRoot.getElementById('portInput').value = ''
|
this.shadowRoot.getElementById('portInput').value = ''
|
||||||
|
|
||||||
this.shadowRoot.querySelector('#addNodeDialog').close()
|
this.shadowRoot.querySelector('#addNodeDialog').close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.handleAddNodeSelection(choosedNode, choosedIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,11 +432,15 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
|
||||||
let stored = JSON.parse(localStorage.getItem('myQortalNodes'))
|
let stored = JSON.parse(localStorage.getItem('myQortalNodes'))
|
||||||
|
|
||||||
stored.splice(index, 1)
|
stored.splice(index, 1)
|
||||||
|
|
||||||
localStorage.setItem('myQortalNodes', JSON.stringify(stored))
|
localStorage.setItem('myQortalNodes', JSON.stringify(stored))
|
||||||
|
|
||||||
store.dispatch(doRemoveNode(index))
|
store.dispatch(doRemoveNode(index))
|
||||||
|
|
||||||
let snack6string = get('settings.snack6')
|
let snack6string = get('settings.snack6')
|
||||||
|
|
||||||
snackbar.add({
|
snackbar.add({
|
||||||
labelText: `${snack6string}`,
|
labelText: `${snack6string}`,
|
||||||
dismiss: true
|
dismiss: true
|
||||||
@ -530,16 +461,21 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
protocol: protocolList,
|
protocol: protocolList,
|
||||||
domain: domainInput,
|
domain: domainInput,
|
||||||
port: portInput,
|
port: portInput,
|
||||||
enableManagement: true,
|
enableManagement: true
|
||||||
}
|
}
|
||||||
|
|
||||||
let stored = JSON.parse(localStorage.getItem('myQortalNodes'))
|
let stored = JSON.parse(localStorage.getItem('myQortalNodes'))
|
||||||
|
|
||||||
const copyStored = [...stored]
|
const copyStored = [...stored]
|
||||||
|
|
||||||
copyStored[index] = nodeObject
|
copyStored[index] = nodeObject
|
||||||
|
|
||||||
localStorage.setItem('myQortalNodes', JSON.stringify(copyStored))
|
localStorage.setItem('myQortalNodes', JSON.stringify(copyStored))
|
||||||
|
|
||||||
store.dispatch(doEditNode(index, nodeObject))
|
store.dispatch(doEditNode(index, nodeObject))
|
||||||
|
|
||||||
let snack7string = get('settings.snack7')
|
let snack7string = get('settings.snack7')
|
||||||
|
|
||||||
snackbar.add({
|
snackbar.add({
|
||||||
labelText: `${snack7string}`,
|
labelText: `${snack7string}`,
|
||||||
dismiss: true
|
dismiss: true
|
||||||
@ -551,7 +487,6 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
this.shadowRoot.getElementById('portInput').value = ''
|
this.shadowRoot.getElementById('portInput').value = ''
|
||||||
this.isBeingEdited = false
|
this.isBeingEdited = false
|
||||||
this.isBeingEditedIndex = null
|
this.isBeingEditedIndex = null
|
||||||
|
|
||||||
this.shadowRoot.querySelector('#addNodeDialog').close()
|
this.shadowRoot.querySelector('#addNodeDialog').close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -566,26 +501,25 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
|
|
||||||
renderExportNodesListButton() {
|
renderExportNodesListButton() {
|
||||||
return html`
|
return html`
|
||||||
<mwc-button
|
<mwc-button dense unelevated label="${translate('settings.export')}" @click="${() => this.exportQortalNodesList()}"></mwc-button>
|
||||||
dense
|
|
||||||
unelevated
|
|
||||||
label="${translate('settings.export')}"
|
|
||||||
@click="${() => this.exportQortalNodesList()}"
|
|
||||||
>
|
|
||||||
</mwc-button>
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
exportQortalNodesList() {
|
exportQortalNodesList() {
|
||||||
let nodelist = ''
|
let nodelist = ''
|
||||||
|
|
||||||
const qortalNodesList = JSON.stringify(
|
const qortalNodesList = JSON.stringify(
|
||||||
localStorage.getItem('myQortalNodes')
|
localStorage.getItem('myQortalNodes')
|
||||||
);
|
)
|
||||||
|
|
||||||
const qortalNodesListSave = JSON.parse(qortalNodesList || '[]')
|
const qortalNodesListSave = JSON.parse(qortalNodesList || '[]')
|
||||||
|
|
||||||
const blob = new Blob([qortalNodesListSave], {
|
const blob = new Blob([qortalNodesListSave], {
|
||||||
type: 'text/plain;charset=utf-8',
|
type: 'text/plain;charset=utf-8'
|
||||||
})
|
})
|
||||||
|
|
||||||
nodelist = 'qortal.nodes'
|
nodelist = 'qortal.nodes'
|
||||||
|
|
||||||
this.saveFileToDisk(blob, nodelist)
|
this.saveFileToDisk(blob, nodelist)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -595,16 +529,20 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
suggestedName: fileName,
|
suggestedName: fileName,
|
||||||
types: [{
|
types: [{
|
||||||
description: 'File'
|
description: 'File'
|
||||||
}],
|
}]
|
||||||
})
|
})
|
||||||
|
|
||||||
const writeFile = async (fileHandle, contents) => {
|
const writeFile = async (fileHandle, contents) => {
|
||||||
const writable = await fileHandle.createWritable()
|
const writable = await fileHandle.createWritable()
|
||||||
|
|
||||||
await writable.write(contents)
|
await writable.write(contents)
|
||||||
await writable.close()
|
await writable.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFile(fileHandle, blob).then(() => console.log('FILE SAVED'))
|
writeFile(fileHandle, blob).then(() => console.log('FILE SAVED'))
|
||||||
|
|
||||||
let snack4string = get('settings.snack4')
|
let snack4string = get('settings.snack4')
|
||||||
|
|
||||||
snackbar.add({
|
snackbar.add({
|
||||||
labelText: `${snack4string} qortal.nodes`,
|
labelText: `${snack4string} qortal.nodes`,
|
||||||
dismiss: true
|
dismiss: true
|
||||||
@ -613,29 +551,28 @@ class SettingsPage extends connect(store)(LitElement) {
|
|||||||
if (error.name === 'AbortError') {
|
if (error.name === 'AbortError') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSaver.saveAs(blob, fileName)
|
FileSaver.saveAs(blob, fileName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderImportNodesListButton() {
|
renderImportNodesListButton() {
|
||||||
return html`
|
return html`
|
||||||
<mwc-button
|
<mwc-button dense unelevated label="${translate('settings.import')}" @click="${() => this.openImportNodesDialog()}"></mwc-button>
|
||||||
dense
|
|
||||||
unelevated
|
|
||||||
label="${translate('settings.import')}"
|
|
||||||
@click="${() => this.openImportNodesDialog()}"
|
|
||||||
>
|
|
||||||
</mwc-button>
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
async importQortalNodesList(file) {
|
async importQortalNodesList(file) {
|
||||||
localStorage.removeItem('myQortalNodes')
|
localStorage.removeItem('myQortalNodes')
|
||||||
|
|
||||||
const newItems = JSON.parse(file || '[]')
|
const newItems = JSON.parse(file || '[]')
|
||||||
|
|
||||||
localStorage.setItem('myQortalNodes', JSON.stringify(newItems))
|
localStorage.setItem('myQortalNodes', JSON.stringify(newItems))
|
||||||
|
|
||||||
this.shadowRoot.querySelector('#importQortalNodesListDialog').close()
|
this.shadowRoot.querySelector('#importQortalNodesListDialog').close()
|
||||||
|
|
||||||
let snack5string = get('settings.snack5')
|
let snack5string = get('settings.snack5')
|
||||||
|
|
||||||
snackbar.add({
|
snackbar.add({
|
||||||
labelText: `${snack5string}`,
|
labelText: `${snack5string}`,
|
||||||
dismiss: true,
|
dismiss: true,
|
||||||
@ -657,5 +594,4 @@ window.customElements.define('settings-page', SettingsPage)
|
|||||||
|
|
||||||
const settings = document.createElement('settings-page')
|
const settings = document.createElement('settings-page')
|
||||||
settingsDialog = document.body.appendChild(settings)
|
settingsDialog = document.body.appendChild(settings)
|
||||||
|
|
||||||
export default settingsDialog
|
export default settingsDialog
|
@ -1,157 +0,0 @@
|
|||||||
import {css} from 'lit'
|
|
||||||
|
|
||||||
export const sideMenuItemStyle = css`
|
|
||||||
:host {
|
|
||||||
--font-family: "Roboto", sans-serif;
|
|
||||||
--item-font-size: 0.9375rem;
|
|
||||||
--sub-item-font-size: 0.75rem;
|
|
||||||
--item-padding: 0.875rem;
|
|
||||||
--item-content-padding: 0.875rem;
|
|
||||||
--icon-height: 1.125rem;
|
|
||||||
--icon-width: 1.125rem;
|
|
||||||
--item-border-radius: 5px;
|
|
||||||
--item-selected-color: #dddddd;
|
|
||||||
--item-selected-color-text: #333333;
|
|
||||||
--item-color-active: #d1d1d1;
|
|
||||||
--item-color-hover: #eeeeee;
|
|
||||||
--item-text-color: #080808;
|
|
||||||
--item-icon-color: #080808;
|
|
||||||
--item-border-color: #eeeeee;
|
|
||||||
--item-border-selected-color: #333333;
|
|
||||||
|
|
||||||
--overlay-box-shadow: 0 2px 4px -1px hsla(214, 53%, 23%, 0.16), 0 3px 12px -1px hsla(214, 50%, 22%, 0.26);
|
|
||||||
--overlay-background-color: #ffffff;
|
|
||||||
|
|
||||||
--spacing: 4px;
|
|
||||||
|
|
||||||
font-family: var(--font-family);
|
|
||||||
display: flex;
|
|
||||||
overflow: hidden;
|
|
||||||
flex-direction: column;
|
|
||||||
border-radius: var(--item-border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
#itemLink {
|
|
||||||
align-items: center;
|
|
||||||
font-size: var(--item-font-size);
|
|
||||||
font-weight: 400;
|
|
||||||
height: var(--icon-height);
|
|
||||||
transition: background-color 200ms;
|
|
||||||
padding: var(--item-padding);
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-flex;
|
|
||||||
flex-grow: 1;
|
|
||||||
align-items: center;
|
|
||||||
overflow: hidden;
|
|
||||||
text-decoration: none;
|
|
||||||
border-bottom: 1px solid var(--item-border-color);
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
.hideItem {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#itemLink:hover {
|
|
||||||
background-color: var(--item-color-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
#itemLink:active {
|
|
||||||
background-color: var(--item-color-active);
|
|
||||||
}
|
|
||||||
|
|
||||||
#content {
|
|
||||||
padding-left: var(--item-content-padding);
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([compact]) #content {
|
|
||||||
padding-left: 0;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([selected]) #itemLink {
|
|
||||||
background-color: var(--item-selected-color);
|
|
||||||
color: var(--item-selected-color-text);
|
|
||||||
border-left: 3px solid var(--item-border-selected-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([selected]) slot[name="icon"]::slotted(*) {
|
|
||||||
color: var(--item-selected-color-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
:host(:not([selected])) #itemLink{
|
|
||||||
color: var(--item-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([expanded]){
|
|
||||||
background-color: var(--item-selected-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([hasSelectedChild]){
|
|
||||||
background-color: var(--item-selected-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
:host span {
|
|
||||||
cursor: inherit;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
slot[name="icon"]::slotted(*) {
|
|
||||||
flex-shrink: 0;
|
|
||||||
color: var(--item-icon-color);
|
|
||||||
height: var(--icon-height);
|
|
||||||
width: var(--icon-width);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#collapse-button {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([compact]) #itemLink[level]:not([level="0"]) {
|
|
||||||
padding: calc( var(--item-padding) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
:host(:not([compact])) #itemLink[level]:not([level="0"]) {
|
|
||||||
padding-left: calc(var(--icon-width) + var(--item-content-padding));
|
|
||||||
}
|
|
||||||
|
|
||||||
#itemLink[level]:not([level="0"]) #content {
|
|
||||||
display: block;
|
|
||||||
visibility: visible;
|
|
||||||
width: auto;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: var(--sub-item-font-size)
|
|
||||||
}
|
|
||||||
|
|
||||||
#overlay {
|
|
||||||
display: block;
|
|
||||||
left: 101%;
|
|
||||||
min-width: 200px;
|
|
||||||
padding: 4px 2px;
|
|
||||||
background-color: var(--overlay-background-color);
|
|
||||||
background-image: var(--overlay-background-image, none);
|
|
||||||
box-shadow: var(--overlay-box-shadow);
|
|
||||||
border: 1px solid var(--overlay-background-color);
|
|
||||||
border-left: 0;
|
|
||||||
border-radius: 0 3px 3px 0;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
animation: pop 200ms forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pop{
|
|
||||||
0% {
|
|
||||||
transform: translateX(-5px);
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translateX(0);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
@ -1,213 +1,221 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import {ifDefined} from 'lit/directives/if-defined.js'
|
import { ifDefined } from 'lit/directives/if-defined.js'
|
||||||
import {sideMenuItemStyle} from './side-menu-item-style.js'
|
import { sideMenuItemStyles } from '../styles/core-css'
|
||||||
import '@vaadin/icon'
|
import '@vaadin/icon'
|
||||||
import '@vaadin/icons'
|
import '@vaadin/icons'
|
||||||
import '@polymer/paper-tooltip'
|
import '@polymer/paper-tooltip'
|
||||||
|
|
||||||
export class SideMenuItem extends LitElement {
|
export class SideMenuItem extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
selected: { type: Boolean, reflect: true },
|
selected: { type: Boolean, reflect: true },
|
||||||
label: { type: String, reflect: true },
|
label: { type: String, reflect: true },
|
||||||
expanded: { type: Boolean, reflect: true },
|
expanded: { type: Boolean, reflect: true },
|
||||||
compact: { type: Boolean, reflect: true },
|
compact: { type: Boolean, reflect: true },
|
||||||
href: { type: String, reflect: true },
|
href: { type: String, reflect: true },
|
||||||
target: { type: String, reflect: true },
|
target: { type: String, reflect: true },
|
||||||
hide: { type: Boolean }
|
hide: { type: Boolean }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [sideMenuItemStyles]
|
||||||
${sideMenuItemStyle}
|
}
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.selected = false
|
this.selected = false
|
||||||
this.expanded = false
|
this.expanded = false
|
||||||
this.hide = false
|
this.hide = false
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
${this._itemLinkTemplate()} ${this._tooltipTemplate()}
|
${this._itemLinkTemplate()} ${this._tooltipTemplate()}
|
||||||
${this._childrenTemplate()}
|
${this._childrenTemplate()}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated(changedProperties) {
|
firstUpdated(changedProperties) {
|
||||||
if (!this.hasChildren()) {
|
if (!this.hasChildren()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.collapseExpandIcon = document.createElement("vaadin-icon")
|
|
||||||
this.collapseExpandIcon.id = "collapse-button"
|
|
||||||
this.shadowRoot.getElementById("content").appendChild(this.collapseExpandIcon)
|
|
||||||
this._boundOutsideClickListener = this._outsideClickListener.bind(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
_itemLinkTemplate() {
|
this.collapseExpandIcon = document.createElement("vaadin-icon")
|
||||||
return html`
|
this.collapseExpandIcon.id = "collapse-button"
|
||||||
<a id="itemLink"
|
this.shadowRoot.getElementById("content").appendChild(this.collapseExpandIcon)
|
||||||
level=${this._getLevel}
|
this._boundOutsideClickListener = this._outsideClickListener.bind(this)
|
||||||
href=${this.href || '#!'}
|
}
|
||||||
@click="${(e) => this._onClick(e)}"
|
|
||||||
target=${ifDefined(this.target)}
|
|
||||||
class=${this.hide ? 'hideItem' : ''}
|
|
||||||
>
|
|
||||||
<slot class="icon" name="icon"></slot>
|
|
||||||
<div id ="content">
|
|
||||||
<span>${this.label}</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
_tooltipTemplate() {
|
_itemLinkTemplate() {
|
||||||
return html`
|
return html`
|
||||||
${this._getLevel === 0 && this.compact ? html`
|
<a id="itemLink"
|
||||||
<paper-tooltip for="itemLink" position="right" animation-delay="0">
|
level=${this._getLevel}
|
||||||
${this.label}
|
href=${this.href || '#!'}
|
||||||
</paper-tooltip>
|
@click="${(e) => this._onClick(e)}"
|
||||||
`
|
target=${ifDefined(this.target)}
|
||||||
: undefined}
|
class=${this.hide ? 'hideItem' : ''}
|
||||||
`
|
>
|
||||||
}
|
<slot class="icon" name="icon"></slot>
|
||||||
|
<div id ="content">
|
||||||
|
<span>${this.label}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
_childrenTemplate() {
|
_tooltipTemplate() {
|
||||||
return html`
|
return html`
|
||||||
${this.expanded ? html`
|
${this._getLevel === 0 && this.compact ?
|
||||||
${this.compact ? html`
|
html`
|
||||||
<div id="overlay"><slot></slot></div>
|
<paper-tooltip for="itemLink" position="right" animation-delay="0">
|
||||||
`
|
${this.label}
|
||||||
: html`
|
</paper-tooltip>
|
||||||
<slot></slot>
|
` : undefined
|
||||||
`}
|
}
|
||||||
`
|
`
|
||||||
: undefined}
|
}
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
updated(changedProperties) {
|
_childrenTemplate() {
|
||||||
changedProperties.forEach((oldValue, propName) => {
|
return html`
|
||||||
if (propName === "compact") {
|
${this.expanded ?
|
||||||
this._onCompactChanged()
|
html`
|
||||||
}
|
${this.compact ?
|
||||||
|
html`
|
||||||
|
<div id="overlay"><slot></slot></div>
|
||||||
|
` : html`
|
||||||
|
<slot></slot>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
` : undefined
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
if (propName === "expanded") {
|
updated(changedProperties) {
|
||||||
this._onExpandedChanged()
|
changedProperties.forEach((oldValue, propName) => {
|
||||||
}
|
if (propName === "compact") {
|
||||||
|
this._onCompactChanged()
|
||||||
|
}
|
||||||
|
|
||||||
if (propName === "selected"){
|
if (propName === "expanded") {
|
||||||
if (oldValue === this.selected){
|
this._onExpandedChanged()
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (this.selected) {
|
if (propName === "selected") {
|
||||||
this._changeSelectedState(true)
|
if (oldValue === this.selected) {
|
||||||
this._markParentWithSelectedChild()
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_onCompactChanged() {
|
if (this.selected) {
|
||||||
this.expanded = false;
|
this._changeSelectedState(true)
|
||||||
|
this._markParentWithSelectedChild()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (this.collapseExpandIcon == null) {
|
_onCompactChanged() {
|
||||||
return;
|
this.expanded = false
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.compact) {
|
if (this.collapseExpandIcon == null) {
|
||||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-down-small"
|
return
|
||||||
} else {
|
}
|
||||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-right-small"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onExpandedChanged() {
|
if (!this.compact) {
|
||||||
if (this.collapseExpandIcon == null) {
|
this.collapseExpandIcon["icon"] = "vaadin:chevron-down-small"
|
||||||
return;
|
} else {
|
||||||
}
|
this.collapseExpandIcon["icon"] = "vaadin:chevron-right-small"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.expanded) {
|
_onExpandedChanged() {
|
||||||
this._onHandleExpanded();
|
if (this.collapseExpandIcon == null) {
|
||||||
} else {
|
return
|
||||||
this._onHandleCollapsed();
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onHandleExpanded() {
|
if (this.expanded) {
|
||||||
if (!this.compact) {
|
this._onHandleExpanded()
|
||||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-up-small"
|
} else {
|
||||||
} else {
|
this._onHandleCollapsed()
|
||||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-left-small"
|
}
|
||||||
document.addEventListener("click", this._boundOutsideClickListener, true)
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onHandleCollapsed() {
|
_onHandleExpanded() {
|
||||||
if (!this.compact) {
|
if (!this.compact) {
|
||||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-down-small"
|
this.collapseExpandIcon["icon"] = "vaadin:chevron-up-small"
|
||||||
} else {
|
} else {
|
||||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-right-small"
|
this.collapseExpandIcon["icon"] = "vaadin:chevron-left-small"
|
||||||
document.removeEventListener(
|
document.addEventListener("click", this._boundOutsideClickListener, true)
|
||||||
"click",
|
}
|
||||||
this._boundOutsideClickListener,
|
}
|
||||||
true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onClick(e) {
|
_onHandleCollapsed() {
|
||||||
if (!this.hasChildren()) {
|
if (!this.compact) {
|
||||||
this.selected = true
|
this.collapseExpandIcon["icon"] = "vaadin:chevron-down-small"
|
||||||
} else {
|
} else {
|
||||||
this.expanded = !this.expanded
|
this.collapseExpandIcon["icon"] = "vaadin:chevron-right-small"
|
||||||
e.preventDefault()
|
document.removeEventListener(
|
||||||
}
|
"click",
|
||||||
}
|
this._boundOutsideClickListener,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_outsideClickListener(event) {
|
_onClick(e) {
|
||||||
const eventPath = event.composedPath()
|
if (!this.hasChildren()) {
|
||||||
if (eventPath.indexOf(this) < 0) {
|
this.selected = true
|
||||||
this.expanded = false
|
} else {
|
||||||
}
|
this.expanded = !this.expanded
|
||||||
}
|
|
||||||
|
|
||||||
_changeSelectedState(selected, sourceEvent) {
|
e.preventDefault()
|
||||||
this.selected = selected
|
}
|
||||||
let evt = new CustomEvent("side-menu-item-select", {
|
}
|
||||||
bubbles: true,
|
|
||||||
cancelable: true,
|
|
||||||
detail: { sourceEvent: sourceEvent }
|
|
||||||
});
|
|
||||||
this.dispatchEvent(evt)
|
|
||||||
}
|
|
||||||
|
|
||||||
hasChildren() {
|
_outsideClickListener(event) {
|
||||||
return !!this.querySelector("side-menu-item")
|
const eventPath = event.composedPath()
|
||||||
}
|
|
||||||
|
|
||||||
_markParentWithSelectedChild() {
|
if (eventPath.indexOf(this) < 0) {
|
||||||
let element = this.parentElement;
|
this.expanded = false
|
||||||
while (element instanceof SideMenuItem) {
|
}
|
||||||
element.setAttribute('hasSelectedChild', true)
|
}
|
||||||
element = element.parentElement;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get _getLevel() {
|
_changeSelectedState(selected, sourceEvent) {
|
||||||
let level = 0
|
this.selected = selected
|
||||||
let element = this.parentElement
|
|
||||||
while (element instanceof SideMenuItem) {
|
let evt = new CustomEvent("side-menu-item-select", {
|
||||||
level++;
|
bubbles: true,
|
||||||
element = element.parentElement
|
cancelable: true,
|
||||||
}
|
detail: { sourceEvent: sourceEvent }
|
||||||
return level
|
})
|
||||||
}
|
|
||||||
|
this.dispatchEvent(evt)
|
||||||
|
}
|
||||||
|
|
||||||
|
hasChildren() {
|
||||||
|
return !!this.querySelector("side-menu-item")
|
||||||
|
}
|
||||||
|
|
||||||
|
_markParentWithSelectedChild() {
|
||||||
|
let element = this.parentElement;
|
||||||
|
while (element instanceof SideMenuItem) {
|
||||||
|
element.setAttribute('hasSelectedChild', true)
|
||||||
|
element = element.parentElement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get _getLevel() {
|
||||||
|
let level = 0
|
||||||
|
let element = this.parentElement
|
||||||
|
|
||||||
|
while (element instanceof SideMenuItem) {
|
||||||
|
level++
|
||||||
|
element = element.parentElement
|
||||||
|
}
|
||||||
|
|
||||||
|
return level
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define("side-menu-item", SideMenuItem);
|
window.customElements.define("side-menu-item", SideMenuItem)
|
@ -1,78 +1,68 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
|
import { sideMenuStyles } from '../styles/core-css'
|
||||||
|
|
||||||
class SideMenu extends LitElement {
|
class SideMenu extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
items: {type: Array},
|
items: { type: Array },
|
||||||
selectedValue: {type: String, reflect: true},
|
selectedValue: { type: String, reflect: true },
|
||||||
compact: {type: Boolean, reflect: true}
|
compact: { type: Boolean, reflect: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [sideMenuStyles]
|
||||||
nav {
|
}
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host {
|
constructor() {
|
||||||
list-style: none;
|
super()
|
||||||
width: 100%;
|
this.compact = false
|
||||||
position: relative;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
:host([compact]) {
|
render() {
|
||||||
width: auto;
|
return html`
|
||||||
}
|
<nav @side-menu-item-select=${this._handleSelect}>
|
||||||
`
|
<slot></slot>
|
||||||
}
|
</nav>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
firstUpdated(_changedProperties) {
|
||||||
super()
|
this.items = [...this.querySelectorAll("side-menu-item")]
|
||||||
this.compact = false
|
}
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
_handleSelect(event) {
|
||||||
return html`
|
let targetItem = event.target
|
||||||
<nav @side-menu-item-select=${this._handleSelect}>
|
this._deselectAllItems()
|
||||||
<slot></slot>
|
targetItem.selected = true
|
||||||
</nav>
|
this.selectedValue = targetItem.label
|
||||||
`
|
}
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated(_changedProperties) {
|
_deselectAllItems() {
|
||||||
this.items = [...this.querySelectorAll("side-menu-item")]
|
this.items.forEach(element => {
|
||||||
}
|
if (this.compact) {
|
||||||
|
element.expanded = false
|
||||||
|
}
|
||||||
|
|
||||||
_handleSelect(event) {
|
element.selected = false
|
||||||
let targetItem = event.target
|
element.hasChildren() ? element.removeAttribute('hasSelectedChild') : undefined
|
||||||
this._deselectAllItems()
|
})
|
||||||
targetItem.selected = true
|
}
|
||||||
this.selectedValue = targetItem.label
|
|
||||||
}
|
|
||||||
|
|
||||||
_deselectAllItems() {
|
updated(changedProperties) {
|
||||||
this.items.forEach(element => {
|
changedProperties.forEach((oldValue, propName) => {
|
||||||
if (this.compact) {
|
if (propName === "compact") {
|
||||||
element.expanded = false
|
this.items.forEach(item => (item.compact = this.compact))
|
||||||
}
|
|
||||||
element.selected = false
|
|
||||||
element.hasChildren() ? element.removeAttribute('hasSelectedChild') : undefined
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updated(changedProperties) {
|
let evt = new CustomEvent("side-menu-compact-change", {
|
||||||
changedProperties.forEach((oldValue, propName) => {
|
bubbles: true,
|
||||||
if (propName === "compact") {
|
cancelable: true
|
||||||
this.items.forEach(item => (item.compact = this.compact))
|
})
|
||||||
let evt = new CustomEvent("side-menu-compact-change", {
|
|
||||||
bubbles: true,
|
this.dispatchEvent(evt)
|
||||||
cancelable: true
|
}
|
||||||
})
|
})
|
||||||
this.dispatchEvent(evt)
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define("side-menu", SideMenu);
|
window.customElements.define("side-menu", SideMenu)
|
@ -1,78 +1,66 @@
|
|||||||
import {css, html, LitElement} from 'lit'
|
import { html, LitElement } from 'lit'
|
||||||
import '@material/mwc-snackbar'
|
import '@material/mwc-snackbar'
|
||||||
|
|
||||||
let queueElement
|
let queueElement
|
||||||
|
|
||||||
class SnackQueue extends LitElement {
|
class SnackQueue extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
busy: {
|
busy: { type: Boolean, attribute: 'busy', reflectToAttribute: true },
|
||||||
type: Boolean,
|
currentSnack: { type: Object, attribute: 'current-snack', reflectToAttribute: true },
|
||||||
attribute: 'busy',
|
_queue: { type: Array },
|
||||||
reflectToAttribute: true
|
_labelText: { type: String },
|
||||||
},
|
_stacked: { type: Boolean },
|
||||||
currentSnack: {
|
_leading: { type: Boolean },
|
||||||
type: Object,
|
_closeOnEscape: { type: Boolean },
|
||||||
attribute: 'current-snack',
|
_timeoutMs: { type: Number },
|
||||||
reflectToAttribute: true
|
_dismiss: {},
|
||||||
},
|
_dismissIcon: { type: String },
|
||||||
_queue: {
|
action: {}
|
||||||
type: Array
|
}
|
||||||
},
|
}
|
||||||
_labelText: { type: String },
|
|
||||||
_stacked: { type: Boolean },
|
|
||||||
_leading: { type: Boolean },
|
|
||||||
_closeOnEscape: { type: Boolean },
|
|
||||||
_timeoutMs: { type: Number },
|
|
||||||
action: {},
|
|
||||||
_dismiss: {},
|
|
||||||
_dismissIcon: { type: String }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
constructor() {
|
||||||
return css``
|
super()
|
||||||
}
|
this._queue = []
|
||||||
|
this.busy = false
|
||||||
|
this._timeoutMs = 5000
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
render() {
|
||||||
super()
|
return html`
|
||||||
this._queue = []
|
<mwc-snackbar id="snack" labelText="${this._labelText}" ?stacked=${this._stacked} ?leading=${this._leading} ?closeOnEscape=${this._closeOnEscape} timeoutMs=${this._timeoutMs}>
|
||||||
this.busy = false
|
${this._action}
|
||||||
this._timeoutMs = 5000
|
${this._dismiss ?
|
||||||
}
|
html`
|
||||||
|
<mwc-icon-button icon="${this._dismissIcon}" slot="dismiss"></mwc-icon-button>
|
||||||
|
` : ''
|
||||||
|
}
|
||||||
|
</mwc-snackbar>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
firstUpdated() {
|
||||||
return html`
|
this._snackbar = this.shadowRoot.getElementById('snack')
|
||||||
<mwc-snackbar id="snack" labelText="${this._labelText}" ?stacked=${this._stacked} ?leading=${this._leading} ?closeOnEscape=${this._closeOnEscape} timeoutMs=${this._timeoutMs}>
|
}
|
||||||
${this._action}
|
|
||||||
${this._dismiss ? html`
|
|
||||||
<mwc-icon-button icon="${this._dismissIcon}" slot="dismiss"></mwc-icon-button>
|
|
||||||
` : ''}
|
|
||||||
</mwc-snackbar>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated() {
|
_shift() {
|
||||||
this._snackbar = this.shadowRoot.getElementById('snack')
|
if (this.busy || this._queue.length === 0) return
|
||||||
}
|
const item = this._queue.shift()
|
||||||
|
this._labelText = item.labelText || ''
|
||||||
|
this._action = item.action || ''
|
||||||
|
this._dismiss = item.dismiss || false
|
||||||
|
this._dismissIcon = item.dismissIcon || 'close'
|
||||||
|
this._leading = !!item.leading
|
||||||
|
this._closeOnEscape = (item.closeOnEscape && item.closeOnEscape !== false) // JSON.parse maybe needs to be compared to 'false'...in which case no need for complex expression
|
||||||
|
this._timeoutMs = (item.timeoutMs >= 4000 && item.timeoutMs <= 10000) ? item.timeoutMs : 5000
|
||||||
|
this._snackbar.show()
|
||||||
|
}
|
||||||
|
|
||||||
_shift() {
|
add(item) {
|
||||||
if (this.busy || this._queue.length === 0) return
|
this._queue.push(item)
|
||||||
const item = this._queue.shift()
|
this._shift()
|
||||||
this._labelText = item.labelText || ''
|
}
|
||||||
this._action = item.action || ''
|
|
||||||
this._dismiss = item.dismiss || false
|
|
||||||
this._dismissIcon = item.dismissIcon || 'close'
|
|
||||||
this._leading = !!item.leading
|
|
||||||
this._closeOnEscape = (item.closeOnEscape && item.closeOnEscape !== false) // JSON.parse maybe needs to be compared to 'false'...in which case no need for complex expression
|
|
||||||
this._timeoutMs = (item.timeoutMs >= 4000 && item.timeoutMs <= 10000) ? item.timeoutMs : 5000
|
|
||||||
this._snackbar.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
add(item) {
|
|
||||||
this._queue.push(item)
|
|
||||||
this._shift()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('snack-queue', SnackQueue)
|
window.customElements.define('snack-queue', SnackQueue)
|
||||||
@ -82,9 +70,9 @@ queueNode.id = 'queue-node'
|
|||||||
queueNode.loadingMessage = ''
|
queueNode.loadingMessage = ''
|
||||||
queueElement = document.body.appendChild(queueNode)
|
queueElement = document.body.appendChild(queueNode)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
queueElement = document.getElementById('queue-node')
|
queueElement = document.getElementById('queue-node')
|
||||||
const mainApp = document.getElementById('main-app')
|
const mainApp = document.getElementById('main-app')
|
||||||
const shadow = mainApp.shadowRoot
|
const shadow = mainApp.shadowRoot
|
||||||
queueElement = shadow.appendChild(queueElement)
|
queueElement = shadow.appendChild(queueElement)
|
||||||
}, 500)
|
}, 500)
|
||||||
export default queueElement
|
export default queueElement
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Sourced from https://gist.github.com/letsgetrandy/1e05a68ea74ba6736eb5
|
|
||||||
|
|
||||||
export const EXCEPTIONS = {
|
export const EXCEPTIONS = {
|
||||||
'are': 'were',
|
'are': 'were',
|
||||||
'eat': 'ate',
|
'eat': 'ate',
|
||||||
@ -16,21 +14,27 @@ export const getPastTense = (verb, exceptions = EXCEPTIONS) => {
|
|||||||
if (exceptions[verb]) {
|
if (exceptions[verb]) {
|
||||||
return exceptions[verb]
|
return exceptions[verb]
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((/e$/i).test(verb)) {
|
if ((/e$/i).test(verb)) {
|
||||||
return verb + 'd'
|
return verb + 'd'
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((/[aeiou]c$/i).test(verb)) {
|
if ((/[aeiou]c$/i).test(verb)) {
|
||||||
return verb + 'ked'
|
return verb + 'ked'
|
||||||
}
|
}
|
||||||
|
|
||||||
// for american english only
|
// for american english only
|
||||||
if ((/el$/i).test(verb)) {
|
if ((/el$/i).test(verb)) {
|
||||||
return verb + 'ed'
|
return verb + 'ed'
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((/[aeio][aeiou][dlmnprst]$/).test(verb)) {
|
if ((/[aeio][aeiou][dlmnprst]$/).test(verb)) {
|
||||||
return verb + 'ed'
|
return verb + 'ed'
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((/[aeiou][bdglmnprst]$/i).test(verb)) {
|
if ((/[aeiou][bdglmnprst]$/i).test(verb)) {
|
||||||
return verb.replace(/(.+[aeiou])([bdglmnprst])/, '$1$2$2ed')
|
return verb.replace(/(.+[aeiou])([bdglmnprst])/, '$1$2$2ed')
|
||||||
}
|
}
|
||||||
|
|
||||||
return verb + 'ed'
|
return verb + 'ed'
|
||||||
}
|
}
|
File diff suppressed because one or more lines are too long
@ -1,41 +1,39 @@
|
|||||||
import {store} from './store.js'
|
import { store } from './store'
|
||||||
import {doLoadConfigFromAPI} from './redux/config/config-actions.js'
|
import { doLoadConfigFromAPI } from './redux/config/config-actions'
|
||||||
import {doInitWorkers, doLoadNodeConfig} from './redux/app/app-actions.js'
|
import { doInitWorkers, doLoadNodeConfig } from './redux/app/app-actions'
|
||||||
import {doLoadNotificationConfig} from './redux/user/user-actions.js'
|
import { doLoadNotificationConfig } from './redux/user/user-actions'
|
||||||
|
import { initApi } from 'qortal-ui-crypto'
|
||||||
import './persistState.js'
|
import './persistState'
|
||||||
|
|
||||||
import {initApi} from 'qortal-ui-crypto'
|
|
||||||
|
|
||||||
initApi(store)
|
initApi(store)
|
||||||
|
|
||||||
const workerInitChecker = () => {
|
const workerInitChecker = () => {
|
||||||
const state = store.getState()
|
const state = store.getState()
|
||||||
|
|
||||||
if (store.getState().app.nodeConfig.knownNodes.length === 0) {
|
if (store.getState().app.nodeConfig.knownNodes.length === 0) {
|
||||||
store.dispatch(doLoadNodeConfig())
|
store.dispatch(doLoadNodeConfig())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.config.loaded) {
|
if (state.config.loaded) {
|
||||||
store.dispatch(doLoadNodeConfig())
|
store.dispatch(doLoadNodeConfig())
|
||||||
|
|
||||||
if (state.app.workers.ready) {
|
if (state.app.workers.ready) {
|
||||||
|
workerInitSubscription()
|
||||||
workerInitSubscription()
|
} else {
|
||||||
} else {
|
if (!state.app.workers.loading) store.dispatch(doInitWorkers(state.config.crypto.kdfThreads, state.config.user.constants.workerURL))
|
||||||
|
}
|
||||||
if (!state.app.workers.loading) store.dispatch(doInitWorkers(state.config.crypto.kdfThreads, state.config.user.constants.workerURL))
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
workerInitChecker()
|
workerInitChecker()
|
||||||
|
|
||||||
const workerInitSubscription = store.subscribe(workerInitChecker)
|
const workerInitSubscription = store.subscribe(workerInitChecker)
|
||||||
|
|
||||||
if (!store.getState().config.loaded) {
|
if (!store.getState().config.loaded) {
|
||||||
store.dispatch(doLoadConfigFromAPI())
|
store.dispatch(doLoadConfigFromAPI())
|
||||||
store.dispatch(doLoadNodeConfig())
|
store.dispatch(doLoadNodeConfig())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!store.getState().user.loaded) {
|
if (!store.getState().user.loaded) {
|
||||||
store.dispatch(doLoadNotificationConfig())
|
store.dispatch(doLoadNotificationConfig())
|
||||||
}
|
}
|
@ -1,19 +1,19 @@
|
|||||||
export const loadStateFromLocalStorage = (key) => {
|
export const loadStateFromLocalStorage = (key) => {
|
||||||
try {
|
try {
|
||||||
const config = localStorage.getItem(key)
|
const config = localStorage.getItem(key)
|
||||||
if (config === null) return void 0
|
if (config === null) return void 0
|
||||||
return JSON.parse(config)
|
return JSON.parse(config)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Could add error handling in case there's a weird one...don't want to overwrite config if it's malfunctioned
|
// Could add error handling in case there's a weird one...don't want to overwrite config if it's malfunctioned
|
||||||
return void 0
|
return void 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const saveStateToLocalStorage = (key, state) => {
|
export const saveStateToLocalStorage = (key, state) => {
|
||||||
try {
|
try {
|
||||||
const stateJSON = JSON.stringify(state)
|
const stateJSON = JSON.stringify(state)
|
||||||
localStorage.setItem(key, stateJSON)
|
localStorage.setItem(key, stateJSON)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e, 'e')
|
console.error(e, 'e')
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,11 +1,13 @@
|
|||||||
import CryptoJS from 'crypto-js'
|
import CryptoJS from 'crypto-js'
|
||||||
|
|
||||||
export const encryptData = (data, salt) => CryptoJS.AES.encrypt(JSON.stringify(data), salt).toString()
|
export const encryptData = (data, salt) => CryptoJS.AES.encrypt(JSON.stringify(data), salt).toString()
|
||||||
|
|
||||||
export const decryptData = (ciphertext, salt) => {
|
export const decryptData = (ciphertext, salt) => {
|
||||||
const bytes = CryptoJS.AES.decrypt(ciphertext, salt)
|
const bytes = CryptoJS.AES.decrypt(ciphertext, salt)
|
||||||
try {
|
|
||||||
return JSON.parse(bytes.toString(CryptoJS.enc.Utf8))
|
try {
|
||||||
} catch(err) {
|
return JSON.parse(bytes.toString(CryptoJS.enc.Utf8))
|
||||||
return null
|
} catch (err) {
|
||||||
}
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,2 +1,2 @@
|
|||||||
import './initStore.js'
|
import './initStore'
|
||||||
import './components/main-app.js'
|
import './components/main-app'
|
@ -1,12 +1,12 @@
|
|||||||
const config = {
|
const config = {
|
||||||
default: {
|
default: {
|
||||||
title: 'Qortal',
|
title: 'Qortal',
|
||||||
body: 'Qortal Notifications',
|
body: 'Qortal Notifications',
|
||||||
icon: '/img/favicon/favicon-96x96.png'
|
icon: '/img/favicon/favicon-96x96.png'
|
||||||
},
|
},
|
||||||
newMessageAudio: '/sound/newmessage.ogg',
|
newMessageAudio: '/sound/newmessage.ogg',
|
||||||
messageAlert: '/sound/messageAlert.ogg',
|
messageAlert: '/sound/messageAlert.ogg',
|
||||||
blockAlert: '/sound/blockAlert.ogg'
|
blockAlert: '/sound/blockAlert.ogg'
|
||||||
}
|
}
|
||||||
|
|
||||||
export default config
|
export default config
|
@ -1,39 +1,40 @@
|
|||||||
|
import { dispatcher } from './dispatcher'
|
||||||
|
import { NEW_MESSAGE, NEW_MESSAGE_NOTIFICATION_QAPP, NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL } from './types'
|
||||||
import config from './config'
|
import config from './config'
|
||||||
import {dispatcher} from './dispatcher'
|
import snackbar from '../functional-components/snackbar'
|
||||||
import snackbar from '../functional-components/snackbar.js'
|
|
||||||
import {NEW_MESSAGE, NEW_MESSAGE_NOTIFICATION_QAPP, NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL} from './types'
|
|
||||||
|
|
||||||
let initial = 0
|
let initial = 0
|
||||||
let _state
|
let _state
|
||||||
|
|
||||||
const notificationCheck = function () {
|
const notificationCheck = function () {
|
||||||
if (window.Notification && Notification.permission === 'granted') {
|
if (window.Notification && Notification.permission === 'granted') {
|
||||||
// ...
|
return true
|
||||||
return true
|
} else if (window.Notification && Notification.permission !== 'denied') {
|
||||||
} else if (window.Notification && Notification.permission !== 'denied') {
|
Notification.requestPermission().then(permission => {
|
||||||
Notification.requestPermission().then(permission => {
|
if (permission === 'granted') {
|
||||||
if (permission === 'granted') {
|
dispatcher(_state)
|
||||||
dispatcher(_state)
|
_state = ''
|
||||||
_state = ''
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
initial = initial + 1
|
|
||||||
snackbar.add({
|
|
||||||
labelText: 'Notification is disabled, Enable it to recieve notifications.',
|
|
||||||
dismiss: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
if ([1, 3, 5, 7, 9, 11, 13, 15].includes(initial)) {
|
|
||||||
snackbar.add({
|
|
||||||
labelText: 'Notification is disabled in this browser, Enable it to recieve notifications.',
|
|
||||||
dismiss: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
initial = initial + 1
|
return true
|
||||||
}
|
} else {
|
||||||
|
initial = initial + 1
|
||||||
|
|
||||||
|
snackbar.add({
|
||||||
|
labelText: 'Notification is disabled, Enable it to recieve notifications.',
|
||||||
|
dismiss: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if ([1, 3, 5, 7, 9, 11, 13, 15].includes(initial)) {
|
||||||
|
snackbar.add({
|
||||||
|
labelText: 'Notification is disabled in this browser, Enable it to recieve notifications.',
|
||||||
|
dismiss: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
initial = initial + 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,52 +42,53 @@ const notificationCheck = function () {
|
|||||||
* @property notificationState = { type: NEW_MESSAGE, data }
|
* @property notificationState = { type: NEW_MESSAGE, data }
|
||||||
* @property data = { title: 'Qortal Chat', sound: config.messageAlert, options: { body: 'New Message', icon: config.default.icon, badge: config.default.icon }, req }
|
* @property data = { title: 'Qortal Chat', sound: config.messageAlert, options: { body: 'New Message', icon: config.default.icon, badge: config.default.icon }, req }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const doNewMessage = function (req) {
|
export const doNewMessage = function (req) {
|
||||||
const newMessage = () => {
|
const newMessage = () => {
|
||||||
|
let data
|
||||||
|
|
||||||
let data
|
if (req.type && req.type === 'qapp') {
|
||||||
if (req.type && req.type === 'qapp') {
|
data = req
|
||||||
data = req
|
} else if (req.groupId) {
|
||||||
|
const title = `${req.groupName}`
|
||||||
|
const body = `New Message from ${req.senderName === undefined ? req.sender : req.senderName}`
|
||||||
|
|
||||||
} else if (req.groupId) {
|
data = { title, sound: config.messageAlert, options: { body, icon: config.default.icon, badge: config.default.icon }, req }
|
||||||
const title = `${req.groupName}`
|
} else {
|
||||||
const body = `New Message from ${req.senderName === undefined ? req.sender : req.senderName}`
|
const title = `${req.senderName === undefined ? req.sender : req.senderName}`
|
||||||
data = { title, sound: config.messageAlert, options: { body, icon: config.default.icon, badge: config.default.icon }, req }
|
const body = 'New Message'
|
||||||
} else {
|
|
||||||
const title = `${req.senderName === undefined ? req.sender : req.senderName}`
|
|
||||||
const body = 'New Message'
|
|
||||||
data = { title, sound: config.messageAlert, options: { body, icon: config.default.icon, badge: config.default.icon }, req }
|
|
||||||
}
|
|
||||||
|
|
||||||
const notificationState = { type: NEW_MESSAGE, data: data }
|
data = { title, sound: config.messageAlert, options: { body, icon: config.default.icon, badge: config.default.icon }, req }
|
||||||
const notificationStateQapp = { type: NEW_MESSAGE_NOTIFICATION_QAPP, data: data }
|
}
|
||||||
const canI = notificationCheck()
|
|
||||||
|
|
||||||
if (canI === true) {
|
const notificationState = { type: NEW_MESSAGE, data: data }
|
||||||
if (req.type && req.type === 'qapp') {
|
const notificationStateQapp = { type: NEW_MESSAGE_NOTIFICATION_QAPP, data: data }
|
||||||
dispatcher(notificationStateQapp)
|
const canI = notificationCheck()
|
||||||
} else {
|
|
||||||
dispatcher(notificationState)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
if (canI === true) {
|
||||||
_state = notificationState
|
if (req.type && req.type === 'qapp') {
|
||||||
}
|
dispatcher(notificationStateQapp)
|
||||||
}
|
} else {
|
||||||
const page = window.top.location.href
|
dispatcher(notificationState)
|
||||||
if(req.type && req.type === 'qapp-local-notification'){
|
}
|
||||||
try {
|
} else {
|
||||||
dispatcher({ type: NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL, data: req })
|
_state = notificationState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch (error) {
|
const page = window.top.location.href
|
||||||
console.log('error', error)
|
|
||||||
}
|
if (req.type && req.type === 'qapp-local-notification') {
|
||||||
}else if (!document.hasFocus()) {
|
try {
|
||||||
newMessage()
|
dispatcher({ type: NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL, data: req })
|
||||||
} else {
|
|
||||||
if (page.includes(req.url) === false) {
|
} catch (error) {
|
||||||
newMessage()
|
console.log('error', error)
|
||||||
}
|
}
|
||||||
}
|
} else if (!document.hasFocus()) {
|
||||||
|
newMessage()
|
||||||
|
} else {
|
||||||
|
if (page.includes(req.url) === false) {
|
||||||
|
newMessage()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,14 +2,16 @@ import {NEW_MESSAGE, NEW_MESSAGE_NOTIFICATION_QAPP, NEW_MESSAGE_NOTIFICATION_QAP
|
|||||||
import {newMessage, newMessageNotificationQapp, newMessageNotificationQappLocal} from './notification-actions'
|
import {newMessage, newMessageNotificationQapp, newMessageNotificationQappLocal} from './notification-actions'
|
||||||
|
|
||||||
export const dispatcher = function (notificationState) {
|
export const dispatcher = function (notificationState) {
|
||||||
|
switch (notificationState.type) {
|
||||||
|
case NEW_MESSAGE:
|
||||||
|
return newMessage(notificationState.data)
|
||||||
|
|
||||||
switch (notificationState.type) {
|
case NEW_MESSAGE_NOTIFICATION_QAPP:
|
||||||
case NEW_MESSAGE:
|
return newMessageNotificationQapp(notificationState.data)
|
||||||
return newMessage(notificationState.data)
|
|
||||||
case NEW_MESSAGE_NOTIFICATION_QAPP:
|
case NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL:
|
||||||
return newMessageNotificationQapp(notificationState.data)
|
return newMessageNotificationQappLocal(notificationState.data)
|
||||||
case NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL:
|
|
||||||
return newMessageNotificationQappLocal(notificationState.data)
|
default:
|
||||||
default:
|
}
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,267 +1,284 @@
|
|||||||
import {store} from '../../store.js'
|
import { store } from '../../store'
|
||||||
import {doPageUrl, setNewTab} from '../../redux/app/app-actions.js'
|
import { doPageUrl, setNewTab } from '../../redux/app/app-actions'
|
||||||
import isElectron from 'is-electron'
|
import isElectron from 'is-electron'
|
||||||
import ShortUniqueId from 'short-unique-id';
|
import ShortUniqueId from 'short-unique-id'
|
||||||
|
|
||||||
const uid = new ShortUniqueId()
|
const uid = new ShortUniqueId()
|
||||||
|
|
||||||
export const newMessage = (data) => {
|
export const newMessage = (data) => {
|
||||||
const alert = playSound(data.sound)
|
const alert = playSound(data.sound)
|
||||||
|
|
||||||
// Should I show notification ?
|
// Should I show notification ?
|
||||||
if (store.getState().user.notifications.q_chat.showNotification) {
|
if (store.getState().user.notifications.q_chat.showNotification) {
|
||||||
|
// Yes, I can but should I play sound ?
|
||||||
|
if (store.getState().user.notifications.q_chat.playSound) {
|
||||||
|
const notify = new Notification(data.title, data.options)
|
||||||
|
|
||||||
// Yes, I can but should I play sound ?
|
notify.onshow = (e) => {
|
||||||
if (store.getState().user.notifications.q_chat.playSound) {
|
alert.play()
|
||||||
|
}
|
||||||
|
|
||||||
const notify = new Notification(data.title, data.options)
|
notify.onclick = (e) => {
|
||||||
|
const pageUrl = `/app/q-chat/?chat=${data.req.url}`
|
||||||
notify.onshow = (e) => {
|
store.dispatch(doPageUrl(pageUrl))
|
||||||
alert.play()
|
}
|
||||||
}
|
} else {
|
||||||
|
const notify = new Notification(data.title, data.options)
|
||||||
notify.onclick = (e) => {
|
|
||||||
const pageUrl = `/app/q-chat/?chat=${data.req.url}`
|
|
||||||
store.dispatch(doPageUrl(pageUrl))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
const notify = new Notification(data.title, data.options)
|
|
||||||
|
|
||||||
notify.onclick = (e) => {
|
|
||||||
const pageUrl = `/app/q-chat/?chat=${data.req.url}`
|
|
||||||
store.dispatch(doPageUrl(pageUrl))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If sounds are enabled, but notifications are not
|
|
||||||
} else if (store.getState().user.notifications.q_chat.playSound) {
|
|
||||||
alert.play()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
notify.onclick = (e) => {
|
||||||
|
const pageUrl = `/app/q-chat/?chat=${data.req.url}`
|
||||||
|
store.dispatch(doPageUrl(pageUrl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If sounds are enabled, but notifications are not
|
||||||
|
} else if (store.getState().user.notifications.q_chat.playSound) {
|
||||||
|
alert.play()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const newMessageNotificationQapp = (data) => {
|
export const newMessageNotificationQapp = (data) => {
|
||||||
|
const alert = playSound(data.sound)
|
||||||
|
|
||||||
const alert = playSound(data.sound)
|
// Should I show notification ?
|
||||||
|
if (store.getState().user.notifications.q_chat.showNotification) {
|
||||||
|
// Yes, I can but should I play sound ?
|
||||||
|
if (store.getState().user.notifications.q_chat.playSound) {
|
||||||
|
const notify = new Notification(data.title, data.options)
|
||||||
|
|
||||||
// Should I show notification ?
|
notify.onshow = (e) => {
|
||||||
if (store.getState().user.notifications.q_chat.showNotification) {
|
alert.play()
|
||||||
|
}
|
||||||
|
|
||||||
// Yes, I can but should I play sound ?
|
notify.onclick = (e) => {
|
||||||
if (store.getState().user.notifications.q_chat.playSound) {
|
const query = `?service=APP&name=Q-Mail`
|
||||||
|
|
||||||
const notify = new Notification(data.title, data.options)
|
store.dispatch(setNewTab({
|
||||||
|
url: `qdn/browser/index.html${query}`,
|
||||||
|
id: 'q-mail-notification',
|
||||||
|
myPlugObj: {
|
||||||
|
"url": "qapps",
|
||||||
|
"domain": "core",
|
||||||
|
"page": `qdn/browser/index.html${query}`,
|
||||||
|
"title": "Q-Mail",
|
||||||
|
"icon": "vaadin:desktop",
|
||||||
|
"menus": [],
|
||||||
|
"parent": false
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
notify.onshow = (e) => {
|
if (!isElectron()) {
|
||||||
alert.play()
|
window.focus();
|
||||||
}
|
} else {
|
||||||
|
window.electronAPI.focusApp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const notify = new Notification(data.title, data.options)
|
||||||
|
|
||||||
notify.onclick = (e) => {
|
notify.onclick = (e) => {
|
||||||
const query = `?service=APP&name=Q-Mail`
|
const query = `?service=APP&name=Q-Mail`
|
||||||
|
|
||||||
store.dispatch(setNewTab({
|
store.dispatch(setNewTab({
|
||||||
url: `qdn/browser/index.html${query}`,
|
url: `qdn/browser/index.html${query}`,
|
||||||
id: 'q-mail-notification',
|
id: 'q-mail-notification',
|
||||||
myPlugObj: {
|
myPlugObj: {
|
||||||
"url": "qapps",
|
"url": "qapps",
|
||||||
"domain": "core",
|
"domain": "core",
|
||||||
"page": `qdn/browser/index.html${query}`,
|
"page": `qdn/browser/index.html${query}`,
|
||||||
"title": "Q-Mail",
|
"title": "Q-Mail",
|
||||||
"icon": "vaadin:desktop",
|
"icon": "vaadin:desktop",
|
||||||
"menus": [],
|
"menus": [],
|
||||||
"parent": false
|
"parent": false
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
if (!isElectron()) {
|
|
||||||
window.focus();
|
|
||||||
} else {
|
|
||||||
window.electronAPI.focusApp()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
const notify = new Notification(data.title, data.options)
|
|
||||||
|
|
||||||
notify.onclick = (e) => {
|
|
||||||
const query = `?service=APP&name=Q-Mail`
|
|
||||||
|
|
||||||
store.dispatch(setNewTab({
|
|
||||||
url: `qdn/browser/index.html${query}`,
|
|
||||||
id: 'q-mail-notification',
|
|
||||||
myPlugObj: {
|
|
||||||
"url": "qapps",
|
|
||||||
"domain": "core",
|
|
||||||
"page": `qdn/browser/index.html${query}`,
|
|
||||||
"title": "Q-Mail",
|
|
||||||
"icon": "vaadin:desktop",
|
|
||||||
"menus": [],
|
|
||||||
"parent": false
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
if (!isElectron()) {
|
|
||||||
window.focus();
|
|
||||||
} else {
|
|
||||||
window.electronAPI.focusApp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If sounds are enabled, but notifications are not
|
|
||||||
} else if (store.getState().user.notifications.q_chat.playSound) {
|
|
||||||
alert.play()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!isElectron()) {
|
||||||
|
window.focus();
|
||||||
|
} else {
|
||||||
|
window.electronAPI.focusApp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If sounds are enabled, but notifications are not
|
||||||
|
} else if (store.getState().user.notifications.q_chat.playSound) {
|
||||||
|
alert.play()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const extractComponents= async (url)=> {
|
const extractComponents = async (url) => {
|
||||||
if (!url.startsWith("qortal://")) {
|
if (!url.startsWith("qortal://")) {
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
url = url.replace(/^(qortal\:\/\/)/, "");
|
url = url.replace(/^(qortal\:\/\/)/, "")
|
||||||
if (url.includes("/")) {
|
|
||||||
let parts = url.split("/");
|
|
||||||
const service = parts[0].toUpperCase();
|
|
||||||
parts.shift();
|
|
||||||
const name = parts[0];
|
|
||||||
parts.shift();
|
|
||||||
let identifier;
|
|
||||||
|
|
||||||
if (parts.length > 0) {
|
if (url.includes("/")) {
|
||||||
identifier = parts[0]; // Do not shift yet
|
let parts = url.split("/")
|
||||||
// Check if a resource exists with this service, name and identifier combination
|
|
||||||
let responseObj = await parentEpml.request('apiCall', {
|
|
||||||
url: `/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${this.getApiKey()}`
|
|
||||||
})
|
|
||||||
|
|
||||||
if (responseObj.totalChunkCount > 0) {
|
const service = parts[0].toUpperCase()
|
||||||
// Identifier exists, so don't include it in the path
|
|
||||||
parts.shift();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
identifier = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const path = parts.join("/");
|
parts.shift()
|
||||||
|
|
||||||
const components = {};
|
const name = parts[0]
|
||||||
components["service"] = service;
|
|
||||||
components["name"] = name;
|
|
||||||
components["identifier"] = identifier;
|
|
||||||
components["path"] = path;
|
|
||||||
return components;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
parts.shift()
|
||||||
|
|
||||||
|
let identifier
|
||||||
|
|
||||||
|
if (parts.length > 0) {
|
||||||
|
// Do not shift yet
|
||||||
|
identifier = parts[0]
|
||||||
|
|
||||||
|
// Check if a resource exists with this service, name and identifier combination
|
||||||
|
let responseObj = await parentEpml.request('apiCall', {
|
||||||
|
url: `/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${this.getApiKey()}`
|
||||||
|
})
|
||||||
|
|
||||||
|
if (responseObj.totalChunkCount > 0) {
|
||||||
|
// Identifier exists, so don't include it in the path
|
||||||
|
parts.shift()
|
||||||
|
} else {
|
||||||
|
identifier = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = parts.join("/")
|
||||||
|
const components = {}
|
||||||
|
|
||||||
|
components["service"] = service
|
||||||
|
components["name"] = name
|
||||||
|
components["identifier"] = identifier
|
||||||
|
components["path"] = path
|
||||||
|
|
||||||
|
return components
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const newMessageNotificationQappLocal = (data) => {
|
export const newMessageNotificationQappLocal = (data) => {
|
||||||
const alert = playSound(data.sound)
|
const alert = playSound(data.sound)
|
||||||
|
|
||||||
// Should I show notification ?
|
// Should I show notification ?
|
||||||
if (store.getState().user.notifications.q_chat.showNotification) {
|
if (store.getState().user.notifications.q_chat.showNotification) {
|
||||||
|
// Yes, I can but should I play sound ?
|
||||||
|
if (store.getState().user.notifications.q_chat.playSound) {
|
||||||
|
const notify = new Notification(data.title, data.options)
|
||||||
|
|
||||||
// Yes, I can but should I play sound ?
|
notify.onshow = (e) => {
|
||||||
if (store.getState().user.notifications.q_chat.playSound) {
|
alert.play()
|
||||||
|
|
||||||
const notify = new Notification(data.title, data.options)
|
|
||||||
|
|
||||||
notify.onshow = (e) => {
|
|
||||||
alert.play()
|
|
||||||
}
|
|
||||||
|
|
||||||
notify.onclick = async(e) => {
|
|
||||||
let newQuery = data?.url;
|
|
||||||
if (newQuery.endsWith('/')) {
|
|
||||||
newQuery = newQuery.slice(0, -1);
|
|
||||||
}
|
}
|
||||||
const res = await extractComponents(newQuery)
|
|
||||||
if (!res) return
|
notify.onclick = async (e) => {
|
||||||
const { service, name, identifier, path } = res
|
let newQuery = data?.url
|
||||||
let query = `?service=${service}`
|
|
||||||
if (name) {
|
if (newQuery.endsWith('/')) {
|
||||||
query = query + `&name=${name}`
|
newQuery = newQuery.slice(0, -1)
|
||||||
}
|
|
||||||
if (identifier) {
|
|
||||||
query = query + `&identifier=${identifier}`
|
|
||||||
}
|
|
||||||
if (path) {
|
|
||||||
query = query + `&path=${path}`
|
|
||||||
}
|
|
||||||
const tab = {
|
|
||||||
url: `qdn/browser/index.html${query}`,
|
|
||||||
id: uid.rnd(),
|
|
||||||
myPlugObj: {
|
|
||||||
"url": service === 'WEBSITE' ? "websites" : "qapps",
|
|
||||||
"domain": "core",
|
|
||||||
"page": `qdn/browser/index.html${query}`,
|
|
||||||
"title": name,
|
|
||||||
"icon": service === 'WEBSITE' ? 'vaadin:desktop' : 'vaadin:external-browser',
|
|
||||||
"mwcicon": service === 'WEBSITE' ? 'desktop_mac' : 'open_in_browser',
|
|
||||||
"menus": [],
|
|
||||||
"parent": false
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
store.dispatch(setNewTab(tab))
|
|
||||||
if (!isElectron()) {
|
|
||||||
window.focus();
|
|
||||||
} else {
|
|
||||||
window.electronAPI.focusApp()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
const res = await extractComponents(newQuery)
|
||||||
} else {
|
|
||||||
|
|
||||||
const notify = new Notification(data.title, data.options)
|
if (!res) return
|
||||||
|
|
||||||
notify.onclick = async(e) => {
|
const { service, name, identifier, path } = res
|
||||||
let newQuery = data?.url;
|
|
||||||
if (newQuery.endsWith('/')) {
|
let query = `?service=${service}`
|
||||||
newQuery = newQuery.slice(0, -1);
|
|
||||||
}
|
if (name) {
|
||||||
const res = await extractComponents(newQuery)
|
query = query + `&name=${name}`
|
||||||
if (!res) return
|
|
||||||
const { service, name, identifier, path } = res
|
|
||||||
let query = `?service=${service}`
|
|
||||||
if (name) {
|
|
||||||
query = query + `&name=${name}`
|
|
||||||
}
|
|
||||||
if (identifier) {
|
|
||||||
query = query + `&identifier=${identifier}`
|
|
||||||
}
|
|
||||||
if (path) {
|
|
||||||
query = query + `&path=${path}`
|
|
||||||
}
|
|
||||||
const tab = {
|
|
||||||
url: `qdn/browser/index.html${query}`,
|
|
||||||
id: uid.rnd(),
|
|
||||||
myPlugObj: {
|
|
||||||
"url": service === 'WEBSITE' ? "websites" : "qapps",
|
|
||||||
"domain": "core",
|
|
||||||
"page": `qdn/browser/index.html${query}`,
|
|
||||||
"title": name,
|
|
||||||
"icon": service === 'WEBSITE' ? 'vaadin:desktop' : 'vaadin:external-browser',
|
|
||||||
"mwcicon": service === 'WEBSITE' ? 'desktop_mac' : 'open_in_browser',
|
|
||||||
"menus": [],
|
|
||||||
"parent": false
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
store.dispatch(setNewTab(tab))
|
|
||||||
if (!isElectron()) {
|
|
||||||
window.focus();
|
|
||||||
} else {
|
|
||||||
window.electronAPI.focusApp()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
if (identifier) {
|
||||||
}
|
query = query + `&identifier=${identifier}`
|
||||||
// If sounds are enabled, but notifications are not
|
}
|
||||||
} else if (store.getState().user.notifications.q_chat.playSound) {
|
|
||||||
alert.play()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
query = query + `&path=${path}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const tab = {
|
||||||
|
url: `qdn/browser/index.html${query}`,
|
||||||
|
id: uid.rnd(),
|
||||||
|
myPlugObj: {
|
||||||
|
"url": service === 'WEBSITE' ? "websites" : "qapps",
|
||||||
|
"domain": "core",
|
||||||
|
"page": `qdn/browser/index.html${query}`,
|
||||||
|
"title": name,
|
||||||
|
"icon": service === 'WEBSITE' ? 'vaadin:desktop' : 'vaadin:external-browser',
|
||||||
|
"mwcicon": service === 'WEBSITE' ? 'desktop_mac' : 'open_in_browser',
|
||||||
|
"menus": [],
|
||||||
|
"parent": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch(setNewTab(tab))
|
||||||
|
|
||||||
|
if (!isElectron()) {
|
||||||
|
window.focus()
|
||||||
|
} else {
|
||||||
|
window.electronAPI.focusApp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const notify = new Notification(data.title, data.options)
|
||||||
|
|
||||||
|
notify.onclick = async (e) => {
|
||||||
|
let newQuery = data?.url
|
||||||
|
|
||||||
|
if (newQuery.endsWith('/')) {
|
||||||
|
newQuery = newQuery.slice(0, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await extractComponents(newQuery)
|
||||||
|
|
||||||
|
if (!res) return
|
||||||
|
|
||||||
|
const { service, name, identifier, path } = res
|
||||||
|
|
||||||
|
let query = `?service=${service}`
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
query = query + `&name=${name}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identifier) {
|
||||||
|
query = query + `&identifier=${identifier}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
query = query + `&path=${path}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const tab = {
|
||||||
|
url: `qdn/browser/index.html${query}`,
|
||||||
|
id: uid.rnd(),
|
||||||
|
myPlugObj: {
|
||||||
|
"url": service === 'WEBSITE' ? "websites" : "qapps",
|
||||||
|
"domain": "core",
|
||||||
|
"page": `qdn/browser/index.html${query}`,
|
||||||
|
"title": name,
|
||||||
|
"icon": service === 'WEBSITE' ? 'vaadin:desktop' : 'vaadin:external-browser',
|
||||||
|
"mwcicon": service === 'WEBSITE' ? 'desktop_mac' : 'open_in_browser',
|
||||||
|
"menus": [],
|
||||||
|
"parent": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch(setNewTab(tab))
|
||||||
|
|
||||||
|
if (!isElectron()) {
|
||||||
|
window.focus()
|
||||||
|
} else {
|
||||||
|
window.electronAPI.focusApp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If sounds are enabled, but notifications are not
|
||||||
|
} else if (store.getState().user.notifications.q_chat.playSound) {
|
||||||
|
alert.play()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const playSound = (soundUrl) => {
|
const playSound = (soundUrl) => {
|
||||||
return new Audio(soundUrl)
|
return new Audio(soundUrl)
|
||||||
}
|
}
|
@ -1,23 +1,20 @@
|
|||||||
import {store} from './store.js'
|
import { store } from './store'
|
||||||
import {saveStateToLocalStorage} from './localStorageHelpers.js'
|
import { saveStateToLocalStorage } from './localStorageHelpers'
|
||||||
|
|
||||||
const keys = [
|
|
||||||
'config',
|
|
||||||
'user'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
const keys = ['config', 'user']
|
||||||
const oldReducers = {}
|
const oldReducers = {}
|
||||||
|
|
||||||
const oldState = store.getState()
|
const oldState = store.getState()
|
||||||
|
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
oldReducers[key] = oldState[key]
|
oldReducers[key] = oldState[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
store.subscribe(() => {
|
store.subscribe(() => {
|
||||||
const newState = store.getState()
|
const newState = store.getState()
|
||||||
keys.forEach(key => {
|
|
||||||
if (newState[key] !== oldState[key]) {
|
keys.forEach(key => {
|
||||||
saveStateToLocalStorage(key, store.getState()[key])
|
if (newState[key] !== oldState[key]) {
|
||||||
}
|
saveStateToLocalStorage(key, store.getState()[key])
|
||||||
})
|
}
|
||||||
|
})
|
||||||
})
|
})
|
@ -1,7 +1,7 @@
|
|||||||
import {routes} from './routes.js'
|
import { routes } from './routes'
|
||||||
|
|
||||||
export const addPluginRoutes = epmlInstance => {
|
export const addPluginRoutes = epmlInstance => {
|
||||||
Object.entries(routes).forEach(([route, handler]) => {
|
Object.entries(routes).forEach(([route, handler]) => {
|
||||||
epmlInstance.route(route, handler)
|
epmlInstance.route(route, handler)
|
||||||
})
|
})
|
||||||
}
|
}
|
@ -1,46 +1,47 @@
|
|||||||
import {store} from '../store.js'
|
import { store } from '../store'
|
||||||
import {Epml} from '../epml.js'
|
import { Epml } from '../epml'
|
||||||
import {addPluginRoutes} from './addPluginRoutes'
|
import { addPluginRoutes } from './addPluginRoutes'
|
||||||
import {doAddPlugin} from '../redux/app/app-actions.js'
|
import { doAddPlugin } from '../redux/app/app-actions'
|
||||||
|
|
||||||
let retryLoadPluginsInterval = 0
|
let retryLoadPluginsInterval = 0
|
||||||
|
|
||||||
export const loadPlugins = () => fetch('/getPlugins')
|
export const loadPlugins = () => fetch('/getPlugins')
|
||||||
.then(response => response.json())
|
.then(response => response.json()).then(response => {
|
||||||
.then(response => {
|
const plugins = response.plugins
|
||||||
const plugins = response.plugins
|
const config = store.getState().config
|
||||||
const config = store.getState().config
|
pluginLoader(plugins, config)
|
||||||
pluginLoader(plugins, config)
|
}).catch(err => {
|
||||||
})
|
retryLoadPluginsInterval += 1000
|
||||||
.catch(err => {
|
console.error(err)
|
||||||
retryLoadPluginsInterval += 1000
|
console.error(`Could not load plugins. Retrying in ${retryLoadPluginsInterval / 1000} second(s)`)
|
||||||
console.error(err)
|
setTimeout(loadPlugins, retryLoadPluginsInterval)
|
||||||
console.error(`Could not load plugins. Retrying in ${retryLoadPluginsInterval / 1000} second(s)`)
|
})
|
||||||
setTimeout(loadPlugins, retryLoadPluginsInterval)
|
|
||||||
})
|
|
||||||
|
|
||||||
export const pluginLoader = (plugins, config) => {
|
export const pluginLoader = (plugins, config) => {
|
||||||
const pluginContentWindows = []
|
const pluginContentWindows = []
|
||||||
plugins.forEach(plugin => {
|
|
||||||
const frame = document.createElement('iframe')
|
|
||||||
frame.className += 'pluginJSFrame'
|
|
||||||
frame.sandbox = 'allow-scripts allow-same-origin'
|
|
||||||
|
|
||||||
frame.src = window.location.origin + '/qortal-components/plugin-mainjs-loader.html#' + plugin + '/main.js'
|
plugins.forEach(plugin => {
|
||||||
|
const frame = document.createElement('iframe')
|
||||||
|
|
||||||
const insertedFrame = window.document.body.appendChild(frame)
|
frame.className += 'pluginJSFrame'
|
||||||
|
frame.sandbox = 'allow-scripts allow-same-origin'
|
||||||
|
frame.src = window.location.origin + '/qortal-components/plugin-mainjs-loader.html#' + plugin + '/main.js'
|
||||||
|
|
||||||
pluginContentWindows.push(insertedFrame.contentWindow)
|
const insertedFrame = window.document.body.appendChild(frame)
|
||||||
|
|
||||||
const epmlInstance = new Epml({
|
pluginContentWindows.push(insertedFrame.contentWindow)
|
||||||
type: 'WINDOW',
|
|
||||||
source: insertedFrame.contentWindow
|
|
||||||
})
|
|
||||||
|
|
||||||
addPluginRoutes(epmlInstance)
|
const epmlInstance = new Epml({
|
||||||
epmlInstance.imReady()
|
type: 'WINDOW',
|
||||||
|
source: insertedFrame.contentWindow
|
||||||
|
})
|
||||||
|
|
||||||
Epml.registerProxyInstance(`${plugin}-plugin`, epmlInstance)
|
addPluginRoutes(epmlInstance)
|
||||||
|
|
||||||
store.dispatch(doAddPlugin(epmlInstance))
|
epmlInstance.imReady()
|
||||||
})
|
|
||||||
|
Epml.registerProxyInstance(`${plugin}-plugin`, epmlInstance)
|
||||||
|
|
||||||
|
store.dispatch(doAddPlugin(epmlInstance))
|
||||||
|
})
|
||||||
}
|
}
|
@ -1,9 +1,6 @@
|
|||||||
<html>
|
<html>
|
||||||
|
<head></head>
|
||||||
<head></head>
|
<body>
|
||||||
|
<script src="/qortal-components/plugin-mainjs-loader.js" type="application/javascript"></script>
|
||||||
<body>
|
</body>
|
||||||
<script src="/qortal-components/plugin-mainjs-loader.js" type="application/javascript"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
</html>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user