mirror of
https://github.com/Qortal/qortal-ui.git
synced 2025-01-27 13:22:14 +00:00
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 ~/.bashrc ``` (For Fedora / CentOS) <br/>
|
||||
``` nvm ls-remote ``` (Fetch list of available versions) <br/>
|
||||
``` nvm install v18.17.1 ``` (LTS: Hydrogen supported by Electron V27) <br/>
|
||||
``` npm --location=global install npm@10.5.0 ``` <br/>
|
||||
``` nvm install v20.11.1 ``` (LTS: Iron supported by Electron V30) <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.
|
||||
|
||||
|
29
build.js
29
build.js
@ -1,28 +1,23 @@
|
||||
const path = require('path')
|
||||
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 build = uiCore('build')
|
||||
|
||||
const config = require('./config/config.js')
|
||||
|
||||
const pluginsController = require('./plugins/default-plugins.js')
|
||||
const buildDefalutPlugins = pluginsController('build')
|
||||
|
||||
|
||||
srcConfig = {
|
||||
...config.build,
|
||||
options: {
|
||||
...config.build.options,
|
||||
outputDir: path.join(__dirname, '/builtWWW'),
|
||||
sassOutputDir: path.join(__dirname, '/builtWWW/styles.bundle.css'),
|
||||
}
|
||||
...config.build,
|
||||
options: {
|
||||
...config.build.options,
|
||||
outputDir: path.join(__dirname, '/builtWWW'),
|
||||
sassOutputDir: path.join(__dirname, '/builtWWW/styles.bundle.css')
|
||||
}
|
||||
}
|
||||
|
||||
const { buildConfig, inlineConfigs } = generateBuildConfig(srcConfig)
|
||||
|
||||
build(buildConfig.options, buildConfig.outputs, buildConfig.outputOptions, buildConfig.inputOptions, inlineConfigs)
|
||||
.then(() => {
|
||||
console.log("Building and Bundling Plugins");
|
||||
buildDefalutPlugins()
|
||||
})
|
||||
build(buildConfig.options, buildConfig.outputs, buildConfig.outputOptions, buildConfig.inputOptions, inlineConfigs).then(() => {
|
||||
console.log("Building and Bundling Plugins")
|
||||
buildDefalutPlugins()
|
||||
})
|
@ -2,13 +2,13 @@ const path = require('path')
|
||||
const defaultConfig = require('./default.config.js')
|
||||
|
||||
const build = {
|
||||
options: {
|
||||
outputDir: path.join(__dirname, '../build'),
|
||||
imgDir: path.join(__dirname, '../img')
|
||||
},
|
||||
aliases: {
|
||||
'qortal-ui-crypto': path.join(__dirname, '../crypto/api.js')
|
||||
}
|
||||
options: {
|
||||
outputDir: path.join(__dirname, '../build'),
|
||||
imgDir: path.join(__dirname, '../img')
|
||||
},
|
||||
aliases: {
|
||||
'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')
|
||||
|
||||
module.exports = {
|
||||
name: 'Qortal',
|
||||
symbol: 'Qort',
|
||||
addressVersion: 58, // Q for Qortal
|
||||
logo: '/img/QORT_LOGO.svg'
|
||||
}
|
||||
name: 'Qortal',
|
||||
symbol: 'Qort',
|
||||
addressVersion: 58, // Q for Qortal
|
||||
logo: '/img/QORT_LOGO.svg'
|
||||
}
|
@ -1,27 +1,33 @@
|
||||
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') {
|
||||
storeObj[key] = checkKeys(storeObj[key], newObj[key])
|
||||
} else {
|
||||
storeObj[key] = newObj[key]
|
||||
}
|
||||
}
|
||||
return storeObj
|
||||
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') {
|
||||
storeObj[key] = checkKeys(storeObj[key], newObj[key])
|
||||
} else {
|
||||
storeObj[key] = newObj[key]
|
||||
}
|
||||
}
|
||||
|
||||
return storeObj
|
||||
}
|
||||
|
||||
const getConfig = customConfig => {
|
||||
config = checkKeys(config, customConfig)
|
||||
return config
|
||||
config = checkKeys(config, customConfig)
|
||||
return config
|
||||
}
|
||||
|
||||
module.exports = getConfig(userConfig)
|
||||
module.exports = getConfig(userConfig)
|
@ -1,3 +1,3 @@
|
||||
const defaultConfig = require('./default.config.js')
|
||||
|
||||
module.exports = {}
|
||||
module.exports = {}
|
@ -4,10 +4,4 @@ const styles = require('./styles.config.js')
|
||||
const build = require('./build.config.js')
|
||||
const user = require('./user.config.js')
|
||||
|
||||
module.exports = {
|
||||
coin,
|
||||
styles,
|
||||
build,
|
||||
user,
|
||||
crypto
|
||||
}
|
||||
module.exports = { coin, styles, build, user, crypto }
|
@ -1,5 +1,4 @@
|
||||
const uiCore = require('../core/ui-core.js')
|
||||
const defaultConfig = uiCore('default_config')
|
||||
|
||||
|
||||
module.exports = defaultConfig
|
||||
module.exports = defaultConfig
|
@ -1 +1 @@
|
||||
module.exports = {}
|
||||
module.exports = {}
|
@ -1,10 +1,11 @@
|
||||
const user = require('./default.config.js').user
|
||||
|
||||
module.exports = {
|
||||
node: 0, // set to mainnet
|
||||
server: {
|
||||
primary: {
|
||||
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,4 +4,4 @@ const user = require('./default.user.config.js')
|
||||
const styles = require('./default.styles.config.js')
|
||||
const build = require('./default.build.options.js')
|
||||
|
||||
module.exports = { coin, crypto, user, styles, build }
|
||||
module.exports = { coin, crypto, user, styles, build }
|
@ -4,132 +4,132 @@ const { makeSourceAbsolute } = require('../tooling/utils.js')
|
||||
const srcDir = '../src'
|
||||
|
||||
const options = {
|
||||
inputFile: path.join(__dirname, '../src/main.js'),
|
||||
outputDir: path.join(__dirname, '../build'),
|
||||
sassOutputDir: path.join(__dirname, '../build/styles.bundle.css'),
|
||||
imgDir: path.join(__dirname, '../img')
|
||||
inputFile: path.join(__dirname, '../src/main.js'),
|
||||
outputDir: path.join(__dirname, '../build'),
|
||||
sassOutputDir: path.join(__dirname, '../build/styles.bundle.css'),
|
||||
imgDir: path.join(__dirname, '../img')
|
||||
}
|
||||
|
||||
const aliases = {
|
||||
'qortal-ui-crypto': '../../crypto/api.js'
|
||||
'qortal-ui-crypto': '../../crypto/api.js'
|
||||
}
|
||||
|
||||
const apiComponents = {
|
||||
api: {
|
||||
file: '../../crypto/api.js',
|
||||
className: 'api'
|
||||
}
|
||||
api: {
|
||||
file: '../../crypto/api.js',
|
||||
className: 'api'
|
||||
}
|
||||
}
|
||||
|
||||
const functionalComponents = {
|
||||
'loading-ripple': {
|
||||
file: 'functional-components/loading-ripple.js',
|
||||
className: 'LoadingRipple'
|
||||
},
|
||||
'confirm-transaction-dialog': {
|
||||
file: 'functional-components/confirm-transaction-dialog',
|
||||
className: 'ConfirmTransactionDialog'
|
||||
}
|
||||
'loading-ripple': {
|
||||
file: 'functional-components/loading-ripple.js',
|
||||
className: 'LoadingRipple'
|
||||
},
|
||||
'confirm-transaction-dialog': {
|
||||
file: 'functional-components/confirm-transaction-dialog',
|
||||
className: 'ConfirmTransactionDialog'
|
||||
}
|
||||
}
|
||||
|
||||
const inlineComponents = [
|
||||
{
|
||||
className: 'worker',
|
||||
input: path.join(__dirname, srcDir, 'worker.js'),
|
||||
output: 'worker.js'
|
||||
},
|
||||
{
|
||||
className: 'PluginMainJSLoader',
|
||||
input: path.join(__dirname, srcDir, '/plugins/plugin-mainjs-loader.js'),
|
||||
output: 'plugins/plugin-mainjs-loader.js'
|
||||
}
|
||||
{
|
||||
className: 'worker',
|
||||
input: path.join(__dirname, srcDir, 'worker.js'),
|
||||
output: 'worker.js'
|
||||
},
|
||||
{
|
||||
className: 'PluginMainJSLoader',
|
||||
input: path.join(__dirname, srcDir, '/plugins/plugin-mainjs-loader.js'),
|
||||
output: 'plugins/plugin-mainjs-loader.js'
|
||||
}
|
||||
]
|
||||
|
||||
const elementComponents = {
|
||||
'main-app': {
|
||||
file: 'components/main-app.js',
|
||||
className: 'MainApp',
|
||||
children: {
|
||||
'app-styles': {
|
||||
file: 'styles/app-styles.js',
|
||||
className: 'AppStyles',
|
||||
children: {
|
||||
'app-theme': {
|
||||
className: 'AppTheme',
|
||||
file: 'styles/app-theme.js'
|
||||
}
|
||||
}
|
||||
},
|
||||
'app-view': {
|
||||
file: 'components/app-view.js',
|
||||
className: 'AppView',
|
||||
children: {
|
||||
'show-plugin': {
|
||||
file: 'components/show-plugin.js',
|
||||
className: 'ShowPlugin'
|
||||
},
|
||||
'wallet-profile': {
|
||||
file: 'components/wallet-profile.js',
|
||||
className: 'WalletProfile'
|
||||
},
|
||||
'app-info': {
|
||||
file: 'components/app-info.js',
|
||||
className: 'AppInfo'
|
||||
}
|
||||
}
|
||||
},
|
||||
'login-view': {
|
||||
file: 'components/login-view/login-view.js',
|
||||
className: 'LoginView',
|
||||
children: {
|
||||
'create-account-section': {
|
||||
file: 'components/login-view/create-account-section.js',
|
||||
className: 'CreateAccountSection'
|
||||
},
|
||||
'login-section': {
|
||||
file: 'components/login-view/login-section.js',
|
||||
className: 'LoginSection'
|
||||
}
|
||||
}
|
||||
},
|
||||
'settings-view': {
|
||||
file: 'components/settings-view/user-settings.js',
|
||||
className: 'UserSettings',
|
||||
children: {
|
||||
'account-view': {
|
||||
file: 'components/settings-view/account-view.js',
|
||||
className: 'AccountView'
|
||||
},
|
||||
'security-view': {
|
||||
file: 'components/settings-view/security-view.js',
|
||||
className: 'SecurityView'
|
||||
},
|
||||
'qr-login-view': {
|
||||
file: 'components/settings-view/qr-login-view.js',
|
||||
className: 'QRLoginView'
|
||||
},
|
||||
'notifications-view': {
|
||||
file: 'components/settings-view/notifications-view.js',
|
||||
className: 'NotificationsView'
|
||||
}
|
||||
}
|
||||
},
|
||||
'user-info-view': {
|
||||
file: 'components/user-info-view/user-info-view.js',
|
||||
className: 'UserInfoView'
|
||||
}
|
||||
}
|
||||
}
|
||||
'main-app': {
|
||||
file: 'components/main-app.js',
|
||||
className: 'MainApp',
|
||||
children: {
|
||||
'app-styles': {
|
||||
file: 'styles/app-styles.js',
|
||||
className: 'AppStyles',
|
||||
children: {
|
||||
'app-theme': {
|
||||
className: 'AppTheme',
|
||||
file: 'styles/app-theme.js'
|
||||
}
|
||||
}
|
||||
},
|
||||
'app-view': {
|
||||
file: 'components/app-view.js',
|
||||
className: 'AppView',
|
||||
children: {
|
||||
'show-plugin': {
|
||||
file: 'components/show-plugin.js',
|
||||
className: 'ShowPlugin'
|
||||
},
|
||||
'wallet-profile': {
|
||||
file: 'components/wallet-profile.js',
|
||||
className: 'WalletProfile'
|
||||
},
|
||||
'app-info': {
|
||||
file: 'components/app-info.js',
|
||||
className: 'AppInfo'
|
||||
}
|
||||
}
|
||||
},
|
||||
'login-view': {
|
||||
file: 'components/login-view/login-view.js',
|
||||
className: 'LoginView',
|
||||
children: {
|
||||
'create-account-section': {
|
||||
file: 'components/login-view/create-account-section.js',
|
||||
className: 'CreateAccountSection'
|
||||
},
|
||||
'login-section': {
|
||||
file: 'components/login-view/login-section.js',
|
||||
className: 'LoginSection'
|
||||
}
|
||||
}
|
||||
},
|
||||
'settings-view': {
|
||||
file: 'components/settings-view/user-settings.js',
|
||||
className: 'UserSettings',
|
||||
children: {
|
||||
'account-view': {
|
||||
file: 'components/settings-view/account-view.js',
|
||||
className: 'AccountView'
|
||||
},
|
||||
'security-view': {
|
||||
file: 'components/settings-view/security-view.js',
|
||||
className: 'SecurityView'
|
||||
},
|
||||
'qr-login-view': {
|
||||
file: 'components/settings-view/qr-login-view.js',
|
||||
className: 'QRLoginView'
|
||||
},
|
||||
'notifications-view': {
|
||||
file: 'components/settings-view/notifications-view.js',
|
||||
className: 'NotificationsView'
|
||||
}
|
||||
}
|
||||
},
|
||||
'user-info-view': {
|
||||
file: 'components/user-info-view/user-info-view.js',
|
||||
className: 'UserInfoView'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
makeSourceAbsolute(path.join(__dirname, srcDir), elementComponents)
|
||||
makeSourceAbsolute(path.join(__dirname, srcDir), functionalComponents)
|
||||
|
||||
module.exports = {
|
||||
options,
|
||||
elementComponents,
|
||||
functionalComponents,
|
||||
inlineComponents,
|
||||
apiComponents,
|
||||
aliases
|
||||
}
|
||||
options,
|
||||
elementComponents,
|
||||
functionalComponents,
|
||||
inlineComponents,
|
||||
apiComponents,
|
||||
aliases
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
const coin = {
|
||||
name: 'Qortal',
|
||||
symbol: 'QORT',
|
||||
addressCount: 1,
|
||||
addressVersion: 58,
|
||||
decimals: 100000000,
|
||||
logo: '/img/QORT_LOGO.png',
|
||||
icon: '/img/QORT_LOGO.png'
|
||||
name: 'Qortal',
|
||||
symbol: 'QORT',
|
||||
addressCount: 1,
|
||||
addressVersion: 58,
|
||||
decimals: 100000000,
|
||||
logo: '/img/QORT_LOGO.png',
|
||||
icon: '/img/QORT_LOGO.png'
|
||||
}
|
||||
|
||||
module.exports = coin
|
||||
module.exports = coin
|
@ -1,11 +1,11 @@
|
||||
const crypto = {
|
||||
kdfThreads: 16,
|
||||
staticSalt: '4ghkVQExoneGqZqHTMMhhFfxXsVg2A75QeS1HCM5KAih', // Base58 encoded
|
||||
bcryptRounds: 11, // Note it's kinda bcryptRounds * log.2.16, cause it runs on all 16 threads
|
||||
bcryptVersion: '2a',
|
||||
get staticBcryptSalt () {
|
||||
return `$${this.bcryptVersion}$${this.bcryptRounds}$IxVE941tXVUD4cW0TNVm.O`
|
||||
}
|
||||
kdfThreads: 16,
|
||||
staticSalt: '4ghkVQExoneGqZqHTMMhhFfxXsVg2A75QeS1HCM5KAih', // Base58 encoded
|
||||
bcryptRounds: 11, // Note it's kinda bcryptRounds * log.2.16, cause it runs on all 16 threads
|
||||
bcryptVersion: '2a',
|
||||
get staticBcryptSalt() {
|
||||
return `$${this.bcryptVersion}$${this.bcryptRounds}$IxVE941tXVUD4cW0TNVm.O`
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = crypto
|
||||
module.exports = crypto
|
@ -1,40 +1,41 @@
|
||||
const styles = {
|
||||
breakpoints: {
|
||||
desktop: '',
|
||||
laptop: '',
|
||||
tablet: '',
|
||||
mobile: ''
|
||||
},
|
||||
theme: {
|
||||
colors: {
|
||||
primary: '#03a9f4', /* Sets the text 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. */
|
||||
breakpoints: {
|
||||
desktop: '',
|
||||
laptop: '',
|
||||
tablet: '',
|
||||
mobile: ''
|
||||
},
|
||||
theme: {
|
||||
colors: {
|
||||
primary: '#03a9f4', /* Sets the text 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. */
|
||||
|
||||
secondary: '#03a9f4', /* Sets the text 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. */
|
||||
secondary: '#03a9f4', /* Sets the text 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. */
|
||||
|
||||
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. */
|
||||
background: '#eee', /* Sets the background color to the theme 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. */
|
||||
background: '#eee', /* Sets the background color to the theme background color. */
|
||||
|
||||
warning: '#FFA000',
|
||||
error: '#F44336'
|
||||
},
|
||||
warning: '#FFA000',
|
||||
error: '#F44336'
|
||||
},
|
||||
|
||||
addressColors: [
|
||||
'#256480',
|
||||
'#002530',
|
||||
'#02564e',
|
||||
'#d32f2f',
|
||||
'#795548',
|
||||
'#004d40',
|
||||
'#006064',
|
||||
'#9c27b0',
|
||||
'#2196f3',
|
||||
'#d81b60'
|
||||
]
|
||||
}
|
||||
addressColors: [
|
||||
'#256480',
|
||||
'#002530',
|
||||
'#02564e',
|
||||
'#d32f2f',
|
||||
'#795548',
|
||||
'#004d40',
|
||||
'#006064',
|
||||
'#9c27b0',
|
||||
'#2196f3',
|
||||
'#d81b60'
|
||||
]
|
||||
}
|
||||
}
|
||||
module.exports = styles
|
||||
|
||||
module.exports = styles
|
@ -1,42 +1,43 @@
|
||||
const path = require('path')
|
||||
|
||||
const user = {
|
||||
node: 0,
|
||||
nodeSettings: {
|
||||
pingInterval: 30 * 1000,
|
||||
},
|
||||
server: {
|
||||
writeHosts: {
|
||||
enabled: true,
|
||||
},
|
||||
relativeTo: path.join(__dirname, '../'),
|
||||
primary: {
|
||||
domain: '0.0.0.0',
|
||||
address: '0.0.0.0',
|
||||
port: 12388,
|
||||
directory: './src/',
|
||||
page404: './src/404.html',
|
||||
host: '0.0.0.0',
|
||||
},
|
||||
},
|
||||
tls: {
|
||||
enabled: false,
|
||||
options: {
|
||||
key: '',
|
||||
cert: '',
|
||||
},
|
||||
},
|
||||
constants: {
|
||||
pollingInterval: 30 * 1000, // How long between checking for new unconfirmed transactions and new blocks (in milliseconds).
|
||||
workerURL: '/build/worker.js',
|
||||
},
|
||||
node: 0,
|
||||
nodeSettings: {
|
||||
pingInterval: 30 * 1000
|
||||
},
|
||||
server: {
|
||||
writeHosts: {
|
||||
enabled: true
|
||||
},
|
||||
relativeTo: path.join(__dirname, '../'),
|
||||
primary: {
|
||||
domain: '0.0.0.0',
|
||||
address: '0.0.0.0',
|
||||
port: 12388,
|
||||
directory: './src/',
|
||||
page404: './src/404.html',
|
||||
host: '0.0.0.0'
|
||||
}
|
||||
},
|
||||
tls: {
|
||||
enabled: false,
|
||||
options: {
|
||||
key: '',
|
||||
cert: ''
|
||||
}
|
||||
},
|
||||
constants: {
|
||||
pollingInterval: 30 * 1000, // How long between checking for new unconfirmed transactions and new blocks (in milliseconds).
|
||||
workerURL: '/build/worker.js'
|
||||
},
|
||||
|
||||
// Notification Settings (All defaults to true)
|
||||
notifications: {
|
||||
q_chat: {
|
||||
playSound: true,
|
||||
showNotification: true,
|
||||
},
|
||||
},
|
||||
// Notification Settings (All defaults to true)
|
||||
notifications: {
|
||||
q_chat: {
|
||||
playSound: true,
|
||||
showNotification: true
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = user
|
||||
|
||||
module.exports = user
|
@ -1,21 +1,22 @@
|
||||
let config = require('./config.js')
|
||||
|
||||
const checkKeys = (storeObj, newObj) => {
|
||||
for (const key in newObj) {
|
||||
if (!Object.prototype.hasOwnProperty.call(storeObj, key)) return
|
||||
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
|
||||
if (typeof newObj[key] === 'object') {
|
||||
storeObj[key] = checkKeys(storeObj[key], newObj[key])
|
||||
} else {
|
||||
storeObj[key] = newObj[key]
|
||||
}
|
||||
}
|
||||
|
||||
return storeObj
|
||||
}
|
||||
|
||||
const getConfig = customConfig => {
|
||||
config = checkKeys(config, customConfig)
|
||||
return config
|
||||
config = checkKeys(config, customConfig)
|
||||
return config
|
||||
}
|
||||
|
||||
module.exports = getConfig
|
||||
module.exports = getConfig
|
@ -6,6 +6,7 @@ html {
|
||||
--plugback: #ffffff;
|
||||
--border: #d0d6de;
|
||||
--border2: #dde2e8;
|
||||
--border3: #080808;
|
||||
--copybutton: #707584;
|
||||
--chat-group: #080808;
|
||||
--chat-bubble: #9f9f9f0a;
|
||||
@ -83,6 +84,7 @@ html[theme="dark"] {
|
||||
--plugback: #0f1a2e;
|
||||
--border: #0b305e;
|
||||
--border2: #0b305e;
|
||||
--border3: #767676;
|
||||
--copybutton: #d0d6de;
|
||||
--chat-group: #ffffff;
|
||||
--chat-bubble: #9694941a;
|
||||
|
@ -945,7 +945,16 @@
|
||||
"gchange56": "Group Name To Search",
|
||||
"gchange57": "Private Group Name Not Found",
|
||||
"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": {
|
||||
"pchange1": "Puzzles",
|
||||
|
@ -1,139 +1,121 @@
|
||||
<!DOCTYPE html>
|
||||
<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);
|
||||
|
||||
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>
|
||||
|
||||
<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);
|
||||
|
||||
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>
|
||||
</html>
|
||||
|
@ -3,32 +3,33 @@ const Hapi = require('@hapi/hapi')
|
||||
const Inert = require('@hapi/inert')
|
||||
|
||||
function serverFactory(routes, address, port, tls) {
|
||||
this.server = new Hapi.Server({
|
||||
routes: {
|
||||
files: {
|
||||
relativeTo: Path.join(__dirname, '../')
|
||||
}
|
||||
},
|
||||
address: address,
|
||||
port: port,
|
||||
tls: tls
|
||||
})
|
||||
this.server = new Hapi.Server({
|
||||
routes: {
|
||||
files: {
|
||||
relativeTo: Path.join(__dirname, '../')
|
||||
}
|
||||
},
|
||||
address: address,
|
||||
port: port,
|
||||
tls: tls
|
||||
})
|
||||
|
||||
this.startServer = async () => {
|
||||
try {
|
||||
await this.server.register([Inert])
|
||||
this.startServer = async () => {
|
||||
try {
|
||||
await this.server.register([Inert])
|
||||
|
||||
this.server.route(routes)
|
||||
this.server.route(routes)
|
||||
|
||||
await this.server.start()
|
||||
await this.server.start()
|
||||
|
||||
delete this.startServer
|
||||
return this.server
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
delete this.startServer
|
||||
|
||||
return this.server
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = serverFactory
|
||||
module.exports = serverFactory
|
@ -1,106 +1,106 @@
|
||||
const path = require('path')
|
||||
|
||||
const routesOptions = {
|
||||
security: {
|
||||
hsts: {
|
||||
maxAge: 15768000,
|
||||
includeSubDomains: true,
|
||||
preload: true
|
||||
},
|
||||
xframe: 'sameorigin'
|
||||
}
|
||||
security: {
|
||||
hsts: {
|
||||
maxAge: 15768000,
|
||||
includeSubDomains: true,
|
||||
preload: true
|
||||
},
|
||||
xframe: 'sameorigin'
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
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
|
||||
delete response.config.build
|
||||
|
||||
delete response.config.user.tls
|
||||
delete response.config.build
|
||||
return JSON.stringify(response)
|
||||
},
|
||||
options: routesOptions
|
||||
}
|
||||
return JSON.stringify(response)
|
||||
},
|
||||
options: routesOptions
|
||||
}
|
||||
]
|
||||
|
||||
module.exports = createRoutes
|
||||
module.exports = createRoutes
|
@ -1,141 +1,140 @@
|
||||
const path = require('path')
|
||||
|
||||
const createCommonRoutes = require('./createCommonRoutes.js')
|
||||
|
||||
const createPrimaryRoutes = (config, plugins) => {
|
||||
const routes = createCommonRoutes(config)
|
||||
const routes = createCommonRoutes(config)
|
||||
|
||||
let myPlugins = plugins
|
||||
let myPlugins = plugins
|
||||
|
||||
const pluginFolders = {}
|
||||
const pluginFolders = {}
|
||||
|
||||
const routesOptions = {
|
||||
security: {
|
||||
hsts: {
|
||||
maxAge: 15768000,
|
||||
includeSubDomains: true,
|
||||
preload: true
|
||||
},
|
||||
xframe: 'sameorigin'
|
||||
}
|
||||
}
|
||||
const routesOptions = {
|
||||
security: {
|
||||
hsts: {
|
||||
maxAge: 15768000,
|
||||
includeSubDomains: true,
|
||||
preload: true
|
||||
},
|
||||
xframe: 'sameorigin'
|
||||
}
|
||||
}
|
||||
|
||||
plugins.reduce((obj, plugin) => {
|
||||
obj[plugin.name] = plugin.folder
|
||||
return obj
|
||||
}, pluginFolders)
|
||||
plugins.reduce((obj, plugin) => {
|
||||
obj[plugin.name] = plugin.folder
|
||||
return obj
|
||||
}, pluginFolders)
|
||||
|
||||
|
||||
routes.push(
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/',
|
||||
handler: (request, reply) => {
|
||||
return reply.redirect('/app')
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/{path*}',
|
||||
handler: (request, h) => {
|
||||
const filePath = path.join(__dirname, '../../public/index.html')
|
||||
const response = h.file(filePath, {
|
||||
confine: true
|
||||
})
|
||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||
return response
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/getPlugins',
|
||||
handler: (request, h) => {
|
||||
return { plugins: myPlugins.map(p => p.name) }
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/build/{param*}',
|
||||
handler: {
|
||||
directory: {
|
||||
path: config.build.options.outputDir,
|
||||
redirectToSlash: true,
|
||||
index: true
|
||||
}
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/src/{param*}',
|
||||
handler: {
|
||||
directory: {
|
||||
path: path.join(__dirname, '../../src'),
|
||||
redirectToSlash: true,
|
||||
index: true
|
||||
}
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/plugin/{path*}',
|
||||
handler: (request, h) => {
|
||||
routes.push(
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/',
|
||||
handler: (request, reply) => {
|
||||
return reply.redirect('/app')
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/{path*}',
|
||||
handler: (request, h) => {
|
||||
const filePath = path.join(__dirname, '../../public/index.html')
|
||||
const response = h.file(filePath, {
|
||||
confine: true
|
||||
})
|
||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||
return response
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/getPlugins',
|
||||
handler: (request, h) => {
|
||||
return { plugins: myPlugins.map(p => p.name) }
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/build/{param*}',
|
||||
handler: {
|
||||
directory: {
|
||||
path: config.build.options.outputDir,
|
||||
redirectToSlash: true,
|
||||
index: true
|
||||
}
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/src/{param*}',
|
||||
handler: {
|
||||
directory: {
|
||||
path: path.join(__dirname, '../../src'),
|
||||
redirectToSlash: true,
|
||||
index: true
|
||||
}
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/plugin/{path*}',
|
||||
handler: (request, h) => {
|
||||
|
||||
const plugin = request.params.path.split('/')[0]
|
||||
const filePath = path.join(pluginFolders[plugin], '../', request.params.path)
|
||||
const plugin = request.params.path.split('/')[0]
|
||||
const filePath = path.join(pluginFolders[plugin], '../', request.params.path)
|
||||
|
||||
const response = h.file(filePath, {
|
||||
confine: false
|
||||
})
|
||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||
return response
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/plugin/404',
|
||||
handler: (request, h) => {
|
||||
const response = h.file(path.join(config.server.primary.page404))
|
||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||
return response
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/qortal-components/plugin-mainjs-loader.html',
|
||||
handler: (request, h) => {
|
||||
const response = h.file(path.join(__dirname, '../../src/plugins/plugin-mainjs-loader.html'), {
|
||||
confine: false
|
||||
})
|
||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||
return response
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/qortal-components/plugin-mainjs-loader.js',
|
||||
handler: (request, h) => {
|
||||
const file = path.join(config.build.options.outputDir, '/plugins/plugin-mainjs-loader.js')
|
||||
const response = h.file(filePath, {
|
||||
confine: false
|
||||
})
|
||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||
return response
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/plugin/404',
|
||||
handler: (request, h) => {
|
||||
const response = h.file(path.join(config.server.primary.page404))
|
||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||
return response
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/qortal-components/plugin-mainjs-loader.html',
|
||||
handler: (request, h) => {
|
||||
const response = h.file(path.join(__dirname, '../../src/plugins/plugin-mainjs-loader.html'), {
|
||||
confine: false
|
||||
})
|
||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||
return response
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/qortal-components/plugin-mainjs-loader.js',
|
||||
handler: (request, h) => {
|
||||
const file = path.join(config.build.options.outputDir, '/plugins/plugin-mainjs-loader.js')
|
||||
|
||||
const response = h.file(file, {
|
||||
confine: false
|
||||
})
|
||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||
return response
|
||||
},
|
||||
options: routesOptions
|
||||
},
|
||||
const response = h.file(file, {
|
||||
confine: false
|
||||
})
|
||||
response.header('Access-Control-Allow-Origin', request.info.host)
|
||||
return response
|
||||
},
|
||||
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 createServer = (config, plugins) => {
|
||||
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)
|
||||
primaryServer.startServer()
|
||||
.then(server => {
|
||||
console.log(`Qortal UI Server started at ${server.info.uri} and listening on ${server.info.address}`)
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
}
|
||||
return this
|
||||
}
|
||||
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)
|
||||
primaryServer.startServer().then(server => {
|
||||
console.log(`Qortal UI Server started at ${server.info.uri} and listening on ${server.info.address}`)
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
const serverExports = {
|
||||
createServer
|
||||
createServer
|
||||
}
|
||||
|
||||
module.exports = serverExports
|
||||
module.exports = serverExports
|
@ -1,54 +1,55 @@
|
||||
import * as api from 'qortal-ui-crypto'
|
||||
import mykey from './functional-components/mykey-page.js'
|
||||
|
||||
'use strict'
|
||||
import mykey from './functional-components/mykey-page'
|
||||
|
||||
export const checkApiKey = async (nodeConfig) => {
|
||||
let selectedNode = nodeConfig.knownNodes[nodeConfig.node]
|
||||
let apiKey = selectedNode.apiKey
|
||||
|
||||
let selectedNode = nodeConfig.knownNodes[nodeConfig.node];
|
||||
let apiKey = selectedNode.apiKey;
|
||||
// Attempt to generate an API key
|
||||
const generateUrl = '/admin/apikey/generate'
|
||||
|
||||
// Attempt to generate an API key
|
||||
const generateUrl = "/admin/apikey/generate";
|
||||
let generateRes = await api.request(generateUrl, {
|
||||
method: "POST"
|
||||
});
|
||||
let generateRes = await api.request(generateUrl, {
|
||||
method: 'POST'
|
||||
})
|
||||
|
||||
if (generateRes != null && generateRes.error == null && generateRes.length >= 8) {
|
||||
console.log("Generated API key");
|
||||
apiKey = generateRes;
|
||||
if (generateRes != null && generateRes.error == null && generateRes.length >= 8) {
|
||||
console.log('Generated API key')
|
||||
|
||||
// Store the generated API key
|
||||
selectedNode.apiKey = apiKey;
|
||||
nodeConfig.knownNodes[nodeConfig.node] = selectedNode;
|
||||
localStorage.setItem('myQortalNodes', JSON.stringify(nodeConfig.knownNodes));
|
||||
}
|
||||
else {
|
||||
console.log("Unable to generate API key");
|
||||
}
|
||||
apiKey = generateRes
|
||||
|
||||
// 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(
|
||||
// Store the generated API key
|
||||
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
|
||||
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', {
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}),
|
||||
);
|
||||
}
|
||||
bubbles: true,
|
||||
composed: true
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const testApiKey = async (apiKey) => {
|
||||
const testUrl = "/admin/apikey/test?apiKey=" + apiKey;
|
||||
let testRes = await api.request(testUrl, {
|
||||
method: "GET"
|
||||
});
|
||||
return testRes === true;
|
||||
const testUrl = '/admin/apikey/test?apiKey=' + apiKey
|
||||
|
||||
}
|
||||
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 {connect} from 'pwa-helpers'
|
||||
import {store} from '../store.js'
|
||||
import {translate} from '../../translate'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../store'
|
||||
import { appInfoStyles } from '../styles/core-css'
|
||||
|
||||
// Multi language support
|
||||
import { translate } from '../../translate'
|
||||
|
||||
class AppInfo extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
nodeInfo: { type: Array },
|
||||
coreInfo: { type: Array },
|
||||
nodeConfig: { type: Object },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
nodeInfo: { type: Array },
|
||||
coreInfo: { type: Array },
|
||||
nodeConfig: { type: Object },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
* {
|
||||
--mdc-theme-primary: rgb(3, 169, 244);
|
||||
--paper-input-container-focus-color: var(--mdc-theme-primary);
|
||||
}
|
||||
static get styles() {
|
||||
return [appInfoStyles]
|
||||
}
|
||||
|
||||
.normal {
|
||||
--mdc-theme-primary: rgb(3, 169, 244);
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.nodeInfo = []
|
||||
this.coreInfo = []
|
||||
this.nodeConfig = {}
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
|
||||
#profileInMenu {
|
||||
flex: 0 0 100px;
|
||||
padding:12px;
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--sidetopbar);
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<div id="profileInMenu">
|
||||
<span class="info">${translate("appinfo.uiversion")}: ${this.nodeConfig.version ? this.nodeConfig.version : ''}</span>
|
||||
${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 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 100;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding-bottom: 8px;
|
||||
color: var(--black);
|
||||
}
|
||||
firstUpdated() {
|
||||
this.getNodeInfo()
|
||||
this.getCoreInfo()
|
||||
|
||||
.blue {
|
||||
color: #03a9f4;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 200;
|
||||
display: inline;
|
||||
}
|
||||
setInterval(() => {
|
||||
this.getNodeInfo()
|
||||
this.getCoreInfo()
|
||||
}, 60000)
|
||||
}
|
||||
|
||||
.black {
|
||||
color: var(--black);
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 200;
|
||||
display: inline;
|
||||
}
|
||||
`
|
||||
}
|
||||
async getNodeInfo() {
|
||||
const appinfoNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
const appinfoUrl = appinfoNode.protocol + '://' + appinfoNode.domain + ':' + appinfoNode.port
|
||||
const url = `${appinfoUrl}/admin/status`
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.nodeInfo = []
|
||||
this.coreInfo = []
|
||||
this.nodeConfig = {}
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
await fetch(url).then(response => {
|
||||
return response.json()
|
||||
}).then(data => {
|
||||
this.nodeInfo = data
|
||||
}).catch(err => {
|
||||
console.error('Request failed', err)
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div id="profileInMenu">
|
||||
<span class="info">${translate("appinfo.uiversion")}: ${this.nodeConfig.version ? this.nodeConfig.version : ''}</span>
|
||||
${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>
|
||||
`
|
||||
}
|
||||
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`
|
||||
|
||||
firstUpdated() {
|
||||
this.getNodeInfo()
|
||||
this.getCoreInfo()
|
||||
await fetch(url).then(response => {
|
||||
return response.json()
|
||||
}).then(data => {
|
||||
this.coreInfo = data
|
||||
}).catch(err => {
|
||||
console.error('Request failed', err)
|
||||
})
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
this.getNodeInfo()
|
||||
this.getCoreInfo()
|
||||
}, 60000)
|
||||
}
|
||||
_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 ''
|
||||
}
|
||||
}
|
||||
|
||||
async getNodeInfo() {
|
||||
const appinfoNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
const appinfoUrl = appinfoNode.protocol + '://' + appinfoNode.domain + ':' + appinfoNode.port
|
||||
const url = `${appinfoUrl}/admin/status`
|
||||
_renderCoreVersion() {
|
||||
return html`<span class="info">${translate("appinfo.coreversion")}: ${this.coreInfo.buildVersion ? this.coreInfo.buildVersion : ''}</span>`
|
||||
}
|
||||
|
||||
await fetch(url).then(response => {
|
||||
return response.json()
|
||||
}).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
|
||||
}
|
||||
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 {connect} from 'pwa-helpers'
|
||||
import {store} from '../store.js'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../store'
|
||||
|
||||
class MyElement extends connect(store)(LitElement) {
|
||||
static get properties () {
|
||||
return {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles () {
|
||||
return css``
|
||||
}
|
||||
|
||||
render () {
|
||||
return html`
|
||||
<style>
|
||||
</style>
|
||||
`
|
||||
}
|
||||
|
||||
stateChanged (state) {
|
||||
}
|
||||
render () {
|
||||
return html`<style></style>`
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('my-element', MyElement)
|
||||
window.customElements.define('my-element', MyElement)
|
@ -1,11 +1,13 @@
|
||||
import {css, html, LitElement} from 'lit'
|
||||
import {store} from '../../store'
|
||||
import {connect} from 'pwa-helpers'
|
||||
import {translate} from '../../../translate'
|
||||
import {parentEpml} from '../show-plugin'
|
||||
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { parentEpml } from '../show-plugin'
|
||||
import { syncIndicator2Styles } from '../../styles/core-css'
|
||||
import '@material/mwc-icon'
|
||||
|
||||
// Multi language support
|
||||
import {translate} from '../../../translate'
|
||||
|
||||
class SyncIndicator extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
@ -18,6 +20,10 @@ class SyncIndicator extends connect(store)(LitElement) {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [syncIndicator2Styles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.blocksBehind = 0
|
||||
@ -32,64 +38,6 @@ class SyncIndicator extends connect(store)(LitElement) {
|
||||
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() {
|
||||
return html`
|
||||
${!this.hasCoreRunning ? html`
|
||||
@ -225,7 +173,7 @@ class SyncIndicator extends connect(store)(LitElement) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('open-welcome-modal-sync', {
|
||||
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 {driver} from 'driver.js';
|
||||
import 'driver.js/dist/driver.css';
|
||||
import '@material/mwc-icon';
|
||||
import '@polymer/paper-spinner/paper-spinner-lite.js';
|
||||
import '@vaadin/tooltip';
|
||||
import '@material/mwc-button';
|
||||
import {get, translate} from '../../../translate';
|
||||
import '@polymer/paper-dialog/paper-dialog.js';
|
||||
import {setNewTab} from '../../redux/app/app-actions.js';
|
||||
import {store} from '../../store.js';
|
||||
import {connect} from 'pwa-helpers';
|
||||
import './tour.css';
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { setNewTab } from '../../redux/app/app-actions'
|
||||
import { tourComponentStyles } from '../../styles/core-css'
|
||||
import { driver } from 'driver.js'
|
||||
import 'driver.js/dist/driver.css'
|
||||
import './tour.css'
|
||||
import '@material/mwc-button'
|
||||
import '@material/mwc-icon'
|
||||
import '@polymer/paper-dialog/paper-dialog.js'
|
||||
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
||||
import '@vaadin/tooltip'
|
||||
|
||||
// Multi language support
|
||||
import { get, translate } from '../../../translate'
|
||||
|
||||
class TourComponent extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
@ -18,242 +21,112 @@ class TourComponent extends connect(store)(LitElement) {
|
||||
getElements: { attribute: false },
|
||||
dialogOpenedCongrats: { type: Boolean },
|
||||
hasViewedTour: { type: Boolean },
|
||||
disableTour: {type: Boolean}
|
||||
};
|
||||
disableTour: { type: Boolean },
|
||||
nodeUrl: { type: String },
|
||||
address: { type: String }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [tourComponentStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.dialogOpenedCongrats = false;
|
||||
this._controlOpenWelcomeModal =
|
||||
this._controlOpenWelcomeModal.bind(this);
|
||||
this.hasName = false;
|
||||
this.nodeUrl = this.getNodeUrl();
|
||||
this.myNode = this.getMyNode();
|
||||
super()
|
||||
this.dialogOpenedCongrats = false
|
||||
this._controlOpenWelcomeModal = this._controlOpenWelcomeModal.bind(this)
|
||||
this.hasName = false
|
||||
this.nodeUrl = ''
|
||||
this.address = ''
|
||||
this._disableTour = this._disableTour.bind(this)
|
||||
this.disableTour = false
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
* {
|
||||
--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);
|
||||
box-sizing: border-box;
|
||||
color: var(--black);
|
||||
background: var(--white);
|
||||
}
|
||||
|
||||
:host {
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
bottom: 25px;
|
||||
right: 25px;
|
||||
z-index: 50000;
|
||||
}
|
||||
|
||||
.full-info-wrapper {
|
||||
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;
|
||||
}
|
||||
`;
|
||||
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>
|
||||
` : ''}
|
||||
`
|
||||
}
|
||||
|
||||
_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() {
|
||||
this.getNodeUrl()
|
||||
this.address = store.getState().app.selectedAddress.address
|
||||
const hasViewedTour = JSON.parse(
|
||||
localStorage.getItem(`hasViewedTour-${this.address}`) || 'false'
|
||||
);
|
||||
const name = await this.getName(this.address);
|
||||
|
||||
const hasViewedTour = JSON.parse(localStorage.getItem(`hasViewedTour-${this.address}`) || 'false')
|
||||
const name = await this.getName(this.address)
|
||||
|
||||
if (name) {
|
||||
this.hasName = true;
|
||||
this.hasName = true
|
||||
}
|
||||
this.hasViewedTour = hasViewedTour;
|
||||
|
||||
this.hasViewedTour = hasViewedTour
|
||||
|
||||
if (!hasViewedTour) {
|
||||
try {
|
||||
if (name) {
|
||||
this.hasViewedTour = true;
|
||||
this.hasName = true;
|
||||
this.hasViewedTour = true
|
||||
this.hasName = true
|
||||
localStorage.setItem(`hasViewedTour-${this.address}`, JSON.stringify(true))
|
||||
}
|
||||
} catch (error) {
|
||||
console.log({ error });
|
||||
console.log({ error })
|
||||
}
|
||||
}
|
||||
|
||||
await new Promise((res) => {
|
||||
setTimeout(() => {
|
||||
res();
|
||||
}, 1000);
|
||||
});
|
||||
res()
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
if (!this.hasViewedTour && this.disableTour !== true) {
|
||||
const elements = this.getElements();
|
||||
let steps = [
|
||||
{
|
||||
popover: {
|
||||
title: get("tour.tour6"),
|
||||
description: `
|
||||
<div style="display:flex;justify-content:center;gap:15px">
|
||||
<img style="height:40px;width:auto;margin:15px 0px;" src="/img/qort.png" />
|
||||
</div>
|
||||
<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>
|
||||
<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.tour8")}</p>
|
||||
</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> <p style="margin:0px;padding:0px">${get("tour.tour9")}</p>
|
||||
</div>
|
||||
`,
|
||||
// ... other options
|
||||
},
|
||||
},
|
||||
];
|
||||
const step2 = elements['core-sync-status-id'];
|
||||
const step3 = elements['tab'];
|
||||
const step4 = elements['checklist'];
|
||||
const elements = this.getElements()
|
||||
|
||||
let steps = [{
|
||||
popover: {
|
||||
title: get("tour.tour6"),
|
||||
description: `
|
||||
<div style="display:flex;justify-content:center;gap:15px">
|
||||
<img style="height:40px;width:auto;margin:15px 0px;" src="/img/qort.png" />
|
||||
</div>
|
||||
<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>
|
||||
<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.tour8")}</p>
|
||||
</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>
|
||||
<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']
|
||||
|
||||
if (step2) {
|
||||
steps.push({
|
||||
@ -261,58 +134,54 @@ class TourComponent extends connect(store)(LitElement) {
|
||||
popover: {
|
||||
title: get("tour.tour5"),
|
||||
description: `
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
<p style="margin:0px;padding:0px">${get("tour.tour2")}</p>
|
||||
</div>
|
||||
<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>
|
||||
<p style="margin:0px;padding:0px">${get("tour.tour3")}</p>
|
||||
</div>
|
||||
<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>
|
||||
<p style="margin:0px;padding:0px">${get("tour.tour4")}</p>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
<p style="margin:0px;padding:0px">${get("tour.tour2")}</p>
|
||||
</div>
|
||||
<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>
|
||||
<p style="margin:0px;padding:0px">${get("tour.tour3")}</p>
|
||||
</div>
|
||||
<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>
|
||||
<p style="margin:0px;padding:0px">${get("tour.tour4")}</p>
|
||||
</div>
|
||||
|
||||
`,
|
||||
},
|
||||
});
|
||||
`
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (step3) {
|
||||
steps.push({
|
||||
element: step3,
|
||||
popover: {
|
||||
title: 'Tab View',
|
||||
description: `
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
<p style="margin:0px;padding:0px">You can also bookmark other Q-Apps and Plugins by clicking on the ${get(
|
||||
'tabmenu.tm19'
|
||||
)} button</p>
|
||||
</div>
|
||||
`,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (step4) {
|
||||
steps.push(
|
||||
{
|
||||
element: step4,
|
||||
popover: {
|
||||
title: get("tour.tour11"),
|
||||
description: get("tour.tour12"),
|
||||
},
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
<p style="margin:0px;padding:0px">
|
||||
You can also bookmark other Q-Apps and Plugins by clicking on the ${get('tabmenu.tm19')} button
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
);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({
|
||||
popoverClass: 'driverjs-theme',
|
||||
showProgress: true,
|
||||
@ -321,25 +190,93 @@ class TourComponent extends connect(store)(LitElement) {
|
||||
allowClose: false,
|
||||
onDestroyed: () => {
|
||||
localStorage.setItem(`hasViewedTour-${this.address}`, JSON.stringify(true))
|
||||
this.hasViewedTour = true;
|
||||
this.openWelcomeModal();
|
||||
this.hasViewedTour = true
|
||||
this.openWelcomeModal()
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
driverObj.drive();
|
||||
driverObj.drive()
|
||||
} else {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('send-tour-finished', {
|
||||
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() {
|
||||
this.onClose();
|
||||
const query = `?service=APP&name=Q-Tube`;
|
||||
this.onClose()
|
||||
const query = `?service=APP&name=Q-Tube`
|
||||
store.dispatch(
|
||||
setNewTab({
|
||||
url: `qdn/browser/index.html${query}`,
|
||||
@ -350,59 +287,16 @@ class TourComponent extends connect(store)(LitElement) {
|
||||
page: `qdn/browser/index.html${query}`,
|
||||
title: 'Q-Tube',
|
||||
menus: [],
|
||||
parent: false,
|
||||
},
|
||||
parent: false
|
||||
}
|
||||
})
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
onClose() {
|
||||
localStorage.setItem(`welcome-sync-${this.address}`, JSON.stringify(true))
|
||||
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>
|
||||
`
|
||||
: ''}
|
||||
`;
|
||||
this.dialogOpenedCongrats = false
|
||||
}
|
||||
}
|
||||
customElements.define('tour-component', TourComponent);
|
||||
|
||||
window.customElements.define('tour-component', TourComponent)
|
@ -1,10 +1,11 @@
|
||||
import {css, html, LitElement} from 'lit'
|
||||
import {translate} from '../../translate'
|
||||
import { html, LitElement } from 'lit'
|
||||
import isElectron from 'is-electron'
|
||||
|
||||
import '@polymer/paper-icon-button/paper-icon-button.js'
|
||||
import '@polymer/iron-icons/iron-icons.js'
|
||||
|
||||
// Multi language support
|
||||
import { translate } from '../../translate'
|
||||
|
||||
class CheckForUpdate extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
@ -17,11 +18,6 @@ class CheckForUpdate extends LitElement {
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
`
|
||||
]
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${this.renderUpdateButton()}
|
||||
@ -29,6 +25,7 @@ class CheckForUpdate extends LitElement {
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
// ...
|
||||
}
|
||||
|
||||
renderUpdateButton() {
|
||||
@ -48,4 +45,4 @@ class CheckForUpdate extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('check-for-update', CheckForUpdate)
|
||||
window.customElements.define('check-for-update', CheckForUpdate)
|
@ -1,82 +1,58 @@
|
||||
import {Sha256} from 'asmcrypto.js'
|
||||
import { Sha256 } from 'asmcrypto.js'
|
||||
|
||||
|
||||
function sbrk(size, heap){
|
||||
let brk = 512 * 1024 // stack top
|
||||
let old = brk
|
||||
brk += size
|
||||
|
||||
if (brk > heap.length)
|
||||
throw new Error('heap exhausted')
|
||||
|
||||
return old
|
||||
function sbrk(size, heap) {
|
||||
let brk = 512 * 1024 // stack top
|
||||
let old = brk
|
||||
brk += size
|
||||
if (brk > heap.length) throw new Error('heap exhausted')
|
||||
return old
|
||||
}
|
||||
|
||||
|
||||
|
||||
self.addEventListener('message', async e => {
|
||||
const response = await computePow(e.data.chatBytes, e.data.path, e.data.difficulty)
|
||||
postMessage(response)
|
||||
const response = await computePow(e.data.chatBytes, e.data.path, e.data.difficulty)
|
||||
postMessage(response)
|
||||
|
||||
})
|
||||
|
||||
|
||||
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
|
||||
const heap = new Uint8Array(memory.buffer)
|
||||
|
||||
|
||||
|
||||
const computePow = async (chatBytes, path, difficulty) => {
|
||||
|
||||
let response = null
|
||||
|
||||
await new Promise((resolve, reject)=> {
|
||||
|
||||
const _chatBytesArray = Object.keys(chatBytes).map(function (key) { return chatBytes[key]; });
|
||||
const chatBytesArray = new Uint8Array(_chatBytesArray);
|
||||
const chatBytesHash = new Sha256().process(chatBytesArray).finish().result;
|
||||
const hashPtr = sbrk(32, heap);
|
||||
const hashAry = new Uint8Array(memory.buffer, hashPtr, 32);
|
||||
hashAry.set(chatBytesHash);
|
||||
|
||||
|
||||
const workBufferLength = 8 * 1024 * 1024;
|
||||
const workBufferPtr = sbrk(workBufferLength, heap);
|
||||
|
||||
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
loadWebAssembly(path)
|
||||
.then(wasmModule => {
|
||||
response = {
|
||||
nonce : wasmModule.exports.compute2(hashPtr, workBufferPtr, workBufferLength, difficulty),
|
||||
chatBytesArray
|
||||
}
|
||||
|
||||
resolve()
|
||||
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
let response = null
|
||||
await new Promise((resolve, reject) => {
|
||||
const _chatBytesArray = Object.keys(chatBytes).map(function (key) { return chatBytes[key]; })
|
||||
const chatBytesArray = new Uint8Array(_chatBytesArray)
|
||||
const chatBytesHash = new Sha256().process(chatBytesArray).finish().result
|
||||
const hashPtr = sbrk(32, heap)
|
||||
const hashAry = new Uint8Array(memory.buffer, hashPtr, 32)
|
||||
hashAry.set(chatBytesHash)
|
||||
const workBufferLength = 8 * 1024 * 1024
|
||||
const workBufferPtr = sbrk(workBufferLength, heap)
|
||||
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)
|
||||
})
|
||||
}
|
||||
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'
|
||||
|
||||
|
||||
|
||||
function sbrk(size, heap){
|
||||
let brk = 512 * 1024 // stack top
|
||||
let old = brk
|
||||
brk += size
|
||||
|
||||
if (brk > heap.length)
|
||||
throw new Error('heap exhausted')
|
||||
|
||||
return old
|
||||
function sbrk(size, heap) {
|
||||
let brk = 512 * 1024 // stack top
|
||||
let old = brk
|
||||
brk += size
|
||||
if (brk > heap.length) throw new Error('heap exhausted')
|
||||
return old
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
self.addEventListener('message', async e => {
|
||||
const response = await computePow(e.data.convertedBytes, e.data.path)
|
||||
postMessage(response)
|
||||
|
||||
})
|
||||
const response = await computePow(e.data.convertedBytes, e.data.path)
|
||||
postMessage(response)
|
||||
|
||||
})
|
||||
|
||||
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
|
||||
const heap = new Uint8Array(memory.buffer)
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
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),
|
||||
|
||||
}
|
||||
resolve()
|
||||
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
return response
|
||||
}
|
@ -1,320 +1,303 @@
|
||||
import {html, LitElement} from 'lit';
|
||||
import '@material/mwc-icon';
|
||||
import {store} from '../../store';
|
||||
import {connect} from 'pwa-helpers';
|
||||
import '@vaadin/tooltip';
|
||||
import {parentEpml} from '../show-plugin';
|
||||
import {setCoinBalances} from '../../redux/app/app-actions';
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { parentEpml } from '../show-plugin'
|
||||
import { setCoinBalances } from '../../redux/app/app-actions'
|
||||
|
||||
class CoinBalancesController extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
coinList: { type: Object },
|
||||
};
|
||||
coinList: { type: Object }
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.coinList = {}
|
||||
this.nodeUrl = this.getNodeUrl();
|
||||
this.myNode = this.getMyNode();
|
||||
this.fetchBalance = this.fetchBalance.bind(this)
|
||||
this._updateCoinList = this._updateCoinList.bind(this)
|
||||
this.stop = false
|
||||
this.nodeUrl = this.getNodeUrl()
|
||||
this.myNode = this.getMyNode()
|
||||
this.fetchBalance = this.fetchBalance.bind(this)
|
||||
this._updateCoinList = this._updateCoinList.bind(this)
|
||||
this.stop = false
|
||||
}
|
||||
|
||||
getNodeUrl() {
|
||||
const myNode =
|
||||
store.getState().app.nodeConfig.knownNodes[
|
||||
store.getState().app.nodeConfig.node
|
||||
]
|
||||
render() {
|
||||
return html``
|
||||
}
|
||||
|
||||
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
|
||||
]
|
||||
return store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
}
|
||||
|
||||
async updateQortWalletBalance() {
|
||||
let qortAddress = store.getState().app.selectedAddress.address
|
||||
|
||||
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')
|
||||
})
|
||||
}
|
||||
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()
|
||||
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 updateBtcWalletBalance() {
|
||||
let _url = `/crosschain/btc/walletbalance?apiKey=${this.myNode.apiKey}`
|
||||
let _body = store.getState().app.selectedAddress.btcWallet.derivedMasterPublicKey
|
||||
|
||||
async fetchCoins(arrayOfCoins){
|
||||
const getCoinBalances = (arrayOfCoins || []).map(
|
||||
async (coin) => {
|
||||
if(coin === 'ltc'){
|
||||
await this.updateLtcWalletBalance()
|
||||
} else if(coin === 'qort'){
|
||||
await this.updateQortWalletBalance()
|
||||
} else if(coin === 'doge'){
|
||||
await this.updateDogeWalletBalance()
|
||||
} else if(coin === 'btc'){
|
||||
await this.updateBtcWalletBalance()
|
||||
} else if(coin === 'dgb'){
|
||||
await this.updateDgbWalletBalance()
|
||||
} else if(coin === 'rvn'){
|
||||
await this.updateRvnWalletBalance()
|
||||
}else if(coin === 'arrr'){
|
||||
await this.updateArrrWalletBalance()
|
||||
}
|
||||
})
|
||||
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')
|
||||
})
|
||||
}
|
||||
|
||||
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 {
|
||||
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]
|
||||
}
|
||||
}).catch(() => {
|
||||
console.log('error')
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
async updateDogeWalletBalance() {
|
||||
let _url = `/crosschain/doge/walletbalance?apiKey=${this.myNode.apiKey}`
|
||||
let _body = store.getState().app.selectedAddress.dogeWallet.derivedMasterPublicKey
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.intervalID = setInterval(this.fetchBalance, 45000);
|
||||
window.addEventListener(
|
||||
'ping-coin-controller-with-coin',
|
||||
this._updateCoinList
|
||||
);
|
||||
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 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() {
|
||||
|
||||
super.disconnectedCallback();
|
||||
window.removeEventListener(
|
||||
'ping-coin-controller-with-coin',
|
||||
this._updateCoinList
|
||||
);
|
||||
if(this.intervalID){
|
||||
clearInterval(this.intervalID);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
render() {
|
||||
return html``;
|
||||
if (this.intervalID) { clearInterval(this.intervalID) }
|
||||
window.removeEventListener('ping-coin-controller-with-coin', this._updateCoinList)
|
||||
super.disconnectedCallback()
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('coin-balances-controller', CoinBalancesController);
|
||||
window.customElements.define('coin-balances-controller', CoinBalancesController)
|
@ -1,221 +1,204 @@
|
||||
import {css, html, LitElement} from 'lit'
|
||||
import {get} from '../../../translate'
|
||||
import '@material/mwc-icon'
|
||||
import '@vaadin/tooltip';
|
||||
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { get } from '../../../translate'
|
||||
import { chatSideNavHeadsStyles } from '../../styles/core-css'
|
||||
import './friend-item-actions'
|
||||
import '@material/mwc-icon'
|
||||
import '@vaadin/tooltip'
|
||||
|
||||
class ChatSideNavHeads extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
selectedAddress: { type: Object },
|
||||
config: { type: Object },
|
||||
chatInfo: { type: Object },
|
||||
iconName: { type: String },
|
||||
activeChatHeadUrl: { type: String },
|
||||
isImageLoaded: { type: Boolean },
|
||||
setActiveChatHeadUrl: {attribute: false},
|
||||
openEditFriend: {attribute: false},
|
||||
closeSidePanel: {attribute: false, type: Object}
|
||||
}
|
||||
}
|
||||
class ChatSideNavHeads extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
selectedAddress: { type: Object },
|
||||
config: { type: Object },
|
||||
chatInfo: { type: Object },
|
||||
iconName: { type: String },
|
||||
activeChatHeadUrl: { type: String },
|
||||
isImageLoaded: { type: Boolean },
|
||||
setActiveChatHeadUrl: { attribute: false },
|
||||
openEditFriend: { attribute: false },
|
||||
closeSidePanel: { attribute: false, type: Object }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
: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;
|
||||
}
|
||||
static get styles() {
|
||||
return [chatSideNavHeadsStyles]
|
||||
}
|
||||
|
||||
li:hover {
|
||||
background-color: var(--lightChatHeadHover);
|
||||
}
|
||||
|
||||
.active {
|
||||
background: var(--menuactive);
|
||||
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);
|
||||
constructor() {
|
||||
super()
|
||||
this.selectedAddress = {}
|
||||
this.config = {
|
||||
user: {
|
||||
node: {
|
||||
}
|
||||
}} 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>
|
||||
}
|
||||
}
|
||||
this.chatInfo = {}
|
||||
this.iconName = ''
|
||||
this.activeChatHeadUrl = ''
|
||||
this.isImageLoaded = false
|
||||
this.imageFetches = 0
|
||||
}
|
||||
|
||||
</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
|
||||
render() {
|
||||
let avatarImg = ''
|
||||
|
||||
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>
|
||||
`
|
||||
}
|
||||
if (this.chatInfo.name) {
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||
const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.chatInfo.name}/qortal_avatar?async=true`
|
||||
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.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) {
|
||||
if(changedProperties.has('activeChatHeadUrl')){
|
||||
return true
|
||||
}
|
||||
if(changedProperties.has('chatInfo')){
|
||||
return true
|
||||
}
|
||||
return !!changedProperties.has('isImageLoaded');
|
||||
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
|
||||
}
|
||||
|
||||
getUrl(chatUrl) {
|
||||
this.setActiveChatHeadUrl(chatUrl)
|
||||
}
|
||||
imageHTMLRes.onerror = () => {
|
||||
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 {translate,} from '../../../translate'
|
||||
import '@material/mwc-button';
|
||||
import '@material/mwc-dialog';
|
||||
import '@material/mwc-checkbox';
|
||||
import {connect} from 'pwa-helpers';
|
||||
import {store} from '../../store';
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { translate, } from '../../../translate'
|
||||
import { addFriendsModalStyles } from '../../styles/core-css'
|
||||
import '@material/mwc-button'
|
||||
import '@material/mwc-checkbox'
|
||||
import '@material/mwc-dialog'
|
||||
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
||||
|
||||
class AddFriendsModal extends connect(store)(LitElement) {
|
||||
@ -21,199 +22,195 @@ class AddFriendsModal extends connect(store)(LitElement) {
|
||||
editContent: { type: Object },
|
||||
onClose: { attribute: false },
|
||||
mySelectedFeeds: { type: Array },
|
||||
availableFeeedSchemas: {type: Array},
|
||||
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;
|
||||
availableFeeedSchemas: { type: Array },
|
||||
isLoadingSchemas: { type: Boolean }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
* {
|
||||
--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;
|
||||
}
|
||||
`;
|
||||
return [addFriendsModalStyles]
|
||||
}
|
||||
|
||||
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() {
|
||||
const myNode =
|
||||
store.getState().app.nodeConfig.knownNodes[
|
||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||
];
|
||||
|
||||
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
||||
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[
|
||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||
];
|
||||
return store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
}
|
||||
|
||||
clearFields() {
|
||||
this.alias = '';
|
||||
this.willFollow = true;
|
||||
this.notes = '';
|
||||
this.alias = ''
|
||||
this.willFollow = true
|
||||
this.notes = ''
|
||||
}
|
||||
|
||||
addFriend() {
|
||||
@ -223,10 +220,10 @@ class AddFriendsModal extends connect(store)(LitElement) {
|
||||
notes: this.notes,
|
||||
willFollow: this.willFollow,
|
||||
mySelectedFeeds: this.mySelectedFeeds
|
||||
})
|
||||
|
||||
});
|
||||
this.clearFields();
|
||||
this.onClose();
|
||||
this.clearFields()
|
||||
this.onClose()
|
||||
}
|
||||
|
||||
removeFriend() {
|
||||
@ -239,244 +236,60 @@ class AddFriendsModal extends connect(store)(LitElement) {
|
||||
mySelectedFeeds: this.mySelectedFeeds
|
||||
},
|
||||
true
|
||||
);
|
||||
this.clearFields();
|
||||
this.onClose();
|
||||
)
|
||||
|
||||
this.clearFields()
|
||||
this.onClose()
|
||||
}
|
||||
|
||||
async updated(changedProperties) {
|
||||
if (
|
||||
changedProperties &&
|
||||
changedProperties.has('editContent') &&
|
||||
this.editContent
|
||||
) {
|
||||
this.userSelected = {
|
||||
name: this.editContent.name ?? '',
|
||||
};
|
||||
this.notes = this.editContent.notes ?? '';
|
||||
this.willFollow = this.editContent.willFollow ?? true;
|
||||
this.alias = this.editContent.alias ?? '';
|
||||
if (changedProperties && changedProperties.has('editContent') && this.editContent) {
|
||||
this.userSelected = { name: this.editContent.name ?? '' }
|
||||
this.notes = this.editContent.notes ?? ''
|
||||
this.willFollow = this.editContent.willFollow ?? true
|
||||
this.alias = this.editContent.alias ?? ''
|
||||
this.requestUpdate()
|
||||
}
|
||||
if (
|
||||
changedProperties &&
|
||||
changedProperties.has('isOpen') && this.isOpen
|
||||
) {
|
||||
|
||||
if (changedProperties && changedProperties.has('isOpen') && this.isOpen) {
|
||||
await this.getAvailableFeedSchemas()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async getAvailableFeedSchemas() {
|
||||
try {
|
||||
this.isLoadingSchemas= true
|
||||
const url = `${this.nodeUrl}/arbitrary/resources/search?service=DOCUMENT&identifier=ui_schema_feed&prefix=true`;
|
||||
const res = await fetch(url);
|
||||
const data = await res.json();
|
||||
this.isLoadingSchemas = true
|
||||
const url = `${this.nodeUrl}/arbitrary/resources/search?service=DOCUMENT&identifier=ui_schema_feed&prefix=true`
|
||||
const res = await fetch(url)
|
||||
const data = await res.json()
|
||||
|
||||
if (data.error === 401) {
|
||||
this.availableFeeedSchemas = [];
|
||||
this.availableFeeedSchemas = []
|
||||
} else {
|
||||
this.availableFeeedSchemas = data.filter(
|
||||
(item) => item.identifier === 'ui_schema_feed'
|
||||
);
|
||||
this.availableFeeedSchemas = data.filter((item) => item.identifier === 'ui_schema_feed')
|
||||
}
|
||||
this.userFoundModalOpen = true;
|
||||
} catch (error) {} finally {
|
||||
this.isLoadingSchemas= false
|
||||
|
||||
this.userFoundModalOpen = true
|
||||
} catch (error) {
|
||||
} finally {
|
||||
this.isLoadingSchemas = false
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="modal-overlay ${this.isOpen ? '' : 'hidden'}">
|
||||
// Standard functions
|
||||
getApiKey() {
|
||||
const coreNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
return coreNode.apiKey
|
||||
}
|
||||
|
||||
<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>
|
||||
`
|
||||
: ''}
|
||||
isEmptyArray(arr) {
|
||||
if (!arr) { return true }
|
||||
return arr.length === 0
|
||||
}
|
||||
|
||||
<button
|
||||
?disabled="${this.isLoading}"
|
||||
class="modal-button"
|
||||
@click=${() => {
|
||||
this.addFriend();
|
||||
}}
|
||||
>
|
||||
${this.editContent
|
||||
? translate('friends.friend10')
|
||||
: translate('friends.friend2')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
round(number) {
|
||||
return (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8)
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('add-friends-modal', AddFriendsModal);
|
||||
window.customElements.define('add-friends-modal', AddFriendsModal)
|
@ -1,16 +1,17 @@
|
||||
import {css, html, LitElement} from 'lit';
|
||||
import axios from 'axios';
|
||||
import '@material/mwc-menu';
|
||||
import '@material/mwc-list/mwc-list-item.js';
|
||||
import {RequestQueueWithPromise} from '../../../../plugins/plugins/utils/queue';
|
||||
import '../../../../plugins/plugins/core/components/TimeAgo';
|
||||
import {connect} from 'pwa-helpers';
|
||||
import {store} from '../../store';
|
||||
import ShortUniqueId from 'short-unique-id';
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { RequestQueueWithPromise } from '../../../../plugins/plugins/utils/classes'
|
||||
import { avatarComponentStyles } from '../../styles/core-css'
|
||||
import axios from 'axios'
|
||||
import ShortUniqueId from 'short-unique-id'
|
||||
import '../../../../plugins/plugins/core/components/TimeAgo'
|
||||
import '@material/mwc-menu'
|
||||
import '@material/mwc-list/mwc-list-item.js'
|
||||
|
||||
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 AvatarComponent extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
@ -18,284 +19,210 @@ export class AvatarComponent extends connect(store)(LitElement) {
|
||||
resource: { type: Object },
|
||||
isReady: { type: Boolean },
|
||||
status: { type: Object },
|
||||
name: { type: String },
|
||||
};
|
||||
name: { 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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
`;
|
||||
return [avatarComponentStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
super()
|
||||
this.resource = {
|
||||
identifier: '',
|
||||
name: '',
|
||||
service: '',
|
||||
};
|
||||
service: ''
|
||||
}
|
||||
this.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;
|
||||
status: ''
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
// 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();
|
||||
this.isReady = false
|
||||
this.nodeUrl = this.getNodeUrl()
|
||||
this.myNode = this.getMyNode()
|
||||
this.isFetching = false
|
||||
this.uid = new ShortUniqueId()
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div>
|
||||
${this.status.status !== 'READY'
|
||||
? html`
|
||||
<mwc-icon style="user-select:none;"
|
||||
>account_circle</mwc-icon
|
||||
>
|
||||
`
|
||||
: ''}
|
||||
${this.status.status === 'READY'
|
||||
? html`
|
||||
<div
|
||||
style="height: 24px;width: 24px;overflow: hidden;"
|
||||
>
|
||||
<img
|
||||
src="${this
|
||||
.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>
|
||||
`
|
||||
: ''}
|
||||
${this.status.status !== 'READY' ?
|
||||
html`
|
||||
<mwc-icon style="user-select:none;">account_circle</mwc-icon>
|
||||
` : ''
|
||||
}
|
||||
${this.status.status === 'READY' ?
|
||||
html`
|
||||
<div style="height: 24px;width: 24px;overflow: hidden;">
|
||||
<img
|
||||
src="${this.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>
|
||||
`;
|
||||
`
|
||||
}
|
||||
|
||||
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 {connect} from 'pwa-helpers';
|
||||
|
||||
import '@vaadin/item';
|
||||
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 '@material/mwc-icon';
|
||||
import {get} from '../../../translate';
|
||||
import '../../../../plugins/plugins/core/components/TimeAgo.js';
|
||||
import '../notification-view/popover.js';
|
||||
import ShortUniqueId from 'short-unique-id';
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { setNewTab } from '../../redux/app/app-actions'
|
||||
import { get } from '../../../translate'
|
||||
import { beginnerChecklistStyles } from '../../styles/core-css'
|
||||
import ShortUniqueId from 'short-unique-id'
|
||||
import '../notification-view/popover'
|
||||
import '../../../../plugins/plugins/core/components/TimeAgo'
|
||||
import '@material/mwc-icon'
|
||||
import '@polymer/paper-icon-button/paper-icon-button.js'
|
||||
import '@polymer/iron-icons/iron-icons.js'
|
||||
import '@vaadin/item'
|
||||
import '@vaadin/list-box'
|
||||
|
||||
class BeginnerChecklist extends connect(store)(LitElement) {
|
||||
static properties = {
|
||||
notifications: { type: Array },
|
||||
showChecklist: { type: Boolean },
|
||||
theme: { type: String, reflect: true },
|
||||
isSynced: { type: Boolean },
|
||||
hasName: { type: Boolean },
|
||||
hasTourFinished: { type: Boolean },
|
||||
};
|
||||
|
||||
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();
|
||||
static get properties() {
|
||||
return {
|
||||
notifications: { type: Array },
|
||||
showChecklist: { type: Boolean },
|
||||
isSynced: { type: Boolean },
|
||||
hasName: { type: Boolean },
|
||||
hasTourFinished: { type: Boolean },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
_controlTourFinished() {
|
||||
this.hasTourFinished = true;
|
||||
static get styles() {
|
||||
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() {
|
||||
this.address = store.getState().app.selectedAddress.address;
|
||||
this.hasTourFinished = JSON.parse(
|
||||
localStorage.getItem(`hasViewedTour-${this.address}`) || 'null'
|
||||
);
|
||||
this.address = store.getState().app.selectedAddress.address
|
||||
this.hasTourFinished = JSON.parse(localStorage.getItem(`hasViewedTour-${this.address}`) || 'null')
|
||||
}
|
||||
|
||||
_controlTourFinished() {
|
||||
this.hasTourFinished = true
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
window.addEventListener(
|
||||
'send-tour-finished',
|
||||
this._controlTourFinished
|
||||
);
|
||||
super.connectedCallback()
|
||||
window.addEventListener('send-tour-finished', this._controlTourFinished)
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
window.removeEventListener(
|
||||
'send-tour-finished',
|
||||
this._controlTourFinished
|
||||
);
|
||||
|
||||
super.disconnectedCallback();
|
||||
window.removeEventListener('send-tour-finished', this._controlTourFinished)
|
||||
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;
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.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
|
||||
];
|
||||
return store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
}
|
||||
|
||||
async getName(recipient) {
|
||||
try {
|
||||
if (!recipient) return '';
|
||||
const endpoint = `${this.nodeUrl}/names/address/${recipient}`;
|
||||
const res = await fetch(endpoint);
|
||||
const getNames = await res.json();
|
||||
if (!recipient) return ''
|
||||
|
||||
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) {
|
||||
return '';
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
stateChanged(state) {
|
||||
if (
|
||||
state.app.nodeStatus &&
|
||||
state.app.nodeStatus.syncPercent !== this.syncPercentage
|
||||
) {
|
||||
this.syncPercentage = state.app.nodeStatus.syncPercent;
|
||||
if (state.app.nodeStatus && state.app.nodeStatus.syncPercent !== this.syncPercentage) {
|
||||
this.syncPercentage = state.app.nodeStatus.syncPercent
|
||||
|
||||
if (
|
||||
!this.hasAttempted &&
|
||||
state.app.selectedAddress &&
|
||||
state.app.nodeStatus.syncPercent === 100
|
||||
) {
|
||||
this.hasAttempted = true;
|
||||
this.getName(state.app.selectedAddress.address);
|
||||
if (!this.hasAttempted && state.app.selectedAddress && state.app.nodeStatus.syncPercent === 100) {
|
||||
this.hasAttempted = true
|
||||
this.getName(state.app.selectedAddress.address)
|
||||
}
|
||||
}
|
||||
if (
|
||||
state.app.accountInfo &&
|
||||
state.app.accountInfo.names.length &&
|
||||
state.app.nodeStatus &&
|
||||
state.app.nodeStatus.syncPercent === 100 &&
|
||||
this.hasName === false &&
|
||||
this.hasAttempted &&
|
||||
state.app.accountInfo &&
|
||||
state.app.accountInfo.names &&
|
||||
|
||||
if (state.app.accountInfo &&
|
||||
state.app.accountInfo.names.length && state.app.nodeStatus && state.app.nodeStatus.syncPercent === 100 &&
|
||||
this.hasName === false && this.hasAttempted && state.app.accountInfo && state.app.accountInfo.names &&
|
||||
state.app.accountInfo.names.length > 0
|
||||
) {
|
||||
this.hasName = true;
|
||||
this.hasName = true
|
||||
}
|
||||
}
|
||||
|
||||
handleBlur() {
|
||||
setTimeout(() => {
|
||||
if (!this.shadowRoot.contains(document.activeElement)) {
|
||||
this.showChecklist = false;
|
||||
this.showChecklist = false
|
||||
}
|
||||
}, 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>
|
||||
`
|
||||
: '';
|
||||
}, 0)
|
||||
}
|
||||
|
||||
_toggleChecklist() {
|
||||
this.showChecklist = !this.showChecklist;
|
||||
this.showChecklist = !this.showChecklist
|
||||
|
||||
if (this.showChecklist) {
|
||||
requestAnimationFrame(() => {
|
||||
this.shadowRoot.getElementById('checklist-panel').focus();
|
||||
});
|
||||
this.shadowRoot.getElementById('checklist-panel').focus()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
// Standard functions
|
||||
getApiKey() {
|
||||
const coreNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
return coreNode.apiKey
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
isEmptyArray(arr) {
|
||||
if (!arr) { return true }
|
||||
return arr.length === 0
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
`;
|
||||
round(number) {
|
||||
return (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8)
|
||||
}
|
||||
}
|
||||
|
||||
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){
|
||||
let brk = 512 * 1024 // stack top
|
||||
let old = brk
|
||||
brk += size
|
||||
|
||||
if (brk > heap.length)
|
||||
throw new Error('heap exhausted')
|
||||
|
||||
return old
|
||||
function sbrk(size, heap) {
|
||||
let brk = 512 * 1024 // stack top
|
||||
let old = brk
|
||||
brk += size
|
||||
if (brk > heap.length) throw new Error('heap exhausted')
|
||||
return old
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
self.addEventListener('message', async e => {
|
||||
const response = await computePow(e.data.convertedBytes, e.data.path)
|
||||
postMessage(response)
|
||||
|
||||
const response = await computePow(e.data.convertedBytes, e.data.path)
|
||||
postMessage(response)
|
||||
})
|
||||
|
||||
|
||||
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
|
||||
const heap = new Uint8Array(memory.buffer)
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
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),
|
||||
|
||||
}
|
||||
resolve()
|
||||
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
return response
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
import {css, html, LitElement} from 'lit'
|
||||
import {store} from '../../store'
|
||||
import {connect} from 'pwa-helpers'
|
||||
import {translate} from '../../../translate'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { translate } from '../../../translate'
|
||||
import { coreSyncStatusStyles } from '../../styles/core-css'
|
||||
|
||||
class CoreSyncStatus extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
@ -12,6 +13,10 @@ class CoreSyncStatus extends connect(store)(LitElement) {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [coreSyncStatusStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.nodeInfos = []
|
||||
@ -19,69 +24,6 @@ class CoreSyncStatus extends connect(store)(LitElement) {
|
||||
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() {
|
||||
return html`
|
||||
<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>
|
||||
<div class="bottom">
|
||||
<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.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>
|
||||
@ -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>
|
||||
<div class="bottom">
|
||||
<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("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>
|
||||
@ -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>
|
||||
<div class="bottom">
|
||||
<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("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>
|
||||
@ -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>
|
||||
<div class="bottom">
|
||||
<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("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>
|
||||
@ -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>
|
||||
<div class="bottom">
|
||||
<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("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>
|
||||
@ -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>
|
||||
<div class="bottom">
|
||||
<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.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>
|
||||
@ -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 {translate,} from '../../../translate'
|
||||
import { html, LitElement } from 'lit'
|
||||
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 '@material/mwc-menu';
|
||||
import '@material/mwc-list/mwc-list-item.js'
|
||||
import {RequestQueueWithPromise} from '../../../../plugins/plugins/utils/queue';
|
||||
import ShortUniqueId from 'short-unique-id'
|
||||
import '../../../../plugins/plugins/core/components/TimeAgo'
|
||||
import {connect} from 'pwa-helpers';
|
||||
import {store} from '../../store';
|
||||
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);
|
||||
import '@material/mwc-menu'
|
||||
import '@material/mwc-list/mwc-list-item.js'
|
||||
|
||||
const requestQueue = new RequestQueueWithPromise(3)
|
||||
const requestQueueRawData = new RequestQueueWithPromise(3)
|
||||
const requestQueueStatus = new RequestQueueWithPromise(3)
|
||||
|
||||
export class FeedItem extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
resource: { type: Object },
|
||||
isReady: { type: Boolean},
|
||||
status: {type: Object},
|
||||
feedItem: {type: Object},
|
||||
appName: {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;
|
||||
static get properties() {
|
||||
return {
|
||||
resource: { type: Object },
|
||||
isReady: { type: Boolean },
|
||||
status: { type: Object },
|
||||
feedItem: { type: Object },
|
||||
appName: { type: String },
|
||||
link: { type: String }
|
||||
}
|
||||
}
|
||||
|
||||
.app-name {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
static get styles() {
|
||||
return [feedItemStyles]
|
||||
}
|
||||
|
||||
mwc-menu {
|
||||
position: absolute;
|
||||
}
|
||||
constructor() {
|
||||
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 {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
firstUpdated() {
|
||||
this.observer.observe(this)
|
||||
}
|
||||
|
||||
@keyframes loadingAnimation {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
getNodeUrl() {
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||
}
|
||||
|
||||
constructor() {
|
||||
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()
|
||||
getMyNode() {
|
||||
return store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
getNodeUrl(){
|
||||
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.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
|
||||
|
||||
return myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||
}
|
||||
getMyNode(){
|
||||
return window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
||||
}
|
||||
} catch (error) {
|
||||
this.isFetching = false
|
||||
}
|
||||
}
|
||||
|
||||
getApiKey() {
|
||||
const myNode =
|
||||
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||
];
|
||||
return myNode.apiKey;
|
||||
}
|
||||
async fetchVideoUrl() {
|
||||
await this.fetchResource()
|
||||
}
|
||||
|
||||
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
|
||||
async getRawData() {
|
||||
const url = `${this.nodeUrl}/arbitrary/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
|
||||
|
||||
} catch (error) {
|
||||
this.isFetching = false
|
||||
}
|
||||
}
|
||||
return await requestQueueRawData.enqueue(() => {
|
||||
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(){
|
||||
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'
|
||||
// }
|
||||
// })
|
||||
async fetchStatus() {
|
||||
let isCalling = false
|
||||
let percentLoaded = 0
|
||||
let timer = 24
|
||||
|
||||
// const responseData2 = await response2.json()
|
||||
// return responseData2
|
||||
}
|
||||
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}`)
|
||||
})
|
||||
|
||||
updateDisplayWithPlaceholders(display, resource, rawdata) {
|
||||
const pattern = /\$\$\{([a-zA-Z0-9_\.]+)\}\$\$/g;
|
||||
if (response && response.data && response.data.status === 'READY') {
|
||||
const rawData = await this.getRawData()
|
||||
|
||||
for (const key in display) {
|
||||
const value = display[key];
|
||||
const object = {
|
||||
...this.resource.schema.display
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
this.updateDisplayWithPlaceholders(object, {}, rawData.data)
|
||||
this.feedItem = object
|
||||
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}`)
|
||||
})
|
||||
|
||||
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'){
|
||||
const rawData = await this.getRawData()
|
||||
const object = {
|
||||
...this.resource.schema.display
|
||||
}
|
||||
this.updateDisplayWithPlaceholders(object, {},rawData.data)
|
||||
this.feedItem = object
|
||||
this.status = response.data
|
||||
const res = data.data
|
||||
|
||||
return
|
||||
}
|
||||
const intervalId = setInterval(async () => {
|
||||
if (isCalling) return
|
||||
isCalling = true
|
||||
isCalling = false
|
||||
if (res.localChunkCount) {
|
||||
if (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'
|
||||
}
|
||||
|
||||
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
|
||||
setTimeout(() => {
|
||||
isCalling = false
|
||||
this.fetchResource()
|
||||
}, 25000)
|
||||
|
||||
isCalling = false
|
||||
if (res.localChunkCount) {
|
||||
if (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'
|
||||
}
|
||||
return
|
||||
}
|
||||
percentLoaded = res.percentLoaded
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
isCalling = false
|
||||
this.fetchResource()
|
||||
}, 25000)
|
||||
return
|
||||
}
|
||||
percentLoaded = res.percentLoaded
|
||||
}
|
||||
this.status = res
|
||||
|
||||
this.status = res
|
||||
if(this.status.status === 'DOWNLOADED'){
|
||||
await this.fetchResource()
|
||||
}
|
||||
}
|
||||
if (this.status.status === 'DOWNLOADED') {
|
||||
await this.fetchResource()
|
||||
}
|
||||
}
|
||||
|
||||
// check if progress is 100% and clear interval if true
|
||||
if (res.status === 'READY') {
|
||||
const rawData = await this.getRawData()
|
||||
const object = {
|
||||
...this.resource.schema.display
|
||||
}
|
||||
this.updateDisplayWithPlaceholders(object, {},rawData.data)
|
||||
this.feedItem = object
|
||||
clearInterval(intervalId)
|
||||
this.status = res
|
||||
this.isReady = true
|
||||
}
|
||||
}, 5000) // 1 second interval
|
||||
}
|
||||
// check if progress is 100% and clear interval if true
|
||||
if (res.status === 'READY') {
|
||||
const rawData = await this.getRawData()
|
||||
const object = {
|
||||
...this.resource.schema.display
|
||||
}
|
||||
this.updateDisplayWithPlaceholders(object, {}, rawData.data)
|
||||
this.feedItem = object
|
||||
clearInterval(intervalId)
|
||||
this.status = res
|
||||
this.isReady = true
|
||||
}
|
||||
}, 5000) // 5 second interval
|
||||
}
|
||||
|
||||
async _fetchImage() {
|
||||
try {
|
||||
await this.fetchVideoUrl()
|
||||
await this.fetchStatus()
|
||||
} catch (error) { /* empty */ }
|
||||
}
|
||||
async _fetchImage() {
|
||||
try {
|
||||
await this.fetchVideoUrl()
|
||||
await this.fetchStatus()
|
||||
} catch (error) { /* empty */ }
|
||||
}
|
||||
|
||||
firstUpdated(){
|
||||
this.observer.observe(this);
|
||||
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}`
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
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({
|
||||
store.dispatch(setNewTab({
|
||||
url: `qdn/browser/index.html${query}`,
|
||||
id: this.uid.rnd(),
|
||||
myPlugObj: {
|
||||
@ -373,137 +289,72 @@ getMyNode(){
|
||||
"menus": [],
|
||||
"parent": false
|
||||
},
|
||||
openExisting: true
|
||||
openExisting: true
|
||||
}))
|
||||
} catch (error) {
|
||||
console.log({error})
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log({ error })
|
||||
}
|
||||
}
|
||||
|
||||
async extractComponents(url) {
|
||||
if (!url.startsWith("qortal://")) {
|
||||
return null
|
||||
}
|
||||
|
||||
url = url.replace(/^(qortal\:\/\/)/, "")
|
||||
|
||||
async extractComponents(url) {
|
||||
if (!url.startsWith("qortal://")) {
|
||||
return null
|
||||
}
|
||||
if (url.includes("/")) {
|
||||
let parts = url.split("/")
|
||||
const service = parts[0].toUpperCase()
|
||||
parts.shift()
|
||||
const name = parts[0]
|
||||
parts.shift()
|
||||
let identifier
|
||||
|
||||
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) {
|
||||
identifier = parts[0] // Do not shift yet
|
||||
// Check if a resource exists with this service, name and identifier combination
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||
const url = `${nodeUrl}/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${myNode.apiKey}}`
|
||||
|
||||
if (parts.length > 0) {
|
||||
identifier = parts[0] // Do not shift yet
|
||||
// Check if a resource exists with this service, name and identifier combination
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||
const url = `${nodeUrl}/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${myNode.apiKey}}`
|
||||
const res = await fetch(url);
|
||||
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 res = await fetch(url);
|
||||
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 path = parts.join("/")
|
||||
const components = {}
|
||||
components["service"] = service
|
||||
components["name"] = name
|
||||
components["identifier"] = identifier
|
||||
components["path"] = path
|
||||
return components
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const components = {}
|
||||
components["service"] = service
|
||||
components["name"] = name
|
||||
components["identifier"] = identifier
|
||||
components["path"] = path
|
||||
return components
|
||||
}
|
||||
return null
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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);
|
||||
window.customElements.define('feed-item', FeedItem)
|
@ -1,146 +1,42 @@
|
||||
// popover-component.js
|
||||
import {css, html, LitElement} from 'lit';
|
||||
import {createPopper} from '@popperjs/core';
|
||||
import '@material/mwc-icon';
|
||||
import {translate} from '../../../translate'
|
||||
import {store} from '../../store';
|
||||
import {connect} from 'pwa-helpers';
|
||||
import {setNewTab, setSideEffectAction} from '../../redux/app/app-actions';
|
||||
import ShortUniqueId from 'short-unique-id';
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { createPopper } from '@popperjs/core'
|
||||
import { setNewTab, setSideEffectAction } from '../../redux/app/app-actions'
|
||||
import { translate } from '../../../translate'
|
||||
import { friendItemActionsStyles } from '../../styles/core-css'
|
||||
import ShortUniqueId from 'short-unique-id'
|
||||
import '@material/mwc-icon'
|
||||
|
||||
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() {
|
||||
return {
|
||||
for: { type: String, reflect: true },
|
||||
message: { type: String },
|
||||
openEditFriend: { attribute: false },
|
||||
name: { type: String },
|
||||
closeSidePanel: { attribute: false, type: Object },
|
||||
};
|
||||
closeSidePanel: { attribute: false, type: Object }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [friendItemActionsStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.message = '';
|
||||
this.nodeUrl = this.getNodeUrl();
|
||||
this.uid = new ShortUniqueId();
|
||||
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 '';
|
||||
}
|
||||
super()
|
||||
this.message = ''
|
||||
this.nodeUrl = this.getNodeUrl()
|
||||
this.uid = new ShortUniqueId()
|
||||
this.getUserAddress = this.getUserAddress.bind(this)
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div id="parent-div" tabindex="0" @blur=${this.handleBlur}>
|
||||
<span class="close-icon" @click="${this.closePopover}"
|
||||
><mwc-icon style="color: var(--black)"
|
||||
>close</mwc-icon
|
||||
></span
|
||||
>
|
||||
<span class="close-icon" @click="${this.closePopover}">
|
||||
<mwc-icon style="color: var(--black)">close</mwc-icon>
|
||||
</span>
|
||||
<div class="action-parent">
|
||||
<div
|
||||
class="send-message-button"
|
||||
@ -164,16 +60,15 @@ export class FriendItemActions extends connect(store)(LitElement) {
|
||||
myPlugObj: {
|
||||
url: 'q-chat',
|
||||
domain: 'core',
|
||||
page: 'messaging/q-chat/index.html',
|
||||
page: 'q-chat/index.html',
|
||||
title: 'Q-Chat',
|
||||
icon: 'vaadin:chat',
|
||||
mwcicon: 'forum',
|
||||
pluginNumber: 'plugin-qhsyOnpRhT',
|
||||
menus: [],
|
||||
parent: false,
|
||||
parent: false
|
||||
},
|
||||
|
||||
openExisting: true,
|
||||
openExisting: true
|
||||
})
|
||||
);
|
||||
store.dispatch(
|
||||
@ -181,8 +76,8 @@ export class FriendItemActions extends connect(store)(LitElement) {
|
||||
type: 'openPrivateChat',
|
||||
data: {
|
||||
address,
|
||||
name: this.name,
|
||||
},
|
||||
name: this.name
|
||||
}
|
||||
})
|
||||
);
|
||||
this.closePopover();
|
||||
@ -208,9 +103,9 @@ export class FriendItemActions extends connect(store)(LitElement) {
|
||||
icon: 'vaadin:mailbox',
|
||||
mwcicon: 'mail_outline',
|
||||
menus: [],
|
||||
parent: false,
|
||||
parent: false
|
||||
},
|
||||
openExisting: true,
|
||||
openExisting: true
|
||||
})
|
||||
);
|
||||
this.closePopover();
|
||||
@ -223,26 +118,89 @@ export class FriendItemActions extends connect(store)(LitElement) {
|
||||
<div
|
||||
class="send-message-button"
|
||||
@click="${() => {
|
||||
const customEvent = new CustomEvent(
|
||||
'open-visiting-profile',
|
||||
{
|
||||
detail: this.name,
|
||||
}
|
||||
);
|
||||
const customEvent = new CustomEvent('open-visiting-profile', { detail: this.name });
|
||||
window.dispatchEvent(customEvent);
|
||||
this.closePopover();
|
||||
this.closeSidePanel();
|
||||
}}"
|
||||
>
|
||||
<mwc-icon style="color: var(--black)"
|
||||
>person</mwc-icon
|
||||
>
|
||||
<mwc-icon style="color: var(--black)">person</mwc-icon>
|
||||
${translate('profile.profile18')}
|
||||
</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 '@material/mwc-icon';
|
||||
import './friends-view'
|
||||
import {friendsViewStyles} from './friends-view-css';
|
||||
import {connect} from 'pwa-helpers';
|
||||
import {store} from '../../store';
|
||||
import { html, LitElement } from 'lit'
|
||||
import { store } from '../../store'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { translate } from '../../../translate'
|
||||
import { friendsViewStyles } from '../../styles/core-css'
|
||||
import './feed-item'
|
||||
import {translate} from '../../../translate'
|
||||
|
||||
import './friends-view'
|
||||
import '@material/mwc-icon'
|
||||
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) {
|
||||
static get properties() {
|
||||
static get properties() {
|
||||
return {
|
||||
feed: {type: Array},
|
||||
setHasNewFeed: {attribute:false},
|
||||
isLoading: {type: Boolean},
|
||||
hasFetched: {type: Boolean},
|
||||
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)
|
||||
|
||||
feed: { type: Array },
|
||||
setHasNewFeed: { attribute: false },
|
||||
isLoading: { type: Boolean },
|
||||
hasFetched: { type: Boolean },
|
||||
mySelectedFeeds: { type: Array }
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
const myNode =
|
||||
store.getState().app.nodeConfig.knownNodes[
|
||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||
]
|
||||
|
||||
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[
|
||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||
]
|
||||
return store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
}
|
||||
|
||||
_updateFeeds(event) {
|
||||
_updateFeeds(event) {
|
||||
this.mySelectedFeeds = event.detail
|
||||
this.reFetchFeedData()
|
||||
this.requestUpdate()
|
||||
this.reFetchFeedData()
|
||||
this.requestUpdate()
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback()
|
||||
window.addEventListener('friends-my-selected-feeds-event', this._updateFeeds) }
|
||||
window.addEventListener('friends-my-selected-feeds-event', this._updateFeeds)
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
window.removeEventListener('friends-my-selected-feeds-event', this._updateFeeds)
|
||||
super.disconnectedCallback()
|
||||
}
|
||||
|
||||
async getSchemas(){
|
||||
this.mySelectedFeeds = JSON.parse(localStorage.getItem('friends-my-selected-feeds') || "[]")
|
||||
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)
|
||||
}
|
||||
async getSchemas() {
|
||||
this.mySelectedFeeds = JSON.parse(localStorage.getItem('friends-my-selected-feeds') || "[]")
|
||||
const schemas = this.mySelectedFeeds
|
||||
|
||||
getFeedOnInterval(){
|
||||
let interval = null;
|
||||
let stop = false;
|
||||
const getAnswer = async () => {
|
||||
|
||||
if (!stop) {
|
||||
stop = true;
|
||||
const getAllSchemas = (schemas || []).map(
|
||||
async (schema) => {
|
||||
try {
|
||||
await this.reFetchFeedData()
|
||||
} catch (error) {}
|
||||
stop = false;
|
||||
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
|
||||
}
|
||||
}
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
const res = await Promise.all(getAllSchemas)
|
||||
return res.filter((item) => !!item)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
getFeedOnInterval() {
|
||||
let interval = null
|
||||
let stop = false
|
||||
|
||||
async refresh(){
|
||||
try {
|
||||
await this.getEndpoints()
|
||||
await this.reFetchFeedData()
|
||||
} catch (error) {
|
||||
const getAnswer = async () => {
|
||||
if (!stop) {
|
||||
stop = true
|
||||
try {
|
||||
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() {
|
||||
const options = {
|
||||
rootMargin: '0px',
|
||||
threshold: 1,
|
||||
};
|
||||
threshold: 1
|
||||
}
|
||||
|
||||
// identify an element to observe
|
||||
const elementToObserve = this.downObserverElement;
|
||||
const elementToObserve = this.downObserverElement
|
||||
|
||||
// passing it a callback function
|
||||
const observer = new IntersectionObserver(
|
||||
this.observerHandler,
|
||||
options
|
||||
);
|
||||
)
|
||||
|
||||
// call `observe()` on that MutationObserver instance,
|
||||
// passing it the element to observe, and the options object
|
||||
observer.observe(elementToObserve);
|
||||
observer.observe(elementToObserve)
|
||||
}
|
||||
|
||||
observerHandler(entries) {
|
||||
if (!entries[0].isIntersecting) {
|
||||
|
||||
} else {
|
||||
if (this.feedToRender.length < 20) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
this.getMoreFeed();
|
||||
this.getMoreFeed()
|
||||
}
|
||||
}
|
||||
|
||||
async fetchDataFromEndpoint(endpointIndex, count) {
|
||||
const offset = this.endpointOffsets[endpointIndex];
|
||||
const url = `${this.endpoints[endpointIndex].url}&limit=${count}&offset=${offset}`;
|
||||
const res = await fetch(url)
|
||||
const data = await res.json()
|
||||
return data.map((i)=> {
|
||||
return {
|
||||
...this.endpoints[endpointIndex],
|
||||
...i
|
||||
}
|
||||
})
|
||||
async fetchDataFromEndpoint(endpointIndex, count) {
|
||||
const offset = this.endpointOffsets[endpointIndex]
|
||||
const url = `${this.endpoints[endpointIndex].url}&limit=${count}&offset=${offset}`
|
||||
const res = await fetch(url)
|
||||
const data = await res.json()
|
||||
|
||||
}
|
||||
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() {
|
||||
let results = [];
|
||||
let totalFetched = 0;
|
||||
let i = 0;
|
||||
let madeProgress = true;
|
||||
let exhaustedEndpoints = new Set();
|
||||
while (totalFetched < totalDesiredCount && madeProgress) {
|
||||
madeProgress = false
|
||||
this.isLoading = true
|
||||
for (i = 0; i < this.endpoints.length; i++) {
|
||||
if (exhaustedEndpoints.has(i)) {
|
||||
continue
|
||||
}
|
||||
|
||||
while (totalFetched < totalDesiredCount && madeProgress) {
|
||||
madeProgress = false;
|
||||
this.isLoading = true
|
||||
for (i = 0; i < this.endpoints.length; i++) {
|
||||
if (exhaustedEndpoints.has(i)) {
|
||||
continue;
|
||||
}
|
||||
const remainingCount = totalDesiredCount - totalFetched
|
||||
|
||||
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
|
||||
if (remainingCount <= 0) {
|
||||
break;
|
||||
}
|
||||
let fetchCount = Math.min(perEndpointCount, remainingCount)
|
||||
let data = await this.fetchDataFromEndpoint(i, fetchCount)
|
||||
|
||||
let fetchCount = Math.min(perEndpointCount, remainingCount);
|
||||
let data = await this.fetchDataFromEndpoint(i, fetchCount);
|
||||
// Increment the offset for this endpoint by the number of items fetched
|
||||
this.endpointOffsets[i] += data.length
|
||||
|
||||
// Increment the offset for this endpoint by the number of items fetched
|
||||
this.endpointOffsets[i] += data.length;
|
||||
if (data.length > 0) {
|
||||
madeProgress = true
|
||||
}
|
||||
|
||||
if (data.length > 0) {
|
||||
madeProgress = true;
|
||||
}
|
||||
if (data.length < fetchCount) {
|
||||
exhaustedEndpoints.add(i)
|
||||
}
|
||||
|
||||
if (data.length < fetchCount) {
|
||||
exhaustedEndpoints.add(i);
|
||||
}
|
||||
results = results.concat(data)
|
||||
totalFetched += data.length
|
||||
}
|
||||
|
||||
results = results.concat(data);
|
||||
totalFetched += data.length;
|
||||
}
|
||||
if (exhaustedEndpoints.size === this.endpoints.length) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (exhaustedEndpoints.size === this.endpoints.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.isLoading = false
|
||||
this.hasFetched = true;
|
||||
// Trim the results if somehow they are over the totalDesiredCount
|
||||
return results.slice(0, totalDesiredCount);
|
||||
}
|
||||
this.isLoading = false
|
||||
this.hasFetched = true
|
||||
|
||||
// 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) {
|
||||
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);
|
||||
}
|
||||
let newResource = {
|
||||
identifier: newItem.identifier,
|
||||
service: newItem.service,
|
||||
name: newItem.name
|
||||
}
|
||||
|
||||
if (newItem.schema) {
|
||||
const resource = newItem
|
||||
|
||||
async addExtraData(data){
|
||||
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;
|
||||
let clickValue1 = newItem.schema.click;
|
||||
|
||||
newItem.link = replacePlaceholders(clickValue1, resource, newItem.schema.customParams)
|
||||
newData.push(newItem)
|
||||
}
|
||||
}
|
||||
return newData
|
||||
newData.push(newItem)
|
||||
}
|
||||
}
|
||||
return newData
|
||||
}
|
||||
|
||||
}
|
||||
async reFetchFeedData() {
|
||||
// Resetting offsets to start fresh.
|
||||
this.endpointOffsets = Array(this.endpoints.length).fill(0);
|
||||
await this.getEndpoints()
|
||||
const oldIdentifiers = new Set(this.feed.map(item => item.identifier));
|
||||
const newData = await this.initialLoad();
|
||||
async reFetchFeedData() {
|
||||
// Resetting offsets to start fresh.
|
||||
this.endpointOffsets = Array(this.endpoints.length).fill(0)
|
||||
await this.getEndpoints()
|
||||
const oldIdentifiers = new Set(this.feed.map(item => item.identifier))
|
||||
const newData = await this.initialLoad()
|
||||
|
||||
// Filter out items that are already in the feed
|
||||
const trulyNewData = newData.filter(item => !oldIdentifiers.has(item.identifier));
|
||||
// Filter out items that are already in the feed
|
||||
const trulyNewData = newData.filter(item => !oldIdentifiers.has(item.identifier))
|
||||
|
||||
if (trulyNewData.length > 0) {
|
||||
// Adding extra data and merging with old data
|
||||
const enhancedNewData = await this.addExtraData(trulyNewData);
|
||||
if (trulyNewData.length > 0) {
|
||||
// Adding extra data and merging with old data
|
||||
const enhancedNewData = await this.addExtraData(trulyNewData)
|
||||
|
||||
// Merge new data with old data immutably
|
||||
this.feed = [...enhancedNewData, ...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 = this.trimDataToLimit(this.feed, maxResultsInMemory); // Trim to the maximum allowed in memory
|
||||
this.feedToRender = this.feed.slice(0, 20);
|
||||
this.hasInitialFetch = true;
|
||||
// Merge new data with old data immutably
|
||||
this.feed = [...enhancedNewData, ...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 = this.trimDataToLimit(this.feed, maxResultsInMemory) // Trim to the maximum allowed in memory
|
||||
this.feedToRender = this.feed.slice(0, 20)
|
||||
this.hasInitialFetch = true
|
||||
|
||||
const created = trulyNewData[0].created;
|
||||
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')
|
||||
const created = trulyNewData[0].created
|
||||
let value = localStorage.getItem('lastSeenFeed')
|
||||
if (((+value || 0) < created)) {
|
||||
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) {
|
||||
if (typeof value !== 'string') return value;
|
||||
|
||||
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;
|
||||
});
|
||||
if (typeof value !== 'string') return value
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
export function constructUrl(base, search, dynamicVars) {
|
||||
let queryStrings = [];
|
||||
|
||||
for (const [key, value] of Object.entries(search)) {
|
||||
const substitutedValue = substituteDynamicVar(value, dynamicVars);
|
||||
queryStrings.push(`${key}=${encodeURIComponent(substitutedValue)}`);
|
||||
}
|
||||
|
||||
return queryStrings.length > 0 ? `${base}&${queryStrings.join('&')}` : base;
|
||||
let queryStrings = []
|
||||
for (const [key, value] of Object.entries(search)) {
|
||||
const substitutedValue = substituteDynamicVar(value, dynamicVars)
|
||||
queryStrings.push(`${key}=${encodeURIComponent(substitutedValue)}`)
|
||||
}
|
||||
return queryStrings.length > 0 ? `${base}&${queryStrings.join('&')}` : base
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export function replacePlaceholders(template, resource, customParams) {
|
||||
const dataSource = { resource, customParams };
|
||||
|
||||
return template.replace(/\$\$\{(.*?)\}\$\$/g, (match, p1) => {
|
||||
const keys = p1.split('.');
|
||||
let value = dataSource;
|
||||
|
||||
for (let key of keys) {
|
||||
if (value[key] !== undefined) {
|
||||
value = value[key];
|
||||
} else {
|
||||
return match; // Return placeholder unchanged
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const dataSource = { resource, customParams }
|
||||
return template.replace(/\$\$\{(.*?)\}\$\$/g, (match, p1) => {
|
||||
const keys = p1.split('.')
|
||||
let value = dataSource
|
||||
for (let key of keys) {
|
||||
if (value[key] !== undefined) {
|
||||
value = value[key]
|
||||
} else {
|
||||
return match // Return placeholder unchanged
|
||||
}
|
||||
}
|
||||
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 './friends-side-panel.js'
|
||||
import '@vaadin/tooltip'
|
||||
import {translate} from '../../../translate'
|
||||
|
||||
class FriendsSidePanelParent extends LitElement {
|
||||
class FriendsSidePanelParent extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
isOpen: {type: Boolean},
|
||||
hasNewFeed: {type: Boolean}
|
||||
isOpen: { type: Boolean },
|
||||
hasNewFeed: { type: Boolean }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [friendsSidePanelParentStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.isOpen = 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() {
|
||||
return html`
|
||||
<mwc-icon
|
||||
id="friends-icon"
|
||||
@click=${()=> {
|
||||
@click=${() => {
|
||||
this.isOpen = !this.isOpen
|
||||
if(this.isOpen && this.hasNewFeed) {
|
||||
if (this.isOpen && this.hasNewFeed) {
|
||||
localStorage.setItem('lastSeenFeed', Date.now())
|
||||
this.hasNewFeed = false
|
||||
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
|
||||
</mwc-icon>
|
||||
@ -72,12 +47,33 @@ class FriendsSidePanelParent extends LitElement {
|
||||
hover-delay=${400}
|
||||
hide-delay=${1}
|
||||
text=${translate('friends.friend12')}
|
||||
>
|
||||
</vaadin-tooltip>
|
||||
<friends-side-panel .setHasNewFeed=${(val)=> this.setHasNewFeed(val)} ?isOpen=${this.isOpen} .setIsOpen=${(val)=> this.isOpen = val}></friends-side-panel>
|
||||
|
||||
></vaadin-tooltip>
|
||||
<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 '@material/mwc-icon';
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { translate } from '../../../translate'
|
||||
import { friendsSidePanelStyles } from '../../styles/core-css'
|
||||
import './friends-view'
|
||||
import './friends-feed'
|
||||
import {translate} from '../../../translate'
|
||||
import '@material/mwc-icon'
|
||||
|
||||
class FriendsSidePanel extends LitElement {
|
||||
static get properties() {
|
||||
class FriendsSidePanel extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
setIsOpen: { attribute: false},
|
||||
isOpen: {type: Boolean},
|
||||
selected: {type: String},
|
||||
setHasNewFeed: {attribute: false},
|
||||
closeSidePanel: {attribute: false, type: Object},
|
||||
openSidePanel: {attribute: false, type: Object}
|
||||
};
|
||||
setIsOpen: { attribute: false },
|
||||
isOpen: { type: Boolean },
|
||||
selected: { type: String },
|
||||
setHasNewFeed: { attribute: false },
|
||||
closeSidePanel: { attribute: false, type: Object },
|
||||
openSidePanel: { attribute: false, type: Object }
|
||||
}
|
||||
}
|
||||
|
||||
constructor(){
|
||||
static get styles() {
|
||||
return [friendsSidePanelStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.selected = 'friends'
|
||||
this.closeSidePanel = this.closeSidePanel.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() {
|
||||
return html`
|
||||
<div class="parent">
|
||||
<div class="header">
|
||||
<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 = 'feed'} class="${this.selected === 'feed' ? 'active' : 'default'}">${translate('friends.friend13')}</span>
|
||||
</div>
|
||||
<div style="display:flex;gap:15px;align-items:center">
|
||||
<mwc-icon @click=${()=> {
|
||||
this.refreshFeed()
|
||||
}} style="color: var(--black); cursor:pointer;">refresh</mwc-icon>
|
||||
<mwc-icon style="cursor:pointer" @click=${()=> {
|
||||
this.setIsOpen(false)
|
||||
}}>close</mwc-icon>
|
||||
</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 class="header">
|
||||
<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 = 'feed'} class="${this.selected === 'feed' ? 'active' : 'default'}">${translate('friends.friend13')}</span>
|
||||
</div>
|
||||
<div style="display:flex;gap:15px;align-items:center">
|
||||
<mwc-icon @click=${() => { this.refreshFeed(); }} style="color: var(--black); cursor:pointer;">
|
||||
refresh
|
||||
</mwc-icon>
|
||||
<mwc-icon style="cursor:pointer" @click=${() => { this.setIsOpen(false); }}>
|
||||
close
|
||||
</mwc-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="${this.selected === 'feed' ? 'active-content' : 'default-content'}">
|
||||
<friends-feed .setHasNewFeed=${(val)=> this.setHasNewFeed(val)}></friends-feed>
|
||||
<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 class="${this.selected === 'feed' ? 'active-content' : 'default-content'}">
|
||||
<friends-feed .setHasNewFeed=${(val) => this.setHasNewFeed(val)}></friends-feed>
|
||||
</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 {connect} from 'pwa-helpers';
|
||||
|
||||
import '@material/mwc-button';
|
||||
import '@material/mwc-dialog';
|
||||
import '@polymer/paper-spinner/paper-spinner-lite.js';
|
||||
import '@polymer/paper-progress/paper-progress.js';
|
||||
import '@material/mwc-icon';
|
||||
import { html, LitElement } from 'lit'
|
||||
import { store } from '../../store'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { parentEpml } from '../show-plugin'
|
||||
import { translate } from '../../../translate'
|
||||
import { friendsViewStyles } from '../../styles/core-css'
|
||||
import './add-friends-modal'
|
||||
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/icons'
|
||||
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';
|
||||
import '@vaadin/button'
|
||||
|
||||
class FriendsView extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
@ -29,94 +27,159 @@ class FriendsView extends connect(store)(LitElement) {
|
||||
setUserName: { attribute: false },
|
||||
friendList: { type: Array },
|
||||
userSelected: { type: Object },
|
||||
isLoading: {type: Boolean},
|
||||
userFoundModalOpen: {type: Boolean},
|
||||
userFound: { type: Array},
|
||||
isOpenAddFriendsModal: {type: Boolean},
|
||||
editContent: {type: Object},
|
||||
mySelectedFeeds: {type: Array},
|
||||
refreshFeed: {attribute: false},
|
||||
closeSidePanel: {attribute: false, type: Object},
|
||||
openSidePanel: {attribute:false, type: Object}
|
||||
};
|
||||
isLoading: { type: Boolean },
|
||||
userFoundModalOpen: { type: Boolean },
|
||||
userFound: { type: Array },
|
||||
isOpenAddFriendsModal: { type: Boolean },
|
||||
editContent: { type: Object },
|
||||
mySelectedFeeds: { type: Array },
|
||||
refreshFeed: { attribute: false },
|
||||
closeSidePanel: { attribute: false, type: Object },
|
||||
openSidePanel: { attribute: false, type: Object }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [friendsViewStyles];
|
||||
return [friendsViewStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.error = false;
|
||||
this.observerHandler = this.observerHandler.bind(this);
|
||||
this.viewElement = '';
|
||||
this.downObserverElement = '';
|
||||
this.myAddress =
|
||||
window.parent.reduxStore.getState().app.selectedAddress.address;
|
||||
this.errorMessage = '';
|
||||
this.successMessage = '';
|
||||
this.friendList = [];
|
||||
this.userSelected = {};
|
||||
this.isLoading = false;
|
||||
super()
|
||||
this.error = false
|
||||
this.observerHandler = this.observerHandler.bind(this)
|
||||
this.viewElement = ''
|
||||
this.downObserverElement = ''
|
||||
this.myAddress = store.getState().app.selectedAddress.address
|
||||
this.errorMessage = ''
|
||||
this.successMessage = ''
|
||||
this.friendList = []
|
||||
this.userSelected = {}
|
||||
this.isLoading = false
|
||||
this.userFoundModalOpen = false
|
||||
this.userFound = [];
|
||||
this.nodeUrl = this.getNodeUrl();
|
||||
this.myNode = this.getMyNode();
|
||||
this.userFound = []
|
||||
this.nodeUrl = this.getNodeUrl()
|
||||
this.myNode = this.getMyNode()
|
||||
this.isOpenAddFriendsModal = false
|
||||
this.editContent = null
|
||||
this.addToFriendList = this.addToFriendList.bind(this)
|
||||
this._updateFriends = this._updateFriends.bind(this)
|
||||
this._updateFeed = this._updateFeed.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() {
|
||||
const myNode =
|
||||
store.getState().app.nodeConfig.knownNodes[
|
||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||
]
|
||||
|
||||
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[
|
||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||
]
|
||||
return store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
}
|
||||
|
||||
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') || "[]")
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
getMoreFriends() { }
|
||||
|
||||
_updateFriends(event) {
|
||||
this.friendList = event.detail
|
||||
}
|
||||
|
||||
_updateFeed(event) {
|
||||
this.mySelectedFeeds = event.detail
|
||||
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 {
|
||||
this.userSelected = {
|
||||
name
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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-selected-feeds-event', this._updateFeed)
|
||||
window.removeEventListener('add-friend', this._addFriend)
|
||||
|
||||
super.disconnectedCallback()
|
||||
}
|
||||
|
||||
@ -142,18 +204,21 @@ class FriendsView extends connect(store)(LitElement) {
|
||||
const options = {
|
||||
root: this.viewElement,
|
||||
rootMargin: '0px',
|
||||
threshold: 1,
|
||||
};
|
||||
threshold: 1
|
||||
}
|
||||
|
||||
// identify an element to observe
|
||||
const elementToObserve = this.downObserverElement;
|
||||
const elementToObserve = this.downObserverElement
|
||||
|
||||
// passing it a callback function
|
||||
const observer = new IntersectionObserver(
|
||||
this.observerHandler,
|
||||
options
|
||||
);
|
||||
)
|
||||
|
||||
// call `observe()` on that MutationObserver instance,
|
||||
// passing it the element to observe, and the options object
|
||||
observer.observe(elementToObserve);
|
||||
observer.observe(elementToObserve)
|
||||
}
|
||||
|
||||
observerHandler(entries) {
|
||||
@ -163,112 +228,121 @@ class FriendsView extends connect(store)(LitElement) {
|
||||
if (this.friendList.length < 20) {
|
||||
return;
|
||||
}
|
||||
this.getMoreFriends();
|
||||
|
||||
this.getMoreFriends()
|
||||
}
|
||||
}
|
||||
|
||||
async userSearch() {
|
||||
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.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 = []
|
||||
} else {
|
||||
this.userFound = [
|
||||
result
|
||||
];
|
||||
}
|
||||
this.userFoundModalOpen = true;
|
||||
} catch (error) {
|
||||
// let err4string = get("chatpage.cchange35");
|
||||
// parentEpml.request('showSnackBar', `${err4string}`)
|
||||
} else {
|
||||
this.userFound = [
|
||||
result
|
||||
]
|
||||
}
|
||||
|
||||
this.userFoundModalOpen = true;
|
||||
} catch (error) {
|
||||
// let err4string = get("chatpage.cchange35")
|
||||
// parentEpml.request('showSnackBar', `${err4string}`)
|
||||
}
|
||||
}
|
||||
|
||||
getApiKey() {
|
||||
const apiNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
||||
return apiNode.apiKey
|
||||
}
|
||||
async myFollowName(name) {
|
||||
let items = [
|
||||
name
|
||||
]
|
||||
|
||||
async myFollowName(name) {
|
||||
let items = [
|
||||
name
|
||||
]
|
||||
let namesJsonString = JSON.stringify({ "items": items })
|
||||
let namesJsonString = JSON.stringify({ "items": items })
|
||||
|
||||
return await parentEpml.request('apiCall', {
|
||||
url: `/lists/followedNames?apiKey=${this.getApiKey()}`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: `${namesJsonString}`
|
||||
})
|
||||
}
|
||||
return await parentEpml.request('apiCall', {
|
||||
url: `/lists/followedNames?apiKey=${this.getApiKey()}`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: `${namesJsonString}`
|
||||
})
|
||||
}
|
||||
|
||||
async unFollowName(name) {
|
||||
let items = [
|
||||
name
|
||||
]
|
||||
let namesJsonString = JSON.stringify({ "items": items })
|
||||
async unFollowName(name) {
|
||||
let items = [
|
||||
name
|
||||
]
|
||||
|
||||
return await parentEpml.request('apiCall', {
|
||||
url: `/lists/followedNames?apiKey=${this.getApiKey()}`,
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: `${namesJsonString}`
|
||||
})
|
||||
}
|
||||
async addToFriendList(val, isRemove){
|
||||
const copyVal = {...val}
|
||||
let namesJsonString = JSON.stringify({ "items": items })
|
||||
|
||||
return await parentEpml.request('apiCall', {
|
||||
url: `/lists/followedNames?apiKey=${this.getApiKey()}`,
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: `${namesJsonString}`
|
||||
})
|
||||
}
|
||||
|
||||
async addToFriendList(val, isRemove) {
|
||||
const copyVal = { ...val }
|
||||
delete copyVal.mySelectedFeeds
|
||||
if(isRemove){
|
||||
this.friendList = this.friendList.filter((item)=> item.name !== copyVal.name)
|
||||
}else if(this.editContent){
|
||||
const findFriend = this.friendList.findIndex(item=> item.name === copyVal.name)
|
||||
if(findFriend !== -1){
|
||||
|
||||
if (isRemove) {
|
||||
this.friendList = this.friendList.filter((item) => item.name !== copyVal.name)
|
||||
} else if (this.editContent) {
|
||||
const findFriend = this.friendList.findIndex(item => item.name === copyVal.name)
|
||||
if (findFriend !== -1) {
|
||||
const copyList = [...this.friendList]
|
||||
copyList[findFriend] = copyVal
|
||||
this.friendList = copyList
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
this.friendList = [...this.friendList, copyVal]
|
||||
}
|
||||
if(!copyVal.willFollow || isRemove) {
|
||||
|
||||
if (!copyVal.willFollow || isRemove) {
|
||||
await this.unFollowName(copyVal.name)
|
||||
} else if(copyVal.willFollow){
|
||||
} else if (copyVal.willFollow) {
|
||||
await this.myFollowName(copyVal.name)
|
||||
}
|
||||
|
||||
this.setMySelectedFeeds(val.mySelectedFeeds)
|
||||
await new Promise((res)=> {
|
||||
setTimeout(()=> {
|
||||
|
||||
await new Promise((res) => {
|
||||
setTimeout(() => {
|
||||
res()
|
||||
},50)
|
||||
}, 50)
|
||||
})
|
||||
this.userSelected = {};
|
||||
|
||||
this.userSelected = {}
|
||||
this.shadowRoot.getElementById('sendTo').value = ''
|
||||
this.isLoading = false;
|
||||
this.isLoading = false
|
||||
this.isOpenAddFriendsModal = false
|
||||
this.editContent = null
|
||||
this.setMyFriends(this.friendList)
|
||||
if(!isRemove && this.friendList.length === 1){
|
||||
|
||||
if (!isRemove && this.friendList.length === 1) {
|
||||
this.refreshFeed()
|
||||
}
|
||||
}
|
||||
setMyFriends(friendList){
|
||||
|
||||
setMyFriends(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 = {
|
||||
...tempSettingsData,
|
||||
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(
|
||||
new CustomEvent('temp-settings-data-event', {
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}),
|
||||
);
|
||||
|
||||
bubbles: true,
|
||||
composed: true
|
||||
})
|
||||
)
|
||||
}
|
||||
setMySelectedFeeds(mySelectedFeeds){
|
||||
|
||||
setMySelectedFeeds(mySelectedFeeds) {
|
||||
this.mySelectedFeeds = mySelectedFeeds
|
||||
const tempSettingsData= JSON.parse(localStorage.getItem('temp-settings-data') || "{}")
|
||||
const tempSettingsData = JSON.parse(localStorage.getItem('temp-settings-data') || "{}")
|
||||
|
||||
const newTemp = {
|
||||
...tempSettingsData,
|
||||
friendsFeed: {
|
||||
@ -297,98 +373,37 @@ class FriendsView extends connect(store)(LitElement) {
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem('temp-settings-data', JSON.stringify(newTemp));
|
||||
localStorage.setItem('friends-my-selected-feeds', JSON.stringify(mySelectedFeeds));
|
||||
}
|
||||
openEditFriend(val){
|
||||
this.isOpenAddFriendsModal = true
|
||||
this.userSelected = val
|
||||
this.editContent = {...val, mySelectedFeeds: this.mySelectedFeeds}
|
||||
localStorage.setItem('temp-settings-data', JSON.stringify(newTemp))
|
||||
localStorage.setItem('friends-my-selected-feeds', JSON.stringify(mySelectedFeeds))
|
||||
}
|
||||
|
||||
onClose(){
|
||||
openEditFriend(val) {
|
||||
this.isOpenAddFriendsModal = true
|
||||
this.userSelected = val
|
||||
this.editContent = { ...val, mySelectedFeeds: this.mySelectedFeeds }
|
||||
}
|
||||
|
||||
onClose() {
|
||||
this.isLoading = false;
|
||||
this.isOpenAddFriendsModal = false
|
||||
this.editContent = null
|
||||
this.userSelected = {}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
// Standard functions
|
||||
getApiKey() {
|
||||
const coreNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
return coreNode.apiKey
|
||||
}
|
||||
|
||||
<vaadin-icon
|
||||
@click=${this.userSearch}
|
||||
slot="icon"
|
||||
icon="vaadin:search"
|
||||
class="search-icon">
|
||||
</vaadin-icon>
|
||||
isEmptyArray(arr) {
|
||||
if (!arr) { return true }
|
||||
return arr.length === 0
|
||||
}
|
||||
|
||||
</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>
|
||||
`;
|
||||
round(number) {
|
||||
return (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8)
|
||||
}
|
||||
}
|
||||
|
||||
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 {registerTranslateConfig, translate, use} from '../../translate'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { languageSelectorStyles } from '../styles/core-css'
|
||||
|
||||
// Multi language support
|
||||
import { registerTranslateConfig, translate, use } from '../../translate'
|
||||
|
||||
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')
|
||||
|
||||
if (checkLanguage === null || checkLanguage.length === 0) {
|
||||
localStorage.setItem('qortalLanguage', 'us')
|
||||
use('us')
|
||||
localStorage.setItem('qortalLanguage', 'us')
|
||||
use('us')
|
||||
} else {
|
||||
use(checkLanguage)
|
||||
use(checkLanguage)
|
||||
}
|
||||
|
||||
class LanguageSelector extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [
|
||||
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;
|
||||
}
|
||||
static get styles() {
|
||||
return [languageSelectorStyles]
|
||||
}
|
||||
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
|
||||
select option {
|
||||
color: var(--black);
|
||||
background: var(--white);
|
||||
line-height: 34px;
|
||||
}
|
||||
`
|
||||
]
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<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>
|
||||
`
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
firstUpdated() {
|
||||
const myElement = this.shadowRoot.getElementById('languageSelect')
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<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>
|
||||
`
|
||||
}
|
||||
myElement.addEventListener('change', () => {
|
||||
this.selectElement()
|
||||
})
|
||||
|
||||
firstUpdated() {
|
||||
const myElement = this.shadowRoot.getElementById('languageSelect')
|
||||
this.selectElement()
|
||||
}
|
||||
|
||||
myElement.addEventListener("change", () => {
|
||||
this.selectElement()
|
||||
})
|
||||
selectElement() {
|
||||
const selectedLanguage = localStorage.getItem('qortalLanguage')
|
||||
let element = this.shadowRoot.getElementById('languageSelect')
|
||||
element.value = selectedLanguage
|
||||
}
|
||||
|
||||
this.selectElement()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
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 {connect} from 'pwa-helpers'
|
||||
import {store} from '../../store.js'
|
||||
import {stateAwait} from '../../stateAwait.js'
|
||||
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 { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { stateAwait } from '../../stateAwait'
|
||||
import {
|
||||
addAutoLoadImageChat,
|
||||
addChatLastSeen,
|
||||
@ -32,352 +19,388 @@ import {
|
||||
setNewTab,
|
||||
setSideEffectAction,
|
||||
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.reduxAction = {
|
||||
addAutoLoadImageChat: addAutoLoadImageChat,
|
||||
removeAutoLoadImageChat: removeAutoLoadImageChat,
|
||||
addChatLastSeen: addChatLastSeen,
|
||||
allowQAPPAutoAuth: allowQAPPAutoAuth,
|
||||
removeQAPPAutoAuth: removeQAPPAutoAuth,
|
||||
allowQAPPAutoLists: allowQAPPAutoLists,
|
||||
removeQAPPAutoLists: removeQAPPAutoLists,
|
||||
addTabInfo: addTabInfo,
|
||||
setTabNotifications: setTabNotifications,
|
||||
setNewTab: setNewTab,
|
||||
setNewNotification: setNewNotification,
|
||||
setSideEffectAction: setSideEffectAction,
|
||||
allowQAPPAutoFriendsList: allowQAPPAutoFriendsList,
|
||||
removeQAPPAutoFriendsList: removeQAPPAutoFriendsList,
|
||||
allowShowSyncIndicator: allowShowSyncIndicator,
|
||||
removeShowSyncIndicator: removeShowSyncIndicator
|
||||
addAutoLoadImageChat: addAutoLoadImageChat,
|
||||
removeAutoLoadImageChat: removeAutoLoadImageChat,
|
||||
addChatLastSeen: addChatLastSeen,
|
||||
allowQAPPAutoAuth: allowQAPPAutoAuth,
|
||||
removeQAPPAutoAuth: removeQAPPAutoAuth,
|
||||
allowQAPPAutoLists: allowQAPPAutoLists,
|
||||
removeQAPPAutoLists: removeQAPPAutoLists,
|
||||
addTabInfo: addTabInfo,
|
||||
setTabNotifications: setTabNotifications,
|
||||
setNewTab: setNewTab,
|
||||
setNewNotification: setNewNotification,
|
||||
setSideEffectAction: setSideEffectAction,
|
||||
allowQAPPAutoFriendsList: allowQAPPAutoFriendsList,
|
||||
removeQAPPAutoFriendsList: removeQAPPAutoFriendsList,
|
||||
allowShowSyncIndicator: allowShowSyncIndicator,
|
||||
removeShowSyncIndicator: removeShowSyncIndicator
|
||||
}
|
||||
|
||||
const animationDuration = 0.7 // Seconds
|
||||
|
||||
class LoginView extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
loggedIn: { type: Boolean },
|
||||
selectedPage: { type: String },
|
||||
pages: { type: Object },
|
||||
rippleIsOpen: { type: Boolean },
|
||||
config: { type: Object },
|
||||
rippleLoadingMessage: { type: String },
|
||||
selectedPageElement: {},
|
||||
nodeConfig: { type: Object },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
loggedIn: { type: Boolean },
|
||||
selectedPage: { type: String },
|
||||
pages: { type: Object },
|
||||
rippleIsOpen: { type: Boolean },
|
||||
config: { type: Object },
|
||||
rippleLoadingMessage: { type: String },
|
||||
selectedPageElement: {},
|
||||
nodeConfig: { type: Object },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [
|
||||
css``
|
||||
]
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
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() {
|
||||
return 'welcome'
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<style>
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
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'
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
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 => {
|
||||
return 'primary' in state.config.styles.theme.colors
|
||||
}).catch(e => console.error(e))
|
||||
.login-card-container {
|
||||
max-width: 1240px;
|
||||
max-height: var(--window-height);
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
width: calc(100vw);
|
||||
}
|
||||
|
||||
const loginContainerPages = this.shadowRoot.querySelector('#loginContainerPages')
|
||||
const loginCard = this.shadowRoot.querySelector('#login-card')
|
||||
const navigate = e => {
|
||||
this.selectPage(e.detail.page)
|
||||
}
|
||||
const updatedProperty = e => {
|
||||
// ...
|
||||
const selectedPageElement = this.selectedPageElement
|
||||
this.selectedPageElement = {}
|
||||
setTimeout(() => { this.selectedPageElement = selectedPageElement }, 1) // Yuck
|
||||
}
|
||||
loginContainerPages.addEventListener('selected-item-changed', () => {
|
||||
.qortal-logo {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 200px;
|
||||
max-width: 40%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
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) {
|
||||
this.selectedPageElement.removeEventListener('navigate', navigate)
|
||||
this.selectedPageElement.removeEventListener('updatedProperty', updatedProperty)
|
||||
}
|
||||
this.selectedPageElement = {}
|
||||
loginCard.classList.remove('animated')
|
||||
loginCard.className += ' animated'
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
#loginContainerPages {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
this.selectedPageElement = loginContainerPages.selectedItem
|
||||
#loginContainerPages [page] {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
this.selectedPageElement.addEventListener('navigate', navigate)
|
||||
this.selectedPageElement.addEventListener('updatedProperty', updatedProperty)
|
||||
setTimeout(() => loginCard.classList.remove('animated'), animationDuration * 1000)
|
||||
}, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
.login-card {
|
||||
min-width: 340px;
|
||||
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;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<style>
|
||||
canvas {
|
||||
display: block;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
.login-card p {
|
||||
margin-top: 0;
|
||||
font-size: 1rem;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
.login-card h1 {
|
||||
margin-bottom: 12px;
|
||||
font-size: 64px;
|
||||
}
|
||||
|
||||
.login-card-container {
|
||||
max-width:1240px;
|
||||
max-height:var(--window-height);
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
width: calc(100vw);
|
||||
}
|
||||
.login-card h5 {
|
||||
margin-top: -16px;
|
||||
margin-left: 100px;
|
||||
font-size: 14px;
|
||||
color: var(--black);
|
||||
}
|
||||
|
||||
.qortal-logo {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width:200px;
|
||||
max-width:40%;
|
||||
z-index:1;
|
||||
}
|
||||
.login-card h6 {
|
||||
font-size: 12px;
|
||||
color: var(--mdc-theme-primary);
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
.login-card iron-pages {
|
||||
height: 100%;
|
||||
margin-top: -16px;
|
||||
}
|
||||
|
||||
#loginContainerPages {
|
||||
display:inline;
|
||||
}
|
||||
.backButton {
|
||||
padding-top: 18px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#loginContainerPages [page] {
|
||||
background: none;
|
||||
padding:0;
|
||||
}
|
||||
#login-pages-nav {
|
||||
text-align: left;
|
||||
padding: 12px 0 8px 0;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
min-width: 340px;
|
||||
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;
|
||||
}
|
||||
#nav-next {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.login-card p {
|
||||
margin-top: 0;
|
||||
font-size: 1rem;
|
||||
font-style: italic;
|
||||
}
|
||||
@media only screen and (min-width: ${getComputedStyle(document.body).getPropertyValue('--layout-breakpoint-tablet')}) {
|
||||
|
||||
.login-card h1 {
|
||||
margin-bottom:12px;
|
||||
font-size:64px;
|
||||
}
|
||||
/* Desktop/tablet */
|
||||
.login-card {
|
||||
max-width: 460px;
|
||||
}
|
||||
|
||||
.login-card h5 {
|
||||
margin-top: -16px;
|
||||
margin-left: 100px;
|
||||
font-size: 14px;
|
||||
color: var(--black);
|
||||
}
|
||||
#loginContainerPages [page] {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.login-card h6 {
|
||||
font-size: 12px;
|
||||
color: var(--mdc-theme-primary);
|
||||
}
|
||||
#loginContainerPages [page="welcome"] {}
|
||||
}
|
||||
|
||||
.login-card iron-pages {
|
||||
height:100%;
|
||||
margin-top: -16px;
|
||||
}
|
||||
@media only screen and (max-width: ${getComputedStyle(document.body).getPropertyValue('--layout-breakpoint-tablet')}) {
|
||||
|
||||
.backButton {
|
||||
padding-top:18px;
|
||||
text-align:center;
|
||||
}
|
||||
/* Mobile */
|
||||
.qortal-logo {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#login-pages-nav {
|
||||
text-align: left;
|
||||
padding: 12px 0 8px 0;
|
||||
}
|
||||
.login-card {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
top: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#nav-next {
|
||||
float: right;
|
||||
}
|
||||
.backButton {
|
||||
text-align: left;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: ${getComputedStyle(document.body).getPropertyValue('--layout-breakpoint-tablet')}) {
|
||||
/* Desktop/tablet */
|
||||
.login-card {
|
||||
max-width:460px;
|
||||
}
|
||||
#loginContainerPages [page] {
|
||||
border-radius: 4px;
|
||||
}
|
||||
#loginContainerPages [page="welcome"] {
|
||||
}
|
||||
}
|
||||
.login-card h5 {
|
||||
margin-top: 0px;
|
||||
margin-left: 0px;
|
||||
font-size: 14px;
|
||||
color: var(--black);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: ${getComputedStyle(document.body).getPropertyValue('--layout-breakpoint-tablet')}) {
|
||||
/* Mobile */
|
||||
.qortal-logo {
|
||||
display:none;
|
||||
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 {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-20%);
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-20%);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes grow-up {
|
||||
from {
|
||||
overflow:hidden;
|
||||
max-height:0;
|
||||
}
|
||||
to {
|
||||
overflow:hidden;
|
||||
max-height:var(--window-height);
|
||||
}
|
||||
}
|
||||
@keyframes grow-up {
|
||||
from {
|
||||
overflow: hidden;
|
||||
max-height: 0;
|
||||
}
|
||||
|
||||
iron-pages .animated, .animated {
|
||||
animation-duration: ${animationDuration}s;
|
||||
animation-name: grow-up;
|
||||
}
|
||||
to {
|
||||
overflow: hidden;
|
||||
max-height: var(--window-height);
|
||||
}
|
||||
}
|
||||
|
||||
div[page] > paper-icon-button {
|
||||
margin:12px;
|
||||
}
|
||||
iron-pages .animated,
|
||||
.animated {
|
||||
animation-duration: ${animationDuration}s;
|
||||
animation-name: grow-up;
|
||||
}
|
||||
|
||||
.corner-box {
|
||||
border-color: var(--mdc-theme-primary) !important;
|
||||
}
|
||||
div[page]>paper-icon-button {
|
||||
margin: 12px;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
visibility: hidden;
|
||||
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>
|
||||
`
|
||||
}
|
||||
.corner-box {
|
||||
border-color: var(--mdc-theme-primary) !important;
|
||||
}
|
||||
|
||||
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}`
|
||||
[hidden] {
|
||||
visibility: hidden;
|
||||
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>
|
||||
`
|
||||
}
|
||||
|
||||
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) {
|
||||
this.selectedPage = newPage
|
||||
}
|
||||
const navigate = e => {
|
||||
this.selectPage(e.detail.page)
|
||||
}
|
||||
|
||||
stateChanged(state) {
|
||||
if (this.loggedIn && !state.app.loggedIn) this.cleanup()
|
||||
this.loggedIn = state.app.loggedIn
|
||||
this.config = state.config
|
||||
this.nodeConfig = state.app.nodeConfig
|
||||
}
|
||||
const updatedProperty = e => {
|
||||
const selectedPageElement = this.selectedPageElement
|
||||
|
||||
cleanup() {
|
||||
this.selectedPage = 'welcome'
|
||||
}
|
||||
this.selectedPageElement = {}
|
||||
|
||||
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 {translate} from '../../../translate'
|
||||
|
||||
import { html, LitElement } from 'lit'
|
||||
import { welcomePageStyles } from '../../styles/core-css'
|
||||
import '@material/mwc-button'
|
||||
|
||||
// Multi language support
|
||||
import { translate } from '../../../translate'
|
||||
|
||||
class WelcomePage extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
nextHidden: { type: Boolean, notify: true },
|
||||
nextEnabled: { type: Boolean, notify: 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 properties() {
|
||||
return {
|
||||
hideNav: { type: Boolean, notify: true },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
* {
|
||||
--mdc-theme-primary: var(--login-button);
|
||||
--mdc-theme-secondary: var(--mdc-theme-primary);
|
||||
--mdc-button-outline-color: var(--general-color-blue);
|
||||
}
|
||||
static get styles() {
|
||||
return [welcomePageStyles]
|
||||
}
|
||||
|
||||
.button-outline {
|
||||
margin: 6px;
|
||||
width: 90%;
|
||||
max-width:90vw;
|
||||
border-top: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.hideNav = true
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
|
||||
.welcome-page {
|
||||
padding: 12px 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
`
|
||||
}
|
||||
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>
|
||||
`
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.hideNav = true
|
||||
this.nextText = ''
|
||||
this.welcomeMessage = 'Welcome to Qortal'
|
||||
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
|
||||
}))
|
||||
}
|
||||
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 {connect} from 'pwa-helpers'
|
||||
import {store} from '../../store.js'
|
||||
import {doLogout} from '../../redux/app/app-actions.js'
|
||||
import {translate} from '../../../translate'
|
||||
|
||||
import '@polymer/paper-dialog/paper-dialog.js'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store.js'
|
||||
import { doLogout } from '../../redux/app/app-actions.js'
|
||||
import { logoutViewStyles } from '../../styles/core-css'
|
||||
import '@material/mwc-button'
|
||||
import '@polymer/paper-dialog/paper-dialog.js'
|
||||
|
||||
// Multi language support
|
||||
import { translate } from '../../../translate'
|
||||
|
||||
class LogoutView extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
* {
|
||||
--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);
|
||||
}
|
||||
static get styles() {
|
||||
return [logoutViewStyles]
|
||||
}
|
||||
|
||||
.decline {
|
||||
--mdc-theme-primary: var(--mdc-theme-error)
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
|
||||
.buttons {
|
||||
text-align:right;
|
||||
}
|
||||
`
|
||||
}
|
||||
render() {
|
||||
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() {
|
||||
super()
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
openLogout() {
|
||||
this.shadowRoot.getElementById('userLogoutDialog').open()
|
||||
}
|
||||
|
||||
render() {
|
||||
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=${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>
|
||||
`
|
||||
}
|
||||
async confirm(e) {
|
||||
store.dispatch(doLogout())
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
openLogout() {
|
||||
this.shadowRoot.getElementById('userLogoutDialog').open()
|
||||
}
|
||||
|
||||
async confirm(e) {
|
||||
store.dispatch(doLogout())
|
||||
}
|
||||
|
||||
decline(e) {
|
||||
this.shadowRoot.getElementById('userLogoutDialog').close()
|
||||
}
|
||||
decline() {
|
||||
this.shadowRoot.getElementById('userLogoutDialog').close()
|
||||
this.requestUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('logout-view', LogoutView)
|
||||
window.customElements.define('logout-view', LogoutView)
|
@ -1,77 +1,66 @@
|
||||
import {html, LitElement} from 'lit'
|
||||
import {installRouter} from 'pwa-helpers/router.js'
|
||||
import {connect} from 'pwa-helpers'
|
||||
import {store} from '../store.js'
|
||||
import {doNavigate} from '../redux/app/app-actions.js'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../store'
|
||||
import { installRouter } from 'pwa-helpers/router'
|
||||
import { doNavigate } from '../redux/app/app-actions'
|
||||
import { loadPlugins } from '../plugins/load-plugins'
|
||||
import isElectron from 'is-electron'
|
||||
import '../plugins/streams.js'
|
||||
|
||||
import {loadPlugins} from '../plugins/load-plugins.js'
|
||||
|
||||
import '../styles/app-styles.js'
|
||||
import './login-view/login-view.js'
|
||||
import './app-view.js'
|
||||
import './login-view/login-view'
|
||||
import './app-view'
|
||||
import '../plugins/streams'
|
||||
import '../styles/app-styles'
|
||||
|
||||
installRouter((location) => store.dispatch(doNavigate(location)))
|
||||
|
||||
class MainApp extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
name: { type: String },
|
||||
loggedIn: { type: Boolean }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
name: { type: String },
|
||||
loggedIn: { type: Boolean }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return []
|
||||
}
|
||||
render() {
|
||||
return html`${this.renderViews(this.loggedIn)}`
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`${this.renderViews(this.loggedIn)}`
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback()
|
||||
this.initial = 0
|
||||
|
||||
/**
|
||||
* Dynamic renderViews method to introduce conditional rendering of views based on user's logged in state.
|
||||
* @param {Boolean} isLoggedIn
|
||||
*/
|
||||
if (!isElectron()) {
|
||||
} else {
|
||||
window.addEventListener('contextmenu', (event) => {
|
||||
event.preventDefault()
|
||||
window.electronAPI.showMyMenu()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
renderViews(isLoggedIn) {
|
||||
if (isLoggedIn) {
|
||||
return html`
|
||||
<app-view></app-view>
|
||||
`
|
||||
} else {
|
||||
return html`
|
||||
<login-view></login-view>
|
||||
`
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Dynamic renderViews method to introduce conditional rendering of views based on user's logged in state.
|
||||
* @param {Boolean} isLoggedIn
|
||||
*/
|
||||
renderViews(isLoggedIn) {
|
||||
if (isLoggedIn) {
|
||||
return html`<app-view></app-view>`
|
||||
} else {
|
||||
return html`<login-view></login-view>`
|
||||
}
|
||||
}
|
||||
|
||||
stateChanged(state) {
|
||||
this.loggedIn = state.app.loggedIn
|
||||
if (this.loggedIn === true && this.initial === 0) {
|
||||
this.initial = this.initial + 1
|
||||
this._loadPlugins()
|
||||
}
|
||||
document.title = state.config.coin.name
|
||||
}
|
||||
_loadPlugins() {
|
||||
loadPlugins()
|
||||
}
|
||||
|
||||
_loadPlugins() {
|
||||
loadPlugins()
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback()
|
||||
this.initial = 0
|
||||
|
||||
if (!isElectron()) {
|
||||
} else {
|
||||
window.addEventListener('contextmenu', (event) => {
|
||||
event.preventDefault()
|
||||
window.electronAPI.showMyMenu()
|
||||
})
|
||||
}
|
||||
}
|
||||
stateChanged(state) {
|
||||
this.loggedIn = state.app.loggedIn
|
||||
if (this.loggedIn === true && this.initial === 0) {
|
||||
this.initial = this.initial + 1
|
||||
this._loadPlugins()
|
||||
}
|
||||
document.title = state.config.coin.name
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('main-app', MainApp)
|
||||
window.customElements.define('main-app', MainApp)
|
@ -1,140 +1,114 @@
|
||||
import {css, html, LitElement} from 'lit'
|
||||
import {registerTranslateConfig, translate, use} from '../../translate'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { newSelectorStyles } from '../styles/core-css'
|
||||
|
||||
// Multi language support
|
||||
import { registerTranslateConfig, translate, use } from '../../translate'
|
||||
|
||||
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')
|
||||
|
||||
if (checkLanguage === null || checkLanguage.length === 0) {
|
||||
localStorage.setItem('qortalLanguage', 'us')
|
||||
use('us')
|
||||
localStorage.setItem('qortalLanguage', 'us')
|
||||
use('us')
|
||||
} else {
|
||||
use(checkLanguage)
|
||||
use(checkLanguage)
|
||||
}
|
||||
|
||||
class NewSelector extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [
|
||||
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;
|
||||
}
|
||||
static get styles() {
|
||||
return [newSelectorStyles]
|
||||
}
|
||||
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
|
||||
select option {
|
||||
color: var(--black);
|
||||
background: var(--white);
|
||||
line-height: 34px;
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<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>
|
||||
|
||||
select option:hover {
|
||||
color: var(--white);
|
||||
background: var(--black);
|
||||
line-height: 34px;
|
||||
cursor: pointer;
|
||||
}
|
||||
`
|
||||
]
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
firstUpdated() {
|
||||
const myElement = this.shadowRoot.getElementById('languageNew')
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<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('change', () => {
|
||||
this.selectElement()
|
||||
})
|
||||
|
||||
`
|
||||
}
|
||||
myElement.addEventListener('click', () => {
|
||||
const element1 = localStorage.getItem('qortalLanguage')
|
||||
const element2 = this.shadowRoot.getElementById('languageNew').value
|
||||
|
||||
firstUpdated() {
|
||||
const myElement = this.shadowRoot.getElementById('languageNew')
|
||||
if (element1 === element2) {
|
||||
myElement.style.display = 'none'
|
||||
}
|
||||
})
|
||||
|
||||
myElement.addEventListener("change", () => {
|
||||
this.selectElement()
|
||||
})
|
||||
this.selectElement()
|
||||
}
|
||||
|
||||
myElement.addEventListener("click", () => {
|
||||
const element1 = localStorage.getItem('qortalLanguage')
|
||||
const element2 = this.shadowRoot.getElementById('languageNew').value
|
||||
if (element1 === element2) {
|
||||
myElement.style.display = "none"
|
||||
}
|
||||
})
|
||||
selectElement() {
|
||||
const selectedLanguage = localStorage.getItem('qortalLanguage')
|
||||
|
||||
this.selectElement()
|
||||
}
|
||||
let element = this.shadowRoot.getElementById('languageNew')
|
||||
|
||||
selectElement() {
|
||||
const selectedLanguage = localStorage.getItem('qortalLanguage')
|
||||
let element = this.shadowRoot.getElementById('languageNew')
|
||||
element.value = selectedLanguage
|
||||
element.style.display = "none"
|
||||
}
|
||||
element.value = selectedLanguage
|
||||
element.style.display = 'none'
|
||||
}
|
||||
|
||||
changeLanguage(event) {
|
||||
use(event.target.value)
|
||||
localStorage.setItem('qortalLanguage', event.target.value)
|
||||
}
|
||||
changeLanguage(event) {
|
||||
use(event.target.value)
|
||||
localStorage.setItem('qortalLanguage', event.target.value)
|
||||
}
|
||||
|
||||
toggleMenu() {
|
||||
let mySwitchDisplay = this.shadowRoot.getElementById('languageNew')
|
||||
if(mySwitchDisplay.style.display == "none") {
|
||||
mySwitchDisplay.style.display = "block"
|
||||
} else {
|
||||
mySwitchDisplay.style.display = "none"
|
||||
}
|
||||
}
|
||||
toggleMenu() {
|
||||
let mySwitchDisplay = this.shadowRoot.getElementById('languageNew')
|
||||
|
||||
if (mySwitchDisplay.style.display == 'none') {
|
||||
mySwitchDisplay.style.display = 'block'
|
||||
} 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 {connect} from 'pwa-helpers';
|
||||
|
||||
import '@vaadin/item';
|
||||
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 {setNewNotification} from '../../redux/app/app-actions.js';
|
||||
import '@material/mwc-icon';
|
||||
import {get, translate} from '../../../translate'
|
||||
import {repeat} from 'lit/directives/repeat.js';
|
||||
import '../../../../plugins/plugins/core/components/TimeAgo.js';
|
||||
import './popover.js';
|
||||
import { html, LitElement } from 'lit'
|
||||
import { repeat } from 'lit/directives/repeat.js'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { setNewNotification } from '../../redux/app/app-actions'
|
||||
import { notificationBellGeneralStyles, notificationItemTxStyles } from '../../styles/core-css'
|
||||
import './popover.js'
|
||||
import '../../../../plugins/plugins/core/components/TimeAgo'
|
||||
import '@material/mwc-icon'
|
||||
import '@polymer/paper-icon-button/paper-icon-button.js'
|
||||
import '@polymer/iron-icons/iron-icons.js'
|
||||
import '@vaadin/item'
|
||||
import '@vaadin/list-box'
|
||||
|
||||
// Multi language support
|
||||
import { get, translate } from '../../../translate'
|
||||
|
||||
class NotificationBellGeneral extends connect(store)(LitElement) {
|
||||
static properties = {
|
||||
notifications: { type: Array },
|
||||
showNotifications: { type: Boolean },
|
||||
notificationCount: { type: Boolean },
|
||||
theme: { type: String, reflect: true },
|
||||
currentNotification: { type: Object },
|
||||
};
|
||||
static get properties() {
|
||||
return {
|
||||
notifications: { type: Array },
|
||||
showNotifications: { type: Boolean },
|
||||
notificationCount: { type: Boolean },
|
||||
currentNotification: { type: Object },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [notificationBellGeneralStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.notifications = [];
|
||||
this.showNotifications = false;
|
||||
this.notificationCount = false;
|
||||
this.initialFetch = false;
|
||||
this.theme = localStorage.getItem('qortalTheme')
|
||||
? localStorage.getItem('qortalTheme')
|
||||
: '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;
|
||||
|
||||
}
|
||||
super()
|
||||
this.notifications = []
|
||||
this.showNotifications = false
|
||||
this.notificationCount = false
|
||||
this.initialFetch = false
|
||||
this.currentNotification = null
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
|
||||
render() {
|
||||
const hasOngoing = this.notifications.find(
|
||||
(notification) => notification.status !== 'confirmed'
|
||||
);
|
||||
)
|
||||
|
||||
return html`
|
||||
<div class="layout">
|
||||
<popover-component
|
||||
for="popover-notification"
|
||||
message=${get('notifications.explanation')}
|
||||
></popover-component>
|
||||
<div
|
||||
id="popover-notification"
|
||||
@click=${() => this._toggleNotifications()}
|
||||
>
|
||||
${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>
|
||||
`}
|
||||
<popover-component for="popover-notification" message=${get('notifications.explanation')}></popover-component>
|
||||
<div id="popover-notification" @click=${() => this._toggleNotifications()}>
|
||||
${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>
|
||||
${hasOngoing
|
||||
? html`
|
||||
<span
|
||||
class="count"
|
||||
style="cursor:pointer"
|
||||
@click=${() => this._toggleNotifications()}
|
||||
>
|
||||
<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}
|
||||
>
|
||||
${hasOngoing ? html`
|
||||
<span class="count" style="cursor:pointer" @click=${() => this._toggleNotifications()}>
|
||||
<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">
|
||||
${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(
|
||||
this.notifications,
|
||||
@ -166,18 +74,82 @@ class NotificationBellGeneral extends connect(store)(LitElement) {
|
||||
<notification-item-tx
|
||||
.changeStatus=${(val1, val2) =>
|
||||
this.changeStatus(val1, val2)}
|
||||
status=${notification.status}
|
||||
timestamp=${notification.timestamp}
|
||||
type=${notification.type}
|
||||
signature=${notification.reference
|
||||
.signature}
|
||||
status=${notification.status}
|
||||
timestamp=${notification.timestamp}
|
||||
type=${notification.type}
|
||||
signature=${notification.reference
|
||||
.signature
|
||||
}
|
||||
></notification-item-tx>
|
||||
`
|
||||
)}
|
||||
</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() {
|
||||
@ -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) {
|
||||
static properties = {
|
||||
status: { type: String },
|
||||
type: { type: String },
|
||||
timestamp: { type: Number },
|
||||
signature: { type: String },
|
||||
changeStatus: { attribute: false },
|
||||
};
|
||||
static get properties() {
|
||||
return {
|
||||
status: { type: String },
|
||||
type: { type: String },
|
||||
timestamp: { type: Number },
|
||||
signature: { type: String },
|
||||
changeStatus: { attribute: false }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [notificationItemTxStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.nodeUrl = this.getNodeUrl();
|
||||
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();
|
||||
super()
|
||||
this.nodeUrl = this.getNodeUrl()
|
||||
this.myNode = this.getMyNode()
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="notification-item" @click=${() => {}}>
|
||||
<div class="notification-item" @click=${() => { }}>
|
||||
<div>
|
||||
<p style="margin-bottom:10px; font-weight:bold">
|
||||
${translate('transpage.tchange1')}
|
||||
@ -345,187 +198,66 @@ class NotificationItemTx extends connect(store)(LitElement) {
|
||||
${translate('walletpage.wchange35')}: ${this.type}
|
||||
</p>
|
||||
<p style="margin-bottom:5px">
|
||||
${translate('tubespage.schange28')}:
|
||||
${this.status === 'confirming'
|
||||
? translate('notifications.notify1')
|
||||
: translate('notifications.notify2')}
|
||||
${translate('tubespage.schange28')}: ${this.status === 'confirming' ? translate('notifications.notify1') : translate('notifications.notify2')}
|
||||
</p>
|
||||
${this.status !== 'confirmed'
|
||||
? html`
|
||||
<div class="centered">
|
||||
<div class="loader">Loading...</div>
|
||||
</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
|
||||
>
|
||||
`
|
||||
: ''}
|
||||
${this.status !== 'confirmed' ? html`<div class="centered"><div class="loader">Loading...</div></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>
|
||||
`;
|
||||
`
|
||||
}
|
||||
|
||||
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() {
|
||||
if (this.notifications.length === 0) return;
|
||||
this.showNotifications = !this.showNotifications;
|
||||
if (this.notifications.length === 0) return
|
||||
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 {connect} from 'pwa-helpers'
|
||||
|
||||
import { css, html, LitElement } from 'lit'
|
||||
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/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) {
|
||||
static get properties() {
|
||||
return {
|
||||
notifications: { type: Array },
|
||||
showNotifications: { type: Boolean },
|
||||
notificationCount: { type: Boolean },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static properties = {
|
||||
notifications: { type: Array },
|
||||
showNotifications: { type: Boolean },
|
||||
notificationCount: { type: Boolean },
|
||||
theme: { type: String, reflect: true },
|
||||
}
|
||||
static get styles() {
|
||||
return [notificationBellStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.notifications = []
|
||||
this.showNotifications = false
|
||||
this.notificationCount = false
|
||||
this.initialFetch = false
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.notifications = []
|
||||
this.showNotifications = false
|
||||
this.notificationCount = false
|
||||
this.initialFetch = false
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.getNotifications();
|
||||
document.addEventListener('click', (event) => {
|
||||
const path = event.composedPath()
|
||||
if (!path.includes(this)) {
|
||||
this.showNotifications = false
|
||||
}
|
||||
})
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<div class="layout">
|
||||
${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`
|
||||
<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() {
|
||||
const apiNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
||||
firstUpdated() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
async getNotifications() {
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||
async getNotifications() {
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||
|
||||
let interval = null
|
||||
let stop = false
|
||||
let interval = null
|
||||
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 query = `qortal_qmail_${recipientName.slice(
|
||||
0,
|
||||
20
|
||||
)}_${recipientAddress.slice(-6)}_mail_`
|
||||
const url = `${nodeUrl}/arbitrary/resources/search?service=MAIL_PRIVATE&query=${query}&limit=10&includemetadata=false&offset=0&reverse=true&excludeblocked=true`
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
const url = `${nodeUrl}/arbitrary/resources/search?service=MAIL_PRIVATE&query=${query}&limit=10&includemetadata=false&offset=0&reverse=true&excludeblocked=true`
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
return await response.json()
|
||||
}
|
||||
}
|
||||
|
||||
if (!stop && !this.showNotifications) {
|
||||
stop = true
|
||||
try {
|
||||
const address = window.parent.reduxStore.getState().app?.selectedAddress?.address;
|
||||
const name = window.parent.reduxStore.getState().app?.accountInfo?.names[0]?.name
|
||||
if (!stop && !this.showNotifications) {
|
||||
stop = true
|
||||
|
||||
if (!name || !address) return
|
||||
const mailArray = await getMail(name, address)
|
||||
let notificationsToShow = []
|
||||
if (mailArray.length > 0) {
|
||||
const lastVisited = localStorage.getItem("Q-Mail-last-visited")
|
||||
try {
|
||||
const address = window.parent.reduxStore.getState().app?.selectedAddress?.address;
|
||||
const name = window.parent.reduxStore.getState().app?.accountInfo?.names[0]?.name
|
||||
|
||||
if (lastVisited) {
|
||||
mailArray.forEach((mail) => {
|
||||
if (mail.created > lastVisited) notificationsToShow.push(mail)
|
||||
})
|
||||
} else {
|
||||
notificationsToShow = mailArray
|
||||
}
|
||||
if (!name || !address) return
|
||||
|
||||
}
|
||||
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
|
||||
const mailArray = await getMail(name, address)
|
||||
|
||||
this.notificationCount = this.notifications.length !== 0;
|
||||
let notificationsToShow = []
|
||||
|
||||
if (!this.initialFetch) this.initialFetch = true
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
stop = false
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (mailArray.length > 0) {
|
||||
const lastVisited = localStorage.getItem("Q-Mail-last-visited")
|
||||
|
||||
setTimeout(() => {
|
||||
getNewMail()
|
||||
}, 5000)
|
||||
if (lastVisited) {
|
||||
mailArray.forEach((mail) => {
|
||||
if (mail.created > lastVisited) notificationsToShow.push(mail)
|
||||
})
|
||||
} else {
|
||||
notificationsToShow = mailArray
|
||||
}
|
||||
|
||||
interval = setInterval(getNewMail, 60000)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="layout">
|
||||
${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>
|
||||
if (!this.initialFetch && notificationsToShow.length > 0) {
|
||||
const mail = notificationsToShow[0]
|
||||
const urlPic = `${nodeUrl}/arbitrary/THUMBNAIL/${mail.name}/qortal_avatar?async=true}`
|
||||
|
||||
` : 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>
|
||||
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}`
|
||||
|
||||
`}
|
||||
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`
|
||||
<span class="count">${this.notifications.length}</span>
|
||||
` : ''}
|
||||
this.notifications = notificationsToShow
|
||||
|
||||
<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>
|
||||
`
|
||||
}
|
||||
this.notificationCount = this.notifications.length !== 0
|
||||
|
||||
_toggleNotifications() {
|
||||
if (this.notifications.length === 0) return
|
||||
this.showNotifications = !this.showNotifications
|
||||
}
|
||||
_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
|
||||
}
|
||||
}))
|
||||
}
|
||||
if (!this.initialFetch) this.initialFetch = true
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
stop = false
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
user-select: none;
|
||||
}
|
||||
try {
|
||||
setTimeout(() => {
|
||||
getNewMail()
|
||||
}, 5000)
|
||||
|
||||
.nocount {
|
||||
display: none;
|
||||
}
|
||||
interval = setInterval(getNewMail, 60000)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
_toggleNotifications() {
|
||||
if (this.notifications.length === 0) return
|
||||
this.showNotifications = !this.showNotifications
|
||||
}
|
||||
|
||||
.popover-panel::-webkit-scrollbar {
|
||||
width: 11px;
|
||||
}
|
||||
_openTabQmail() {
|
||||
const query = `?service=APP&name=Q-Mail`
|
||||
|
||||
.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;
|
||||
}
|
||||
`
|
||||
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
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
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 {createPopper} from '@popperjs/core';
|
||||
import { css, html, LitElement } from 'lit'
|
||||
import { createPopper } from '@popperjs/core'
|
||||
import { popoverComponentStyles } from '../../styles/core-css'
|
||||
import '@material/mwc-icon'
|
||||
|
||||
export class PopoverComponent extends 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;
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
for: { type: String, reflect: true },
|
||||
message: { type: String }
|
||||
}
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
margin-left: 10px;
|
||||
color: var(--black)
|
||||
}
|
||||
static get styles() {
|
||||
return [popoverComponentStyles]
|
||||
}
|
||||
|
||||
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 = {
|
||||
for: { type: String, reflect: true },
|
||||
message: { type: String }
|
||||
};
|
||||
attachToTarget(target) {
|
||||
if (!this.popperInstance && target) {
|
||||
this.popperInstance = createPopper(target, this, {
|
||||
placement: 'bottom',
|
||||
strategy: 'fixed'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.message = '';
|
||||
}
|
||||
openPopover(target) {
|
||||
this.attachToTarget(target)
|
||||
this.style.display = 'block'
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
// We'll defer the popper attachment to the openPopover() method to ensure target availability
|
||||
}
|
||||
closePopover() {
|
||||
this.style.display = 'none'
|
||||
|
||||
attachToTarget(target) {
|
||||
if (!this.popperInstance && target) {
|
||||
this.popperInstance = createPopper(target, this, {
|
||||
placement: 'bottom',
|
||||
strategy: 'fixed'
|
||||
});
|
||||
}
|
||||
}
|
||||
if (this.popperInstance) {
|
||||
this.popperInstance.destroy()
|
||||
this.popperInstance = null
|
||||
}
|
||||
|
||||
openPopover(target) {
|
||||
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>
|
||||
`;
|
||||
}
|
||||
this.requestUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('popover-component', PopoverComponent);
|
||||
window.customElements.define('popover-component', PopoverComponent)
|
@ -1,134 +1,62 @@
|
||||
import {css, html, LitElement} from 'lit'
|
||||
import {svgMoon, svgSun} from '../../assets/js/svg.js'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { svgMoon, svgSun } from '../../assets/js/svg'
|
||||
import { qortThemeToggleStyles } from '../styles/core-css'
|
||||
|
||||
class QortThemeToggle extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
theme: {
|
||||
type: String,
|
||||
reflect: true
|
||||
}
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light';
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
:host {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 54px;
|
||||
height: 32px;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
static get styles() {
|
||||
return [qortThemeToggleStyles]
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
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>
|
||||
`
|
||||
}
|
||||
|
||||
input {
|
||||
cursor: pointer;
|
||||
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();
|
||||
}
|
||||
firstUpdated() {
|
||||
this.initTheme()
|
||||
}
|
||||
|
||||
|
||||
toggleTheme() {
|
||||
if (this.theme === 'light') {
|
||||
this.theme = 'dark';
|
||||
} else {
|
||||
this.theme = 'light';
|
||||
}
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('qort-theme-change', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: this.theme,
|
||||
}),
|
||||
);
|
||||
toggleTheme() {
|
||||
if (this.theme === 'light') {
|
||||
this.theme = 'dark'
|
||||
} else {
|
||||
this.theme = 'light'
|
||||
}
|
||||
|
||||
window.localStorage.setItem('qortalTheme', this.theme);
|
||||
this.initTheme();
|
||||
}
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('qort-theme-change', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: this.theme
|
||||
})
|
||||
)
|
||||
|
||||
initTheme() {
|
||||
document.querySelector('html').setAttribute('theme', this.theme);
|
||||
}
|
||||
window.localStorage.setItem('qortalTheme', 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 {get, translate} from '../../translate'
|
||||
import snackbar from '../functional-components/snackbar.js'
|
||||
|
||||
import { html, LitElement } from 'lit'
|
||||
import { searchModalStyles } from '../styles/core-css'
|
||||
import snackbar from '../functional-components/snackbar'
|
||||
import '@polymer/paper-icon-button/paper-icon-button.js'
|
||||
import '@polymer/iron-icons/iron-icons.js'
|
||||
import '@polymer/paper-dialog/paper-dialog.js'
|
||||
import '@vaadin/text-field'
|
||||
|
||||
// Multi language support
|
||||
import { get, translate } from '../../translate'
|
||||
|
||||
class SearchModal extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
@ -15,56 +17,16 @@ class SearchModal extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [searchModalStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.searchContentString = ''
|
||||
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() {
|
||||
return html`
|
||||
<div style="display: inline;">
|
||||
@ -92,6 +54,7 @@ class SearchModal extends LitElement {
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
// ...
|
||||
}
|
||||
|
||||
openSearch() {
|
||||
@ -110,23 +73,28 @@ class SearchModal extends LitElement {
|
||||
|
||||
openUserInfo() {
|
||||
const checkvalue = this.shadowRoot.getElementById('searchContent').value
|
||||
|
||||
if (checkvalue.length < 3) {
|
||||
let snackbar1string = get("publishpage.pchange20")
|
||||
let snackbar2string = get("welcomepage.wcchange4")
|
||||
|
||||
snackbar.add({
|
||||
labelText: `${snackbar1string} ${snackbar2string}`,
|
||||
dismiss: true
|
||||
})
|
||||
this.shadowRoot.getElementById('searchContent').value = this.searchContentString
|
||||
|
||||
this.shadowRoot.getElementById('searchContent').value = this.searchContentString
|
||||
} else {
|
||||
let sendInfoAddress = this.shadowRoot.getElementById('searchContent').value
|
||||
|
||||
const infoDialog = document.getElementById('main-app').shadowRoot.querySelector('app-view').shadowRoot.querySelector('user-info-view')
|
||||
|
||||
infoDialog.openUserInfo(sendInfoAddress)
|
||||
|
||||
this.shadowRoot.getElementById('searchContent').value = this.searchContentString
|
||||
this.shadowRoot.getElementById('searchSettingsDialog').close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('search-modal', SearchModal)
|
||||
window.customElements.define('search-modal', SearchModal)
|
@ -1,138 +1,92 @@
|
||||
import {css, html, LitElement} from 'lit'
|
||||
import {connect} from 'pwa-helpers'
|
||||
import {store} from '../../store.js'
|
||||
import {get, translate} from '../../../translate'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { accountViewStyles } from '../../styles/core-css'
|
||||
|
||||
// Multi language support
|
||||
import { get, translate } from '../../../translate'
|
||||
|
||||
class AccountView extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
accountInfo: { type: Object },
|
||||
theme: { type: String, reflect: true },
|
||||
switchAvatar: { type: String }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
accountInfo: { type: Object },
|
||||
switchAvatar: { type: String },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
static get styles() {
|
||||
return [accountViewStyles]
|
||||
}
|
||||
|
||||
.sub-main {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.accountInfo = store.getState().app.accountInfo
|
||||
this.switchAvatar = ''
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
|
||||
.center-box {
|
||||
position: relative;
|
||||
top: 45%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
text-align: center;
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<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>
|
||||
`
|
||||
}
|
||||
|
||||
.img-icon {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
firstUpdated() {
|
||||
this.getSwitchAvatar()
|
||||
|
||||
.content-box {
|
||||
border: 1px solid #a1a1a1;
|
||||
padding: 10px 25px;
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
}
|
||||
setInterval(() => {
|
||||
this.getSwitchAvatar()
|
||||
}, 10000)
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
display: block;
|
||||
line-height: 32px;
|
||||
opacity: 0.66;
|
||||
}
|
||||
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`
|
||||
|
||||
.value {
|
||||
font-size: 16px;
|
||||
display: inline-block;
|
||||
}
|
||||
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`
|
||||
|
||||
#accountName {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight:500;
|
||||
display: inline-block;
|
||||
width:100%;
|
||||
}
|
||||
`
|
||||
}
|
||||
return html`<img src="${url}" style="width:150px; height:150px; border-radius: 25%;" onerror="this.src='/img/noavatar_dark.png';">`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.accountInfo = store.getState().app.accountInfo
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
this.switchAvatar = ''
|
||||
}
|
||||
getSwitchAvatar() {
|
||||
this.switchAvatar = localStorage.getItem('qortalTheme')
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<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
|
||||
}
|
||||
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 {connect} from 'pwa-helpers'
|
||||
import {store} from '../../store.js'
|
||||
import {Epml} from '../../epml.js'
|
||||
import {addTradeBotRoutes} from '../../tradebot/addTradeBotRoutes.js'
|
||||
import {get, translate} from '../../../translate'
|
||||
import snackbar from '../../functional-components/snackbar.js'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { Epml } from '../../epml'
|
||||
import { addTradeBotRoutes } from '../../tradebot/addTradeBotRoutes'
|
||||
import { exportKeysStyles } from '../../styles/core-css'
|
||||
import FileSaver from 'file-saver'
|
||||
|
||||
import '@material/mwc-dialog'
|
||||
import snackbar from '../../functional-components/snackbar'
|
||||
import '@material/mwc-button'
|
||||
import '@material/mwc-dialog'
|
||||
import '@material/mwc-icon'
|
||||
|
||||
// Multi language support
|
||||
import { get, translate } from '../../../translate'
|
||||
|
||||
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||
|
||||
class ExportKeys extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
theme: { type: String, reflect: true },
|
||||
backupErrorMessage: { type: String },
|
||||
btcPMK: { type: String },
|
||||
ltcPMK: { type: String },
|
||||
dogePMK: { type: String },
|
||||
dgbPMK: { type: String },
|
||||
rvnPMK: { type: String },
|
||||
arrrPMK: { type: String },
|
||||
btcWALLET: { type: String },
|
||||
ltcWALLET: { type: String },
|
||||
dogeWALLET: { type: String },
|
||||
dgbWALLET: { type: String },
|
||||
rvnWALLET: { type: String },
|
||||
arrrWALLET: { type: String },
|
||||
btcName: { type: String },
|
||||
ltcName: { type: String },
|
||||
dogeName: { type: String },
|
||||
dgbName: { type: String },
|
||||
rvnName: { type: String },
|
||||
arrrName: { type: String },
|
||||
btcShort: { type: String },
|
||||
ltcShort: { type: String },
|
||||
dogeShort: { type: String },
|
||||
dgbShort: { type: String },
|
||||
rvnShort: { type: String },
|
||||
arrrShort: { type: String },
|
||||
enableArrr: { type: Boolean },
|
||||
dWalletAddress: { type: String },
|
||||
dPrivateKey: { type: String },
|
||||
dCoinName: { type: String },
|
||||
dCoinShort: { type: String }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
theme: { type: String, reflect: true },
|
||||
backupErrorMessage: { type: String },
|
||||
btcPMK: { type: String },
|
||||
ltcPMK: { type: String },
|
||||
dogePMK: { type: String },
|
||||
dgbPMK: { type: String },
|
||||
rvnPMK: { type: String },
|
||||
arrrPMK: { type: String },
|
||||
btcWALLET: { type: String },
|
||||
ltcWALLET: { type: String },
|
||||
dogeWALLET: { type: String },
|
||||
dgbWALLET: { type: String },
|
||||
rvnWALLET: { type: String },
|
||||
arrrWALLET: { type: String },
|
||||
btcName: { type: String },
|
||||
ltcName: { type: String },
|
||||
dogeName: { type: String },
|
||||
dgbName: { type: String },
|
||||
rvnName: { type: String },
|
||||
arrrName: { type: String },
|
||||
btcShort: { type: String },
|
||||
ltcShort: { type: String },
|
||||
dogeShort: { type: String },
|
||||
dgbShort: { type: String },
|
||||
rvnShort: { type: String },
|
||||
arrrShort: { type: String },
|
||||
enableArrr: { type: Boolean },
|
||||
dWalletAddress: { type: String },
|
||||
dPrivateKey: { type: String },
|
||||
dCoinName: { type: String },
|
||||
dCoinShort: { type: String }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
* {
|
||||
--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);
|
||||
}
|
||||
static get styles() {
|
||||
return [exportKeysStyles]
|
||||
}
|
||||
|
||||
.center-box {
|
||||
position: relative;
|
||||
top: 45%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
text-align: center;
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
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'
|
||||
}
|
||||
|
||||
.sub-main {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<div style="position: relative;">
|
||||
<div class="center-box">
|
||||
<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 {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
min-width: 400px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
async firstUpdated() {
|
||||
addTradeBotRoutes(parentEpml)
|
||||
parentEpml.imReady()
|
||||
|
||||
.export-button {
|
||||
display: inline-flex;
|
||||
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;
|
||||
}
|
||||
await this.fetchArrrWalletAddress()
|
||||
await this.checkArrrWalletPrivateKey()
|
||||
}
|
||||
|
||||
.red {
|
||||
--mdc-theme-primary: #F44336;
|
||||
}
|
||||
async fetchArrrWalletAddress() {
|
||||
let resAD = await parentEpml.request('apiCall', {
|
||||
url: `/crosschain/arrr/walletaddress?apiKey=${this.getApiKey()}`,
|
||||
method: 'POST',
|
||||
body: `${store.getState().app.selectedAddress.arrrWallet.seed58}`
|
||||
})
|
||||
|
||||
.green {
|
||||
--mdc-theme-primary: #198754;
|
||||
}
|
||||
if (resAD != null && resAD.error != 1201) {
|
||||
this.arrrWALLET = ''
|
||||
this.enableArrr = true
|
||||
this.arrrWALLET = resAD
|
||||
} else {
|
||||
this.arrrWALLET = ''
|
||||
this.enableArrr = false
|
||||
this.shadowRoot.querySelector('#arrrWalletNotSynced').show()
|
||||
}
|
||||
}
|
||||
|
||||
.button-row {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
font-family: Montserrat, sans-serif;
|
||||
font-weight: 600;
|
||||
color: var(--black);
|
||||
margin-top: 20px;
|
||||
}
|
||||
async checkArrrWalletPrivateKey() {
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||
const privateKeyUrl = `${nodeUrl}/crosschain/arrr/walletprivatekey?apiKey=${this.getApiKey()}`
|
||||
|
||||
.repair-button {
|
||||
height: 40px;
|
||||
padding: 10px 10px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
background-color: #03a9f4;
|
||||
color: white;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 20px;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
}
|
||||
await fetch(privateKeyUrl, {
|
||||
method: 'POST',
|
||||
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()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
.repair-button:hover {
|
||||
opacity: 0.8;
|
||||
cursor: pointer;
|
||||
}
|
||||
async fetchArrrWalletPrivateKey() {
|
||||
let resPK = await parentEpml.request('apiCall', {
|
||||
url: `/crosschain/arrr/walletprivatekey?apiKey=${this.getApiKey()}`,
|
||||
method: 'POST',
|
||||
body: `${store.getState().app.selectedAddress.arrrWallet.seed58}`
|
||||
})
|
||||
|
||||
.lds-roller {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
if (resPK != null && resPK.error != 1201) {
|
||||
this.arrrPMK = ''
|
||||
this.enableArrr = true
|
||||
this.arrrPMK = resPK
|
||||
} else {
|
||||
this.arrrPMK = ''
|
||||
this.enableArrr = false
|
||||
this.shadowRoot.querySelector('#arrrWalletNotSynced').show()
|
||||
}
|
||||
}
|
||||
|
||||
.lds-roller div {
|
||||
animation: lds-roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
||||
transform-origin: 40px 40px;
|
||||
}
|
||||
closeArrrWalletNotSynced() {
|
||||
this.shadowRoot.querySelector('#arrrWalletNotSynced').close()
|
||||
}
|
||||
|
||||
.lds-roller div:after {
|
||||
content: " ";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: var(--black);
|
||||
margin: -4px 0 0 -4px;
|
||||
}
|
||||
closeNeedCoreUpdate() {
|
||||
this.arrrPMK = ''
|
||||
this.enableArrr = false
|
||||
this.shadowRoot.querySelector('#needCoreUpdate').close()
|
||||
}
|
||||
|
||||
.lds-roller div:nth-child(1) {
|
||||
animation-delay: -0.036s;
|
||||
}
|
||||
closeSavePkmDialog() {
|
||||
this.shadowRoot.querySelector('#savePkmDialog').close()
|
||||
}
|
||||
|
||||
.lds-roller div:nth-child(1):after {
|
||||
top: 63px;
|
||||
left: 63px;
|
||||
}
|
||||
openRepairLTCDialog() {
|
||||
this.shadowRoot.querySelector('#repairLTCDialog').show()
|
||||
}
|
||||
|
||||
.lds-roller div:nth-child(2) {
|
||||
animation-delay: -0.072s;
|
||||
}
|
||||
closeRepairLTCDialog() {
|
||||
this.shadowRoot.querySelector('#repairLTCDialog').close()
|
||||
}
|
||||
|
||||
.lds-roller div:nth-child(2):after {
|
||||
top: 68px;
|
||||
left: 56px;
|
||||
}
|
||||
async repairLtcWallet() {
|
||||
this.shadowRoot.querySelector('#repairLTCDialog').close()
|
||||
this.shadowRoot.querySelector('#pleaseWaitDialog').show()
|
||||
|
||||
.lds-roller div:nth-child(3) {
|
||||
animation-delay: -0.108s;
|
||||
}
|
||||
let resRepair = await parentEpml.request('apiCall', {
|
||||
url: `/crosschain/ltc/repair?apiKey=${this.getApiKey()}`,
|
||||
method: 'POST',
|
||||
body: `${store.getState().app.selectedAddress.ltcWallet.derivedMasterPrivateKey}`
|
||||
})
|
||||
|
||||
.lds-roller div:nth-child(3):after {
|
||||
top: 71px;
|
||||
left: 48px;
|
||||
}
|
||||
if (resRepair != null && resRepair.error != 128) {
|
||||
this.shadowRoot.querySelector('#pleaseWaitDialog').close()
|
||||
|
||||
.lds-roller div:nth-child(4) {
|
||||
animation-delay: -0.144s;
|
||||
}
|
||||
await this.openOkDialog()
|
||||
} else {
|
||||
this.shadowRoot.querySelector('#pleaseWaitDialog').close()
|
||||
|
||||
.lds-roller div:nth-child(4):after {
|
||||
top: 72px;
|
||||
left: 40px;
|
||||
}
|
||||
await this.openErrorDialog()
|
||||
}
|
||||
}
|
||||
|
||||
.lds-roller div:nth-child(5) {
|
||||
animation-delay: -0.18s;
|
||||
}
|
||||
async openOkDialog() {
|
||||
const okDelay = ms => new Promise(res => setTimeout(res, ms))
|
||||
|
||||
.lds-roller div:nth-child(5):after {
|
||||
top: 71px;
|
||||
left: 32px;
|
||||
}
|
||||
this.shadowRoot.querySelector('#okDialog').show()
|
||||
|
||||
.lds-roller div:nth-child(6) {
|
||||
animation-delay: -0.216s;
|
||||
}
|
||||
await okDelay(3000)
|
||||
|
||||
.lds-roller div:nth-child(6):after {
|
||||
top: 68px;
|
||||
left: 24px;
|
||||
}
|
||||
this.shadowRoot.querySelector('#okDialog').close()
|
||||
}
|
||||
|
||||
.lds-roller div:nth-child(7) {
|
||||
animation-delay: -0.252s;
|
||||
}
|
||||
async openErrorDialog() {
|
||||
const errorDelay = ms => new Promise(res => setTimeout(res, ms))
|
||||
|
||||
.lds-roller div:nth-child(7):after {
|
||||
top: 63px;
|
||||
left: 17px;
|
||||
}
|
||||
this.shadowRoot.querySelector('#errorDialog').show()
|
||||
|
||||
.lds-roller div:nth-child(8) {
|
||||
animation-delay: -0.288s;
|
||||
}
|
||||
await errorDelay(3000)
|
||||
|
||||
.lds-roller div:nth-child(8):after {
|
||||
top: 56px;
|
||||
left: 12px;
|
||||
}
|
||||
this.shadowRoot.querySelector('#errorDialog').close()
|
||||
}
|
||||
|
||||
@keyframes lds-roller {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
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'
|
||||
}
|
||||
async exportKey(cMasterKey, cName, cAddress) {
|
||||
let exportname = ''
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div style="position: relative;">
|
||||
<div class="center-box">
|
||||
<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>
|
||||
`
|
||||
}
|
||||
const myPrivateMasterKey = cMasterKey
|
||||
const myCoinName = cName
|
||||
const myCoinAddress = cAddress
|
||||
const blob = new Blob([`${myPrivateMasterKey}`], { type: 'text/plain;charset=utf-8' })
|
||||
|
||||
async firstUpdated() {
|
||||
addTradeBotRoutes(parentEpml)
|
||||
parentEpml.imReady()
|
||||
await this.fetchArrrWalletAddress()
|
||||
await this.checkArrrWalletPrivateKey()
|
||||
}
|
||||
exportname = 'Private_Master_Key_' + myCoinName + '_' + myCoinAddress + '.txt'
|
||||
|
||||
async fetchArrrWalletAddress() {
|
||||
let resAD = await parentEpml.request('apiCall', {
|
||||
url: `/crosschain/arrr/walletaddress?apiKey=${this.getApiKey()}`,
|
||||
method: 'POST',
|
||||
body: `${store.getState().app.selectedAddress.arrrWallet.seed58}`
|
||||
})
|
||||
await this.saveFileToDisk(blob, exportname)
|
||||
}
|
||||
|
||||
if (resAD != null && resAD.error != 1201) {
|
||||
this.arrrWALLET = ''
|
||||
this.enableArrr = true
|
||||
this.arrrWALLET = resAD
|
||||
} else {
|
||||
this.arrrWALLET = ''
|
||||
this.enableArrr = false
|
||||
this.shadowRoot.querySelector('#arrrWalletNotSynced').show()
|
||||
}
|
||||
}
|
||||
async saveFileToDisk(blob, fileName) {
|
||||
try {
|
||||
const fileHandle = await self.showSaveFilePicker({
|
||||
suggestedName: fileName,
|
||||
types: [{
|
||||
description: "File"
|
||||
}]
|
||||
})
|
||||
|
||||
async checkArrrWalletPrivateKey() {
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||
const privateKeyUrl = `${nodeUrl}/crosschain/arrr/walletprivatekey?apiKey=${this.getApiKey()}`
|
||||
const writeFile = async (fileHandle, contents) => {
|
||||
const writable = await fileHandle.createWritable()
|
||||
|
||||
await fetch(privateKeyUrl, {
|
||||
method: 'POST',
|
||||
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()
|
||||
}
|
||||
})
|
||||
}
|
||||
await writable.write(contents)
|
||||
await writable.close()
|
||||
}
|
||||
|
||||
async fetchArrrWalletPrivateKey() {
|
||||
let resPK = await parentEpml.request('apiCall', {
|
||||
url: `/crosschain/arrr/walletprivatekey?apiKey=${this.getApiKey()}`,
|
||||
method: 'POST',
|
||||
body: `${store.getState().app.selectedAddress.arrrWallet.seed58}`
|
||||
})
|
||||
writeFile(fileHandle, blob).then(() => console.log("FILE SAVED"))
|
||||
|
||||
if (resPK != null && resPK.error != 1201) {
|
||||
this.arrrPMK = ''
|
||||
this.enableArrr = true
|
||||
this.arrrPMK = resPK
|
||||
} else {
|
||||
this.arrrPMK = ''
|
||||
this.enableArrr = false
|
||||
this.shadowRoot.querySelector('#arrrWalletNotSynced').show()
|
||||
}
|
||||
}
|
||||
let snack4string = get("general.save")
|
||||
|
||||
closeArrrWalletNotSynced() {
|
||||
this.shadowRoot.querySelector('#arrrWalletNotSynced').close()
|
||||
}
|
||||
snackbar.add({
|
||||
labelText: `${snack4string} ${fileName} ✅`,
|
||||
dismiss: true
|
||||
})
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') {
|
||||
return
|
||||
}
|
||||
|
||||
closeNeedCoreUpdate() {
|
||||
this.arrrPMK = ''
|
||||
this.enableArrr = false
|
||||
this.shadowRoot.querySelector('#needCoreUpdate').close()
|
||||
}
|
||||
FileSaver.saveAs(blob, fileName)
|
||||
}
|
||||
}
|
||||
|
||||
closeSavePkmDialog() {
|
||||
this.shadowRoot.querySelector('#savePkmDialog').close()
|
||||
}
|
||||
|
||||
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]
|
||||
getApiKey() {
|
||||
const apiNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
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 {connect} from 'pwa-helpers'
|
||||
import {store} from '../../store.js'
|
||||
import {allowShowSyncIndicator, removeShowSyncIndicator} from '../../redux/app/app-actions.js'
|
||||
import {doSetQChatNotificationConfig} from '../../redux/user/user-actions.js'
|
||||
import {translate} from '../../../translate'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { allowShowSyncIndicator, removeShowSyncIndicator } from '../../redux/app/app-actions'
|
||||
import { doSetQChatNotificationConfig } from '../../redux/user/user-actions'
|
||||
import { notificationsViewStyles } from '../../styles/core-css'
|
||||
import isElectron from 'is-electron'
|
||||
|
||||
import '@material/mwc-checkbox'
|
||||
|
||||
// Multi language support
|
||||
import { translate } from '../../../translate'
|
||||
|
||||
class NotificationsView extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
notificationConfig: { type: Object },
|
||||
q_chatConfig: { type: Object },
|
||||
theme: { type: String, reflect: true },
|
||||
appNotificationList: { type: Array }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
notificationConfig: { type: Object },
|
||||
q_chatConfig: { type: Object },
|
||||
theme: { type: String, reflect: true },
|
||||
appNotificationList: { type: Array }
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.notificationConfig = {}
|
||||
this.q_chatConfig = {}
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
this.appNotificationList = [] // Fetch the list of apps from local storage
|
||||
}
|
||||
static get styles() {
|
||||
return [notificationsViewStyles]
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.appNotificationList = this.getAppsFromStorage()
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.notificationConfig = {}
|
||||
this.q_chatConfig = {}
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
this.appNotificationList = []
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
.sub-main {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
firstUpdated() {
|
||||
this.appNotificationList = this.getAppsFromStorage()
|
||||
}
|
||||
|
||||
.notification-box {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 45%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
text-align: center;
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<div class="sub-main">
|
||||
<div class="notification-box">
|
||||
<div class="content-box">
|
||||
<h4>Q-Chat ${translate("settings.notifications")}</h4>
|
||||
<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) {
|
||||
.notification-box {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 30px;
|
||||
}
|
||||
}
|
||||
getAppsFromStorage() {
|
||||
const address = store.getState().app.selectedAddress.address
|
||||
const id = `appNotificationList-${address}`
|
||||
const data = localStorage.getItem(id)
|
||||
|
||||
.checkbox-row {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
font-family: Montserrat, sans-serif;
|
||||
font-weight: 600;
|
||||
color: var(--black);
|
||||
}
|
||||
return data ? Object.keys(JSON.parse(data)) : []
|
||||
}
|
||||
|
||||
.content-box {
|
||||
border: 1px solid #a1a1a1;
|
||||
padding: 10px 25px;
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
min-width: 350px;
|
||||
min-height: 150px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
removeApp(appName) {
|
||||
// Remove the app from local storage
|
||||
this.removeAppFromStorage(appName)
|
||||
|
||||
h4 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
// Update the apps list in the component
|
||||
this.appNotificationList = this.appNotificationList.filter(app => app !== appName)
|
||||
}
|
||||
|
||||
mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::before {
|
||||
background-color:var(--mdc-theme-primary)
|
||||
}
|
||||
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) || '{}')
|
||||
|
||||
label:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
delete data[appName]
|
||||
|
||||
.title {
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
display: block;
|
||||
line-height: 32px;
|
||||
opacity: 0.66;
|
||||
}
|
||||
localStorage.setItem(id, JSON.stringify(data));
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 16px;
|
||||
display: inline-block;
|
||||
}
|
||||
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>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
.q-button {
|
||||
display: inline-flex;
|
||||
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;
|
||||
}
|
||||
checkCoreSettings() {
|
||||
window.electronAPI.setStartCore()
|
||||
}
|
||||
|
||||
.remove-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;
|
||||
cursor: pointer;
|
||||
}
|
||||
`
|
||||
}
|
||||
checkForSyncMessages(e) {
|
||||
if (e.target.checked) {
|
||||
store.dispatch(removeShowSyncIndicator(false))
|
||||
} else {
|
||||
store.dispatch(allowShowSyncIndicator(true))
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="sub-main">
|
||||
<div class="notification-box">
|
||||
<div class="content-box">
|
||||
<h4> Q-Chat ${translate("settings.notifications")} </h4>
|
||||
stateChanged(state) {
|
||||
this.notificationConfig = state.user.notifications
|
||||
this.q_chatConfig = this.notificationConfig.q_chat
|
||||
}
|
||||
|
||||
<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>
|
||||
setQChatNotificationConfig(valueObject) {
|
||||
if (valueObject.type === 'PLAY_SOUND') {
|
||||
let data = {
|
||||
playSound: !valueObject.value,
|
||||
showNotification: this.q_chatConfig.showNotification
|
||||
}
|
||||
|
||||
<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>
|
||||
`
|
||||
}
|
||||
store.dispatch(doSetQChatNotificationConfig(data))
|
||||
} if (valueObject.type === 'SHOW_NOTIFICATION') {
|
||||
let data = {
|
||||
playSound: this.q_chatConfig.playSound,
|
||||
showNotification: !valueObject.value
|
||||
}
|
||||
|
||||
getAppsFromStorage() {
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
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 {connect} from 'pwa-helpers'
|
||||
import {store} from '../../store.js'
|
||||
import {translate} from '../../../translate'
|
||||
|
||||
import '@material/mwc-textfield'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { qrLoginViewStyles } from '../../styles/core-css'
|
||||
import '../../../../plugins/plugins/core/components/QortalQrcodeGenerator'
|
||||
import '@material/mwc-icon'
|
||||
import '@material/mwc-textfield'
|
||||
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) {
|
||||
static get properties() {
|
||||
return {
|
||||
theme: { type: String, reflect: true },
|
||||
savedWalletDataJson: { type: String },
|
||||
translateDescriptionKey: { type: String }, // Description text
|
||||
translateButtonKey: { type: String }, // Button text
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
theme: { type: String, reflect: true },
|
||||
savedWalletDataJson: { type: String },
|
||||
translateDescriptionKey: { type: String },
|
||||
translateButtonKey: { type: String }
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
static get styles() {
|
||||
return [qrLoginViewStyles]
|
||||
}
|
||||
|
||||
.center-box {
|
||||
position: relative;
|
||||
top: 45%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
text-align: center;
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
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')
|
||||
}
|
||||
|
||||
.q-button {
|
||||
display: inline-flex;
|
||||
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;
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<div style="position: relative;" >
|
||||
<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;">
|
||||
<qortal-qrcode-generator id="login-qr-code" data="${this.savedWalletDataJson}" mode="octet" format="html" auto></qortal-qrcode-generator>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
.q-button.outlined {
|
||||
background: unset;
|
||||
border: 1px solid #03a9f4;
|
||||
}
|
||||
isWalletStored() {
|
||||
const state = store.getState()
|
||||
const address0 = state.app.wallet._addresses[0].address
|
||||
const savedWalletData = state.user.storedWallets && state.user.storedWallets[address0]
|
||||
|
||||
:host([theme="light"]) .q-button.outlined {
|
||||
color: #03a9f4;
|
||||
}
|
||||
return !!savedWalletData
|
||||
}
|
||||
|
||||
#qr-toggle-button {
|
||||
margin-left: 12px;
|
||||
}
|
||||
async setSavedWalletDataJson() {
|
||||
const state = store.getState()
|
||||
|
||||
#login-qr-code {
|
||||
margin: auto;
|
||||
}
|
||||
`
|
||||
}
|
||||
let data
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
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')
|
||||
}
|
||||
if (this.isWalletStored()) {
|
||||
const address0 = state.app.wallet._addresses[0].address
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div style="position: relative;" >
|
||||
<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>
|
||||
data = state.user.storedWallets[address0]
|
||||
} else {
|
||||
const password = this.shadowRoot.getElementById('newWalletPassword').value
|
||||
|
||||
<div id="login-qr-code" style="display: none;">
|
||||
<qortal-qrcode-generator id="login-qr-code" data="${this.savedWalletDataJson}" mode="octet" format="html" auto></qortal-qrcode-generator>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
data = await state.app.wallet.generateSaveWalletData(password, state.config.crypto.kdfThreads, () => { })
|
||||
}
|
||||
|
||||
isWalletStored() {
|
||||
const state = store.getState()
|
||||
const address0 = state.app.wallet._addresses[0].address
|
||||
const savedWalletData = state.user.storedWallets && state.user.storedWallets[address0]
|
||||
return !!savedWalletData
|
||||
}
|
||||
this.savedWalletDataJson = JSON.stringify(data)
|
||||
}
|
||||
|
||||
async setSavedWalletDataJson() {
|
||||
const state = store.getState()
|
||||
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() {
|
||||
await this.setSavedWalletDataJson()
|
||||
|
||||
async showQRCode() {
|
||||
await this.setSavedWalletDataJson()
|
||||
let el = this.shadowRoot.getElementById('login-qr-code')
|
||||
el.style.display = 'flex'
|
||||
}
|
||||
let el = this.shadowRoot.getElementById('login-qr-code')
|
||||
|
||||
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 {connect} from 'pwa-helpers'
|
||||
import {store} from '../../store.js'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import {
|
||||
allowQAPPAutoAuth,
|
||||
allowQAPPAutoFriendsList,
|
||||
@ -9,262 +9,194 @@ import {
|
||||
removeQAPPAutoFriendsList,
|
||||
removeQAPPAutoLists,
|
||||
setIsOpenDevDialog
|
||||
} from '../../redux/app/app-actions.js'
|
||||
import {get, translate} from '../../../translate'
|
||||
import snackbar from '../../functional-components/snackbar.js'
|
||||
} from '../../redux/app/app-actions'
|
||||
import { securityViewStyles } from '../../styles/core-css'
|
||||
import FileSaver from 'file-saver'
|
||||
|
||||
import snackbar from '../../functional-components/snackbar'
|
||||
import '@material/mwc-checkbox'
|
||||
import '@material/mwc-textfield'
|
||||
import '@material/mwc-icon'
|
||||
import '@material/mwc-textfield'
|
||||
import '@vaadin/password-field/vaadin-password-field.js'
|
||||
|
||||
// Multi language support
|
||||
import { get, translate } from '../../../translate'
|
||||
|
||||
class SecurityView extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
theme: { type: String, reflect: true },
|
||||
backupErrorMessage: { type: String },
|
||||
closeSettings: {attribute: false}
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
theme: { type: String, reflect: true },
|
||||
backupErrorMessage: { type: String },
|
||||
closeSettings: { attribute: false }
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
--mdc-checkbox-unchecked-color: var(--black);
|
||||
--mdc-theme-on-surface: var(--black);
|
||||
--mdc-checkbox-disabled-color: var(--black);
|
||||
--mdc-checkbox-ink-color: var(--black);
|
||||
}
|
||||
static get styles() {
|
||||
return [securityViewStyles]
|
||||
}
|
||||
|
||||
.center-box {
|
||||
position: relative;
|
||||
top: 45%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
text-align: center;
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
this.backupErrorMessage = ''
|
||||
}
|
||||
|
||||
.checkbox-row {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
font-family: Montserrat, sans-serif;
|
||||
font-weight: 600;
|
||||
color: var(--black);
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<div style="position: relative;" >
|
||||
<div class="center-box">
|
||||
<p>
|
||||
${translate("settings.choose")}
|
||||
</p>
|
||||
<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 {
|
||||
display: inline-flex;
|
||||
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;
|
||||
}
|
||||
checkForAuth(e) {
|
||||
if (e.target.checked) {
|
||||
store.dispatch(removeQAPPAutoAuth(false))
|
||||
} else {
|
||||
store.dispatch(allowQAPPAutoAuth(true))
|
||||
}
|
||||
}
|
||||
|
||||
.add-dev-button {
|
||||
margin-top: 4px;
|
||||
max-height: 28px;
|
||||
padding: 5px 5px;
|
||||
font-size: 14px;
|
||||
background-color: #03a9f4;
|
||||
color: white;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
checkForLists(e) {
|
||||
if (e.target.checked) {
|
||||
store.dispatch(removeQAPPAutoLists(false))
|
||||
} else {
|
||||
store.dispatch(allowQAPPAutoLists(true))
|
||||
}
|
||||
}
|
||||
|
||||
.add-dev-button:hover {
|
||||
opacity: 0.8;
|
||||
cursor: pointer;
|
||||
}
|
||||
`
|
||||
}
|
||||
checkForFriends(e) {
|
||||
if (e.target.checked) {
|
||||
store.dispatch(removeQAPPAutoFriendsList(false))
|
||||
} else {
|
||||
store.dispatch(allowQAPPAutoFriendsList(true))
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
this.backupErrorMessage = ''
|
||||
}
|
||||
checkForDownload() {
|
||||
const checkPass = this.shadowRoot.getElementById('downloadBackupPassword').value
|
||||
const rePass = this.shadowRoot.getElementById('rePassword').value
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div style="position: relative;" >
|
||||
<div class="center-box">
|
||||
<p>
|
||||
${translate("settings.choose")}
|
||||
</p>
|
||||
<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>
|
||||
`
|
||||
}
|
||||
if (checkPass === '') {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
stateChanged(state) {
|
||||
}
|
||||
openDevDialog() {
|
||||
this.closeSettings()
|
||||
store.dispatch(setIsOpenDevDialog(true))
|
||||
}
|
||||
|
||||
checkForAuth(e) {
|
||||
if (e.target.checked) {
|
||||
store.dispatch(removeQAPPAutoAuth(false))
|
||||
} else {
|
||||
store.dispatch(allowQAPPAutoAuth(true))
|
||||
}
|
||||
}
|
||||
async downloadBackup() {
|
||||
let backupname = ''
|
||||
|
||||
checkForLists(e) {
|
||||
if (e.target.checked) {
|
||||
store.dispatch(removeQAPPAutoLists(false))
|
||||
} else {
|
||||
store.dispatch(allowQAPPAutoLists(true))
|
||||
}
|
||||
}
|
||||
this.backupErrorMessage = ''
|
||||
|
||||
checkForFriends(e) {
|
||||
if (e.target.checked) {
|
||||
store.dispatch(removeQAPPAutoFriendsList(false))
|
||||
} else {
|
||||
store.dispatch(allowQAPPAutoFriendsList(true))
|
||||
}
|
||||
}
|
||||
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' })
|
||||
|
||||
checkForDownload() {
|
||||
const checkPass = this.shadowRoot.getElementById('downloadBackupPassword').value
|
||||
const rePass = this.shadowRoot.getElementById('rePassword').value
|
||||
backupname = 'qortal_backup_' + state.app.selectedAddress.address + '.json'
|
||||
|
||||
if (checkPass === '') {
|
||||
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()
|
||||
}
|
||||
}
|
||||
await this.saveFileToDisk(blob, backupname)
|
||||
}
|
||||
|
||||
openDevDialog() {
|
||||
this.closeSettings()
|
||||
store.dispatch(setIsOpenDevDialog(true))
|
||||
}
|
||||
async saveFileToDisk(blob, fileName) {
|
||||
try {
|
||||
const fileHandle = await self.showSaveFilePicker({
|
||||
suggestedName: fileName,
|
||||
types: [{
|
||||
description: "File"
|
||||
}]
|
||||
})
|
||||
|
||||
async downloadBackup() {
|
||||
let backupname = ''
|
||||
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)
|
||||
}
|
||||
const writeFile = async (fileHandle, contents) => {
|
||||
const writable = await fileHandle.createWritable()
|
||||
|
||||
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
|
||||
})
|
||||
this.shadowRoot.getElementById('downloadBackupPassword').value = ''
|
||||
this.shadowRoot.getElementById('rePassword').value = ''
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') {
|
||||
return
|
||||
}
|
||||
FileSaver.saveAs(blob, fileName)
|
||||
this.shadowRoot.getElementById('downloadBackupPassword').value = ''
|
||||
this.shadowRoot.getElementById('rePassword').value = ''
|
||||
}
|
||||
}
|
||||
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
|
||||
})
|
||||
|
||||
this.shadowRoot.getElementById('downloadBackupPassword').value = ''
|
||||
this.shadowRoot.getElementById('rePassword').value = ''
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') {
|
||||
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 {connect} from 'pwa-helpers'
|
||||
import {store} from '../../store.js'
|
||||
import {translate} from '../../../translate'
|
||||
|
||||
import '@polymer/paper-dialog/paper-dialog.js'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../../store'
|
||||
import { userSettingsStyles } from '../../styles/core-css'
|
||||
import './account-view'
|
||||
import './export-keys'
|
||||
import './notifications-view'
|
||||
import './qr-login-view'
|
||||
import './security-view'
|
||||
import '@material/mwc-button'
|
||||
import '@polymer/paper-dialog/paper-dialog.js'
|
||||
|
||||
import './account-view.js'
|
||||
import './security-view.js'
|
||||
import './notifications-view.js'
|
||||
import './qr-login-view.js'
|
||||
import './export-keys.js'
|
||||
// Multi language support
|
||||
import { translate } from '../../../translate'
|
||||
|
||||
class UserSettings extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
loggedIn: { type: Boolean },
|
||||
pages: { type: Array },
|
||||
selectedView: { type: Object },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
loggedIn: { type: Boolean },
|
||||
pages: { type: Array },
|
||||
selectedView: { type: Object },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
:host {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
height: 100%;
|
||||
max-height: 100vh;
|
||||
background-color: var(--white);
|
||||
color: var(--black);
|
||||
line-height: 1.6;
|
||||
}
|
||||
static get styles() {
|
||||
return [userSettingsStyles]
|
||||
}
|
||||
|
||||
.decline {
|
||||
--mdc-theme-primary: var(--mdc-theme-error)
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.selectedView = { id: 'info', name: 'General Account Info' }
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
|
||||
paper-dialog.userSettings {
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
height: 100%;
|
||||
max-height: 100vh;
|
||||
background-color: var(--white);
|
||||
color: var(--black);
|
||||
line-height: 1.6;
|
||||
overflow-y: auto;
|
||||
}
|
||||
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>
|
||||
`
|
||||
}
|
||||
|
||||
.actions {
|
||||
display:flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 4em;
|
||||
margin: 15px 0 -2px 0;
|
||||
}
|
||||
stateChanged(state) {
|
||||
this.loggedIn = state.app.loggedIn
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
font-size: 36px;
|
||||
}
|
||||
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>`
|
||||
}
|
||||
}
|
||||
|
||||
.close-icon:hover {
|
||||
cursor: pointer;
|
||||
opacity: .6;
|
||||
}
|
||||
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")}`
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
text-align:right;
|
||||
}
|
||||
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' }
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 90vw;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 20px;
|
||||
padding: .6em;
|
||||
}
|
||||
openSettings() {
|
||||
if (this.loggedIn) {
|
||||
this.shadowRoot.getElementById('userSettingsDialog').open()
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
closeSettings() {
|
||||
this.shadowRoot.getElementById('userSettingsDialog').close()
|
||||
this.cleanUp()
|
||||
}
|
||||
|
||||
.leftBar {
|
||||
background-color: var(--white);
|
||||
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' }
|
||||
}
|
||||
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 {connect} from 'pwa-helpers';
|
||||
import {store} from '../store.js';
|
||||
import {get, translate} from '../../translate'
|
||||
import {asyncReplace} from 'lit/directives/async-replace.js';
|
||||
|
||||
import '../functional-components/my-button.js';
|
||||
import {routes} from '../plugins/routes.js';
|
||||
import { html, LitElement } from 'lit'
|
||||
import { asyncReplace } from 'lit/directives/async-replace.js'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../store.js'
|
||||
import { routes } from '../plugins/routes'
|
||||
import { startMintingStyles } from '../styles/core-css'
|
||||
import '../functional-components/my-button'
|
||||
import "@material/mwc-button"
|
||||
import '@material/mwc-dialog'
|
||||
|
||||
// Multi language support
|
||||
import { get, translate } from '../../translate'
|
||||
|
||||
async function* countDown(count, callback) {
|
||||
while (count > 0) {
|
||||
yield count--;
|
||||
await new Promise((r) => setTimeout(r, 1000));
|
||||
yield count--
|
||||
await new Promise((r) => setTimeout(r, 1000))
|
||||
if (count === 0) {
|
||||
callback()
|
||||
}
|
||||
@ -30,187 +31,56 @@ class StartMinting extends connect(store)(LitElement) {
|
||||
status: { type: Number },
|
||||
timer: { type: Number },
|
||||
privateRewardShareKey: { type: String }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [
|
||||
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);
|
||||
}
|
||||
`,
|
||||
];
|
||||
return [startMintingStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.addressInfo = {};
|
||||
this.mintingAccountData = [];
|
||||
this.errorMsg = '';
|
||||
this.openDialogRewardShare = false;
|
||||
this.status = 0;
|
||||
this.privateRewardShareKey = "";
|
||||
this.address = this.getAddress();
|
||||
this.nonce = this.getNonce();
|
||||
super()
|
||||
this.addressInfo = {}
|
||||
this.mintingAccountData = []
|
||||
this.errorMsg = ''
|
||||
this.openDialogRewardShare = false
|
||||
this.status = 0
|
||||
this.privateRewardShareKey = ''
|
||||
this.address = this.getAddress()
|
||||
this.nonce = this.getNonce()
|
||||
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() {
|
||||
return html` ${this.renderStartMintingButton()} `;
|
||||
return html`${this.renderStartMintingButton()}`
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -230,124 +100,130 @@ const nonce = selectedAddress && selectedAddress.nonce;
|
||||
}
|
||||
|
||||
async getMintingAcccounts() {
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node];
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
||||
const url = `${nodeUrl}/admin/mintingaccounts`;
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||
const url = `${nodeUrl}/admin/mintingaccounts`
|
||||
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
this.mintingAccountData = await res.json();
|
||||
const res = await fetch(url)
|
||||
|
||||
this.mintingAccountData = await res.json()
|
||||
} catch (error) {
|
||||
this.errorMsg = this.renderErrorMsg1();
|
||||
this.errorMsg = this.renderErrorMsg1()
|
||||
}
|
||||
}
|
||||
|
||||
async changeStatus(value){
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node];
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
||||
this.status = value
|
||||
async changeStatus(value) {
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||
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.
|
||||
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 url = `${nodeUrl}/admin/mintingaccounts?apiKey=${myNode.apiKey}`;
|
||||
const url = `${nodeUrl}/admin/mintingaccounts?apiKey=${myNode.apiKey}`
|
||||
return await fetch(url, {
|
||||
method: 'DELETE',
|
||||
body: publicKey,
|
||||
});
|
||||
};
|
||||
body: publicKey
|
||||
})
|
||||
}
|
||||
|
||||
const addMintingAccount = async (sponsorshipKeyValue) => {
|
||||
const url = `${nodeUrl}/admin/mintingaccounts?apiKey=${myNode.apiKey}`;
|
||||
const url = `${nodeUrl}/admin/mintingaccounts?apiKey=${myNode.apiKey}`
|
||||
|
||||
return await fetch(url, {
|
||||
method: 'POST',
|
||||
body: sponsorshipKeyValue,
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
if (
|
||||
findMintingAccountFromOtherUser &&
|
||||
findMintingAccountFromOtherUser.publicKey &&
|
||||
findMintingAccountFromOtherUser.publicKey[0]
|
||||
) {
|
||||
await removeMintingAccount(
|
||||
findMintingAccountFromOtherUser.publicKey[0]
|
||||
);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
this.errorMsg = this.renderErrorMsg2();
|
||||
return;
|
||||
body: sponsorshipKeyValue
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
await addMintingAccount(this.privateRewardShareKey);
|
||||
await routes.showSnackBar({
|
||||
data: translate('becomeMinterPage.bchange19'),
|
||||
});
|
||||
this.status = 5;
|
||||
await this.getMintingAcccounts();
|
||||
if (findMintingAccountFromOtherUser && findMintingAccountFromOtherUser.publicKey && findMintingAccountFromOtherUser.publicKey[0]) {
|
||||
await removeMintingAccount(
|
||||
findMintingAccountFromOtherUser.publicKey[0]
|
||||
)
|
||||
}
|
||||
} 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() {
|
||||
const myNode =
|
||||
store.getState().app.nodeConfig.knownNodes[
|
||||
store.getState().app.nodeConfig.node
|
||||
];
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||
|
||||
let interval = null
|
||||
let stop = false
|
||||
|
||||
this.status = 2
|
||||
|
||||
const getAnswer = async () => {
|
||||
const rewardShares = async (minterAddr) => {
|
||||
const url = `${nodeUrl}/addresses/rewardshares?minters=${minterAddr}&recipients=${minterAddr}`;
|
||||
const res = await fetch(url);
|
||||
return await res.json();
|
||||
};
|
||||
const url = `${nodeUrl}/addresses/rewardshares?minters=${minterAddr}&recipients=${minterAddr}`
|
||||
const res = await fetch(url)
|
||||
|
||||
return await res.json()
|
||||
}
|
||||
|
||||
if (!stop) {
|
||||
stop = true;
|
||||
stop = true
|
||||
|
||||
try {
|
||||
const address = this.address
|
||||
const myRewardShareArray = await rewardShares(address);
|
||||
const myRewardShareArray = await rewardShares(address)
|
||||
|
||||
if (myRewardShareArray.length > 0) {
|
||||
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
|
||||
}
|
||||
};
|
||||
interval = setInterval(getAnswer, 5000);
|
||||
}
|
||||
|
||||
interval = setInterval(getAnswer, 5000)
|
||||
}
|
||||
|
||||
renderStartMintingButton() {
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node];
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
||||
const mintingAccountData = this.mintingAccountData;
|
||||
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||
const mintingAccountData = this.mintingAccountData
|
||||
const addressInfo = window.parent.reduxStore.getState().app.accountInfo.addressInfo
|
||||
const address = this.address
|
||||
const nonce = this.nonce
|
||||
const publicAddress = this.base58PublicKey
|
||||
const findMintingAccount = mintingAccountData.find((ma) => ma.mintingAccount === address);
|
||||
const isMinterButKeyMintingKeyNotAssigned = addressInfo && addressInfo.error !== 124 && addressInfo.level >= 1 && !findMintingAccount;
|
||||
const findMintingAccount = mintingAccountData.find((ma) => ma.mintingAccount === address)
|
||||
const isMinterButKeyMintingKeyNotAssigned = addressInfo && addressInfo.error !== 124 && addressInfo.level >= 1 && !findMintingAccount
|
||||
|
||||
const makeTransactionRequest = async (lastRef) => {
|
||||
let mylastRef = lastRef;
|
||||
let rewarddialog1 = get('transactions.rewarddialog1');
|
||||
let rewarddialog2 = get('transactions.rewarddialog2');
|
||||
let rewarddialog3 = get('transactions.rewarddialog3');
|
||||
let rewarddialog4 = get('transactions.rewarddialog4');
|
||||
let mylastRef = lastRef
|
||||
let rewarddialog1 = get('transactions.rewarddialog1')
|
||||
let rewarddialog2 = get('transactions.rewarddialog2')
|
||||
let rewarddialog3 = get('transactions.rewarddialog3')
|
||||
let rewarddialog4 = get('transactions.rewarddialog4')
|
||||
|
||||
return await routes.transaction({
|
||||
data: {
|
||||
@ -360,170 +236,150 @@ const nonce = selectedAddress && selectedAddress.nonce;
|
||||
rewarddialog1: rewarddialog1,
|
||||
rewarddialog2: rewarddialog2,
|
||||
rewarddialog3: rewarddialog3,
|
||||
rewarddialog4: rewarddialog4,
|
||||
},
|
||||
rewarddialog4: rewarddialog4
|
||||
}
|
||||
},
|
||||
disableModal: true,
|
||||
});
|
||||
};
|
||||
disableModal: true
|
||||
})
|
||||
}
|
||||
|
||||
const getTxnRequestResponse = (txnResponse) => {
|
||||
let err6string = get('rewardsharepage.rchange21');
|
||||
let err6string = get('rewardsharepage.rchange21')
|
||||
|
||||
if (txnResponse && txnResponse.extraData && txnResponse.extraData.rewardSharePrivateKey &&
|
||||
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) {
|
||||
throw txnResponse;
|
||||
throw txnResponse
|
||||
} else if (txnResponse.success === true && txnResponse.data && !txnResponse.data.error) {
|
||||
return err6string;
|
||||
return err6string
|
||||
} else {
|
||||
throw txnResponse;
|
||||
throw txnResponse
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
const createSponsorshipKey = async () => {
|
||||
this.status = 1;
|
||||
let lastRef = await getLastRef();
|
||||
let myTransaction = await makeTransactionRequest(lastRef);
|
||||
getTxnRequestResponse(myTransaction);
|
||||
this.status = 1
|
||||
|
||||
let lastRef = await getLastRef()
|
||||
let myTransaction = await makeTransactionRequest(lastRef)
|
||||
|
||||
getTxnRequestResponse(myTransaction)
|
||||
|
||||
if (myTransaction && myTransaction.extraData) {
|
||||
return myTransaction.extraData.rewardSharePrivateKey;
|
||||
return myTransaction.extraData.rewardSharePrivateKey
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const getLastRef = async () => {
|
||||
const url = `${nodeUrl}/addresses/lastreference/${address}`;
|
||||
const res = await fetch(url);
|
||||
return await res.text();
|
||||
};
|
||||
const url = `${nodeUrl}/addresses/lastreference/${address}`
|
||||
const res = await fetch(url)
|
||||
|
||||
return await res.text()
|
||||
}
|
||||
|
||||
const startMinting = async () => {
|
||||
this.openDialogRewardShare = true
|
||||
this.errorMsg = '';
|
||||
this.errorMsg = ''
|
||||
|
||||
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")
|
||||
return;
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
this.privateRewardShareKey = await createSponsorshipKey();
|
||||
this.privateRewardShareKey = await createSponsorshipKey()
|
||||
|
||||
await this.confirmRelationship(publicAddress)
|
||||
} catch (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`
|
||||
${isMinterButKeyMintingKeyNotAssigned ? html`
|
||||
<div class="start-minting-wrapper">
|
||||
<my-button label="${translate('becomeMinterPage.bchange18')}"
|
||||
<my-button
|
||||
label="${translate('becomeMinterPage.bchange18')}"
|
||||
?isLoading=${false}
|
||||
.onClick=${async () => {
|
||||
await startMinting();
|
||||
if (this.errorMsg) {
|
||||
await routes.showSnackBar({
|
||||
data: this.errorMsg,
|
||||
data: this.errorMsg
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
</my-button>
|
||||
></my-button>
|
||||
</div>
|
||||
|
||||
<!-- Dialog for tracking the progress of starting minting -->
|
||||
|
||||
${this.openDialogRewardShare ? html`
|
||||
<div class="dialogCustom">
|
||||
<div class="dialogCustomInner">
|
||||
<div class="dialog-header" >
|
||||
<div class="dialog-header" >
|
||||
<div class="row">
|
||||
<h1>In progress</h1>
|
||||
<div class=${`smallLoading marginLoader ${this.status > 3 && 'hide'}`}></div>
|
||||
<h1>In progress</h1>
|
||||
<div class=${`smallLoading marginLoader ${this.status > 3 && 'hide'}`}></div>
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
<div class="dialog-container">
|
||||
<ul>
|
||||
<li class="row between">
|
||||
<p>
|
||||
1. ${translate("startminting.smchange5")}
|
||||
</p>
|
||||
<div class=${`smallLoading marginLoader ${this.status !== 1 && 'hide'}`}></div>
|
||||
</li>
|
||||
|
||||
<li class=${`row between ${this.status < 2 && 'inactiveText'}`}>
|
||||
<p>
|
||||
2. ${translate("startminting.smchange6")}
|
||||
</p>
|
||||
<div class=${`smallLoading marginLoader ${this.status !== 2 && 'hide'}`}></div>
|
||||
</li>
|
||||
|
||||
<li class=${`row between ${this.status < 3 && 'inactiveText'}`}>
|
||||
<p>
|
||||
3. ${translate("startminting.smchange7")}
|
||||
</p>
|
||||
<div class="row no-width">
|
||||
<div class=${`smallLoading marginLoader marginRight ${this.status !== 3 && 'hide'}`} ></div> <p>${asyncReplace(this.timer)}</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class=${`row between ${this.status < 4 && 'inactiveText'}`}>
|
||||
<p>
|
||||
4. ${translate("startminting.smchange8")}
|
||||
</p>
|
||||
<div class=${`smallLoading marginLoader ${this.status !== 4 && 'hide'}`}></div>
|
||||
</li>
|
||||
|
||||
<li class=${`row between ${this.status < 5 && 'inactiveText'}`}>
|
||||
<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 class="dialog-container">
|
||||
<ul>
|
||||
<li class="row between">
|
||||
<p>1. ${translate("startminting.smchange5")}</p>
|
||||
<div class=${`smallLoading marginLoader ${this.status !== 1 && 'hide'}`}></div>
|
||||
</li>
|
||||
<li class=${`row between ${this.status < 2 && 'inactiveText'}`}>
|
||||
<p>2. ${translate("startminting.smchange6")}</p>
|
||||
<div class=${`smallLoading marginLoader ${this.status !== 2 && 'hide'}`}></div>
|
||||
</li>
|
||||
<li class=${`row between ${this.status < 3 && 'inactiveText'}`}>
|
||||
<p>3. ${translate("startminting.smchange7")}</p>
|
||||
<div class="row no-width">
|
||||
<div class=${`smallLoading marginLoader marginRight ${this.status !== 3 && 'hide'}`} ></div>
|
||||
<p>${asyncReplace(this.timer)}</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class=${`row between ${this.status < 4 && 'inactiveText'}`}>
|
||||
<p>4. ${translate("startminting.smchange8")}</p>
|
||||
<div class=${`smallLoading marginLoader ${this.status !== 4 && 'hide'}`}></div>
|
||||
</li>
|
||||
<li class=${`row between ${this.status < 5 && 'inactiveText'}`}>
|
||||
<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 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 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>
|
||||
|
||||
` : ""}
|
||||
` : ''}
|
||||
` : ''}
|
||||
`;
|
||||
`
|
||||
}
|
||||
|
||||
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 {translate} from '../../translate'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { themeToggleStyles } from '../styles/core-css'
|
||||
import '@polymer/paper-icon-button/paper-icon-button.js'
|
||||
import '@polymer/iron-icons/image-icons.js'
|
||||
import '@polymer/iron-icons/iron-icons.js'
|
||||
|
||||
// Multi language support
|
||||
import { translate } from '../../translate'
|
||||
|
||||
class ThemeToggle extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
@ -11,58 +14,15 @@ class ThemeToggle extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [themeToggleStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
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() {
|
||||
return html`
|
||||
<div style="display: inline;">
|
||||
@ -87,16 +47,17 @@ class ThemeToggle extends LitElement {
|
||||
this.dispatchEvent(new CustomEvent('qort-theme-change', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: this.theme,
|
||||
detail: this.theme
|
||||
}))
|
||||
|
||||
window.localStorage.setItem('qortalTheme', this.theme)
|
||||
|
||||
this.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 {connect} from 'pwa-helpers'
|
||||
import {store} from '../store.js'
|
||||
import {translate} from '../../translate'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../store'
|
||||
import { walletProfileStyles } from '../styles/core-css'
|
||||
|
||||
// Multi language support
|
||||
import { translate } from '../../translate'
|
||||
|
||||
class WalletProfile extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
wallet: { type: Object },
|
||||
nodeConfig: { type: Object },
|
||||
accountInfo: { type: Object },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
wallet: { type: Object },
|
||||
nodeConfig: { type: Object },
|
||||
accountInfo: { type: Object },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
#profileInMenu {
|
||||
padding: 12px;
|
||||
border-top: var(--border);
|
||||
background: var(--sidetopbar);
|
||||
color: var(--black);
|
||||
}
|
||||
static get styles() {
|
||||
return [walletProfileStyles]
|
||||
}
|
||||
|
||||
#accountName {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
width: 100%;
|
||||
padding-bottom: 8px;
|
||||
display: flex;
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.wallet = {}
|
||||
this.nodeConfig = {}
|
||||
this.accountInfo = {
|
||||
names: [],
|
||||
addressInfo: {}
|
||||
}
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
|
||||
#blocksMinted {
|
||||
margin:0;
|
||||
margin-top: 0;
|
||||
font-size: 12px;
|
||||
color: #03a9f4;
|
||||
}
|
||||
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>
|
||||
`
|
||||
}
|
||||
|
||||
#address {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin:0;
|
||||
margin-top: 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
firstUpdated() {
|
||||
// ...
|
||||
}
|
||||
|
||||
.round-fullinfo {
|
||||
position: relative;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
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`
|
||||
|
||||
.full-info-logo {
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
return html`<img class="round-fullinfo" src="${url}" onerror="this.src='/img/incognito.png';" />`
|
||||
}
|
||||
}
|
||||
|
||||
.inline-block-child {
|
||||
flex: 1;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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 = {
|
||||
int32ToBytes (word) {
|
||||
var byteArray = []
|
||||
for (var b = 0; b < 32; b += 8) {
|
||||
byteArray.push((word >>> (24 - b % 32)) & 0xFF)
|
||||
}
|
||||
return byteArray
|
||||
},
|
||||
int32ToBytes(word) {
|
||||
var byteArray = []
|
||||
for (var b = 0; b < 32; b += 8) {
|
||||
byteArray.push((word >>> (24 - b % 32)) & 0xFF)
|
||||
}
|
||||
return byteArray
|
||||
},
|
||||
|
||||
stringtoUTF8Array (message) {
|
||||
if (typeof message === 'string') {
|
||||
var s = unescape(encodeURIComponent(message)) // UTF-8
|
||||
message = new Uint8Array(s.length)
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
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
|
||||
},
|
||||
stringtoUTF8Array(message) {
|
||||
if (typeof message === 'string') {
|
||||
var s = unescape(encodeURIComponent(message)) // UTF-8
|
||||
message = new Uint8Array(s.length)
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
message[i] = s.charCodeAt(i) & 0xff
|
||||
}
|
||||
}
|
||||
|
||||
int64ToBytes (int64) {
|
||||
// we want to represent the input as a 8-bytes array
|
||||
var byteArray = [0, 0, 0, 0, 0, 0, 0, 0]
|
||||
return message
|
||||
},
|
||||
|
||||
for (var index = 0; index < byteArray.length; index++) {
|
||||
var byte = int64 & 0xff
|
||||
byteArray[byteArray.length - index - 1] = byte
|
||||
int64 = (int64 - byte) / 256
|
||||
}
|
||||
// ...buffers then buffers.foreach and append to buffer1
|
||||
appendBuffer(buffer1, buffer2) {
|
||||
buffer1 = new Uint8Array(buffer1)
|
||||
buffer2 = new Uint8Array(buffer2)
|
||||
|
||||
return byteArray
|
||||
},
|
||||
const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength)
|
||||
|
||||
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
|
||||
}
|
||||
tmp.set(buffer1, 0)
|
||||
tmp.set(buffer2, buffer1.byteLength)
|
||||
|
||||
return tmp
|
||||
},
|
||||
|
||||
int64ToBytes(int64) {
|
||||
// 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 = [
|
||||
{
|
||||
"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",
|
||||
"domain": "core",
|
||||
|
@ -15,4 +15,4 @@ Epml.registerPlugin(EpmlStreamPlugin)
|
||||
Epml.registerPlugin(EpmlProxyPlugin)
|
||||
Epml.allowProxying = true
|
||||
|
||||
export { Epml, EpmlStream }
|
||||
export { Epml, EpmlStream }
|
@ -1,106 +1,83 @@
|
||||
import {css, html, LitElement} from 'lit'
|
||||
import {connect} from 'pwa-helpers'
|
||||
import {store} from '../store.js'
|
||||
import {get, translate} from '../../translate'
|
||||
|
||||
import {listenForRequest} from '../transactionRequest.js'
|
||||
|
||||
import '@polymer/paper-dialog/paper-dialog.js'
|
||||
import { css, html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../store'
|
||||
import { listenForRequest } from '../transactionRequest'
|
||||
import { confirmTransactionDialogStyles } from '../styles/core-css'
|
||||
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) {
|
||||
static get properties() {
|
||||
return {
|
||||
txInfo: { type: Object },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
txInfo: { type: Object },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
* {
|
||||
--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);
|
||||
}
|
||||
static get styles() {
|
||||
return [confirmTransactionDialogStyles]
|
||||
}
|
||||
|
||||
.decline {
|
||||
--mdc-theme-primary: var(--mdc-theme-error)
|
||||
}
|
||||
constructor() {
|
||||
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 {
|
||||
text-align: left;
|
||||
max-width: 520px;
|
||||
color: var(--black);
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<paper-dialog style="background: var(--white);" id="confirmDialog" modal>
|
||||
<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 {
|
||||
text-align:right;
|
||||
}
|
||||
firstUpdated() {
|
||||
// ...
|
||||
}
|
||||
|
||||
table td, th{
|
||||
padding:4px;
|
||||
text-align:left;
|
||||
font-size:14px;
|
||||
color: var(--black);
|
||||
}
|
||||
`
|
||||
}
|
||||
requestTransaction(transaction) {
|
||||
this.shadowRoot.getElementById('confirmDialog').open()
|
||||
this.transaction = transaction
|
||||
this.txInfo = transaction.render(html)
|
||||
|
||||
constructor() {
|
||||
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';
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._resolve = resolve
|
||||
this._reject = reject
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<paper-dialog style="background: var(--white);" id="confirmDialog" modal>
|
||||
<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>
|
||||
`
|
||||
}
|
||||
confirm(e) {
|
||||
this._resolve({
|
||||
success: true
|
||||
})
|
||||
}
|
||||
|
||||
stateChanged(state) {
|
||||
this.loggedIn = state.app.loggedIn
|
||||
}
|
||||
decline(e) {
|
||||
const rejecterror = get("transactions.declined")
|
||||
this._reject(new Error(rejecterror))
|
||||
}
|
||||
|
||||
requestTransaction(transaction) {
|
||||
this.shadowRoot.getElementById('confirmDialog').open()
|
||||
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))
|
||||
}
|
||||
stateChanged(state) {
|
||||
this.loggedIn = state.app.loggedIn
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('confirm-transaction-dialog', ConfirmTransactionDialog)
|
||||
|
||||
const txDialog = document.createElement('confirm-transaction-dialog')
|
||||
export const requestTransactionDialog = document.body.appendChild(txDialog)
|
||||
export const requestTransactionDialog = document.body.appendChild(txDialog)
|
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']
|
||||
|
||||
let rippleElement
|
||||
|
||||
class LoadingRipple extends LitElement {
|
||||
static get properties () {
|
||||
return {
|
||||
welcomeMessage: {
|
||||
type: String,
|
||||
attribute: 'welcome-message',
|
||||
reflectToAttribute: true
|
||||
},
|
||||
loadingMessage: {
|
||||
type: String,
|
||||
attribute: 'loading-message',
|
||||
reflectToAttribute: true
|
||||
}
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
welcomeMessage: { type: String, attribute: 'welcome-message', reflectToAttribute: true },
|
||||
loadingMessage: { type: String, attribute: 'loading-message', reflectToAttribute: true}
|
||||
}
|
||||
}
|
||||
|
||||
constructor () {
|
||||
super()
|
||||
this.welcomeMessage = ''
|
||||
this.loadingMessage = ''
|
||||
}
|
||||
static get styles() {
|
||||
return [loadingRippleStyles]
|
||||
}
|
||||
|
||||
static get styles () {
|
||||
return css`
|
||||
* {
|
||||
--mdc-theme-primary: rgb(3, 169, 244);
|
||||
--mdc-theme-secondary: var(--mdc-theme-primary);
|
||||
--paper-spinner-color: var(--mdc-theme-secondary);
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.welcomeMessage = ''
|
||||
this.loadingMessage = ''
|
||||
}
|
||||
|
||||
#rippleWrapper{
|
||||
position:fixed;
|
||||
top:0;
|
||||
left:0;
|
||||
bottom:0;
|
||||
right:0;
|
||||
height:0;
|
||||
width:0;
|
||||
z-index:999;
|
||||
overflow: visible;
|
||||
--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);
|
||||
--ripple-disable-transition: opacity 0.5s ease;
|
||||
}
|
||||
#ripple {
|
||||
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);
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<div id="rippleWrapper">
|
||||
<div id="ripple">
|
||||
<div id="rippleShader"></div>
|
||||
<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() {
|
||||
this._rippleWrapper = this.shadowRoot.getElementById('rippleWrapper')
|
||||
this._ripple = this.shadowRoot.getElementById('ripple')
|
||||
this._rippleContentWrapper = this.shadowRoot.getElementById('rippleContentWrapper')
|
||||
}
|
||||
|
||||
render () {
|
||||
return html`
|
||||
<div id="rippleWrapper">
|
||||
<div id="ripple">
|
||||
<div id="rippleShader"></div>
|
||||
<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>
|
||||
`
|
||||
}
|
||||
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'
|
||||
|
||||
firstUpdated () {
|
||||
this._rippleWrapper = this.shadowRoot.getElementById('rippleWrapper')
|
||||
this._ripple = this.shadowRoot.getElementById('ripple')
|
||||
this._rippleContentWrapper = this.shadowRoot.getElementById('rippleContentWrapper')
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._ripple.classList.add('activating')
|
||||
|
||||
// duh
|
||||
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'
|
||||
let isOpened = false
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this._ripple.classList.add('activating')
|
||||
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))
|
||||
})
|
||||
}
|
||||
const doneOpeningEvent = () => {
|
||||
if (isOpened) return
|
||||
|
||||
// Fades out
|
||||
fade () {
|
||||
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()
|
||||
})
|
||||
}
|
||||
// Clear events
|
||||
TRANSITION_EVENT_NAMES.forEach(name => this._ripple.removeEventListener(name, doneOpeningEvent))
|
||||
|
||||
// un-ripples...
|
||||
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))
|
||||
})
|
||||
}
|
||||
this._ripple.classList.add('activating-done')
|
||||
|
||||
stateChanged (state) {
|
||||
// this.loggedIn = state.app.loggedIn
|
||||
}
|
||||
isOpened = true
|
||||
|
||||
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)
|
||||
@ -192,10 +122,9 @@ rippleNode.id = 'ripple-node'
|
||||
rippleNode.loadingMessage = ''
|
||||
rippleElement = document.body.appendChild(rippleNode)
|
||||
setTimeout(() => {
|
||||
const ripple = document.getElementById('ripple-node')
|
||||
const mainApp = document.getElementById('main-app')
|
||||
const shadow = mainApp.shadowRoot
|
||||
// console.log(shadow)
|
||||
rippleElement = shadow.appendChild(ripple)
|
||||
const ripple = document.getElementById('ripple-node')
|
||||
const mainApp = document.getElementById('main-app')
|
||||
const shadow = mainApp.shadowRoot
|
||||
rippleElement = shadow.appendChild(ripple)
|
||||
}, 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 '@vaadin/button';
|
||||
import '@polymer/paper-spinner/paper-spinner-lite.js';
|
||||
import { html, LitElement } from 'lit'
|
||||
import { myButtonStyles } from '../styles/core-css'
|
||||
import '@vaadin/button'
|
||||
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
||||
|
||||
export class MyButton extends LitElement {
|
||||
static properties = {
|
||||
onClick: { type: Function },
|
||||
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;
|
||||
static get properties() {
|
||||
return {
|
||||
onClick: { type: Function },
|
||||
isLoading: { type: Boolean },
|
||||
label: { type: String }
|
||||
}
|
||||
}
|
||||
|
||||
vaadin-button:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
`;
|
||||
static get styles() {
|
||||
return [myButtonStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.onClick = () => {};
|
||||
this.isLoading = false;
|
||||
this.label = '';
|
||||
super()
|
||||
this.onClick = () => { }
|
||||
this.isLoading = false
|
||||
this.label = ''
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<vaadin-button
|
||||
?disabled="${this.isLoading}"
|
||||
@click="${this.onClick}"
|
||||
>
|
||||
${this.isLoading === false
|
||||
? html`${this.label}`
|
||||
: html`<paper-spinner-lite active></paper-spinner-lite>`}
|
||||
<vaadin-button ?disabled="${this.isLoading}" @click="${this.onClick}">
|
||||
${this.isLoading === false ? html`${this.label}` : html`<paper-spinner-lite active></paper-spinner-lite>`}
|
||||
</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 {connect} from 'pwa-helpers'
|
||||
import {store} from '../store.js'
|
||||
import {testApiKey} from '../apiKeyUtils.js'
|
||||
import {get, translate} from '../../translate'
|
||||
|
||||
import '@material/mwc-dialog'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../store'
|
||||
import { testApiKey } from '../apiKeyUtils'
|
||||
import { mykeyPageStyles } from '../styles/core-css'
|
||||
import snackbar from './snackbar'
|
||||
import '@material/mwc-button'
|
||||
import '@material/mwc-select'
|
||||
import '@material/mwc-textfield'
|
||||
import '@material/mwc-dialog'
|
||||
import '@material/mwc-icon'
|
||||
import '@material/mwc-textfield'
|
||||
|
||||
import snackbar from './snackbar.js'
|
||||
// Multi language support
|
||||
import { get, translate } from '../../translate'
|
||||
|
||||
let mykeyDialog
|
||||
|
||||
class MykeyPage extends connect(store)(LitElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
nodeConfig: { type: Object },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
nodeConfig: { type: Object },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
* {
|
||||
--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);
|
||||
}
|
||||
static get styles() {
|
||||
return [mykeyPageStyles]
|
||||
}
|
||||
|
||||
.red {
|
||||
--mdc-theme-primary: red;
|
||||
}
|
||||
`
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.nodeConfig = {}
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.nodeConfig = {}
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light';
|
||||
}
|
||||
render() {
|
||||
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>
|
||||
`
|
||||
}
|
||||
|
||||
render() {
|
||||
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>
|
||||
`
|
||||
}
|
||||
firstUpdated() {
|
||||
// ...
|
||||
}
|
||||
|
||||
stateChanged(state) {
|
||||
this.config = state.config
|
||||
this.nodeConfig = state.app.nodeConfig
|
||||
}
|
||||
show() {
|
||||
this.shadowRoot.getElementById('mykeyDialog').show()
|
||||
}
|
||||
|
||||
show() {
|
||||
this.shadowRoot.getElementById('mykeyDialog').show()
|
||||
}
|
||||
async addMykey() {
|
||||
const mykeyInput = this.shadowRoot.getElementById('mykeyInput').value
|
||||
|
||||
async addMykey() {
|
||||
const mykeyInput = this.shadowRoot.getElementById('mykeyInput').value
|
||||
let selectedNode = this.nodeConfig.knownNodes[this.nodeConfig.node];
|
||||
let testResult = await testApiKey(mykeyInput);
|
||||
let selectedNode = this.nodeConfig.knownNodes[this.nodeConfig.node]
|
||||
let testResult = await testApiKey(mykeyInput)
|
||||
|
||||
if (testResult === true) {
|
||||
selectedNode.apiKey = mykeyInput;
|
||||
this.nodeConfig.knownNodes[this.nodeConfig.node] = selectedNode;
|
||||
localStorage.setItem('myQortalNodes', JSON.stringify(this.nodeConfig.knownNodes));
|
||||
let snackbar1 = get("apipage.achange6")
|
||||
snackbar.add({
|
||||
labelText: `${snackbar1}`,
|
||||
dismiss: true
|
||||
})
|
||||
this.shadowRoot.getElementById('mykeyInput').value = ''
|
||||
this.shadowRoot.querySelector('#mykeyDialog').close()
|
||||
} else {
|
||||
let snackbar2 = get("apipage.achange7")
|
||||
snackbar.add({
|
||||
labelText: `${snackbar2}`,
|
||||
dismiss: true
|
||||
})
|
||||
this.shadowRoot.getElementById('mykeyInput').value = ''
|
||||
this.shadowRoot.querySelector('#mykeyDialog').close()
|
||||
}
|
||||
}
|
||||
if (testResult === true) {
|
||||
selectedNode.apiKey = mykeyInput
|
||||
|
||||
this.nodeConfig.knownNodes[this.nodeConfig.node] = selectedNode
|
||||
|
||||
localStorage.setItem('myQortalNodes', JSON.stringify(this.nodeConfig.knownNodes))
|
||||
|
||||
let snackbar1 = get("apipage.achange6")
|
||||
|
||||
snackbar.add({
|
||||
labelText: `${snackbar1}`,
|
||||
dismiss: true
|
||||
})
|
||||
|
||||
this.shadowRoot.getElementById('mykeyInput').value = ''
|
||||
this.shadowRoot.querySelector('#mykeyDialog').close()
|
||||
} else {
|
||||
let snackbar2 = get("apipage.achange7")
|
||||
|
||||
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)
|
||||
|
||||
const mykey = document.createElement('mykey-page')
|
||||
mykeyDialog = document.body.appendChild(mykey)
|
||||
|
||||
export default mykeyDialog
|
||||
export default mykeyDialog
|
@ -1,38 +1,18 @@
|
||||
// Author: irontiga <irontiga@gmail.com>
|
||||
|
||||
'use strict'
|
||||
import {html, LitElement} from 'lit'
|
||||
import * as WORDLISTS from './wordlists.js'
|
||||
import { html, LitElement } from 'lit'
|
||||
import * as WORDLISTS from './wordlists'
|
||||
|
||||
class RandomSentenceGenerator extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
template: {
|
||||
type: String,
|
||||
attribute: 'template'
|
||||
},
|
||||
parsedString: {
|
||||
type: String
|
||||
},
|
||||
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'
|
||||
}
|
||||
template: { type: String, attribute: 'template' },
|
||||
parsedString: { type: String },
|
||||
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
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${this.parsedString}
|
||||
`
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
// ...
|
||||
}
|
||||
|
||||
updated(changedProperties) {
|
||||
let regen = false
|
||||
|
||||
if (changedProperties.has('template')) {
|
||||
regen = true
|
||||
}
|
||||
|
||||
if (changedProperties.has('maxWordLength')) {
|
||||
console.dir(this.maxWordLength)
|
||||
|
||||
if (this.maxWordLength) {
|
||||
const wl = { ...this._wordlists }
|
||||
|
||||
for (const partOfSpeech in this._wordlists) {
|
||||
console.log(this._wordlists[partOfSpeech])
|
||||
if (Array.isArray(this._wordlists[partOfSpeech])) {
|
||||
wl[partOfSpeech] = this._wordlists[partOfSpeech].filter(word => word.length <= this.maxWordLength)
|
||||
}
|
||||
}
|
||||
|
||||
this._wordlists = wl
|
||||
}
|
||||
|
||||
regen = true
|
||||
}
|
||||
|
||||
if (regen) this.generate()
|
||||
}
|
||||
|
||||
@ -83,13 +80,16 @@ class RandomSentenceGenerator extends LitElement {
|
||||
if (entropy > 1074) {
|
||||
throw new Error('Javascript can not handle that much entropy!')
|
||||
}
|
||||
|
||||
let randNum = 0
|
||||
|
||||
const crypto = window.crypto || window.msCrypto
|
||||
|
||||
if (crypto) {
|
||||
const entropy256 = Math.ceil(entropy / 8)
|
||||
|
||||
let buffer = new Uint8Array(entropy256)
|
||||
|
||||
crypto.getRandomValues(buffer)
|
||||
|
||||
randNum = buffer.reduce((num, value) => {
|
||||
@ -97,8 +97,10 @@ class RandomSentenceGenerator extends LitElement {
|
||||
}, 1) / Math.pow(256, entropy256)
|
||||
} else {
|
||||
console.warn('Secure RNG not found. Using Math.random')
|
||||
|
||||
randNum = Math.random()
|
||||
}
|
||||
|
||||
return randNum
|
||||
}
|
||||
|
||||
@ -127,7 +129,9 @@ class RandomSentenceGenerator extends LitElement {
|
||||
|
||||
parse(template) {
|
||||
const split = template.split(/[\s]/g)
|
||||
|
||||
let entropy = 1
|
||||
|
||||
const final = split.map(word => {
|
||||
const lower = word.toLowerCase()
|
||||
|
||||
@ -139,22 +143,20 @@ class RandomSentenceGenerator extends LitElement {
|
||||
const replacement = this.getWord(partOfSpeech)
|
||||
word = replacement.word + word.slice(partOfSpeech.length) // Append the rest of the "word" (punctuation)
|
||||
entropy = entropy * replacement.entropy
|
||||
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
return word
|
||||
})
|
||||
this.templateEntropy = Math.floor(Math.log(entropy) / Math.log(8))
|
||||
return final.join(' ')
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${this.parsedString}
|
||||
`
|
||||
this.templateEntropy = Math.floor(Math.log(entropy) / Math.log(8))
|
||||
|
||||
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 {connect} from 'pwa-helpers'
|
||||
import {store} from '../store.js'
|
||||
import {doAddNode, doEditNode, doLoadNodeConfig, doRemoveNode, doSetNode} from '../redux/app/app-actions.js'
|
||||
import {get, registerTranslateConfig, translate, use} from '../../translate'
|
||||
import snackbar from './snackbar.js'
|
||||
import '../components/language-selector.js'
|
||||
import '../custom-elements/frag-file-input.js'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { connect } from 'pwa-helpers'
|
||||
import { store } from '../store'
|
||||
import { doAddNode, doEditNode, doLoadNodeConfig, doRemoveNode, doSetNode } from '../redux/app/app-actions'
|
||||
import { settingsPageStyles } from '../styles/core-css'
|
||||
import FileSaver from 'file-saver'
|
||||
|
||||
import '@material/mwc-dialog'
|
||||
import snackbar from './snackbar'
|
||||
import './frag-file-input'
|
||||
import '../components/language-selector'
|
||||
import '@material/mwc-button'
|
||||
import '@material/mwc-select'
|
||||
import '@material/mwc-textfield'
|
||||
import '@material/mwc-dialog'
|
||||
import '@material/mwc-icon'
|
||||
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({
|
||||
loader: (lang) => fetch(`/language/${lang}.json`).then((res) => res.json())
|
||||
@ -35,132 +37,23 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
return {
|
||||
lastSelected: { type: Number },
|
||||
nodeConfig: { type: Object },
|
||||
theme: { type: String, reflect: true },
|
||||
isBeingEdited: { type: Boolean },
|
||||
dropdownOpen: { type: Boolean }
|
||||
dropdownOpen: { type: Boolean },
|
||||
theme: { type: String, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
* {
|
||||
--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;
|
||||
}
|
||||
`
|
||||
return [settingsPageStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
this.nodeConfig = {}
|
||||
this.isBeingEdited = false
|
||||
this.isBeingEditedIndex = null
|
||||
this.dropdownOpen = false
|
||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -176,68 +69,65 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
<div class="selected">
|
||||
<div class="selected-left-side">
|
||||
<mwc-icon style="margin-right: 10px">link</mwc-icon>
|
||||
${this.selectedItem ? html
|
||||
`
|
||||
<div>
|
||||
<span class="name">${this.selectedItem.name}</span>
|
||||
<span>${this.selectedItem.protocol + '://' + this.selectedItem.domain + ':' + this.selectedItem.port}</span>
|
||||
</div>
|
||||
` : html`${translate('settings.selectnode')}`
|
||||
}
|
||||
</div>
|
||||
${this.selectedItem ?
|
||||
html `
|
||||
<div>
|
||||
<span class="name">${this.selectedItem.name}</span>
|
||||
<span>${this.selectedItem.protocol + '://' + this.selectedItem.domain + ':' + this.selectedItem.port}</span>
|
||||
</div>
|
||||
` : html`
|
||||
${translate('settings.selectnode')}
|
||||
`
|
||||
}
|
||||
</div>
|
||||
<mwc-icon>expand_more</mwc-icon>
|
||||
</div>
|
||||
<ul class="${this.dropdownOpen ? 'open' : ''}">
|
||||
${this.nodeConfig.knownNodes.map(
|
||||
(n, index) => html`
|
||||
<li @click="${(e) => this.handleSelection(e, n, index)}">
|
||||
<div class="list-parent">
|
||||
<div>
|
||||
<span class="name">${n.name}</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>
|
||||
${this.nodeConfig.knownNodes.map((n, index) => html`
|
||||
<li @click="${(e) => this.handleSelection(e, n, index)}">
|
||||
<div class="list-parent">
|
||||
<div>
|
||||
<span class="name">${n.name}</span>
|
||||
<span>${n.protocol + '://' + n.domain + ':' + n.port}</span>
|
||||
</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>
|
||||
</div>
|
||||
<p style="margin-top: 30px; text-align: center;">
|
||||
${translate('settings.nodehint')}
|
||||
</p>
|
||||
<center>
|
||||
<mwc-button
|
||||
outlined
|
||||
@click="${() => this.shadowRoot.querySelector('#addNodeDialog').show()}"
|
||||
><mwc-icon class="buttongreen">add</mwc-icon
|
||||
>
|
||||
<mwc-button outlined @click="${() => this.shadowRoot.querySelector('#addNodeDialog').show()}">
|
||||
<mwc-icon class="buttongreen">add</mwc-icon>
|
||||
${translate('settings.addcustomnode')}
|
||||
</mwc-button>
|
||||
</center>
|
||||
@ -261,8 +151,7 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
<br />
|
||||
<center>
|
||||
<div id="main">
|
||||
<mwc-icon class="globe">language</mwc-icon
|
||||
> <language-selector></language-selector>
|
||||
<mwc-icon class="globe">language</mwc-icon> <language-selector></language-selector>
|
||||
</div>
|
||||
</center>
|
||||
</div>
|
||||
@ -270,7 +159,6 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
${translate('general.close')}
|
||||
</mwc-button>
|
||||
</mwc-dialog>
|
||||
|
||||
<mwc-dialog id="addNodeDialog">
|
||||
<div style="text-align: center;">
|
||||
<h2>${translate('settings.addcustomnode')}</h2>
|
||||
@ -293,7 +181,6 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
${translate('settings.addandsave')}
|
||||
</mwc-button>
|
||||
</mwc-dialog>
|
||||
|
||||
<mwc-dialog id="importQortalNodesListDialog">
|
||||
<div style="text-align:center">
|
||||
<h2>${translate('settings.import')}</h2>
|
||||
@ -326,6 +213,7 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
|
||||
firstUpdated() {
|
||||
const checkNode = localStorage.getItem('mySelectedNode')
|
||||
|
||||
if (checkNode === null || checkNode.length === 0) {
|
||||
localStorage.setItem('mySelectedNode', 0)
|
||||
} else {
|
||||
@ -364,13 +252,36 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
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}`
|
||||
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')
|
||||
|
||||
@ -408,10 +319,13 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
}
|
||||
|
||||
var renewNodes = []
|
||||
|
||||
renewNodes.push(obj1, obj2)
|
||||
|
||||
localStorage.setItem('myQortalNodes', JSON.stringify(renewNodes))
|
||||
|
||||
let snack1string = get('settings.snack1')
|
||||
|
||||
snackbar.add({
|
||||
labelText: `${snack1string}`,
|
||||
dismiss: true
|
||||
@ -426,9 +340,9 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
nodeSelected(selectedNodeIndex) {
|
||||
const selectedNode = this.nodeConfig.knownNodes[selectedNodeIndex]
|
||||
const selectedName = `${selectedNode.name}`
|
||||
const selectedNodeUrl = `${selectedNode.protocol + '://' + selectedNode.domain +':' + selectedNode.port}`
|
||||
|
||||
const selectedNodeUrl = `${selectedNode.protocol + '://' + selectedNode.domain + ':' + selectedNode.port}`
|
||||
const index = parseInt(selectedNodeIndex)
|
||||
|
||||
if (isNaN(index)) return
|
||||
|
||||
store.dispatch(doSetNode(selectedNodeIndex))
|
||||
@ -462,13 +376,23 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
store.dispatch(doAddNode(nodeObject))
|
||||
|
||||
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) {
|
||||
var savedNodes = []
|
||||
|
||||
savedNodes.push(nodeObject)
|
||||
|
||||
localStorage.setItem('myQortalNodes', JSON.stringify(savedNodes))
|
||||
|
||||
let snack3string = get('settings.snack3')
|
||||
|
||||
snackbar.add({
|
||||
labelText: `${snack3string}`,
|
||||
dismiss: true
|
||||
@ -478,14 +402,16 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
this.shadowRoot.getElementById('protocolList').value = ''
|
||||
this.shadowRoot.getElementById('domainInput').value = ''
|
||||
this.shadowRoot.getElementById('portInput').value = ''
|
||||
|
||||
this.shadowRoot.querySelector('#addNodeDialog').close()
|
||||
} else {
|
||||
var stored = JSON.parse(localStorage.getItem('myQortalNodes'))
|
||||
|
||||
stored.push(nodeObject)
|
||||
|
||||
localStorage.setItem('myQortalNodes', JSON.stringify(stored))
|
||||
|
||||
let snack3string = get('settings.snack3');
|
||||
let snack3string = get('settings.snack3')
|
||||
|
||||
snackbar.add({
|
||||
labelText: `${snack3string}`,
|
||||
dismiss: true
|
||||
@ -495,9 +421,10 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
this.shadowRoot.getElementById('protocolList').value = ''
|
||||
this.shadowRoot.getElementById('domainInput').value = ''
|
||||
this.shadowRoot.getElementById('portInput').value = ''
|
||||
|
||||
this.shadowRoot.querySelector('#addNodeDialog').close()
|
||||
}
|
||||
|
||||
this.handleAddNodeSelection(choosedNode, choosedIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@ -505,11 +432,15 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
event.stopPropagation()
|
||||
|
||||
let stored = JSON.parse(localStorage.getItem('myQortalNodes'))
|
||||
|
||||
stored.splice(index, 1)
|
||||
|
||||
localStorage.setItem('myQortalNodes', JSON.stringify(stored))
|
||||
|
||||
store.dispatch(doRemoveNode(index))
|
||||
|
||||
let snack6string = get('settings.snack6')
|
||||
|
||||
snackbar.add({
|
||||
labelText: `${snack6string}`,
|
||||
dismiss: true
|
||||
@ -530,16 +461,21 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
protocol: protocolList,
|
||||
domain: domainInput,
|
||||
port: portInput,
|
||||
enableManagement: true,
|
||||
enableManagement: true
|
||||
}
|
||||
|
||||
let stored = JSON.parse(localStorage.getItem('myQortalNodes'))
|
||||
|
||||
const copyStored = [...stored]
|
||||
|
||||
copyStored[index] = nodeObject
|
||||
|
||||
localStorage.setItem('myQortalNodes', JSON.stringify(copyStored))
|
||||
|
||||
store.dispatch(doEditNode(index, nodeObject))
|
||||
|
||||
let snack7string = get('settings.snack7')
|
||||
|
||||
snackbar.add({
|
||||
labelText: `${snack7string}`,
|
||||
dismiss: true
|
||||
@ -551,7 +487,6 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
this.shadowRoot.getElementById('portInput').value = ''
|
||||
this.isBeingEdited = false
|
||||
this.isBeingEditedIndex = null
|
||||
|
||||
this.shadowRoot.querySelector('#addNodeDialog').close()
|
||||
}
|
||||
}
|
||||
@ -566,26 +501,25 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
|
||||
renderExportNodesListButton() {
|
||||
return html`
|
||||
<mwc-button
|
||||
dense
|
||||
unelevated
|
||||
label="${translate('settings.export')}"
|
||||
@click="${() => this.exportQortalNodesList()}"
|
||||
>
|
||||
</mwc-button>
|
||||
<mwc-button dense unelevated label="${translate('settings.export')}" @click="${() => this.exportQortalNodesList()}"></mwc-button>
|
||||
`
|
||||
}
|
||||
|
||||
exportQortalNodesList() {
|
||||
let nodelist = ''
|
||||
|
||||
const qortalNodesList = JSON.stringify(
|
||||
localStorage.getItem('myQortalNodes')
|
||||
);
|
||||
)
|
||||
|
||||
const qortalNodesListSave = JSON.parse(qortalNodesList || '[]')
|
||||
|
||||
const blob = new Blob([qortalNodesListSave], {
|
||||
type: 'text/plain;charset=utf-8',
|
||||
type: 'text/plain;charset=utf-8'
|
||||
})
|
||||
|
||||
nodelist = 'qortal.nodes'
|
||||
|
||||
this.saveFileToDisk(blob, nodelist)
|
||||
}
|
||||
|
||||
@ -595,16 +529,20 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
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('settings.snack4')
|
||||
|
||||
snackbar.add({
|
||||
labelText: `${snack4string} qortal.nodes`,
|
||||
dismiss: true
|
||||
@ -613,29 +551,28 @@ class SettingsPage extends connect(store)(LitElement) {
|
||||
if (error.name === 'AbortError') {
|
||||
return
|
||||
}
|
||||
|
||||
FileSaver.saveAs(blob, fileName)
|
||||
}
|
||||
}
|
||||
|
||||
renderImportNodesListButton() {
|
||||
return html`
|
||||
<mwc-button
|
||||
dense
|
||||
unelevated
|
||||
label="${translate('settings.import')}"
|
||||
@click="${() => this.openImportNodesDialog()}"
|
||||
>
|
||||
</mwc-button>
|
||||
<mwc-button dense unelevated label="${translate('settings.import')}" @click="${() => this.openImportNodesDialog()}"></mwc-button>
|
||||
`
|
||||
}
|
||||
|
||||
async importQortalNodesList(file) {
|
||||
localStorage.removeItem('myQortalNodes')
|
||||
|
||||
const newItems = JSON.parse(file || '[]')
|
||||
|
||||
localStorage.setItem('myQortalNodes', JSON.stringify(newItems))
|
||||
|
||||
this.shadowRoot.querySelector('#importQortalNodesListDialog').close()
|
||||
|
||||
let snack5string = get('settings.snack5')
|
||||
|
||||
snackbar.add({
|
||||
labelText: `${snack5string}`,
|
||||
dismiss: true,
|
||||
@ -657,5 +594,4 @@ window.customElements.define('settings-page', SettingsPage)
|
||||
|
||||
const settings = document.createElement('settings-page')
|
||||
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 {ifDefined} from 'lit/directives/if-defined.js'
|
||||
import {sideMenuItemStyle} from './side-menu-item-style.js'
|
||||
import { html, LitElement } from 'lit'
|
||||
import { ifDefined } from 'lit/directives/if-defined.js'
|
||||
import { sideMenuItemStyles } from '../styles/core-css'
|
||||
import '@vaadin/icon'
|
||||
import '@vaadin/icons'
|
||||
import '@polymer/paper-tooltip'
|
||||
|
||||
export class SideMenuItem extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
selected: { type: Boolean, reflect: true },
|
||||
label: { type: String, reflect: true },
|
||||
expanded: { type: Boolean, reflect: true },
|
||||
compact: { type: Boolean, reflect: true },
|
||||
href: { type: String, reflect: true },
|
||||
target: { type: String, reflect: true },
|
||||
hide: { type: Boolean }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
selected: { type: Boolean, reflect: true },
|
||||
label: { type: String, reflect: true },
|
||||
expanded: { type: Boolean, reflect: true },
|
||||
compact: { type: Boolean, reflect: true },
|
||||
href: { type: String, reflect: true },
|
||||
target: { type: String, reflect: true },
|
||||
hide: { type: Boolean }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
${sideMenuItemStyle}
|
||||
`
|
||||
}
|
||||
static get styles() {
|
||||
return [sideMenuItemStyles]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.selected = false
|
||||
this.expanded = false
|
||||
this.hide = false
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.selected = false
|
||||
this.expanded = false
|
||||
this.hide = false
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${this._itemLinkTemplate()} ${this._tooltipTemplate()}
|
||||
${this._childrenTemplate()}
|
||||
`
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
${this._itemLinkTemplate()} ${this._tooltipTemplate()}
|
||||
${this._childrenTemplate()}
|
||||
`
|
||||
}
|
||||
|
||||
firstUpdated(changedProperties) {
|
||||
if (!this.hasChildren()) {
|
||||
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)
|
||||
}
|
||||
firstUpdated(changedProperties) {
|
||||
if (!this.hasChildren()) {
|
||||
return
|
||||
}
|
||||
|
||||
_itemLinkTemplate() {
|
||||
return html`
|
||||
<a id="itemLink"
|
||||
level=${this._getLevel}
|
||||
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>
|
||||
`
|
||||
}
|
||||
this.collapseExpandIcon = document.createElement("vaadin-icon")
|
||||
this.collapseExpandIcon.id = "collapse-button"
|
||||
this.shadowRoot.getElementById("content").appendChild(this.collapseExpandIcon)
|
||||
this._boundOutsideClickListener = this._outsideClickListener.bind(this)
|
||||
}
|
||||
|
||||
_tooltipTemplate() {
|
||||
return html`
|
||||
${this._getLevel === 0 && this.compact ? html`
|
||||
<paper-tooltip for="itemLink" position="right" animation-delay="0">
|
||||
${this.label}
|
||||
</paper-tooltip>
|
||||
`
|
||||
: undefined}
|
||||
`
|
||||
}
|
||||
_itemLinkTemplate() {
|
||||
return html`
|
||||
<a id="itemLink"
|
||||
level=${this._getLevel}
|
||||
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>
|
||||
`
|
||||
}
|
||||
|
||||
_childrenTemplate() {
|
||||
return html`
|
||||
${this.expanded ? html`
|
||||
${this.compact ? html`
|
||||
<div id="overlay"><slot></slot></div>
|
||||
`
|
||||
: html`
|
||||
<slot></slot>
|
||||
`}
|
||||
`
|
||||
: undefined}
|
||||
`
|
||||
}
|
||||
_tooltipTemplate() {
|
||||
return html`
|
||||
${this._getLevel === 0 && this.compact ?
|
||||
html`
|
||||
<paper-tooltip for="itemLink" position="right" animation-delay="0">
|
||||
${this.label}
|
||||
</paper-tooltip>
|
||||
` : undefined
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
updated(changedProperties) {
|
||||
changedProperties.forEach((oldValue, propName) => {
|
||||
if (propName === "compact") {
|
||||
this._onCompactChanged()
|
||||
}
|
||||
_childrenTemplate() {
|
||||
return html`
|
||||
${this.expanded ?
|
||||
html`
|
||||
${this.compact ?
|
||||
html`
|
||||
<div id="overlay"><slot></slot></div>
|
||||
` : html`
|
||||
<slot></slot>
|
||||
`
|
||||
}
|
||||
` : undefined
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
if (propName === "expanded") {
|
||||
this._onExpandedChanged()
|
||||
}
|
||||
updated(changedProperties) {
|
||||
changedProperties.forEach((oldValue, propName) => {
|
||||
if (propName === "compact") {
|
||||
this._onCompactChanged()
|
||||
}
|
||||
|
||||
if (propName === "selected"){
|
||||
if (oldValue === this.selected){
|
||||
return
|
||||
}
|
||||
if (propName === "expanded") {
|
||||
this._onExpandedChanged()
|
||||
}
|
||||
|
||||
if (this.selected) {
|
||||
this._changeSelectedState(true)
|
||||
this._markParentWithSelectedChild()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (propName === "selected") {
|
||||
if (oldValue === this.selected) {
|
||||
return
|
||||
}
|
||||
|
||||
_onCompactChanged() {
|
||||
this.expanded = false;
|
||||
if (this.selected) {
|
||||
this._changeSelectedState(true)
|
||||
this._markParentWithSelectedChild()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (this.collapseExpandIcon == null) {
|
||||
return;
|
||||
}
|
||||
_onCompactChanged() {
|
||||
this.expanded = false
|
||||
|
||||
if (!this.compact) {
|
||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-down-small"
|
||||
} else {
|
||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-right-small"
|
||||
}
|
||||
}
|
||||
if (this.collapseExpandIcon == null) {
|
||||
return
|
||||
}
|
||||
|
||||
_onExpandedChanged() {
|
||||
if (this.collapseExpandIcon == null) {
|
||||
return;
|
||||
}
|
||||
if (!this.compact) {
|
||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-down-small"
|
||||
} else {
|
||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-right-small"
|
||||
}
|
||||
}
|
||||
|
||||
if (this.expanded) {
|
||||
this._onHandleExpanded();
|
||||
} else {
|
||||
this._onHandleCollapsed();
|
||||
}
|
||||
}
|
||||
_onExpandedChanged() {
|
||||
if (this.collapseExpandIcon == null) {
|
||||
return
|
||||
}
|
||||
|
||||
_onHandleExpanded() {
|
||||
if (!this.compact) {
|
||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-up-small"
|
||||
} else {
|
||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-left-small"
|
||||
document.addEventListener("click", this._boundOutsideClickListener, true)
|
||||
}
|
||||
}
|
||||
if (this.expanded) {
|
||||
this._onHandleExpanded()
|
||||
} else {
|
||||
this._onHandleCollapsed()
|
||||
}
|
||||
}
|
||||
|
||||
_onHandleCollapsed() {
|
||||
if (!this.compact) {
|
||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-down-small"
|
||||
} else {
|
||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-right-small"
|
||||
document.removeEventListener(
|
||||
"click",
|
||||
this._boundOutsideClickListener,
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
_onHandleExpanded() {
|
||||
if (!this.compact) {
|
||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-up-small"
|
||||
} else {
|
||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-left-small"
|
||||
document.addEventListener("click", this._boundOutsideClickListener, true)
|
||||
}
|
||||
}
|
||||
|
||||
_onClick(e) {
|
||||
if (!this.hasChildren()) {
|
||||
this.selected = true
|
||||
} else {
|
||||
this.expanded = !this.expanded
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
_onHandleCollapsed() {
|
||||
if (!this.compact) {
|
||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-down-small"
|
||||
} else {
|
||||
this.collapseExpandIcon["icon"] = "vaadin:chevron-right-small"
|
||||
document.removeEventListener(
|
||||
"click",
|
||||
this._boundOutsideClickListener,
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
_outsideClickListener(event) {
|
||||
const eventPath = event.composedPath()
|
||||
if (eventPath.indexOf(this) < 0) {
|
||||
this.expanded = false
|
||||
}
|
||||
}
|
||||
_onClick(e) {
|
||||
if (!this.hasChildren()) {
|
||||
this.selected = true
|
||||
} else {
|
||||
this.expanded = !this.expanded
|
||||
|
||||
_changeSelectedState(selected, sourceEvent) {
|
||||
this.selected = selected
|
||||
let evt = new CustomEvent("side-menu-item-select", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
detail: { sourceEvent: sourceEvent }
|
||||
});
|
||||
this.dispatchEvent(evt)
|
||||
}
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
hasChildren() {
|
||||
return !!this.querySelector("side-menu-item")
|
||||
}
|
||||
_outsideClickListener(event) {
|
||||
const eventPath = event.composedPath()
|
||||
|
||||
_markParentWithSelectedChild() {
|
||||
let element = this.parentElement;
|
||||
while (element instanceof SideMenuItem) {
|
||||
element.setAttribute('hasSelectedChild', true)
|
||||
element = element.parentElement;
|
||||
}
|
||||
}
|
||||
if (eventPath.indexOf(this) < 0) {
|
||||
this.expanded = false
|
||||
}
|
||||
}
|
||||
|
||||
get _getLevel() {
|
||||
let level = 0
|
||||
let element = this.parentElement
|
||||
while (element instanceof SideMenuItem) {
|
||||
level++;
|
||||
element = element.parentElement
|
||||
}
|
||||
return level
|
||||
}
|
||||
_changeSelectedState(selected, sourceEvent) {
|
||||
this.selected = selected
|
||||
|
||||
let evt = new CustomEvent("side-menu-item-select", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
detail: { sourceEvent: sourceEvent }
|
||||
})
|
||||
|
||||
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 {
|
||||
static get properties() {
|
||||
return {
|
||||
items: {type: Array},
|
||||
selectedValue: {type: String, reflect: true},
|
||||
compact: {type: Boolean, reflect: true}
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
items: { type: Array },
|
||||
selectedValue: { type: String, reflect: true },
|
||||
compact: { type: Boolean, reflect: true }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
nav {
|
||||
padding: 0;
|
||||
}
|
||||
static get styles() {
|
||||
return [sideMenuStyles]
|
||||
}
|
||||
|
||||
:host {
|
||||
list-style: none;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.compact = false
|
||||
}
|
||||
|
||||
:host([compact]) {
|
||||
width: auto;
|
||||
}
|
||||
`
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<nav @side-menu-item-select=${this._handleSelect}>
|
||||
<slot></slot>
|
||||
</nav>
|
||||
`
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.compact = false
|
||||
}
|
||||
firstUpdated(_changedProperties) {
|
||||
this.items = [...this.querySelectorAll("side-menu-item")]
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<nav @side-menu-item-select=${this._handleSelect}>
|
||||
<slot></slot>
|
||||
</nav>
|
||||
`
|
||||
}
|
||||
_handleSelect(event) {
|
||||
let targetItem = event.target
|
||||
this._deselectAllItems()
|
||||
targetItem.selected = true
|
||||
this.selectedValue = targetItem.label
|
||||
}
|
||||
|
||||
firstUpdated(_changedProperties) {
|
||||
this.items = [...this.querySelectorAll("side-menu-item")]
|
||||
}
|
||||
_deselectAllItems() {
|
||||
this.items.forEach(element => {
|
||||
if (this.compact) {
|
||||
element.expanded = false
|
||||
}
|
||||
|
||||
_handleSelect(event) {
|
||||
let targetItem = event.target
|
||||
this._deselectAllItems()
|
||||
targetItem.selected = true
|
||||
this.selectedValue = targetItem.label
|
||||
}
|
||||
element.selected = false
|
||||
element.hasChildren() ? element.removeAttribute('hasSelectedChild') : undefined
|
||||
})
|
||||
}
|
||||
|
||||
_deselectAllItems() {
|
||||
this.items.forEach(element => {
|
||||
if (this.compact) {
|
||||
element.expanded = false
|
||||
}
|
||||
element.selected = false
|
||||
element.hasChildren() ? element.removeAttribute('hasSelectedChild') : undefined
|
||||
});
|
||||
}
|
||||
updated(changedProperties) {
|
||||
changedProperties.forEach((oldValue, propName) => {
|
||||
if (propName === "compact") {
|
||||
this.items.forEach(item => (item.compact = this.compact))
|
||||
|
||||
updated(changedProperties) {
|
||||
changedProperties.forEach((oldValue, propName) => {
|
||||
if (propName === "compact") {
|
||||
this.items.forEach(item => (item.compact = this.compact))
|
||||
let evt = new CustomEvent("side-menu-compact-change", {
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
})
|
||||
this.dispatchEvent(evt)
|
||||
}
|
||||
})
|
||||
}
|
||||
let evt = new CustomEvent("side-menu-compact-change", {
|
||||
bubbles: true,
|
||||
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'
|
||||
|
||||
let queueElement
|
||||
|
||||
class SnackQueue extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
busy: {
|
||||
type: Boolean,
|
||||
attribute: 'busy',
|
||||
reflectToAttribute: true
|
||||
},
|
||||
currentSnack: {
|
||||
type: Object,
|
||||
attribute: 'current-snack',
|
||||
reflectToAttribute: true
|
||||
},
|
||||
_queue: {
|
||||
type: Array
|
||||
},
|
||||
_labelText: { type: String },
|
||||
_stacked: { type: Boolean },
|
||||
_leading: { type: Boolean },
|
||||
_closeOnEscape: { type: Boolean },
|
||||
_timeoutMs: { type: Number },
|
||||
action: {},
|
||||
_dismiss: {},
|
||||
_dismissIcon: { type: String }
|
||||
}
|
||||
}
|
||||
static get properties() {
|
||||
return {
|
||||
busy: { type: Boolean, attribute: 'busy', reflectToAttribute: true },
|
||||
currentSnack: { type: Object, attribute: 'current-snack', reflectToAttribute: true },
|
||||
_queue: { type: Array },
|
||||
_labelText: { type: String },
|
||||
_stacked: { type: Boolean },
|
||||
_leading: { type: Boolean },
|
||||
_closeOnEscape: { type: Boolean },
|
||||
_timeoutMs: { type: Number },
|
||||
_dismiss: {},
|
||||
_dismissIcon: { type: String },
|
||||
action: {}
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css``
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this._queue = []
|
||||
this.busy = false
|
||||
this._timeoutMs = 5000
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this._queue = []
|
||||
this.busy = false
|
||||
this._timeoutMs = 5000
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<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>
|
||||
`
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<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() {
|
||||
this._snackbar = this.shadowRoot.getElementById('snack')
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this._snackbar = this.shadowRoot.getElementById('snack')
|
||||
}
|
||||
_shift() {
|
||||
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() {
|
||||
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()
|
||||
}
|
||||
|
||||
add(item) {
|
||||
this._queue.push(item)
|
||||
this._shift()
|
||||
}
|
||||
add(item) {
|
||||
this._queue.push(item)
|
||||
this._shift()
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('snack-queue', SnackQueue)
|
||||
@ -82,9 +70,9 @@ queueNode.id = 'queue-node'
|
||||
queueNode.loadingMessage = ''
|
||||
queueElement = document.body.appendChild(queueNode)
|
||||
setTimeout(() => {
|
||||
queueElement = document.getElementById('queue-node')
|
||||
const mainApp = document.getElementById('main-app')
|
||||
const shadow = mainApp.shadowRoot
|
||||
queueElement = shadow.appendChild(queueElement)
|
||||
queueElement = document.getElementById('queue-node')
|
||||
const mainApp = document.getElementById('main-app')
|
||||
const shadow = mainApp.shadowRoot
|
||||
queueElement = shadow.appendChild(queueElement)
|
||||
}, 500)
|
||||
export default queueElement
|
||||
|
@ -1,5 +1,3 @@
|
||||
// Sourced from https://gist.github.com/letsgetrandy/1e05a68ea74ba6736eb5
|
||||
|
||||
export const EXCEPTIONS = {
|
||||
'are': 'were',
|
||||
'eat': 'ate',
|
||||
@ -16,21 +14,27 @@ export const getPastTense = (verb, exceptions = EXCEPTIONS) => {
|
||||
if (exceptions[verb]) {
|
||||
return exceptions[verb]
|
||||
}
|
||||
|
||||
if ((/e$/i).test(verb)) {
|
||||
return verb + 'd'
|
||||
}
|
||||
|
||||
if ((/[aeiou]c$/i).test(verb)) {
|
||||
return verb + 'ked'
|
||||
}
|
||||
|
||||
// for american english only
|
||||
if ((/el$/i).test(verb)) {
|
||||
return verb + 'ed'
|
||||
}
|
||||
|
||||
if ((/[aeio][aeiou][dlmnprst]$/).test(verb)) {
|
||||
return verb + 'ed'
|
||||
}
|
||||
|
||||
if ((/[aeiou][bdglmnprst]$/i).test(verb)) {
|
||||
return verb.replace(/(.+[aeiou])([bdglmnprst])/, '$1$2$2ed')
|
||||
}
|
||||
|
||||
return verb + 'ed'
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,41 +1,39 @@
|
||||
import {store} from './store.js'
|
||||
import {doLoadConfigFromAPI} from './redux/config/config-actions.js'
|
||||
import {doInitWorkers, doLoadNodeConfig} from './redux/app/app-actions.js'
|
||||
import {doLoadNotificationConfig} from './redux/user/user-actions.js'
|
||||
|
||||
import './persistState.js'
|
||||
|
||||
import {initApi} from 'qortal-ui-crypto'
|
||||
import { store } from './store'
|
||||
import { doLoadConfigFromAPI } from './redux/config/config-actions'
|
||||
import { doInitWorkers, doLoadNodeConfig } from './redux/app/app-actions'
|
||||
import { doLoadNotificationConfig } from './redux/user/user-actions'
|
||||
import { initApi } from 'qortal-ui-crypto'
|
||||
import './persistState'
|
||||
|
||||
initApi(store)
|
||||
|
||||
const workerInitChecker = () => {
|
||||
const state = store.getState()
|
||||
const state = store.getState()
|
||||
|
||||
if (store.getState().app.nodeConfig.knownNodes.length === 0) {
|
||||
store.dispatch(doLoadNodeConfig())
|
||||
}
|
||||
if (store.getState().app.nodeConfig.knownNodes.length === 0) {
|
||||
store.dispatch(doLoadNodeConfig())
|
||||
}
|
||||
|
||||
if (state.config.loaded) {
|
||||
store.dispatch(doLoadNodeConfig())
|
||||
if (state.config.loaded) {
|
||||
store.dispatch(doLoadNodeConfig())
|
||||
|
||||
if (state.app.workers.ready) {
|
||||
|
||||
workerInitSubscription()
|
||||
} else {
|
||||
|
||||
if (!state.app.workers.loading) store.dispatch(doInitWorkers(state.config.crypto.kdfThreads, state.config.user.constants.workerURL))
|
||||
}
|
||||
}
|
||||
if (state.app.workers.ready) {
|
||||
workerInitSubscription()
|
||||
} else {
|
||||
if (!state.app.workers.loading) store.dispatch(doInitWorkers(state.config.crypto.kdfThreads, state.config.user.constants.workerURL))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
workerInitChecker()
|
||||
|
||||
const workerInitSubscription = store.subscribe(workerInitChecker)
|
||||
|
||||
if (!store.getState().config.loaded) {
|
||||
store.dispatch(doLoadConfigFromAPI())
|
||||
store.dispatch(doLoadNodeConfig())
|
||||
store.dispatch(doLoadConfigFromAPI())
|
||||
store.dispatch(doLoadNodeConfig())
|
||||
}
|
||||
|
||||
if (!store.getState().user.loaded) {
|
||||
store.dispatch(doLoadNotificationConfig())
|
||||
}
|
||||
store.dispatch(doLoadNotificationConfig())
|
||||
}
|
@ -1,19 +1,19 @@
|
||||
export const loadStateFromLocalStorage = (key) => {
|
||||
try {
|
||||
const config = localStorage.getItem(key)
|
||||
if (config === null) return void 0
|
||||
return JSON.parse(config)
|
||||
} catch (e) {
|
||||
// Could add error handling in case there's a weird one...don't want to overwrite config if it's malfunctioned
|
||||
return void 0
|
||||
}
|
||||
try {
|
||||
const config = localStorage.getItem(key)
|
||||
if (config === null) return void 0
|
||||
return JSON.parse(config)
|
||||
} catch (e) {
|
||||
// Could add error handling in case there's a weird one...don't want to overwrite config if it's malfunctioned
|
||||
return void 0
|
||||
}
|
||||
}
|
||||
|
||||
export const saveStateToLocalStorage = (key, state) => {
|
||||
try {
|
||||
const stateJSON = JSON.stringify(state)
|
||||
localStorage.setItem(key, stateJSON)
|
||||
} catch (e) {
|
||||
console.error(e, 'e')
|
||||
}
|
||||
}
|
||||
try {
|
||||
const stateJSON = JSON.stringify(state)
|
||||
localStorage.setItem(key, stateJSON)
|
||||
} catch (e) {
|
||||
console.error(e, 'e')
|
||||
}
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
import CryptoJS from 'crypto-js'
|
||||
|
||||
export const encryptData = (data, salt) => CryptoJS.AES.encrypt(JSON.stringify(data), salt).toString()
|
||||
|
||||
export const decryptData = (ciphertext, salt) => {
|
||||
const bytes = CryptoJS.AES.decrypt(ciphertext, salt)
|
||||
try {
|
||||
return JSON.parse(bytes.toString(CryptoJS.enc.Utf8))
|
||||
} catch(err) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
const bytes = CryptoJS.AES.decrypt(ciphertext, salt)
|
||||
|
||||
try {
|
||||
return JSON.parse(bytes.toString(CryptoJS.enc.Utf8))
|
||||
} catch (err) {
|
||||
return null
|
||||
}
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
import './initStore.js'
|
||||
import './components/main-app.js'
|
||||
import './initStore'
|
||||
import './components/main-app'
|
@ -1,12 +1,12 @@
|
||||
const config = {
|
||||
default: {
|
||||
title: 'Qortal',
|
||||
body: 'Qortal Notifications',
|
||||
icon: '/img/favicon/favicon-96x96.png'
|
||||
},
|
||||
newMessageAudio: '/sound/newmessage.ogg',
|
||||
messageAlert: '/sound/messageAlert.ogg',
|
||||
blockAlert: '/sound/blockAlert.ogg'
|
||||
default: {
|
||||
title: 'Qortal',
|
||||
body: 'Qortal Notifications',
|
||||
icon: '/img/favicon/favicon-96x96.png'
|
||||
},
|
||||
newMessageAudio: '/sound/newmessage.ogg',
|
||||
messageAlert: '/sound/messageAlert.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 {dispatcher} from './dispatcher'
|
||||
import snackbar from '../functional-components/snackbar.js'
|
||||
import {NEW_MESSAGE, NEW_MESSAGE_NOTIFICATION_QAPP, NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL} from './types'
|
||||
import snackbar from '../functional-components/snackbar'
|
||||
|
||||
let initial = 0
|
||||
let _state
|
||||
|
||||
const notificationCheck = function () {
|
||||
if (window.Notification && Notification.permission === 'granted') {
|
||||
// ...
|
||||
return true
|
||||
} else if (window.Notification && Notification.permission !== 'denied') {
|
||||
Notification.requestPermission().then(permission => {
|
||||
if (permission === 'granted') {
|
||||
dispatcher(_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
|
||||
})
|
||||
}
|
||||
if (window.Notification && Notification.permission === 'granted') {
|
||||
return true
|
||||
} else if (window.Notification && Notification.permission !== 'denied') {
|
||||
Notification.requestPermission().then(permission => {
|
||||
if (permission === 'granted') {
|
||||
dispatcher(_state)
|
||||
_state = ''
|
||||
|
||||
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 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) {
|
||||
const newMessage = () => {
|
||||
const newMessage = () => {
|
||||
let data
|
||||
|
||||
let data
|
||||
if (req.type && req.type === 'qapp') {
|
||||
data = req
|
||||
if (req.type && req.type === 'qapp') {
|
||||
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) {
|
||||
const title = `${req.groupName}`
|
||||
const body = `New Message from ${req.senderName === undefined ? req.sender : req.senderName}`
|
||||
data = { title, sound: config.messageAlert, options: { body, icon: config.default.icon, badge: config.default.icon }, req }
|
||||
} 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 }
|
||||
}
|
||||
data = { title, sound: config.messageAlert, options: { body, icon: config.default.icon, badge: config.default.icon }, req }
|
||||
} else {
|
||||
const title = `${req.senderName === undefined ? req.sender : req.senderName}`
|
||||
const body = 'New Message'
|
||||
|
||||
const notificationState = { type: NEW_MESSAGE, data: data }
|
||||
const notificationStateQapp = { type: NEW_MESSAGE_NOTIFICATION_QAPP, data: data }
|
||||
const canI = notificationCheck()
|
||||
data = { title, sound: config.messageAlert, options: { body, icon: config.default.icon, badge: config.default.icon }, req }
|
||||
}
|
||||
|
||||
if (canI === true) {
|
||||
if (req.type && req.type === 'qapp') {
|
||||
dispatcher(notificationStateQapp)
|
||||
} else {
|
||||
dispatcher(notificationState)
|
||||
}
|
||||
const notificationState = { type: NEW_MESSAGE, data: data }
|
||||
const notificationStateQapp = { type: NEW_MESSAGE_NOTIFICATION_QAPP, data: data }
|
||||
const canI = notificationCheck()
|
||||
|
||||
} else {
|
||||
_state = notificationState
|
||||
}
|
||||
}
|
||||
const page = window.top.location.href
|
||||
if(req.type && req.type === 'qapp-local-notification'){
|
||||
try {
|
||||
dispatcher({ type: NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL, data: req })
|
||||
if (canI === true) {
|
||||
if (req.type && req.type === 'qapp') {
|
||||
dispatcher(notificationStateQapp)
|
||||
} else {
|
||||
dispatcher(notificationState)
|
||||
}
|
||||
} else {
|
||||
_state = notificationState
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
}
|
||||
}else if (!document.hasFocus()) {
|
||||
newMessage()
|
||||
} else {
|
||||
if (page.includes(req.url) === false) {
|
||||
newMessage()
|
||||
}
|
||||
}
|
||||
}
|
||||
const page = window.top.location.href
|
||||
|
||||
if (req.type && req.type === 'qapp-local-notification') {
|
||||
try {
|
||||
dispatcher({ type: NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL, data: req })
|
||||
|
||||
} catch (error) {
|
||||
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'
|
||||
|
||||
export const dispatcher = function (notificationState) {
|
||||
switch (notificationState.type) {
|
||||
case NEW_MESSAGE:
|
||||
return newMessage(notificationState.data)
|
||||
|
||||
switch (notificationState.type) {
|
||||
case NEW_MESSAGE:
|
||||
return newMessage(notificationState.data)
|
||||
case NEW_MESSAGE_NOTIFICATION_QAPP:
|
||||
return newMessageNotificationQapp(notificationState.data)
|
||||
case NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL:
|
||||
return newMessageNotificationQappLocal(notificationState.data)
|
||||
default:
|
||||
}
|
||||
}
|
||||
case NEW_MESSAGE_NOTIFICATION_QAPP:
|
||||
return newMessageNotificationQapp(notificationState.data)
|
||||
|
||||
case NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL:
|
||||
return newMessageNotificationQappLocal(notificationState.data)
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
export { newMessage, newMessageNotificationQapp, newMessageNotificationQappLocal } from './new-message'
|
||||
export { newMessage, newMessageNotificationQapp, newMessageNotificationQappLocal } from './new-message'
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user