Compare commits

...

123 Commits

Author SHA1 Message Date
Sean 709008c8bc
Merge pull request #322 from oxen-io/bump-1-8-1
Bump to v1.8.1
2022-11-17 07:51:09 +11:00
Sean Darcy 2bca9c0e7c Bump to v1.8.1 2022-11-17 07:49:34 +11:00
Jason Rhinelander 4bc881e7fc
Merge pull request #316 from jagerman/mac-build-script
Mac build script
2022-09-01 17:06:00 -03:00
Sean fbc06a407b
Merge pull request #320 from oxen-io/sort-sn-staked-list
Sorts the "MY STAKES" service node list alphabetically
2022-08-23 07:17:45 +10:00
Sean 64b38e7abc
Merge pull request #319 from oxen-io/bump-1-8-0
Bump to v1.8.0
2022-08-23 07:17:33 +10:00
Sean a7b9274ddd
Merge pull request #321 from oxen-io/header-text-visability
Header text made visable for hardware wallet list
2022-08-23 07:17:18 +10:00
Sean Darcy 3248e03924 Header text made visable for hardware wallet list
The headers for "Hardware Wallets" and "Normal Wallets" was not visable
due to default white color. This makes it visible.
2022-08-19 16:28:10 +10:00
Sean Darcy 165c140588 Bump to v1.8.0 2022-08-15 11:56:28 +10:00
Sean Darcy c44a3666f3 Sorts the "MY STAKES" service node list alphabetically
The list provided to users under service nodes -> my stakes previously
had no sorting, which made it slightly difficult to find the service
node as they would appear in a random order. This simply applys an
alphabetical ordering over the service node pubkeys so that service node
pubkey "0fa72..." will appear before pubkey "7eee3..."
2022-08-15 11:52:25 +10:00
Sean 019b8d3a9b
Merge pull request #283 from darcys22/hardware-wallet
Hardware wallet
2022-07-14 12:07:02 +10:00
Sean 79b9d14be5
Merge pull request #315 from ianmacd/pr3
Add oxen-rpc.caliban.org as an extra RPC node.
2022-07-14 12:06:53 +10:00
Sean aef30512fa
Update state.js 2022-07-14 12:02:21 +10:00
Sean d33ccfe0cd
Merge branch 'development' into hardware-wallet 2022-07-14 11:53:14 +10:00
Jason Rhinelander ef4e62d8b0
Sync up RPC nodes with mobile wallet 2022-07-04 11:28:03 -03:00
Jason Rhinelander 6dd95d0825 Add mac build script 2022-06-28 22:33:39 -03:00
Jason Rhinelander 6075dde728 Revert "Add CI codesigning for mac build"
This reverts commit 39057d95e9.

Codesigning did not work.
2022-06-28 11:23:35 -03:00
Ian Macdonald 632bedb3f3
Add oxen-rpc.caliban.org as an extra RPC node. 2022-06-26 16:10:08 +02:00
Sean 28c4c3a1d9
Merge pull request #314 from jagerman/v172
macos auto-signing attempts
2022-06-26 07:21:39 +10:00
Jason Rhinelander 39057d95e9
Add CI codesigning for mac build 2022-06-25 14:48:10 -03:00
Jason Rhinelander b846cd2381
Detect unsigned mac builds and rename appropriately 2022-06-25 13:19:58 -03:00
Sean cd4bdee928
Merge pull request #313 from jagerman/drone
Replace github actions with drone ci
2022-06-24 07:09:34 +10:00
Jason Rhinelander 3bed654913
Version bump to fix bad linux versions 2022-06-23 15:27:20 -03:00
Jason Rhinelander fb1c853197
Replace github actions with drone ci 2022-06-23 14:40:39 -03:00
Jeff a3a7d8c235
fix deb builds because it defualts to be idiotic 2022-06-23 14:40:39 -03:00
Sean e81bdea2af
Merge pull request #310 from oxen-io/win-not-windows
typo for windows
2022-06-23 13:22:52 +10:00
Sean Darcy 300dcb5fab typo for windows 2022-06-23 13:20:46 +10:00
Sean Darcy ea46ff9e66 update-package 2022-06-23 10:33:47 +10:00
Sean f62f9ab5f3
Merge pull request #307 from oxen-io/update-workflows
no longer run macos on github workflows, use new script to download b…
2022-06-23 09:35:58 +10:00
Sean Darcy eee77f3474 Update github workflow scripts to use new binary downloads script 2022-06-23 09:29:38 +10:00
Sean Darcy 9f7ab361c9 Merge branch 'development' 2022-06-23 07:23:52 +10:00
Sean Darcy 42a96ca453 bump to v1.7.0 2022-06-23 07:22:13 +10:00
Sean 56459cd174
Merge pull request #306 from oxen-io/bump-1-7-1
bump to v1.7.0
2022-06-23 07:11:53 +10:00
Sean Darcy 6183d53d90 bump to v1.7.0 2022-06-23 07:09:48 +10:00
Sean 478b5305a2
Merge pull request #304 from ianmacd/pr1
Clicking on wallet addresses should open oxensn.com, not lokisn.com.
2022-06-23 07:07:03 +10:00
Sean f19e28f930
Merge pull request #303 from jagerman/mac-build-fixes
Mac packaging updates
2022-06-23 07:06:23 +10:00
Sean 153fbee33e
Merge pull request #305 from ianmacd/pr2
Correct maximum number of contributors.
2022-06-23 07:06:01 +10:00
Ian Macdonald f2dcaf6438
Correct maximum number of contributors. 2022-06-21 15:25:44 +02:00
Jason Rhinelander da6e62e5ca Make mac build and signing work
- Rewrite instructions so that GitHub Actions isn't required to make a
  signed build.
- Switch notarization tool to more modern (supposedly faster) version
- Replace buggy download scripts with one that extracts things for you
2022-06-14 16:27:18 -03:00
Ian Macdonald e5a5ac031c
Clicking on wallet addresses should open oxensn.com, not lokisn.com. 2022-06-14 10:42:42 +02:00
Jason Rhinelander 2e1f0f6faa Mac packaging updates
A newer electron-builder, in particular, is required because older
versions won't build without Python 2 (which doesn't exist on macOS
anymore).
2022-06-13 23:44:59 -03:00
Sean 7bbfede014
Merge pull request #302 from oxen-io/development
Merge to master
2022-06-14 06:59:50 +09:30
Sean 6ae267abd3
Merge pull request #301 from oxen-io/bump-1-7-0
bump to v1.7.0
2022-06-10 14:21:35 +10:00
Sean Darcy 86b1ab6684 bump to v1.7.0 2022-06-10 14:20:29 +10:00
Sean e06fbc31e3
Merge pull request #289 from darcys22/locked-contributions-count
Update algorithm counting contributors
2022-06-10 12:26:21 +10:00
Sean 6c388b895d
Merge pull request #300 from oxen-io/batching-details
adds fields onto the wallet detail about accumulated rewards from batching
2022-06-10 12:24:14 +10:00
Sean 7e3c4933fc
Merge pull request #294 from darcys22/appimage-icon
point the icon image to the provided image
2022-06-10 12:23:01 +10:00
Sean 7613cff693
Merge pull request #298 from darcys22/colour-scheme-fixes
misc colour scheme changes
2022-06-10 12:21:02 +10:00
Sean Darcy 05441b4689 Adds detail to the wallet page about accumulated rewards from batching 2022-06-10 10:26:33 +10:00
Sean Darcy 8fd7e56795 misc colour scheme changes 2022-02-17 14:54:14 +11:00
kylezs 08bff29cce
Merge pull request #295 from jagerman/typo-fix
Fix typo
2021-11-21 18:56:39 +01:00
Jason Rhinelander f2eda062a2 Fix typo 2021-11-19 08:07:09 -04:00
Sean d44a9a3322
Merge pull request #288 from darcys22/observer-sn-link
Update oxen observer link for service nodes
2021-11-10 08:50:58 +11:00
Sean Darcy 6f7cf7c00d point the icon image to the provided image 2021-11-03 16:57:30 +11:00
Sean Darcy 7e311386ad Update algorithm counting contributors
The number of contributions to a service node is dependant on the number
of locked contributions instead of contributors. The difference being
that a single contributor may make several locked contributions to a
service node which will reduce the number of available slots. To clarify
a single contributor may contribute twice to a service node, using 2
slots rather than one.
2021-07-20 14:20:58 +10:00
Sean e7015e7d14
Merge pull request #284 from darcys22/reduce-buffer-ons-purchase
Reduce ONS purchase validation buffer
2021-07-20 11:39:13 +10:00
Sean Darcy cc847a007f Update oxen observer link for service nodes
Previously the oxen observer link for service nodes was of the form

https://oxen.observer/service_node/d8e...8898b6

is now

https://oxen.observer/sn/d8e...8898b6

and this updates the wallet link to reflect the change
2021-07-20 11:30:48 +10:00
Sean Darcy 9c9c089c15 Slow heartbeat for hw wallet and generate hwdev.txt file
The heartbeat was overloading the rpc wallet with calls every 5 seconds
which resulted in an ever growing queue of messages. Have slowed the
hearbeat down significantly for hardware wallets.

Have also added code to ensure that the hwdev.txt file gets created upon
wallet creation, the GUI wallet needs this to know if a wallet is a
hardware wallet and this isn't generated by default unless we pass a
name to the initial RPC create wallet call.
2021-06-15 17:01:13 +10:00
Sean Darcy 8f58c716f8 create wallet checkbox 2021-06-11 15:59:00 +10:00
Sean Darcy 030f4d5f82 Reduce ONS purchase validation buffer
The ONS purchase screen validated that the user needs 8 oxen to purchase
an ONS name. 7 oxen for the name and another single oxen to cover fees.
Given the current fee rates this buffer is too high and 0.1
oxen should be sufficient to cover a single ONS purchase.
2021-06-11 15:32:26 +10:00
Sean Darcy 8e4cb611a8 Merge conflicts 2021-06-11 15:25:52 +10:00
Mikunj 1b6960c636 Display hardware wallets separetely 2021-06-11 12:08:41 +10:00
Mikunj 8afb08936c Add support for creating hardware wallet 2021-06-11 12:04:57 +10:00
Sean fa28657e98
Merge pull request #279 from oxen-io/development
1.6.0
2021-04-27 14:02:48 +10:00
kylezs 4559312e24
Merge pull request #278 from darcys22/bump-version
Bump version to 1.6.0
2021-04-27 13:55:32 +10:00
Sean Darcy e8088929e3 Bump version to 1.6.0 2021-04-27 13:45:47 +10:00
kylezs 6034e19343
Merge pull request #276 from darcys22/export-transfers
Creates a menu item to export the wallets transfers to CSV
2021-04-27 13:39:22 +10:00
Sean Darcy 8ac92f3906 Creates a menu item to export the wallets transfers to CSV
Utilises the export_transfers RPC wallet method that returns a string
which is simply saved to a file prompted to the user.
2021-04-27 13:33:22 +10:00
kylezs 3f6f502d10
Merge pull request #277 from darcys22/update-ons-prices
Reduces the advised prices for ONS purchases
2021-04-27 11:43:12 +10:00
Sean Darcy 80b1d6552b Reduces the advised prices for ONS purchases
In addition reduces the minimum balance required to submit a purchase
2021-04-27 11:15:05 +10:00
kylezs ed98cde9d2
Merge pull request #274 from darcys22/cosmetic-changes
Right click options and grammar changes
2021-04-15 11:36:18 +10:00
kylezs 5d3eede5f7
Merge pull request #275 from darcys22/truncate-address
Truncate address
2021-04-15 11:35:02 +10:00
Sean Darcy 88b2b1bb62 adds a truncation to ons wallet addresses to prevent overflowing 2021-04-14 13:14:46 +10:00
Sean Darcy 8ec2399ac1 truncate values 2021-04-14 11:49:13 +10:00
Sean Darcy c5fff0211a right click ONS records and Grammar 2021-04-14 10:20:32 +10:00
kylezs 35a8a7a366
Merge pull request #272 from jagerman/reserved-contributions
Fix staking for reserved contribution spots
2021-04-09 10:35:50 +10:00
Jason Rhinelander ee21b2219b Only show reserved contribution if it's for us
Fixes the logic around showing reserved contribution nodes:

- if we are a contributor and have a reserved spot, list it first.
- if the node has no open spots, don't list it at all.

Also tweaks the display so that we *do* show unfilled reserved stakes
for us, *even if* the node is unlocking.  (But continue not showing
unstaking open nodes).
2021-04-08 21:14:53 -03:00
kylezs 27afa67d09
Merge pull request #270 from darcys22/ons-wallet-list
list wallet ons types correctly
2021-04-09 09:58:55 +10:00
Jason Rhinelander 72ff2d6b53 Add braces like it's a 12-year old 2021-04-08 20:37:16 -03:00
Jason Rhinelander 9079f4cf2b Fix staking for reserved contribution spots
Reserved contributions for the current wallet were not properly being
added into the available contribution room available to a wallet; this
fixes it to include any unfilled reserved spots when showing available
stakes.
2021-04-08 02:59:23 -03:00
Sean Darcy 7a401a904b list 2021-04-06 15:49:55 +10:00
kylezs 2d1188f2ad
Merge pull request #269 from darcys22/ons_rebrand
Ons Rebrand
2021-04-06 14:46:46 +10:00
Sean Darcy 1da205a4be rename files and folders, Loki Name Service -> Oxen Name Service 2021-04-06 13:32:50 +10:00
Sean Darcy 78e8b89830 initial rebrand 2021-04-06 13:23:47 +10:00
kylezs 2ecb935df7
Merge pull request #268 from buccella/name_change_readme
loki -> oxen name change for README and download-dev-bins script
2021-03-30 10:23:58 +11:00
Chris Buccella 1695b631e5 loki -> oxen name change for README and download-dev-bins script 2021-03-29 00:12:18 -04:00
kylezs 207dce05cd
Merge pull request #267 from buccella/bug/266-base_url_oxen_observer
Update baseUrl to use oxen.observer instead of lokiblocks.com
2021-03-29 14:39:56 +11:00
Chris Buccella 332909819c Update baseUrl to use oxen.observer instead of lokiblocks.com
Fixes #266
2021-03-28 23:38:15 -04:00
Sean Darcy cb8941699d add type=wallet to ons 2021-03-23 11:30:58 +11:00
kylezs 9c42491396
Merge pull request #256 from jagerman/default-rpc-wallet-port
Change the default wallet rpc listen port to 22026
2021-02-10 09:39:02 +11:00
kylezs 56a8373422
Merge pull request #259 from kylezs/rebrand-ci
Rebrand ci
2021-02-10 09:15:44 +11:00
Kyle Zsembery 2561a4ac2c fix naming of ci things for rebrand 2021-02-10 09:11:26 +11:00
Jason Rhinelander dfbd195cc4 Change the default wallet rpc listen port to 22026
18082 will conflict with a running monerod on the system (18082 is
monero's default ZMQ RPC port).

This moves the default to 22026, which is clustered with oxend's other
ports (22022 P2P, 22023 RPC, 22025 OMQ).

(The '24 hole there was once used for the Monero ZMQ RPC interface in
oxend, but we no longer have that--but if we *do* need another port for
something 22024 will likely be it, so 22026 should be safe from future
oxend requirements).
2021-02-05 13:59:37 -04:00
kylezs 1da486eb78
Merge pull request #255 from jagerman/fix-youre-typo
Fixing you're typos
2021-02-05 13:25:59 +11:00
Jason Rhinelander cc4e870656 Fixing you're typos 2021-02-04 22:19:27 -04:00
kylezs bdda74419b
Merge pull request #250 from loki-project/development
v1.5.6
2021-01-11 17:55:48 +11:00
kylezs b37edea6a2
Merge pull request #249 from kylezs/bump-v
bump version to 1.5.6
2021-01-11 17:55:10 +11:00
Kyle Zsembery fa6a7fea61 bump version to 1.5.6 2021-01-11 17:54:27 +11:00
kylezs 873d9b167f
Merge pull request #248 from loki-project/development
v1.5.6 - Improved Colours
2021-01-11 17:14:14 +11:00
kylezs 5c41bed273
Merge pull request #247 from kylezs/oxen-colours
fix readme
2021-01-11 16:38:29 +11:00
Kyle Zsembery 726f5376f5 fix readme 2021-01-11 16:36:36 +11:00
kylezs 99805f46fb
Merge pull request #246 from loki-project/development
Colour improvements
2021-01-11 15:59:57 +11:00
kylezs acc69acd54
Merge pull request #245 from kylezs/oxen-colours
Colour changes
2021-01-11 15:43:43 +11:00
Kyle Zsembery 84d9bd9c33 fix readme 2021-01-11 15:42:12 +11:00
Kyle Zsembery 0b2673d11d colour changes 2021-01-11 15:37:30 +11:00
kylezs d9bda112c4
Merge pull request #1 from loki-project/development
Oxen rebrand upstream
2021-01-11 13:58:23 +11:00
kylezs a93dc48e17
Merge pull request #244 from loki-project/development
v1.5.5 - Oxen rebrand
2021-01-08 16:05:28 +11:00
kylezs c3d0962aa7
Temp oxen binary locations for Oxen GUI release (#243) 2021-01-07 20:43:06 -08:00
kylezs 99ed706ec8
Rebrand icons (#242) 2021-01-06 20:31:44 -08:00
kylezs e80bc806ea
rebrand fixes (#240) 2021-01-06 14:57:46 -08:00
kylezs 1e5cfb2ecc
Oxen rebrand (#239)
Oxen rebranding
2021-01-05 21:26:42 -08:00
kylezs 1c726e707f
Merge pull request #236 from loki-project/development
v1.5.4
2020-11-24 13:58:43 +11:00
kylezs 6e2f9b282b
Merge pull request #235 from kylezs/sign-and-verify
add data and address to signature popup
2020-11-24 13:10:05 +11:00
Kyle Zsembery 43291d6774 add data and address to signature popup 2020-11-24 12:38:46 +11:00
kylezs 945f35d513
Merge pull request #234 from kylezs/sign-and-verify
Sign and verify
2020-11-24 09:34:10 +11:00
Kyle Zsembery 4dbf08fa4c bump version 2020-11-23 16:35:03 +11:00
Kyle Zsembery d9b78c5603 signature from vuex, not local 2020-11-23 16:10:43 +11:00
Kyle Zsembery 17d17082a7 Add clear button for verify 2020-11-23 15:47:12 +11:00
Kyle Zsembery 898f9165c2 sign and verify 2020-11-23 15:32:55 +11:00
kylezs 61bccfd590
Merge pull request #233 from loki-project/development
Release v1.5.3
2020-11-13 16:01:59 +11:00
kylezs 125c4fb1f5
Merge pull request #232 from kylezs/bump-v
add rpc bind ip arg to wallet rpc start list
2020-11-13 15:20:13 +11:00
Kyle Zsembery a746f529ac add rpc bind ip arg to wallet rpc start list 2020-11-13 15:18:28 +11:00
kylezs d670563a45
Merge pull request #231 from kylezs/bump-v
bump version to 1.5.3
2020-11-13 14:42:25 +11:00
Kyle Zsembery 5a2a570afd bump version to 1.5.3 2020-11-13 14:39:36 +11:00
103 changed files with 33110 additions and 4028 deletions

100
.drone.jsonnet Normal file
View File

@ -0,0 +1,100 @@
local docker_image = 'registry.oxen.rocks/lokinet-ci-nodejs';
local apt_get_quiet = 'apt-get -o=Dpkg::Use-Pty=0 -q';
[
{
kind: 'pipeline',
type: 'docker',
name: 'Linux (amd64)',
platform: { arch: 'amd64' },
steps: [
{
name: 'build',
image: docker_image,
environment: {
SSH_KEY: { from_secret: 'SSH_KEY' },
NODE_OPTIONS: '--openssl-legacy-provider',
},
commands: [
'echo "Building on ${DRONE_STAGE_MACHINE}"',
'echo "man-db man-db/auto-update boolean false" | debconf-set-selections',
apt_get_quiet + ' update',
apt_get_quiet + ' install -y eatmydata',
'eatmydata ' + apt_get_quiet + ' dist-upgrade -y',
'./tools/download-oxen-files.sh https://oxen.rocks/oxen-io/oxen-core/oxen-stable-linux-LATEST.tar.xz',
'npm --version',
'node --version',
'mkdir -p $CCACHE_DIR/electron-builder',
'mkdir -p $CCACHE_DIR/npm',
'npm ci --cache $CCACHE_DIR/npm',
'ELECTRON_BUILDER_CACHE=$CCACHE_DIR/electron-builder npm --cache $CCACHE_DIR/npm run build',
'./tools/ci-drone-static-upload.sh',
],
},
],
},
{
kind: 'pipeline',
type: 'docker',
name: 'Windows (x64)',
platform: { arch: 'amd64' },
steps: [
{
name: 'build',
image: docker_image,
environment: {
SSH_KEY: { from_secret: 'SSH_KEY' },
WINEDEBUG: '-all',
NODE_OPTIONS: '--openssl-legacy-provider',
},
commands: [
'echo "Building on ${DRONE_STAGE_MACHINE}"',
'echo "man-db man-db/auto-update boolean false" | debconf-set-selections',
apt_get_quiet + ' update',
apt_get_quiet + ' install -y eatmydata zip',
'eatmydata ' + apt_get_quiet + ' dist-upgrade -y',
'./tools/download-oxen-files.sh https://oxen.rocks/oxen-io/oxen-core/oxen-stable-win-LATEST.zip',
'wine bin/oxend.exe --version',
'wine bin/oxen-wallet-rpc.exe --version',
'npm --version',
'node --version',
'mkdir -p $CCACHE_DIR/electron-builder',
'mkdir -p $CCACHE_DIR/npm',
'npm ci --cache $CCACHE_DIR/npm',
'ELECTRON_BUILDER_CACHE=$CCACHE_DIR/electron-builder npm --cache $CCACHE_DIR/npm run windows',
'./tools/ci-drone-static-upload.sh',
],
},
],
},
{
kind: 'pipeline',
type: 'exec',
name: 'MacOS (unsigned)',
platform: { os: 'darwin', arch: 'amd64' },
steps: [
{
name: 'build',
environment: {
SSH_KEY: { from_secret: 'SSH_KEY' },
CSC_IDENTITY_AUTO_DISCOVERY: 'false',
},
commands: [
'echo "Building on ${DRONE_STAGE_MACHINE}"',
'./tools/download-oxen-files.sh https://oxen.rocks/oxen-io/oxen-core/oxen-stable-macos-LATEST.tar.xz',
'npm --version',
'node --version',
'mkdir -p $CCACHE_DIR/electron-builder',
'mkdir -p $CCACHE_DIR/npm',
'npm ci --cache $CCACHE_DIR/npm',
'ELECTRON_BUILDER_CACHE=$CCACHE_DIR/electron-builder WINEDEBUG=-all npm --cache $CCACHE_DIR/npm run build',
'./tools/ci-drone-static-upload.sh',
],
},
],
},
]

View File

@ -1,89 +0,0 @@
name: Loki Electron Wallet Build
on:
push:
branches:
- master
- development
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
steps:
- name: Checkout git repo
uses: actions/checkout@v1
# Read node version from `.nvmrc` file
- name: Read nvm rc
id: nvmrc
uses: browniebroke/read-nvmrc-action@v1
- name: Install node
uses: actions/setup-node@v1
with:
node-version: ${{ steps.nvmrc.outputs.node_version }}
- name: Install dependencies
run: npm install
- name: Download lokid binaries
run: ./download-asset.sh
env:
OS: ${{ runner.os }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
working-directory: ./downloads
- name: Extract zip binaries
if: runner.os == 'Windows'
run: unzip latest
shell: bash
working-directory: ./downloads
- name: Extract xz binaries
if: runner.os != 'Windows'
run: tar -xf latest
shell: bash
working-directory: ./downloads
- name: Move lokid binaries
run: |
find ./downloads -type f -name "lokid*" -exec cp '{}' ./bin \;
find ./downloads -type f -name "loki-wallet-rpc*" -exec cp '{}' ./bin \;
shell: bash
- name: Verify binaries
run: ls ./bin
shell: bash
- name: Build window and linux binaries
if: runner.os != 'macOS'
run: npm run build
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build mac binaries
if: runner.os == 'macOS'
run: npm run build
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: ${{ secrets.MAC_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTIFICATE_PASSWORD }}
SIGNING_APPLE_ID: ${{ secrets.SIGNING_APPLE_ID }}
SIGNING_APP_PASSWORD: ${{ secrets.SIGNING_APP_PASSWORD }}
SIGNING_TEAM_ID: ${{ secrets.SIGNING_TEAM_ID }}
- name: Remove un-needed artifacts
run: rm -r -- ./*/
shell: bash
working-directory: ./dist/electron/Packaged
- name: Upload artifacts
uses: actions/upload-artifact@v1
with:
name: ${{ runner.OS }}
path: dist/electron/Packaged

View File

@ -1,87 +0,0 @@
name: Loki Electron Wallet With Dev Binaries
on:
push:
branches:
- dev-bins
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
steps:
- name: Checkout git repo
uses: actions/checkout@v1
# Read node version from `.nvmrc` file
- name: Read nvm rc
id: nvmrc
uses: browniebroke/read-nvmrc-action@v1
- name: Install node
uses: actions/setup-node@v1
with:
node-version: ${{ steps.nvmrc.outputs.node_version }}
- name: Install dependencies
run: npm install
- name: Download lokid binaries
run: ./download-dev-bins.sh
env:
OS: ${{ runner.os }}
shell: bash
working-directory: ./downloads
- name: Extract zip binaries
if: runner.os == 'Windows'
run: unzip latest
shell: bash
working-directory: ./downloads
- name: Extract xz binaries
if: runner.os != 'Windows'
run: tar -xf latest
shell: bash
working-directory: ./downloads
- name: Move lokid binaries
run: |
find ./downloads -type f -name "lokid*" -exec cp '{}' ./bin \;
find ./downloads -type f -name "loki-wallet-rpc*" -exec cp '{}' ./bin \;
shell: bash
- name: Verify binaries
run: ls ./bin
shell: bash
- name: Build window and linux binaries
if: runner.os != 'macOS'
run: npm run build
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build mac binaries
if: runner.os == 'macOS'
run: npm run build
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: ${{ secrets.MAC_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTIFICATE_PASSWORD }}
SIGNING_APPLE_ID: ${{ secrets.SIGNING_APPLE_ID }}
SIGNING_APP_PASSWORD: ${{ secrets.SIGNING_APP_PASSWORD }}
SIGNING_TEAM_ID: ${{ secrets.SIGNING_TEAM_ID }}
- name: Remove un-needed artifacts
run: rm -r -- ./*/
shell: bash
working-directory: ./dist/electron/Packaged
- name: Upload artifacts
uses: actions/upload-artifact@v1
with:
name: ${{ runner.OS }}
path: dist/electron/Packaged

View File

@ -1,77 +0,0 @@
name: Loki Electron Wallet Release
on:
push:
branches:
- master
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
steps:
- name: Checkout git repo
uses: actions/checkout@v1
# Read node version from `.nvmrc` file
- name: Read nvm rc
id: nvmrc
uses: browniebroke/read-nvmrc-action@v1
- name: Install node
uses: actions/setup-node@v1
with:
node-version: ${{ steps.nvmrc.outputs.node_version }}
- name: Install dependencies
run: npm install
- name: Download lokid binaries
run: ./download-asset.sh
env:
OS: ${{ runner.os }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
working-directory: ./downloads
- name: Extract zip binaries
if: runner.os == 'Windows'
run: unzip latest
shell: bash
working-directory: ./downloads
- name: Extract xz binaries
if: runner.os != 'Windows'
run: tar -xf latest
shell: bash
working-directory: ./downloads
- name: Move lokid binaries
run: |
find ./downloads -type f -name "lokid*" -exec cp '{}' ./bin \;
find ./downloads -type f -name "loki-wallet-rpc*" -exec cp '{}' ./bin \;
shell: bash
- name: Verify binaries
run: ls ./bin
shell: bash
- name: Publish window and linux binaries
if: runner.os != 'macOS'
run: npm run release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish mac binaries
if: runner.os == 'macOS'
run: npm run release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: ${{ secrets.MAC_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTIFICATE_PASSWORD }}
SIGNING_APPLE_ID: ${{ secrets.SIGNING_APPLE_ID }}
SIGNING_APP_PASSWORD: ${{ secrets.SIGNING_APP_PASSWORD }}
SIGNING_TEAM_ID: ${{ secrets.SIGNING_TEAM_ID }}

View File

@ -1,42 +1,43 @@
# Building
Building loki electron wallet binaries is done using github actions. Windows and linux binaries will build right out of the box but there are some extra steps needed for Mac OS
Set up the supported versions of npm/node/etc.:
## Mac OS
nvm use
The build script for Mac OS requires you to have a valid `Developer ID Application` certificate. Without this the build script cannot sign and notarize the mac binary which is needed for Catalina 10.15 and above.
If you would like to disable this then comment out `"afterSign": "build/notarize.js",` in package.json.
## Linux, Windows
You will also need an [App-specific password](https://support.apple.com/en-al/HT204397) for the apple account you wish to notarize with
npm run build
### Setup
## MacOS
Once you have your `Developer ID Application` you need to export it into a `.p12` file. Keep a note of the password used to encrypt this file as it will be needed later.
If you don't care about signing (i.e. you are not going to distribute) then you should be able to
simply `npm run build`.
We need to Base64 encode this file, so run the following command:
When you want to distribute the app, however, you need to do a bunch of crap to satisfy Apple's
arbitrary security theatre Rube Goldberg machine that purports to keep users safe but in reality is
designed to further Apple lock-in control of the Apple ecosystem.
```
base64 -i certificate.p12 -o encoded.txt
```
1. You have to pay Apple money (every year) to get a developer account.
2. You need a `Developer ID Application` certificate, created and signed from the Apple, and loaded
into your system keychain. `security find-identity -v` should show it.
3. You need to create an [App-specific password](https://support.apple.com/en-al/HT204397) for the
Apple developer account under which you are notarizing.
4. In the project root, create a `.env` file with contents:
#### On GitHub:
SIGNING_APPLE_ID=your-developer-id@example.com
SIGNING_APP_PASSWORD=app-specific-password
1. Navigate to the main page of the repository.
2. Under your repository name, click **Settings**.
3. In the left sidebar, click **Secrets**.
4. Add the following secrets:
1. Certificate
- Name: `MAC_CERTIFICATE`
- Value: The encoded Base64 certificate
2. Certificate password
- Name: `MAC_CERTIFICATE_PASSWORD`
- Value: The password that was set when the certificate was exported.
3. Apple ID
- Name: `SIGNING_APPLE_ID`
- Value: The apple id (email) to use for signing
4. Apple Password
- Name: `SIGNING_APP_PASSWORD`
- Value: The app-specific password that was generated for the apple id
5. Team ID (Optional)
- Name: `SIGNING_TEAM_ID`
- Value: The apple team id if you're sigining the application for a team
This password can be plaintext if absolutely needed (e.g. in a CI job) but should be a [keychain
reference](https://github.com/electron/electron-notarize#safety-when-using-appleidpassword) such
as `@keychain:some-token` for better security where feasible.
- If you have multiple ids and need to use a particular signing team ID you can add:
SIGNING_TEAM_ID=TEAMIDXYZ1
5. If building from a remote connection (e.g. ssh'd into a mac) then unlock the keychain for that
session by running `security unlock`.
With all of that set up, your `npm run build` should produce a signed and notarized installer.
Hopefully. Maybe. Sometimes Apple's servers are broken and you might have to try again. But don't
worry, Apple's incompetence around signing makes everything more secure because... reasons.

View File

@ -1,34 +1,32 @@
# Loki Electron GUI Wallet
# Oxen Electron GUI Wallet
### Introduction
Loki is a private cryptocurrency based on Monero. Loki aims to provide a private data transmission layer using a second layer of Service Nodes.
More information on the project can be found on the [website](https://loki.network) and in the [whitepaper](https://loki.network/whitepaper). Loki is an open source project, and we encourage contributions from anyone with something to offer.
Oxen (formerly Loki) is a private cryptocurrency based on Monero. Oxen aims to provide a private data transmission layer using a second layer of Service Nodes.
More information on the project can be found on the [website](https://oxen.io) and in the [whitepaper](https://loki.network/whitepaper). Oxen is an open source project, and we encourage contributions from anyone with something to offer.
<p align="center">
<img src="https://raw.githubusercontent.com/KeeJef/loki-electron-gui-wallet/master/src-electron/icons/mrcuug.PNG" width="600">
</p>
![Oxen wallet image](./src-electron/icons/mrcuug.PNG)
### About this project
This is the new electron GUI for Loki. It is open source and completely free to use without restrictions, anyone may create an alternative implementation of the Loki Electron GUI that uses the protocol and network in a compatible manner.
This is the new Electron GUI for Oxen. It is open source and completely free to use without restrictions, anyone may create an alternative implementation of the Oxen Electron GUI that uses the protocol and network in a compatible manner.
Please submit any changes as pull requests to the development branch, all changes are assessed in the development branch before being merged to master, release tags are considered stable builds for the GUI.
#### Pre-requisites
- Download latest [Lokid](https://github.com/loki-project/loki/releases/latest)
- Extract the lokid binaries to a folder
- Download latest [oxend](https://github.com/oxen-io/oxen-core/releases/latest)
- Extract the oxend binaries to a folder
#### Commands
```
nvm use 14.11.0
npm install -g @quasar/cli
git clone https://github.com/loki-project/loki-electron-gui-wallet
cd loki-electron-gui-wallet
cp path_to_lokid_binaries/lokid bin/
cp path_to_lokid_binaries/loki-wallet-rpc bin/
git clone https://github.com/oxen-io/oxen-electron-gui-wallet
cd oxen-electron-gui-wallet
cp path_to_oxend_binaries/oxend bin/
cp path_to_oxend_binaries/oxen-wallet-rpc bin/
npm install
```

View File

@ -22,19 +22,25 @@ exports.default = async function notarizing(context) {
log("Notarizing mac application");
const appName = context.packager.appInfo.productFilename;
const { SIGNING_APPLE_ID, SIGNING_APP_PASSWORD, SIGNING_TEAM_ID } = process.env;
const {
SIGNING_APPLE_ID,
SIGNING_APP_PASSWORD,
SIGNING_TEAM_ID
} = process.env;
if (isEmpty(SIGNING_APPLE_ID) || isEmpty(SIGNING_APP_PASSWORD)) {
log("SIGNING_APPLE_ID or SIGNING_APP_PASSWORD not set.\nTerminating noratization.");
log(
"SIGNING_APPLE_ID or SIGNING_APP_PASSWORD not set.\nTerminating noratization."
);
return;
}
const options = {
appBundleId: "com.loki-project.electron-wallet",
tool: "notarytool",
appPath: `${appOutDir}/${appName}.app`,
appleId: SIGNING_APPLE_ID,
appleIdPassword: SIGNING_APP_PASSWORD
};
if (!isEmpty(SIGNING_TEAM_ID)) options.ascProvider = SIGNING_TEAM_ID;
if (!isEmpty(SIGNING_TEAM_ID)) options.teamId = SIGNING_TEAM_ID;
return notarize(options);
};

View File

@ -1,40 +0,0 @@
#!/bin/bash
# Source from: https://github.com/houqp/download-release-assets-action
set -e
if [ -z "$OS" ]; then
echo "OS must be set"
exit 1
fi
if [ -z "$RENAME" ]; then
RENAME="latest"
fi
REPO="loki-project/loki-core"
RELEASE="latest"
if [ "$OS" == "Linux" ]; then
FILE_NAME_REGEX="linux"
elif [ "$OS" == "Windows" ]; then
FILE_NAME_REGEX="win"
elif [ "$OS" == "macOS" ]; then
FILE_NAME_REGEX="macos"
else
echo "OS must be Linux, Windows or macOS"
exit 1
fi
ASSET_URL=$(curl -sL --fail \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${REPO}/releases/${RELEASE}" \
| jq -r ".assets | .[] | select(.name | test(\"${FILE_NAME_REGEX}\")) | .url")
curl -sL --fail \
-H "Accept: application/octet-stream" \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
-o "${RENAME}" \
"$ASSET_URL"

View File

@ -1,32 +0,0 @@
#!/bin/bash
set -e
if [ -z "$OS" ]; then
echo "OS must be set"
exit 1
fi
if [ -z "$RENAME" ]; then
RENAME="latest"
fi
if [ "$OS" == "Linux" ]; then
ASSET_URL="https://builds.lokinet.dev/loki-project/loki-core/loki-dev-linux-LATEST.tar.xz"
elif [ "$OS" == "Windows" ]; then
ASSET_URL="https://builds.lokinet.dev/loki-project/loki-core/loki-dev-win-LATEST.zip"
elif [ "$OS" == "macOS" ]; then
ASSET_URL="https://builds.lokinet.dev/loki-project/loki-core/loki-dev-macos-LATEST.tar.xz"
else
echo "OS must be Linux, Windows or macOS"
exit 1
fi
echo "About to download the binaries"
curl -sL --fail \
-H "Accept: application/octet-stream" \
-o "${RENAME}" \
"$ASSET_URL"
echo "Loki binaries downloaded"

26546
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,22 @@
{
"name": "loki-electron-wallet",
"version": "1.5.2",
"description": "Modern GUI interface for Loki Currency",
"productName": "Loki Electron Wallet",
"name": "oxen-electron-wallet",
"version": "1.8.1",
"description": "Modern GUI interface for Oxen Currency",
"productName": "Oxen Electron Wallet",
"repository": {
"type": "git",
"url": "https://github.com/loki-project/loki-electron-gui-wallet.git"
},
"cordovaId": "com.lokinetwork.wallet",
"cordovaId": "com.oxen.wallet",
"author": {
"name": "Loki Project",
"email": "team@loki.network"
"name": "Oxen",
"email": "oxen@oxen.io"
},
"private": true,
"scripts": {
"dev": "quasar dev -m electron",
"build": "quasar build -m electron --publish=never",
"release": "quasar build -m electron --publish=always",
"windows": "quasar build -m electron --publish=never -T win",
"lint": "eslint --fix .",
"format": "prettier --write \"**/*.+(js|jsx|json|yml|yaml|css|md|vue)\"",
"ready": "npm run lint && npm run format"
@ -48,7 +48,7 @@
"devtron": "^1.4.0",
"dotenv": "^8.1.0",
"electron": "^4.1.1",
"electron-builder": "^22.4.1",
"electron-builder": "^23.0.3",
"electron-debug": "^2.1.0",
"electron-devtools-installer": "^2.2.4",
"electron-notarize": "^0.1.1",
@ -63,7 +63,6 @@
"eslint-plugin-vue": "^5.2.3",
"husky": "^4.2.3",
"lint-staged": "^10.0.8",
"node-sass": "^4.13.1",
"prettier": "^1.19.1",
"sass-loader": "^7.1.0",
"strip-ansi": "^3.0.1"

BIN
public/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,66 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1000 368" style="enable-background:new 0 0 1000 368;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:url(#SVGID_1_);}
.st2{fill:#333333;}
.st3{fill:url(#SVGID_2_);}
.st4{fill:url(#SVGID_3_);}
.st5{fill:#00263A;}
.st6{fill:url(#SVGID_4_);}
.st7{fill:url(#SVGID_5_);}
.st8{fill:url(#SVGID_6_);}
.st9{fill:url(#SVGID_7_);}
.st10{fill:url(#SVGID_8_);}
.st11{fill:url(#SVGID_9_);}
.st12{fill:url(#SVGID_10_);}
.st13{fill:url(#SVGID_11_);}
.st14{fill:url(#SVGID_12_);}
.st15{fill:url(#SVGID_13_);}
.st16{fill:url(#SVGID_14_);}
.st17{fill:url(#SVGID_15_);}
.st18{fill:url(#SVGID_16_);}
.st19{fill:url(#SVGID_17_);}
.st20{opacity:6.000000e-02;}
.st21{opacity:4.000000e-02;fill:#FFFFFF;}
.st22{opacity:7.000000e-02;fill:#FFFFFF;}
.st23{fill:#008522;}
.st24{fill:#78BE20;}
.st25{fill:#005F61;}
.st26{fill:url(#SVGID_18_);}
</style>
<g>
<path class="st0" d="M366.6,78h37.1v178.9H497v32.7H366.6V78z"/>
<path class="st0" d="M619.8,74.5C683.3,74.5,728,120.8,728,184c0,63.1-44.7,109.5-108.2,109.5c-63.5,0-108.2-46.3-108.2-109.5
C511.6,120.8,556.3,74.5,619.8,74.5z M619.8,107.5c-42.8,0-70.1,32.7-70.1,76.5c0,43.5,27.3,76.5,70.1,76.5
c42.5,0,70.1-33,70.1-76.5C689.9,140.2,662.3,107.5,619.8,107.5z"/>
<path class="st0" d="M819.4,200.5L801,222v67.6h-37.1V78H801v100.9L883.8,78h46l-86,99.9l92.3,111.7h-45.7L819.4,200.5z"/>
<path class="st0" d="M960.9,78H998v211.6h-37.1V78z"/>
</g>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="86.8402" y1="268.7968" x2="86.8402" y2="0.426">
<stop offset="0" style="stop-color:#78BE20"/>
<stop offset="0.1197" style="stop-color:#58AF21"/>
<stop offset="0.3682" style="stop-color:#199122"/>
<stop offset="0.486" style="stop-color:#008522"/>
<stop offset="0.6925" style="stop-color:#007242"/>
<stop offset="0.8806" style="stop-color:#006459"/>
<stop offset="1" style="stop-color:#005F61"/>
</linearGradient>
<polygon class="st1" points="132.1,268.8 0.3,137 136.9,0.4 173.3,36.8 73.1,137 168.5,232.4 "/>
</g>
<g>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="212.9564" y1="367.5197" x2="212.9564" y2="99.1484">
<stop offset="0" style="stop-color:#78BE20"/>
<stop offset="0.1197" style="stop-color:#58AF21"/>
<stop offset="0.3682" style="stop-color:#199122"/>
<stop offset="0.486" style="stop-color:#008522"/>
<stop offset="0.6925" style="stop-color:#007242"/>
<stop offset="0.8806" style="stop-color:#006459"/>
<stop offset="1" style="stop-color:#005F61"/>
</linearGradient>
<polygon class="st3" points="162.9,367.5 126.5,331.1 226.7,230.9 131.3,135.6 167.7,99.1 299.5,230.9 "/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

2885
public/oxen-logo.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 210 KiB

30
public/oxen-white.svg Normal file
View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1501.3 321.3" style="enable-background:new 0 0 1501.3 321.3;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#12C7BA;}
</style>
<g>
<g>
<path class="st0" d="M886.1,75.7l-65,56l-65-56c-5.2-4.5-11.9-7-18.8-7l-56.5,0l106.7,91.9l-106.7,91.9l56.5,0
c6.9,0,13.6-2.5,18.8-7l65-56l65,56c5.2,4.5,11.9,7,18.8,7l56.5,0l-106.7-91.9l106.7-91.9h-56.5C898,68.7,891.3,71.2,886.1,75.7z"
/>
<path class="st0" d="M1501.3,68.7h-43v128.4L1299.2,74.7c-5-3.9-11.2-6-17.6-6h-30.9v183.9h43V124.2l159.1,122.4
c5,3.9,11.2,6,17.6,6h30.9V68.7z"/>
<path class="st0" d="M1212,107.9V68.7l-205.7,0c-22.5,25.9-34.5,58-34.5,91.9c0,33.9,12,66.1,34.5,91.9H1212v-39.1h-184.4
c-5.1-9.6-8.8-20.3-10.7-33.2h180.3v-41.1h-180c2-12,5.6-22.2,10.4-31.3H1212z"/>
<path class="st0" d="M674.4,160.7c0-55.4-34-91.9-34-91.9l-193.1,0c0,0-34,36.6-34,91.9s34,91.9,34,91.9h193.1
C640.4,252.6,674.4,216,674.4,160.7z M469.6,213.2c-8.7-15.8-13.3-33.9-13.3-52.5c0-18.6,4.6-36.7,13.3-52.5h148.6
c8.7,15.8,13.3,33.9,13.3,52.5c0,18.6-4.6,36.7-13.3,52.5H469.6z"/>
</g>
<g>
<circle class="st0" cx="160.6" cy="160.6" r="160.6"/>
<g>
<polygon class="st1" points="67.6,80.5 160.6,160.6 253.7,80.5 "/>
<polygon class="st1" points="253.7,240.8 160.6,160.6 67.6,240.8 "/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

2928
public/oxen.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 214 KiB

View File

@ -156,19 +156,33 @@ module.exports = function() {
builder: {
// https://www.electron.build/configuration/configuration
appId: "com.loki-project.electron-wallet",
productName: "Loki Electron Wallet",
copyright:
"Copyright © 2018-2020 Loki Project, 2018 Ryo Currency Project",
appId: "com.oxen.electron-wallet",
productName: "Oxen Electron Wallet",
copyright: "Copyright © 2018-2022 Oxen, 2018 Ryo Currency Project",
afterSign: "build/notarize.js",
artifactName: "loki-electron-wallet-${version}-${os}.${ext}",
artifactName: "oxen-electron-wallet-${version}-${os}.${ext}",
publish: "github",
linux: {
target: ["AppImage", "deb"],
icon: "src-electron/icons/icon_512x512.png",
icon: "oxen-electron-wallet.png",
category: "Finance"
},
// see https://www.electron.build/configuration/linux#debian-package-options
deb: {
depends: [
"libgtk-3-0",
"libnotify4",
"libnss3",
"libxss1",
"libxtst6",
"xdg-utils",
"libatspi2.0-0",
"libuuid1",
"libsecret-1-0",
"libappindicator3-1 | libayatana-appindicator3-1"
]
},
mac: {
// We need zip for auto-updating
@ -184,7 +198,7 @@ module.exports = function() {
},
dmg: {
background: "src-electron/build/loki-dmg.tiff",
background: "src-electron/build/oxen-dmg.tiff",
sign: false
},

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 408 KiB

View File

@ -21,7 +21,10 @@ async function canAutoUpdate() {
// Taken from: https://github.com/electron-userland/electron-builder/blob/d4feb6d3c8b008f8b455c761d654c8088f90d8fa/packages/electron-updater/src/ElectronAppAdapter.ts#L25
const updateFile = isPackaged ? "app-update.yml" : "dev-app-update.yml";
const basePath = isPackaged && process.resourcesPath ? process.resourcesPath : app.getAppPath();
const basePath =
isPackaged && process.resourcesPath
? process.resourcesPath
: app.getAppPath();
const appUpdateConfigPath = path.join(basePath, updateFile);
return new Promise(resolve => {
@ -96,8 +99,8 @@ async function showUpdateDialog(mainWindow) {
const options = {
type: "info",
buttons: ["Restart Wallet", "Later"],
title: "Loki Electron Wallet update available",
message: "There is a new version of Loki Electron Wallet available.",
title: "Oxen Electron Wallet update available",
message: "There is a new version of Oxen Electron Wallet available.",
detail: "Press Restart Wallet to apply the update",
defaultId: LATER_BUTTON,
cancelId: RESTART_BUTTON
@ -115,7 +118,7 @@ async function showCannotUpdateDialog(mainWindow) {
buttons: ["Ok"],
title: "Cannot update",
message:
"Loki Electron Wallet failed to update but there is a new version available. Please go to https://loki.network/ and install the new version manually."
"Oxen Electron Wallet failed to update but there is a new version available. Please go to https://oxen.io/ and install the new version manually."
};
return new Promise(resolve => {

View File

@ -13,7 +13,7 @@ const path = require("upath");
* The reason we are setting it here is that the path needs to be evaluated at runtime
*/
if (process.env.PROD) {
global.__statics = path.join(__dirname, "statics").replace(/\\/g, "\\\\");
global.__statics = path.join(__dirname, "").replace(/\\/g, "\\\\");
global.__ryo_bin = path.join(__dirname, "..", "bin").replace(/\\/g, "\\\\");
} else {
global.__ryo_bin = path.join(process.cwd(), "bin").replace(/\\/g, "\\\\");
@ -57,7 +57,7 @@ function createWindow() {
height: mainWindowState.height,
minWidth: 640,
minHeight: 480,
icon: require("path").join(__statics, "icon_512x512.png"),
icon: require("path").join(__statics, "icon.png"),
title,
webPreferences: {
nodeIntegration: true,
@ -137,7 +137,7 @@ function createWindow() {
mainWindow,
{
title: "Startup error",
message: `Loki Wallet is already open, or port ${config.port} is in use`,
message: `Oxen Wallet is already open, or port ${config.port} is in use`,
type: "error",
buttons: ["ok"]
},

View File

@ -33,7 +33,7 @@ let template = [
{
label: "Learn More",
click() {
require("electron").shell.openExternal("https://loki.network/");
require("electron").shell.openExternal("https://oxen.io/");
}
}
]
@ -42,7 +42,7 @@ let template = [
if (process.platform === "darwin") {
template.unshift({
label: "Loki Electron Wallet",
label: "Oxen Electron Wallet",
submenu: [
{ role: "about" },
{ type: "separator" },

View File

@ -34,16 +34,27 @@ export class Backend {
}
init(config) {
let configDir;
let legacyLokiConfigDir;
if (os.platform() === "win32") {
this.config_dir = "C:\\ProgramData\\loki";
this.wallet_dir = `${os.homedir()}\\Documents\\Loki`;
configDir = "C:\\ProgramData\\oxen";
legacyLokiConfigDir = "C:\\ProgramData\\loki\\";
this.wallet_dir = `${os.homedir()}\\Documents\\Oxen`;
} else {
this.config_dir = path.join(os.homedir(), ".loki");
this.wallet_dir = path.join(os.homedir(), "Loki");
configDir = path.join(os.homedir(), ".oxen");
legacyLokiConfigDir = path.join(os.homedir(), ".loki/");
this.wallet_dir = path.join(os.homedir(), "Oxen");
}
if (!fs.existsSync(this.config_dir)) {
fs.mkdirpSync(this.config_dir);
// if the user has used loki before, just keep the same stuff
if (fs.existsSync(legacyLokiConfigDir)) {
this.config_dir = legacyLokiConfigDir;
} else {
// create the new, Oxen location
this.config_dir = configDir;
if (!fs.existsSync(configDir)) {
fs.mkdirpSync(configDir);
}
}
if (!fs.existsSync(path.join(this.config_dir, "gui"))) {
@ -96,7 +107,7 @@ export class Backend {
net_type: "mainnet"
},
wallet: {
rpc_bind_port: 18082,
rpc_bind_port: 22026,
log_level: 0
}
};
@ -111,19 +122,19 @@ export class Backend {
this.remotes = [
{
host: "imaginary.stream",
host: "public-na.optf.ngo",
port: "22023"
},
{
host: "nodes.hashvault.pro",
port: "22023"
},
{
host: "explorer.loki.aussie-pools.com",
host: "explorer.oxen.aussie-pools.com",
port: "18081"
},
{
host: "public.loki.foundation",
host: "public-eu.optf.ngo",
port: "22023"
},
{
host: "oxen-rpc.caliban.org",
port: "22023"
}
];
@ -277,14 +288,14 @@ export class Backend {
if (params.type === "tx") {
path = "tx";
} else if (params.type === "service_node") {
path = "service_node";
path = "sn";
}
if (path) {
const baseUrl =
net_type === "testnet"
? "https://lokitestnet.com"
: "https://lokiblocks.com";
? "https://testnet.oxen.observer"
: "https://oxen.observer";
const url = `${baseUrl}/${path}/`;
require("electron").shell.openExternal(url + params.id);
}

View File

@ -29,25 +29,25 @@ export class Daemon {
checkVersion() {
return new Promise(resolve => {
if (process.platform === "win32") {
let lokid_path = path.join(__ryo_bin, "lokid.exe");
let lokid_version_cmd = `"${lokid_path}" --version`;
if (!fs.existsSync(lokid_path)) {
let oxend_path = path.join(__ryo_bin, "oxend.exe");
let oxend_version_cmd = `"${oxend_path}" --version`;
if (!fs.existsSync(oxend_path)) {
resolve(false);
}
child_process.exec(lokid_version_cmd, (error, stdout) => {
child_process.exec(oxend_version_cmd, (error, stdout) => {
if (error) {
resolve(false);
}
resolve(stdout);
});
} else {
let lokid_path = path.join(__ryo_bin, "lokid");
let lokid_version_cmd = `"${lokid_path}" --version`;
if (!fs.existsSync(lokid_path)) {
let oxend_path = path.join(__ryo_bin, "oxend");
let oxend_version_cmd = `"${oxend_path}" --version`;
if (!fs.existsSync(oxend_path)) {
resolve(false);
}
child_process.exec(
lokid_version_cmd,
oxend_version_cmd,
{ detached: true },
(error, stdout) => {
if (error) {
@ -148,7 +148,7 @@ export class Daemon {
args.push("--stagenet");
}
args.push("--log-file", path.join(dirs[net_type], "logs", "lokid.log"));
args.push("--log-file", path.join(dirs[net_type], "logs", "oxend.log"));
if (daemon.rpc_bind_ip !== "127.0.0.1") {
args.push("--confirm-external-bind");
}
@ -173,12 +173,12 @@ export class Daemon {
if (status === "closed") {
if (process.platform === "win32") {
this.daemonProcess = child_process.spawn(
path.join(__ryo_bin, "lokid.exe"),
path.join(__ryo_bin, "oxend.exe"),
args
);
} else {
this.daemonProcess = child_process.spawn(
path.join(__ryo_bin, "lokid"),
path.join(__ryo_bin, "oxend"),
args,
{
detached: true
@ -484,14 +484,14 @@ export class Daemon {
});
}
async getLNSRecordsForOwners(owners) {
async getONSRecordsForOwners(owners) {
if (!Array.isArray(owners) || owners.length === 0) {
return [];
}
// only 256 addresses allowed in this call
let ownersMax = owners.slice(0, 256);
const data = await this.sendRPC("lns_owners_to_names", {
const data = await this.sendRPC("ons_owners_to_names", {
entries: ownersMax
});
if (!data.hasOwnProperty("result")) return [];
@ -506,10 +506,10 @@ export class Daemon {
};
});
return this._sanitizeLNSRecords(recordsWithOwners);
return this._sanitizeONSRecords(recordsWithOwners);
}
async getLNSRecord(nameHash) {
async getONSRecord(nameHash) {
if (!nameHash || nameHash.length === 0) {
return null;
}
@ -519,22 +519,23 @@ export class Daemon {
{
name_hash: nameHash,
// 0 = session
// 1 = wallet
// 2 = lokinet
types: [0, 2]
types: [0, 1, 2]
}
]
};
const data = await this.sendRPC("lns_names_to_owners", params);
const data = await this.sendRPC("ons_names_to_owners", params);
if (!data.hasOwnProperty("result")) return null;
const entries = this._sanitizeLNSRecords(data.result.entries);
const entries = this._sanitizeONSRecords(data.result.entries);
if (entries.length === 0) return null;
return entries[0];
}
_sanitizeLNSRecords(records) {
_sanitizeONSRecords(records) {
return (records || []).map(record => {
// Record type is in uint16 format
// Session = 0
@ -543,6 +544,9 @@ export class Daemon {
if (record.type === 0) {
type = "session";
}
if (record.type === 1) {
type = "wallet";
}
return {
...record,
type

View File

@ -18,14 +18,16 @@ export class WalletRPC {
this.id = 0;
this.net_type = "mainnet";
this.heartbeat = null;
this.lnsHeartbeat = null;
this.onsHeartbeat = null;
this.wallet_state = {
open: false,
name: "",
password_hash: null,
balance: null,
unlocked_balance: null,
lnsRecords: []
accrued_balance: null,
accrued_balance_next_payout: null,
onsRecords: []
};
this.isRPCSyncing = false;
this.dirs = null;
@ -89,6 +91,8 @@ export class WalletRPC {
options.wallet.rpc_bind_port,
"--daemon-address",
daemon_address,
"--rpc-bind-ip",
"127.0.0.1",
"--log-level",
options.wallet.log_level
];
@ -135,8 +139,8 @@ export class WalletRPC {
const rpcExecutable =
process.platform === "win32"
? "loki-wallet-rpc.exe"
: "loki-wallet-rpc";
? "oxen-wallet-rpc.exe"
: "oxen-wallet-rpc";
// eslint-disable-next-line no-undef
const rpcPath = path.join(__ryo_bin, rpcExecutable);
@ -144,7 +148,7 @@ export class WalletRPC {
if (!fs.existsSync(rpcPath)) {
reject(
new Error(
"Failed to find Loki Wallet RPC. Please make sure you anti-virus has not removed it."
"Failed to find Oxen Wallet RPC. Please make sure your anti-virus has not removed it."
)
);
return;
@ -251,7 +255,7 @@ export class WalletRPC {
break;
case "decrypt_record": {
const record = await this.decryptLNSRecord(params.type, params.name);
const record = await this.decryptONSRecord(params.type, params.name);
this.sendGateway("set_decrypt_record_result", {
record,
decrypted: !!record
@ -268,7 +272,12 @@ export class WalletRPC {
break;
case "create_wallet":
this.createWallet(params.name, params.password, params.language);
this.createWallet(
params.name,
params.password,
params.language,
params.hardware_wallet
);
break;
case "restore_wallet":
@ -284,7 +293,7 @@ export class WalletRPC {
break;
case "restore_view_wallet":
// TODO: Decide if we want this for loki
// TODO: Decide if we want this for Oxen
this.restoreViewWallet(
params.name,
params.password,
@ -351,8 +360,8 @@ export class WalletRPC {
!!params.isSweepAll
);
break;
case "purchase_lns":
this.purchaseLNS(
case "purchase_ons":
this.purchaseONS(
params.password,
params.type,
params.name,
@ -361,11 +370,11 @@ export class WalletRPC {
params.backup_owner || ""
);
break;
case "lns_renew_mapping":
this.lnsRenewMapping(params.password, params.type, params.name);
case "ons_renew_mapping":
this.onsRenewMapping(params.password, params.type, params.name);
break;
case "update_lns_mapping":
this.updateLNSMapping(
case "update_ons_mapping":
this.updateONSMapping(
params.password,
params.type,
params.name,
@ -388,6 +397,14 @@ export class WalletRPC {
);
break;
case "sign":
this.sign(params.data);
break;
case "verify":
this.verify(params.data, params.address, params.signature);
break;
case "add_address_book":
this.addAddressBook(
params.address,
@ -423,6 +440,9 @@ export class WalletRPC {
case "import_key_images":
this.importKeyImages(params.password, params.path);
break;
case "export_transfers":
this.exportTransfers(params.password, params.path);
break;
case "change_wallet_password":
this.changeWalletPassword(params.old_password, params.new_password);
@ -494,13 +514,20 @@ export class WalletRPC {
});
}
createWallet(filename, password, language) {
isHardwareWallet(filename) {
let hwfile = path.join(this.wallet_dir, filename + ".hwdev.txt");
return fs.existsSync(hwfile);
}
createWallet(filename, password, language, hardware_wallet) {
// Reset the status error
this.sendGateway("reset_wallet_error");
this.sendRPC("create_wallet", {
filename,
password,
language
language,
hardware_wallet: !!hardware_wallet,
device_label: hardware_wallet ? "hardware_wallet" : undefined
}).then(data => {
if (data.hasOwnProperty("error")) {
this.sendGateway("set_wallet_error", { status: data.error });
@ -687,6 +714,14 @@ export class WalletRPC {
errorOnExist: true
});
}
if (fs.existsSync(import_path + ".hwdev.txt")) {
fs.copySync(
import_path + ".hwdev.txt",
destination + ".hwdev.txt",
fs.constants.COPYFILE_EXCL
);
}
} catch (e) {
this.sendGateway("set_wallet_error", {
status: {
@ -744,6 +779,8 @@ export class WalletRPC {
address: "",
balance: 0,
unlocked_balance: 0,
accrued_balance: 0,
accrued_balance_next_payout: 0,
height: 0,
view_only: false
},
@ -764,6 +801,9 @@ export class WalletRPC {
} else if (n.method == "getbalance") {
wallet.info.balance = n.result.balance;
wallet.info.unlocked_balance = n.result.unlocked_balance;
wallet.info.accrued_balance = n.result.accrued_balance;
wallet.info.accrued_balance_next_payout =
n.result.accrued_balance_next_payout;
} else if (n.method == "query_key") {
wallet.secret[n.params.key_type] = n.result.key;
if (n.params.key_type == "spend_key") {
@ -774,6 +814,10 @@ export class WalletRPC {
}
}
if (this.isHardwareWallet(filename)) {
wallet.info.hardware_wallet = true;
}
this.saveWallet().then(() => {
let address_txt_path = path.join(
this.wallet_dir,
@ -790,7 +834,11 @@ export class WalletRPC {
this.sendGateway("set_wallet_data", wallet);
this.startHeartbeat();
if (this.isHardwareWallet(filename)) {
this.startHeartbeat(10);
} else {
this.startHeartbeat();
}
});
}
@ -820,6 +868,12 @@ export class WalletRPC {
});
}
const hardware_wallet_file = path.join(
this.wallet_dir,
filename + ".hwdev.txt"
);
const hardware_wallet = fs.existsSync(hardware_wallet_file);
// store hash of the password so we can check against it later when requesting private keys, or for sending txs
this.wallet_state.password_hash = crypto
.pbkdf2Sync(password, this.auth[2], 1000, 64, "sha512")
@ -827,10 +881,20 @@ export class WalletRPC {
this.wallet_state.name = filename;
this.wallet_state.open = true;
this.startHeartbeat();
if (hardware_wallet) {
this.startHeartbeat(10);
} else {
this.startHeartbeat();
}
this.purchasedNames = {};
this.sendGateway("set_wallet_data", {
info: {
hardware_wallet
}
});
// Check if we have a view only wallet by querying the spend key
this.sendRPC("query_key", { key_type: "spend_key" }).then(data => {
if (data.hasOwnProperty("error") || !data.hasOwnProperty("result")) {
@ -847,18 +911,18 @@ export class WalletRPC {
});
}
startHeartbeat() {
startHeartbeat(multiplier = 1) {
clearInterval(this.heartbeat);
this.heartbeat = setInterval(() => {
this.heartbeatAction();
}, 5000);
}, 5000 * multiplier);
this.heartbeatAction(true);
clearInterval(this.lnsHeartbeat);
this.lnsHeartbeat = setInterval(() => {
this.updateLocalLNSRecords();
}, 30 * 1000); // Every 30 seconds
this.updateLocalLNSRecords();
clearInterval(this.onsHeartbeat);
this.onsHeartbeat = setInterval(() => {
this.updateLocalONSRecords();
}, 30 * 1000 * multiplier); // Every 30 seconds
this.updateLocalONSRecords();
}
heartbeatAction(extended = false) {
@ -915,7 +979,10 @@ export class WalletRPC {
} else if (n.method == "getbalance") {
if (
this.wallet_state.balance == n.result.balance &&
this.wallet_state.unlocked_balance == n.result.unlocked_balance
this.wallet_state.unlocked_balance == n.result.unlocked_balance &&
this.wallet_state.accrued_balance == n.result.accrued_balance &&
this.wallet_state.accrued_balance_next_payout ==
n.result.accrued_balance_next_payout
) {
continue;
}
@ -923,6 +990,10 @@ export class WalletRPC {
this.wallet_state.balance = wallet.info.balance = n.result.balance;
this.wallet_state.unlocked_balance = wallet.info.unlocked_balance =
n.result.unlocked_balance;
this.wallet_state.accrued_balance = wallet.info.accrued_balance =
n.result.accrued_balance;
this.wallet_state.accrued_balance_next_payout = wallet.info.accrued_balance_next_payout =
n.result.accrued_balance_next_payout;
this.sendGateway("set_wallet_data", {
info: wallet.info
});
@ -959,7 +1030,7 @@ export class WalletRPC {
});
}
async updateLocalLNSRecords() {
async updateLocalONSRecords() {
try {
const addressData = await this.sendRPC(
"get_address",
@ -978,12 +1049,12 @@ export class WalletRPC {
const addresses = results.map(a => a.address).filter(a => !!a);
if (addresses.length === 0) return;
const records = await this.backend.daemon.getLNSRecordsForOwners(
const records = await this.backend.daemon.getONSRecordsForOwners(
addresses
);
// We need to ensure that we decrypt any incoming records that we already have
const currentRecords = this.wallet_state.lnsRecords;
const currentRecords = this.wallet_state.onsRecords;
const recordsToUpdate = { ...this.purchasedNames };
const newRecords = records.map(record => {
// If we have a new record or we haven't decrypted our current record then we should return the new record
@ -1013,13 +1084,13 @@ export class WalletRPC {
};
});
this.wallet_state.lnsRecords = newRecords;
this.wallet_state.onsRecords = newRecords;
// fetch the known (cached) records from the wallet and add the data
// to the records being set in state
let known_names = await this.lnsKnownNames();
let known_names = await this.onsKnownNames();
// Fill the necessary decrypted values of the cached LNS names
// Fill the necessary decrypted values of the cached ONS names
for (let r of newRecords) {
for (let k of known_names) {
if (k.hashed === r.name_hash) {
@ -1030,31 +1101,31 @@ export class WalletRPC {
}
}
this.sendGateway("set_wallet_data", { lnsRecords: newRecords });
this.sendGateway("set_wallet_data", { onsRecords: newRecords });
// Decrypt the records serially
let updatePromise = Promise.resolve();
for (const [name, type] of Object.entries(recordsToUpdate)) {
updatePromise = updatePromise.then(() => {
this.decryptLNSRecord(type, name);
this.decryptONSRecord(type, name);
});
}
} catch (e) {
console.debug("Something went wrong when updating lns records: ", e);
console.debug("Something went wrong when updating ons records: ", e);
}
}
/*
Get the LNS records cached in this wallet.
Get the ONS records cached in this wallet.
*/
async lnsKnownNames() {
async onsKnownNames() {
try {
let params = {
decrypt: true,
include_expired: false
};
let data = await this.sendRPC("lns_known_names", params);
let data = await this.sendRPC("ons_known_names", params);
if (data.result && data.result.known_names) {
return data.result.known_names;
@ -1068,11 +1139,11 @@ export class WalletRPC {
}
/*
Renews an LNS (Lokinet) mapping, since they can expire
Renews an ONS (Lokinet) mapping, since they can expire
type can be:
lokinet_1y, lokinet_2y, lokinet_5y, lokinet_10y
*/
lnsRenewMapping(password, type, name) {
onsRenewMapping(password, type, name) {
let _name = name.trim().toLowerCase();
// the RPC accepts names with the .loki already appeneded only
@ -1089,7 +1160,7 @@ export class WalletRPC {
"sha512",
(err, password_hash) => {
if (err) {
this.sendGateway("set_lns_status", {
this.sendGateway("set_ons_status", {
code: -1,
i18n: "notification.errors.internalError",
sending: false
@ -1097,7 +1168,7 @@ export class WalletRPC {
return;
}
if (!this.isValidPasswordHash(password_hash)) {
this.sendGateway("set_lns_status", {
this.sendGateway("set_ons_status", {
code: -1,
i18n: "notification.errors.invalidPassword",
sending: false
@ -1110,12 +1181,12 @@ export class WalletRPC {
name: _name
};
this.sendRPC("lns_renew_mapping", params).then(data => {
this.sendRPC("ons_renew_mapping", params).then(data => {
if (data.hasOwnProperty("error")) {
let error =
data.error.message.charAt(0).toUpperCase() +
data.error.message.slice(1);
this.sendGateway("set_lns_status", {
this.sendGateway("set_ons_status", {
code: -1,
message: error,
sending: false
@ -1125,9 +1196,9 @@ export class WalletRPC {
this.purchasedNames[name.trim()] = type;
setTimeout(() => this.updateLocalLNSRecords(), 5000);
setTimeout(() => this.updateLocalONSRecords(), 5000);
this.sendGateway("set_lns_status", {
this.sendGateway("set_ons_status", {
code: 0,
i18n: "notification.positive.nameRenewed",
sending: false
@ -1138,21 +1209,21 @@ export class WalletRPC {
}
/*
Get our LNS record and update our wallet state with decrypted values.
Get our ONS record and update our wallet state with decrypted values.
This will return `null` if the record is not in our currently stored records.
*/
async decryptLNSRecord(type, name) {
async decryptONSRecord(type, name) {
let _type = type;
// type can initially be "lokinet_1y" etc. on a purchase
if (type.startsWith("lokinet")) {
_type = "lokinet";
}
try {
const record = await this.getLNSRecord(_type, name);
const record = await this.getONSRecord(_type, name);
if (!record) return null;
// Update our current records with the new decrypted record
const currentRecords = this.wallet_state.lnsRecords;
const currentRecords = this.wallet_state.onsRecords;
const isOurRecord = currentRecords.find(
c => c.name_hash === record.name_hash
);
@ -1167,7 +1238,7 @@ export class WalletRPC {
const params = {
names: [_record]
};
this.sendRPC("lns_add_known_names", params);
this.sendRPC("ons_add_known_names", params);
}
const newRecords = currentRecords.map(current => {
@ -1176,21 +1247,21 @@ export class WalletRPC {
}
return current;
});
this.wallet_state.lnsRecords = newRecords;
this.sendGateway("set_wallet_data", { lnsRecords: newRecords });
this.wallet_state.onsRecords = newRecords;
this.sendGateway("set_wallet_data", { onsRecords: newRecords });
return record;
} catch (e) {
console.debug("Something went wrong decrypting lns record: ", e);
console.debug("Something went wrong decrypting ons record: ", e);
return null;
}
}
/*
Get a LNS record associated with the given name
Get a ONS record associated with the given name
*/
async getLNSRecord(type, name) {
// We currently only support session and lokinet
const types = ["session", "lokinet"];
async getONSRecord(type, name) {
// We support session, wallet and lokinet
const types = ["session", "wallet", "lokinet"];
if (!types.includes(type)) return null;
if (!name || name.trim().length === 0) return null;
@ -1202,14 +1273,14 @@ export class WalletRPC {
fullName = fullName + ".loki";
}
const nameHash = await this.hashLNSName(type, lowerCaseName);
const nameHash = await this.hashONSName(type, lowerCaseName);
if (!nameHash) return null;
const record = await this.backend.daemon.getLNSRecord(nameHash);
const record = await this.backend.daemon.getONSRecord(nameHash);
if (!record || !record.encrypted_value) return null;
// Decrypt the value if possible
const value = await this.decryptLNSValue(
const value = await this.decryptONSValue(
type,
fullName,
record.encrypted_value
@ -1222,7 +1293,7 @@ export class WalletRPC {
};
}
async hashLNSName(type, name) {
async hashONSName(type, name) {
if (!type || !name) return null;
let fullName = name;
@ -1231,7 +1302,7 @@ export class WalletRPC {
}
try {
const data = await this.sendRPC("lns_hash_name", {
const data = await this.sendRPC("ons_hash_name", {
type,
name: fullName
});
@ -1245,12 +1316,12 @@ export class WalletRPC {
return (data.result && data.result.name) || null;
} catch (e) {
console.debug("Failed to hash lns name: ", e);
console.debug("Failed to hash ons name: ", e);
return null;
}
}
async decryptLNSValue(type, name, encrypted_value) {
async decryptONSValue(type, name, encrypted_value) {
if (!type || !name || !encrypted_value) return null;
let fullName = name;
@ -1259,7 +1330,7 @@ export class WalletRPC {
}
try {
const data = await this.sendRPC("lns_decrypt_value", {
const data = await this.sendRPC("ons_decrypt_value", {
type,
name: fullName,
encrypted_value
@ -1274,11 +1345,109 @@ export class WalletRPC {
return (data.result && data.result.value) || null;
} catch (e) {
console.debug("Failed to decrypt lns value: ", e);
console.debug("Failed to decrypt ons value: ", e);
return null;
}
}
async sign(data) {
// set to loading
this.sendGateway("set_sign_status", {
code: 0,
sending: true
});
try {
const rpcData = await this.sendRPC("sign", { data });
if (
!rpcData ||
rpcData.hasOwnProperty("error") ||
!rpcData.hasOwnProperty("result")
) {
const error = rpcData?.error?.message || "Unknown error";
this.sendGateway("set_sign_status", {
code: -1,
message: error,
sending: false
});
return;
}
const signature = rpcData.result.signature;
this.sendGateway("set_sign_status", {
code: 1,
sending: false,
signature: signature
});
return;
} catch (err) {
console.debug(`Failed to sign data: ${data} with error: ${err}`);
this.sendGateway("set_sign_status", {
code: -1,
message: err,
sending: false
});
}
}
async verify(data, address, signature) {
this.sendGateway("set_verify_status", {
code: 0,
sending: true
});
try {
const rpcData = await this.sendRPC("verify", {
data,
address,
signature
});
if (
!rpcData ||
rpcData.hasOwnProperty("error") ||
!rpcData.hasOwnProperty("result")
) {
let errorI18n = "";
const error = rpcData.error.message || "Unknown error";
if (error && error.includes("Invalid address")) {
errorI18n = "notification.errors.invalidAddress";
}
this.sendGateway("set_verify_status", {
code: -1,
message: "",
i18n: errorI18n || "Unknown error",
sending: false
});
return;
}
const good = rpcData.result.good;
if (good) {
// success
this.sendGateway("set_verify_status", {
code: 1,
sending: false,
i18n: "notification.positive.signatureVerified",
message: ""
});
} else {
// error
this.sendGateway("set_verify_status", {
code: -1,
sending: false,
i18n: "notification.errors.invalidSignature",
message: ""
});
}
return;
} catch (err) {
this.sendGateway("set_verify_status", {
code: -1,
message: err.toString(),
i18n: "",
sending: false
});
}
}
stake(password, amount, service_node_key, destination) {
crypto.pbkdf2(
password,
@ -1576,6 +1745,9 @@ export class WalletRPC {
// send address and tx fees before sending
// isSweepAll refers to if it's the sweep from service nodes page
transfer(password, amount, address, priority, isSweepAll) {
console.log(
"TODO sean remove this - wallet: " + JSON.stringify(this.wallet)
);
const cryptoCallback = (err, password_hash) => {
if (err) {
this.sendGateway("set_tx_status", {
@ -1676,7 +1848,7 @@ export class WalletRPC {
crypto.pbkdf2(password, this.auth[2], 1000, 64, "sha512", cryptoCallback);
}
purchaseLNS(password, type, name, value, owner, backupOwner) {
purchaseONS(password, type, name, value, owner, backupOwner) {
let _name = name.trim().toLowerCase();
const _owner = owner.trim() === "" ? null : owner;
const backup_owner = backupOwner.trim() === "" ? null : backupOwner;
@ -1696,7 +1868,7 @@ export class WalletRPC {
"sha512",
(err, password_hash) => {
if (err) {
this.sendGateway("set_lns_status", {
this.sendGateway("set_ons_status", {
code: -1,
i18n: "notification.errors.internalError",
sending: false
@ -1704,7 +1876,7 @@ export class WalletRPC {
return;
}
if (!this.isValidPasswordHash(password_hash)) {
this.sendGateway("set_lns_status", {
this.sendGateway("set_ons_status", {
code: -1,
i18n: "notification.errors.invalidPassword",
sending: false
@ -1720,12 +1892,12 @@ export class WalletRPC {
value
};
this.sendRPC("lns_buy_mapping", params).then(data => {
this.sendRPC("ons_buy_mapping", params).then(data => {
if (data.hasOwnProperty("error")) {
let error =
data.error.message.charAt(0).toUpperCase() +
data.error.message.slice(1);
this.sendGateway("set_lns_status", {
this.sendGateway("set_ons_status", {
code: -1,
message: error,
sending: false
@ -1736,9 +1908,9 @@ export class WalletRPC {
this.purchasedNames[name.trim()] = type;
// Fetch new records and then get the decrypted record for the one we just inserted
setTimeout(() => this.updateLocalLNSRecords(), 5000);
setTimeout(() => this.updateLocalONSRecords(), 5000);
this.sendGateway("set_lns_status", {
this.sendGateway("set_ons_status", {
code: 0,
i18n: "notification.positive.namePurchased",
sending: false
@ -1748,7 +1920,7 @@ export class WalletRPC {
);
}
updateLNSMapping(password, type, name, value, owner, backupOwner) {
updateONSMapping(password, type, name, value, owner, backupOwner) {
let _name = name.trim().toLowerCase();
const _owner = owner.trim() === "" ? null : owner;
const backup_owner = backupOwner.trim() === "" ? null : backupOwner;
@ -1768,7 +1940,7 @@ export class WalletRPC {
"sha512",
(err, password_hash) => {
if (err) {
this.sendGateway("set_lns_status", {
this.sendGateway("set_ons_status", {
code: -1,
i18n: "notification.errors.internalError",
sending: false
@ -1776,7 +1948,7 @@ export class WalletRPC {
return;
}
if (!this.isValidPasswordHash(password_hash)) {
this.sendGateway("set_lns_status", {
this.sendGateway("set_ons_status", {
code: -1,
i18n: "notification.errors.invalidPassword",
sending: false
@ -1792,12 +1964,12 @@ export class WalletRPC {
value
};
this.sendRPC("lns_update_mapping", params).then(data => {
this.sendRPC("ons_update_mapping", params).then(data => {
if (data.hasOwnProperty("error")) {
let error =
data.error.message.charAt(0).toUpperCase() +
data.error.message.slice(1);
this.sendGateway("set_lns_status", {
this.sendGateway("set_ons_status", {
code: -1,
message: error,
sending: false
@ -1808,11 +1980,11 @@ export class WalletRPC {
this.purchasedNames[name.trim()] = type;
// Fetch new records and then get the decrypted record for the one we just inserted
setTimeout(() => this.updateLocalLNSRecords(), 5000);
setTimeout(() => this.updateLocalONSRecords(), 5000);
// Optimistically update our record
const { lnsRecords } = this.wallet_state;
const newRecords = lnsRecords.map(record => {
const { onsRecords } = this.wallet_state;
const newRecords = onsRecords.map(record => {
if (
record.type === type &&
record.name &&
@ -1828,12 +2000,12 @@ export class WalletRPC {
return record;
});
this.wallet_state.lnsRecords = newRecords;
this.sendGateway("set_wallet_data", { lnsRecords: newRecords });
this.wallet_state.onsRecords = newRecords;
this.sendGateway("set_wallet_data", { onsRecords: newRecords });
this.sendGateway("set_lns_status", {
this.sendGateway("set_ons_status", {
code: 0,
i18n: "notification.positive.lnsRecordUpdated",
i18n: "notification.positive.onsRecordUpdated",
sending: false
});
});
@ -2004,7 +2176,10 @@ export class WalletRPC {
info: {
address: data[0].result.address,
balance: data[1].result.balance,
unlocked_balance: data[1].result.unlocked_balance
unlocked_balance: data[1].result.unlocked_balance,
accrued_balance: data[1].result.accrued_balance,
accrued_balance_next_payout:
data[1].result.accrued_balance_next_payout
// num_unspent_outputs: data[1].result.num_unspent_outputs
},
address_list: {
@ -2362,6 +2537,79 @@ export class WalletRPC {
);
}
exportTransfers(password, filename = null) {
crypto.pbkdf2(
password,
this.auth[2],
1000,
64,
"sha512",
(err, password_hash) => {
if (err) {
this.sendGateway("show_notification", {
type: "negative",
i18n: "notification.errors.internalError",
timeout: 2000
});
return;
}
if (!this.isValidPasswordHash(password_hash)) {
this.sendGateway("show_notification", {
type: "negative",
i18n: "notification.errors.invalidPassword",
timeout: 2000
});
return;
}
if (filename == null) {
filename = path.join(
this.wallet_data_dir,
"CSV",
this.wallet_state.name,
"transfers.csv"
);
} else {
filename = path.join(filename, "transfers.csv");
}
const onError = () =>
this.sendGateway("show_notification", {
type: "negative",
i18n: "notification.errors.exportTransfers",
timeout: 2000
});
this.sendRPC("export_transfers")
.then(data => {
if (
data.hasOwnProperty("error") ||
!data.hasOwnProperty("result")
) {
onError();
return;
}
if (data.result.data) {
fs.outputFileSync(filename, data.result.data);
this.sendGateway("show_notification", {
i18n: ["notification.positive.exportTransfers", { filename }],
timeout: 2000
});
} else {
this.sendGateway("show_notification", {
type: "warning",
textColor: "black",
i18n: "notification.warnings.noExportTransfers",
timeout: 2000
});
}
})
.catch(onError);
}
);
}
copyOldGuiWallets(wallets) {
this.sendGateway("set_old_gui_import_status", {
code: 1,
@ -2481,7 +2729,7 @@ export class WalletRPC {
return;
}
// Exclude all files without a keys extension
// Exclude all files without keys
if (path.extname(filename) !== ".keys") return;
const wallet_name = path.parse(filename).name;
@ -2490,7 +2738,8 @@ export class WalletRPC {
let wallet_data = {
name: wallet_name,
address: null,
password_protected: null
password_protected: null,
hardware_wallet: false
};
if (
@ -2519,6 +2768,12 @@ export class WalletRPC {
}
}
if (
fs.existsSync(path.join(this.wallet_dir, wallet_name + ".hwdev.txt"))
) {
wallet_data.hardware_wallet = true;
}
wallets.list.push(wallet_data);
} catch (e) {
// Something went wrong
@ -2688,14 +2943,16 @@ export class WalletRPC {
async closeWallet() {
clearInterval(this.heartbeat);
clearInterval(this.lnsHeartbeat);
clearInterval(this.onsHeartbeat);
this.wallet_state = {
open: false,
name: "",
password_hash: null,
balance: null,
unlocked_balance: null,
lnsRecords: []
accrued_balance: null,
accrued_balance_next_payout: null,
onsRecords: []
};
this.purchasedNames = {};

View File

@ -2,7 +2,7 @@
<q-dialog v-model="isVisible" maximized class="address-book-details">
<q-layout v-if="mode == 'edit' || mode == 'new'">
<q-header>
<q-toolbar color="dark" inverted>
<q-toolbar inverted>
<q-btn flat round dense icon="reply" @click="close()" />
<q-toolbar-title v-if="mode == 'new'">
{{ $t("strings.addAddressBookEntry") }}
@ -26,16 +26,15 @@
/>
</q-toolbar>
</q-header>
<q-page-container>
<q-page-container class="detail-page">
<div class="address-book-modal q-mx-md">
<LokiField
<OxenField
:label="$t('fieldLabels.address')"
:error="$v.newEntry.address.$error"
>
<q-input
v-model.trim="newEntry.address"
:placeholder="address_placeholder"
:dark="theme == 'dark'"
borderless
dense
@blur="$v.newEntry.address.$touch"
@ -47,26 +46,20 @@
:icon="newEntry.starred ? 'star' : 'star_border'"
@click="updateStarred"
/>
</LokiField>
<LokiField :label="$t('fieldLabels.name')">
<q-input
v-model.trim="newEntry.name"
:dark="theme == 'dark'"
borderless
dense
/>
</LokiField>
<LokiField :label="$t('fieldLabels.notes')" optional>
</OxenField>
<OxenField :label="$t('fieldLabels.name')">
<q-input v-model.trim="newEntry.name" borderless dense />
</OxenField>
<OxenField :label="$t('fieldLabels.notes')" optional>
<q-input
v-model="newEntry.description"
:placeholder="$t('placeholders.additionalNotes')"
type="textarea"
class="full-width text-area-loki"
:dark="theme == 'dark'"
class="full-width text-area-oxen"
borderless
dense
/>
</LokiField>
</OxenField>
<q-btn
v-if="mode == 'edit'"
@ -81,7 +74,7 @@
<q-layout v-else>
<q-header>
<q-toolbar color="dark" inverted>
<q-toolbar inverted>
<q-btn flat round dense icon="reply" @click="close()" />
<q-toolbar-title>
{{ $t("strings.addressBookDetails") }}
@ -106,6 +99,7 @@
<div class="layout-padding">
<template v-if="entry != null">
<AddressHeader
class="address-details"
:address="entry.address"
:title="entry.name"
:extra="
@ -140,7 +134,7 @@
import { mapState } from "vuex";
import AddressHeader from "components/address_header";
import TxList from "components/tx_list";
import LokiField from "components/loki_field";
import OxenField from "components/oxen_field";
import { address } from "src/validators/common";
import { required } from "vuelidate/lib/validators";
export default {
@ -148,7 +142,7 @@ export default {
components: {
AddressHeader,
TxList,
LokiField
OxenField
},
data() {
return {
@ -256,9 +250,12 @@ export default {
</script>
<style lang="scss">
.address-details {
color: #1f1c47;
}
.address-book-details {
.address-book-modal {
> .loki-field {
> .oxen-field {
margin-top: 16px;
}

View File

@ -7,11 +7,20 @@
<q-toolbar-title>
{{ $t("titles.addressDetails") }}
</q-toolbar-title>
<q-btn flat :label="$t('buttons.showQRCode')" @click="isQRCodeVisible = true" />
<q-btn class="q-ml-sm" color="primary" :label="$t('buttons.copyAddress')" @click="copyAddress()" />
<q-btn
flat
:label="$t('buttons.showQRCode')"
@click="isQRCodeVisible = true"
/>
<q-btn
class="q-ml-sm"
color="primary"
:label="$t('buttons.copyAddress')"
@click="copyAddress()"
/>
</q-toolbar>
</q-header>
<q-page-container>
<q-page-container class="detail-page">
<div class="layout-padding">
<template v-if="address != null">
<AddressHeader
@ -26,10 +35,10 @@
<div class="infoBox">
<div class="infoBoxContent">
<div class="text">
<span>{{ $t("strings.lokiBalance") }}</span>
<span>{{ $t("strings.oxenBalance") }}</span>
</div>
<div class="value">
<span><FormatLoki :amount="address.balance"/></span>
<span><FormatOxen :amount="address.balance"/></span>
</div>
</div>
</div>
@ -37,10 +46,12 @@
<div class="infoBox">
<div class="infoBoxContent">
<div class="text">
<span>{{ $t("strings.lokiUnlockedBalance") }}</span>
<span>{{ $t("strings.oxenUnlockedBalance") }}</span>
</div>
<div class="value">
<span><FormatLoki :amount="address.unlocked_balance"/></span>
<span
><FormatOxen :amount="address.unlocked_balance"
/></span>
</div>
</div>
</div>
@ -62,7 +73,7 @@
<div class="infoBox">
<div class="infoBoxContent">
<div class="text">
<span>{{ $t("strings.lokiBalance") }}</span>
<span>{{ $t("strings.oxenBalance") }}</span>
</div>
<div class="value"><span>N/A</span></div>
</div>
@ -71,7 +82,7 @@
<div class="infoBox">
<div class="infoBoxContent">
<div class="text">
<span>{{ $t("strings.lokiUnlockedBalance") }}</span>
<span>{{ $t("strings.oxenUnlockedBalance") }}</span>
</div>
<div class="value"><span>N/A</span></div>
</div>
@ -91,7 +102,9 @@
<div class="q-mt-sm">
<div class="non-selectable recent-transactions-wrapper">
<q-icon name="history" size="24px" />
<span class="vertical-middle q-ml-xs">{{ $t("strings.recentIncomingTransactionsToAddress") }}</span>
<span class="vertical-middle q-ml-xs">{{
$t("strings.recentIncomingTransactionsToAddress")
}}</span>
</div>
<div style="margin: 12px -16px;">
@ -108,12 +121,21 @@
</q-page-container>
</q-layout>
<template v-if="address != null">
<q-dialog v-model="isQRCodeVisible" minimized :content-class="'qr-code-modal'">
<q-dialog
v-model="isQRCodeVisible"
minimized
:content-class="'qr-code-modal'"
>
<q-card class="qr-code-card">
<div class="text-center q-mb-sm q-pa-md" style="background: white;">
<QrcodeVue ref="qr" :value="address.address" size="240"> </QrcodeVue>
<QrcodeVue ref="qr" :value="address.address" size="240">
</QrcodeVue>
<q-menu content-menu>
<q-list link separator style="min-width: 150px; max-height: 300px;">
<q-list
link
separator
style="min-width: 150px; max-height: 300px;"
>
<q-item v-close-popup @click.native="copyQR()">
<q-item-label :label="$t('menuItems.copyQR')" />
</q-item>
@ -124,7 +146,11 @@
</q-menu>
</div>
<q-card-actions>
<q-btn color="primary" :label="$t('buttons.close')" @click="isQRCodeVisible = false" />
<q-btn
color="primary"
:label="$t('buttons.close')"
@click="isQRCodeVisible = false"
/>
</q-card-actions>
</q-card>
</q-dialog>
@ -136,7 +162,7 @@
import { mapState } from "vuex";
const { clipboard, nativeImage } = require("electron");
import AddressHeader from "components/address_header";
import FormatLoki from "components/format_loki";
import FormatOxen from "components/format_oxen";
import QrcodeVue from "qrcode.vue";
import TxList from "components/tx_list";
export default {
@ -144,7 +170,7 @@ export default {
components: {
AddressHeader,
TxList,
FormatLoki,
FormatOxen,
QrcodeVue
},
data() {
@ -169,7 +195,9 @@ export default {
")";
}
const extra = this.address.used ? this.$t("strings.userUsedAddress") : this.$t("strings.userNotUsedAddress");
const extra = this.address.used
? this.$t("strings.userUsedAddress")
: this.$t("strings.userNotUsedAddress");
return {
title,

View File

@ -1,5 +1,5 @@
<template>
<div>
<div class="address-header-div">
<q-item-section class="self-start">
<q-item-label class="title non-selectable">{{ title }}</q-item-label>
<q-item-label class="row">
@ -87,17 +87,18 @@ export default {
</script>
<style lang="scss">
.title {
font-size: 18px;
margin-bottom: 4px;
color: white;
}
.extra {
margin-top: 8px;
color: white;
.address-header-div {
.title {
font-size: 20px;
color: #1f1c47;
}
.extra {
color: #1f1c47;
}
}
// is this even used?
.address-header {
padding: 0;
img {

View File

@ -1,9 +1,14 @@
<template>
<div class="check-transaction">
<div class="q-pa-md">
<div class="q-mb-lg description">{{ $t("strings.checkTransaction.description") }}</div>
<div class="q-mb-lg tab-desc">
{{ $t("strings.checkTransaction.description") }}
</div>
<div>
<LokiField :label="$t('fieldLabels.transactionId')" :error="$v.txid.$error">
<OxenField
:label="$t('fieldLabels.transactionId')"
:error="$v.txid.$error"
>
<q-input
v-model.trim="txid"
:dark="theme == 'dark'"
@ -12,8 +17,13 @@
dense
@blur="$v.txid.$touch"
/>
</LokiField>
<LokiField class="q-mt-md" :label="$t('fieldLabels.address')" :error="$v.address.$error" optional>
</OxenField>
<OxenField
class="q-mt-md"
:label="$t('fieldLabels.address')"
:error="$v.address.$error"
optional
>
<q-input
v-model.trim="address"
:dark="theme == 'dark'"
@ -22,8 +32,8 @@
dense
@blur="$v.address.$touch"
/>
</LokiField>
<LokiField class="q-mt-md" :label="$t('fieldLabels.message')" optional>
</OxenField>
<OxenField class="q-mt-md" :label="$t('fieldLabels.message')" optional>
<q-input
v-model.trim="message"
:dark="theme == 'dark'"
@ -31,8 +41,12 @@
borderless
dense
/>
</LokiField>
<LokiField class="q-mt-md" :label="$t('fieldLabels.signature')" :error="$v.signature.$error">
</OxenField>
<OxenField
class="q-mt-md"
:label="$t('fieldLabels.signature')"
:error="$v.signature.$error"
>
<q-input
v-model.trim="signature"
:dark="theme == 'dark'"
@ -40,10 +54,15 @@
borderless
dense
/>
</LokiField>
</OxenField>
<div class="submit-button">
<q-btn color="primary" :label="$t('buttons.check')" @click="check" />
<q-btn v-if="canClear" color="secondary" :label="$t('buttons.clear')" @click="clear" />
<q-btn
v-if="canClear"
color="secondsary="
:label="$t('buttons.clear')"
@click="clear"
/>
</div>
</div>
<div v-if="status.state.txid">
@ -52,21 +71,31 @@
<div>{{ status.state.txid }}</div>
</div>
<div class="q-mb-sm">
<div class="title">{{ $t("strings.checkTransaction.infoTitles.validTransaction") }}</div>
<div :class="status.state.good ? 'good' : 'bad'">{{ validTransaction }}</div>
<div class="title">
{{ $t("strings.checkTransaction.infoTitles.validTransaction") }}
</div>
<div :class="status.state.good ? 'good' : 'bad'">
{{ validTransaction }}
</div>
</div>
<div v-if="status.state.received != null" class="q-mb-sm">
<div class="title">{{ $t("strings.checkTransaction.infoTitles.received") }}</div>
<div class="title">
{{ $t("strings.checkTransaction.infoTitles.received") }}
</div>
<div>
<FormatLoki :amount="status.state.received" raw-value />
<FormatOxen :amount="status.state.received" raw-value />
</div>
</div>
<div v-if="status.state.in_pool != null" class="q-mb-sm">
<div class="title">{{ $t("strings.checkTransaction.infoTitles.inPool") }}</div>
<div class="title">
{{ $t("strings.checkTransaction.infoTitles.inPool") }}
</div>
<div>{{ status.state.in_pool }}</div>
</div>
<div v-if="status.state.confirmations != null" class="q-mb-sm">
<div class="title">{{ $t("strings.checkTransaction.infoTitles.confirmations") }}</div>
<div class="title">
{{ $t("strings.checkTransaction.infoTitles.confirmations") }}
</div>
<div>{{ status.state.confirmations }}</div>
</div>
</div>
@ -79,14 +108,14 @@ import { mapState } from "vuex";
import { required } from "vuelidate/lib/validators";
import { address } from "src/validators/common";
import { i18n } from "boot/i18n";
import LokiField from "components/loki_field";
import FormatLoki from "components/format_loki";
import OxenField from "components/oxen_field";
import FormatOxen from "components/format_oxen";
export default {
name: "CheckTransaction",
components: {
LokiField,
FormatLoki
OxenField,
FormatOxen
},
data() {
return {
@ -100,7 +129,12 @@ export default {
theme: state => state.gateway.app.config.appearance.theme,
status: state => state.gateway.check_transaction_status,
canClear() {
return this.txid !== "" || this.address !== "" || this.message !== "" || this.signature != "";
return (
this.txid !== "" ||
this.address !== "" ||
this.message !== "" ||
this.signature != ""
);
},
validTransaction() {
let key = this.status.state.good ? "yes" : "no";

View File

@ -1,11 +1,14 @@
<template>
<div class="prove-transaction">
<div class="q-pa-md">
<div class="q-mb-lg description">
<div class="q-mb-lg tab-desc">
{{ $t("strings.proveTransactionDescription") }}
</div>
<div>
<LokiField :label="$t('fieldLabels.transactionId')" :error="$v.txid.$error">
<OxenField
:label="$t('fieldLabels.transactionId')"
:error="$v.txid.$error"
>
<q-input
v-model.trim="txid"
:dark="theme == 'dark'"
@ -14,8 +17,13 @@
dense
@blur="$v.txid.$touch"
/>
</LokiField>
<LokiField class="q-mt-md" :label="$t('fieldLabels.address')" :error="$v.address.$error" optional>
</OxenField>
<OxenField
class="q-mt-md"
:label="$t('fieldLabels.address')"
:error="$v.address.$error"
optional
>
<q-input
v-model.trim="address"
:dark="theme == 'dark'"
@ -24,8 +32,8 @@
dense
@blur="$v.address.$touch"
/>
</LokiField>
<LokiField class="q-mt-md" :label="$t('fieldLabels.message')" optional>
</OxenField>
<OxenField class="q-mt-md" :label="$t('fieldLabels.message')" optional>
<q-input
v-model.trim="message"
:dark="theme == 'dark'"
@ -33,11 +41,25 @@
borderless
dense
/>
</LokiField>
</OxenField>
<div class="buttons submit-button">
<q-btn color="primary" :label="$t('buttons.generate')" @click="generate" />
<q-btn v-if="canClear" color="secondary" :label="$t('buttons.clear')" @click="clear" />
<q-btn v-if="status.state.signature" color="secondary" :label="$t('buttons.copySignature')" @click="copy" />
<q-btn
color="primary"
:label="$t('buttons.generate')"
@click="generate"
/>
<q-btn
v-if="canClear"
color="accent"
:label="$t('buttons.clear')"
@click="clear"
/>
<q-btn
v-if="status.state.signature"
color="secondary"
:label="$t('buttons.copySignature')"
@click="copy"
/>
</div>
</div>
<div v-if="status.state.signature" class="signature-wrapper">
@ -57,13 +79,13 @@
import { mapState } from "vuex";
import { required } from "vuelidate/lib/validators";
import { address } from "src/validators/common";
import LokiField from "components/loki_field";
import OxenField from "components/oxen_field";
import { clipboard } from "electron";
export default {
name: "ProveTransaction",
components: {
LokiField
OxenField
},
data() {
return {

View File

@ -0,0 +1,246 @@
<template>
<div class="sign-and-verify">
<div class="q-pa-md">
<div class="q-mb-lg tab-desc">
{{ $t("strings.signAndVerifyDescription") }}
</div>
<div v-if="is_view_only">
{{ $t("strings.cannotSign") }}
</div>
<div v-else>
<div class="text-h6 header">{{ $t("titles.advanced.sign") }}</div>
<div class="row justify-between items-end">
<OxenField :label="$t('fieldLabels.data')">
<q-input
v-model.trim="toSign"
:dark="theme == 'dark'"
borderless
dense
:placeholder="$t('placeholders.dataToSign')"
/>
</OxenField>
<div class="btn-wrapper q-ml-md q-py-sm">
<q-btn
color="primary"
:label="$t('buttons.sign')"
:loading="sign_status.sending"
:disable="!toSign"
@click="sign()"
/>
</div>
</div>
</div>
<div class="verify-heading text-h6 header">
{{ $t("titles.advanced.verify") }}
</div>
<div class="justify-between items-end">
<OxenField class="q-mt-md" :label="$t('fieldLabels.signature')">
<q-input
v-model.trim="signatureToVerify"
:dark="theme == 'dark'"
borderless
dense
:placeholder="$t('placeholders.signature')"
/>
</OxenField>
<OxenField class="q-mt-md" :label="$t('fieldLabels.data')">
<q-input
v-model.trim="unsignedData"
:dark="theme == 'dark'"
borderless
dense
:placeholder="$t('placeholders.unsignedData')"
/>
</OxenField>
<OxenField class="q-mt-md" :label="$t('fieldLabels.address')">
<q-input
v-model.trim="address"
:dark="theme == 'dark'"
borderless
dense
:placeholder="$t('placeholders.addressOfSigner')"
/>
</OxenField>
<div class="submit-button">
<q-btn
color="primary"
:label="$t('buttons.verify')"
:disable="!signatureToVerify || !unsignedData || !address"
@click="verify()"
/>
<q-btn
v-if="canClear"
:label="$t('buttons.clear')"
color="accent"
@click="clear"
/>
</div>
<SignatureDialog
:on-copy-signature="copySignature"
:on-copy-unsigned-data="copyUnsignedData"
:on-copy-address="copyAddress"
:on-close="closeDialog"
:signature="signature"
:unsigned-data="toSign"
:address="primary_address"
:show="!!signature"
/>
</div>
</div>
</div>
</template>
<script>
const { clipboard } = require("electron");
import OxenField from "components/oxen_field";
import SignatureDialog from "./signature_dialog";
import { mapState } from "vuex";
export default {
name: "SignAndVerify",
components: {
OxenField,
SignatureDialog
},
data() {
return {
toSign: "",
// entered by the user to verify
signatureToVerify: "",
unsignedData: "",
address: ""
};
},
computed: mapState({
theme: state => state.gateway.app.config.appearance.theme,
sign_status: state => state.gateway.sign_status,
verify_status: state => state.gateway.verify_status,
signature: state => state.gateway.sign_status.signature,
primary_address: state => state.gateway.wallet.info.address,
is_view_only: state => state.gateway.wallet.info.view_only,
canClear() {
const canClear =
this.signatureToVerify !== "" ||
this.address !== "" ||
this.unsignedData !== "";
return canClear;
}
}),
watch: {
sign_status: {
handler(val, old) {
if (val.code == old.code) return;
const { code, message } = val;
switch (code) {
case -1:
this.$q.notify({
type: "negative",
timeout: 3000,
message
});
break;
}
},
deep: true
},
verify_status: {
handler(val, old) {
if (val.code == old.code) return;
const { code, message, i18n } = val;
switch (code) {
case -1:
this.$q.notify({
type: "negative",
timeout: 3000,
message: i18n ? this.$t(i18n) : message
});
break;
case 1:
this.$q.notify({
type: "positive",
timeout: 3000,
message: i18n ? this.$t(i18n) : message
});
break;
}
},
deep: true
}
},
methods: {
sign() {
this.$gateway.send("wallet", "sign", { data: this.toSign });
},
verify() {
this.$gateway.send("wallet", "verify", {
address: this.address,
data: this.unsignedData,
signature: this.signatureToVerify
});
},
copySignature() {
clipboard.writeText(this.signature);
this.$q.notify({
type: "positive",
timeout: 2000,
message: this.$t("notification.positive.signatureCopied")
});
},
// copy from the dialog
copyUnsignedData() {
clipboard.writeText(this.toSign);
this.$q.notify({
type: "positive",
timeout: 2000,
message: this.$t("notification.positive.copied", { item: "Data" })
});
},
copyAddress() {
clipboard.writeText(this.primary_address);
this.$q.notify({
type: "positive",
timeout: 2000,
message: this.$t("notification.positive.addressCopied")
});
},
closeDialog() {
this.$store.commit("gateway/set_sign_status", {
signature: ""
});
},
clear() {
this.signatureToVerify = "";
this.unsignedData = "";
this.address = "";
}
}
};
</script>
<style lang="scss">
.description {
white-space: pre-line;
}
.sign-and-verify {
.height {
font-size: 0.9em;
}
.q-item {
cursor: default;
}
.oxen-field {
flex: 1;
}
}
.verify-heading {
margin-top: 24px;
}
.submit-button {
.q-btn:not(:first-child) {
margin-left: 8px;
}
margin-bottom: 12px;
}
</style>

View File

@ -0,0 +1,150 @@
<template>
<q-dialog v-model="show" persistent>
<q-card class="signature-dialog">
<q-card-section>
<div class="text-h6">{{ $t("dialog.signature.title") }}</div>
<div>
{{ $t("dialog.signature.message") }}
</div>
</q-card-section>
<q-card-section class="info">
<q-item class="signature row">
<div class="col">
<q-item-label class="labels">{{
$t("fieldLabels.signature")
}}</q-item-label>
{{ signature }}
</div>
<div class="col-auto">
<q-btn
color="primary"
icon="file_copy"
size="sm"
padding="xs"
style="width: 100%; margin-top: 16px"
@click="onCopySignature"
>
<q-tooltip
anchor="center left"
self="center right"
:offset="[5, 10]"
>
{{ $t("buttons.copySignature") }}
</q-tooltip>
</q-btn>
</div>
</q-item>
<q-item class="row unsigned-data">
<div class="col">
<q-item-label class="labels">{{
$t("fieldLabels.data")
}}</q-item-label>
{{ unsignedData }}
</div>
<div class="col-auto">
<q-btn
color="primary"
icon="file_copy"
size="sm"
padding="xs"
style="width: 100%; margin-top: 16px"
@click="onCopyUnsignedData"
>
<q-tooltip
anchor="center left"
self="center right"
:offset="[5, 10]"
>
{{ $t("buttons.copyData") }}
</q-tooltip>
</q-btn>
</div>
</q-item>
<q-item class="row address">
<div class="col">
<q-item-label class="labels">{{
$t("fieldLabels.address")
}}</q-item-label>
{{ address }}
</div>
<div class="col-auto">
<q-btn
color="primary"
class="vertical-middle"
icon="file_copy"
size="sm"
style="width: 100%; margin-top: 16px"
padding="xs"
@click="onCopyAddress"
>
<q-tooltip
anchor="center left"
self="center right"
:offset="[5, 10]"
>
{{ $t("buttons.copyAddress") }}
</q-tooltip>
</q-btn>
</div>
</q-item>
</q-card-section>
<q-card-actions align="right">
<q-btn flat :label="$t('buttons.close')" @click="onClose" />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script>
export default {
name: "SignatureDialog",
props: {
onCopySignature: {
type: Function,
required: true
},
onCopyAddress: {
type: Function,
required: true
},
onCopyUnsignedData: {
type: Function,
required: true
},
onClose: {
type: Function,
required: true
},
signature: {
type: String,
required: true
},
show: {
type: Boolean,
required: false,
default: true
},
address: {
type: String,
required: true
},
unsignedData: {
type: String,
required: true
}
}
};
</script>
<style lang="scss">
.info {
flex: 1;
word-break: break-all;
word-wrap: break-word;
.labels {
font-weight: bold;
margin-bottom: 4px;
}
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<q-dialog v-model="show" persistent>
<q-card class="confirm-tx-card" dark>
<q-card class="confirm-tx-card">
<q-card-section>
<div class="text-h6">{{ $t("dialog.confirmTransaction.title") }}</div>
</q-card-section>
@ -15,10 +15,10 @@
</div>
<br />
<span class="label">{{ $t("strings.transactions.amount") }}: </span>
{{ amount }} Loki
{{ amount }} OXEN
<br />
<span class="label">{{ $t("strings.transactions.fee") }}: </span>
{{ fee }} Loki
{{ fee }} OXEN
<br />
<span class="label"
>{{ $t("dialog.confirmTransaction.priority") }}:

View File

@ -0,0 +1,35 @@
<template>
<span> {{ value }} </span>
</template>
<script>
export default {
name: "FormatNextPayout",
props: {
payoutBlock: {
type: Number,
required: true
},
currentBlock: {
type: Number,
required: true
}
},
computed: {
value() {
console.log(this.payoutBlock);
console.log(this.currentBlock);
if (this.payoutBlock == 0) return "";
let blocks = this.payoutBlock - this.currentBlock;
console.log(this.currentBlock);
if (blocks > 720) return (blocks / 720).toFixed(1) + " days";
else if (blocks > 30) return (blocks / 30).toFixed(1) + " hours";
else return blocks * 2 + " minutes";
}
}
};
</script>
<style></style>

View File

@ -1,10 +1,10 @@
<template>
<span> {{ value }} LOKI </span>
<span> {{ value }} OXEN </span>
</template>
<script>
export default {
name: "FormatLoki",
name: "FormatOxen",
props: {
amount: {
type: Number,

View File

@ -8,7 +8,7 @@
v-for="option in options"
:key="option.value"
class="row justify-center items-center"
:color="lang === option.value ? 'primary' : 'secondary'"
:color="lang === option.value ? 'primary' : 'accent'"
size="md"
@click="setLanguage(option.value)"
>

View File

@ -1,66 +0,0 @@
<template>
<div class="lns-purchase">
<div class="q-mb-lg q-px-md q-pt-md">
<div class="description">
{{ $t("strings.lnsPurchaseDescription") }}
</div>
<div class="prices">
{{ $t("strings.lns.prices") }}
<table>
<tr>
<td>{{ $t("strings.lns.sessionID") }}:</td>
<td>15 LOKI</td>
</tr>
<tr>
<td>{{ $t("strings.lns.lokinetName1Year") }}:</td>
<td>15 LOKI</td>
</tr>
<tr>
<td>{{ $t("strings.lns.lokinetNameXYears", { years: 2 }) }}:</td>
<td>30 LOKI</td>
</tr>
<tr>
<td>{{ $t("strings.lns.lokinetNameXYears", { years: 5 }) }}:</td>
<td>60 LOKI</td>
</tr>
<tr>
<td>{{ $t("strings.lns.lokinetNameXYears", { years: 10 }) }}:</td>
<td>90 LOKI</td>
</tr>
</table>
</div>
<LNSInput ref="input" />
</div>
</div>
</template>
<script>
import LNSInput from "./lns_input";
export default {
name: "LNSPurchase",
components: {
LNSInput
},
methods: {
startUpdating(record) {
this.$refs.input.startUpdating(record);
},
startRenewing(record) {
this.$refs.input.startRenewing(record);
}
}
};
</script>
<style lang="scss">
.lns-purchase {
.description {
white-space: pre-line;
color: #cecece;
margin-bottom: 20px;
}
.prices {
color: #cecece;
}
}
</style>

View File

@ -2,7 +2,11 @@
<q-menu context-menu>
<q-list separator class="context-menu-list">
<div v-for="(item, index) in menuItems" :key="index">
<ContextMenuItem :action="item.action" :i18n="item.i18n" @clicked="clickedMenu(item, $event)" />
<ContextMenuItem
:action="item.action"
:i18n="item.i18n"
@clicked="clickedMenu(item, $event)"
/>
</div>
</q-list>
</q-menu>
@ -30,10 +34,4 @@ export default {
};
</script>
<style>
.context-menu-list {
min-width: 150px;
max-height: 300px;
color: white;
}
</style>
<style></style>

View File

@ -3,8 +3,15 @@
<q-btn class="menu" icon="menu" size="md" flat>
<q-menu>
<q-list separator class="menu-list">
<q-item v-if="!disableSwitchWallet" v-close-popup clickable @click.native="switchWallet">
<q-item-label header>{{ $t("menuItems.switchWallet") }}</q-item-label>
<q-item
v-if="!disableSwitchWallet"
v-close-popup
clickable
@click.native="switchWallet"
>
<q-item-label header>{{
$t("menuItems.switchWallet")
}}</q-item-label>
</q-item>
<q-item v-close-popup clickable @click.native="openSettings">
<q-item-label header>{{ $t("menuItems.settings") }}</q-item-label>
@ -22,26 +29,47 @@
<!-- TODO: Move this to it's own component -->
<q-dialog ref="aboutModal" minimized>
<div class="about-modal">
<img class="q-mb-md" src="loki.svg" height="42" />
<img class="q-mb-md" src="oxen.svg" height="42" />
<p class="q-my-sm">Wallet Version: v{{ version }}</p>
<p class="q-my-sm">Deaemon Version: v{{ daemonVersion }}</p>
<p class="q-my-sm">Copyright (c) 2018-2020, Loki Project</p>
<p class="q-my-sm">Daemon Version: v{{ daemonVersion }}</p>
<p class="q-my-sm">Copyright (c) 2018-2021, Oxen</p>
<p class="q-my-sm">Copyright (c) 2018, Ryo Currency Project</p>
<p class="q-my-sm">All rights reserved.</p>
<div class="q-mt-md q-mb-lg external-links">
<p>
<a href="#" @click="openExternal('https://loki.network/')">https://loki.network/</a>
<a href="#" @click="openExternal('https://oxen.io/')"
>https://oxen.io/</a
>
</p>
<p>
<a href="#" @click="openExternal('https://t.me/joinchat/DeNvR0JJ4JPn6TVSQjCsZQ')">Telegram</a>
<a href="#" @click="openExternal('https://t.me/Oxen_Community')"
>Telegram</a
>
-
<a href="#" @click="openExternal('https://discordapp.com/invite/67GXfD6')">Discord</a>
<a
href="#"
@click="openExternal('https://discordapp.com/invite/67GXfD6')"
>Discord</a
>
-
<a href="#" @click="openExternal('https://www.reddit.com/r/LokiProject/')">Reddit</a>
-
<a href="#" @click="openExternal('https://github.com/loki-project/loki-electron-gui-wallet')">Github</a>
<!-- readded once oxen subreddit is known -->
<!-- <a
href="#"
@click="openExternal('https://www.reddit.com/r/LokiProject/')"
>Reddit</a
>
- -->
<a
href="#"
@click="
openExternal(
'https://github.com/loki-project/loki-electron-gui-wallet'
)
"
>Github</a
>
</p>
</div>
<q-btn color="primary" label="Close" @click="showAbout(false)" />
@ -96,13 +124,19 @@ export default {
switchWallet() {
// If the rpc is syncing then we want to tell the user to restart
if (this.isRPCSyncing) {
this.$gateway.confirmClose(this.$t("dialog.switchWallet.restartMessage"), true);
this.$gateway.confirmClose(
this.$t("dialog.switchWallet.restartMessage"),
true
);
return;
}
// TODO: Remove this in hardfork 16
// This is a temporary work around for the issue where wallet rpc hangs after closing a wallet due to long polling still being active
this.$gateway.confirmClose(this.$t("dialog.switchWallet.restartWalletMessage"), true);
this.$gateway.confirmClose(
this.$t("dialog.switchWallet.restartWalletMessage"),
true
);
// Allow switching normally because rpc won't be blocked
// NB: If this is added back, must use the quasar v1 APIs
@ -142,8 +176,8 @@ export default {
<style lang="scss">
.about-modal {
padding: 25px;
background-color: $dark;
color: white;
background-color: white;
color: #1f1c47;
.external-links {
a {

View File

@ -1,22 +1,72 @@
<template>
<div class="wallet-settings">
<q-btn icon-right="more_vert" :label="$t('buttons.settings')" size="md" flat>
<q-btn
icon-right="more_vert"
:label="$t('buttons.settings')"
size="md"
flat
>
<q-menu anchor="bottom right" self="top right">
<q-list separator class="menu-list">
<q-item v-close-popup clickable :disabled="!is_ready" @click.native="getPrivateKeys()">
<q-item-label header>{{ $t("menuItems.showPrivateKeys") }}</q-item-label>
<q-item
v-close-popup
clickable
:disabled="!is_ready"
@click.native="getPrivateKeys()"
>
<q-item-label header>{{
$t("menuItems.showPrivateKeys")
}}</q-item-label>
</q-item>
<q-item v-close-popup clickable :disabled="!is_ready" @click.native="showModal('change_password')">
<q-item-label header>{{ $t("menuItems.changePassword") }}</q-item-label>
<q-item
v-close-popup
clickable
:disabled="!is_ready"
@click.native="showModal('change_password')"
>
<q-item-label header>{{
$t("menuItems.changePassword")
}}</q-item-label>
</q-item>
<q-item v-close-popup clickable :disabled="!is_ready" @click.native="showModal('rescan')">
<q-item-label header>{{ $t("menuItems.rescanWallet") }}</q-item-label>
<q-item
v-close-popup
clickable
:disabled="!is_ready"
@click.native="showModal('export_transfers')"
>
<q-item-label header>{{
$t("menuItems.exportTransfers")
}}</q-item-label>
</q-item>
<q-item v-close-popup clickable :disabled="!is_ready" @click.native="showModal('key_image')">
<q-item-label header>{{ $t("menuItems.manageKeyImages") }}</q-item-label>
<q-item
v-close-popup
clickable
:disabled="!is_ready"
@click.native="showModal('rescan')"
>
<q-item-label header>{{
$t("menuItems.rescanWallet")
}}</q-item-label>
</q-item>
<q-item v-close-popup clickable :disabled="!is_ready" @click.native="deleteWallet()">
<q-item-label header>{{ $t("menuItems.deleteWallet") }}</q-item-label>
<q-item
v-close-popup
clickable
:disabled="!is_ready"
@click.native="showModal('key_image')"
>
<q-item-label header>{{
$t("menuItems.manageKeyImages")
}}</q-item-label>
</q-item>
<q-item
v-close-popup
clickable
:disabled="!is_ready"
@click.native="deleteWallet()"
>
<q-item-label header>{{
$t("menuItems.deleteWallet")
}}</q-item-label>
</q-item>
</q-list>
</q-menu>
@ -24,10 +74,14 @@
<!-- Modals -->
<!-- PRIVATE KEY MODAL -->
<q-dialog v-model="modals.private_keys.visible" minimized class="private-key-modal" @hide="closePrivateKeys()">
<div class="modal">
<q-dialog
v-model="modals.private_keys.visible"
minimized
@hide="closePrivateKeys()"
>
<div class="modal private-key-modal">
<div class="modal-header">{{ $t("titles.privateKeys") }}</div>
<div class="q-ma-lg">
<div class="q-ma-md">
<template v-if="secret.mnemonic">
<h6 class="q-mb-xs q-mt-lg">
{{ $t("strings.seedWords") }}
@ -45,7 +99,11 @@
icon="file_copy"
@click="copyPrivateKey('mnemonic', $event)"
>
<q-tooltip anchor="center left" self="center right" :offset="[5, 10]">
<q-tooltip
anchor="center left"
self="center right"
:offset="[5, 10]"
>
{{ $t("menuItems.copySeedWords") }}
</q-tooltip>
</q-btn>
@ -68,7 +126,11 @@
icon="file_copy"
@click="copyPrivateKey('view_key', $event)"
>
<q-tooltip anchor="center left" self="center right" :offset="[5, 10]">
<q-tooltip
anchor="center left"
self="center right"
:offset="[5, 10]"
>
{{ $t("menuItems.copyViewKey") }}
</q-tooltip>
</q-btn>
@ -91,7 +153,11 @@
icon="file_copy"
@click="copyPrivateKey('spend_key', $event)"
>
<q-tooltip anchor="center left" self="center right" :offset="[5, 10]">
<q-tooltip
anchor="center left"
self="center right"
:offset="[5, 10]"
>
{{ $t("menuItems.copySpendKey") }}
</q-tooltip>
</q-btn>
@ -100,7 +166,11 @@
</template>
<div class="q-mt-lg">
<q-btn color="primary" :label="$t('buttons.close')" @click="hideModal('private_keys')" />
<q-btn
color="primary"
:label="$t('buttons.close')"
@click="hideModal('private_keys')"
/>
</div>
</div>
</div>
@ -108,50 +178,89 @@
<!-- RESCAN MODAL -->
<q-dialog v-model="modals.rescan.visible" minimized>
<div class="modal">
<div class="modal rescan-modal">
<div class="a-ma-lg modal-header">{{ $t("titles.rescanWallet") }}</div>
<div class="q-ma-lg">
<div class="q-ma-md">
<p>{{ $t("strings.rescanModalDescription") }}</p>
<div class="q-mt-lg">
<q-radio v-model="modals.rescan.type" val="full" :label="$t('fieldLabels.rescanFullBlockchain')" />
<q-radio
v-model="modals.rescan.type"
val="full"
:label="$t('fieldLabels.rescanFullBlockchain')"
/>
</div>
<div class="q-mt-sm">
<q-radio v-model="modals.rescan.type" val="spent" :label="$t('fieldLabels.rescanSpentOutputs')" />
<q-radio
v-model="modals.rescan.type"
val="spent"
:label="$t('fieldLabels.rescanSpentOutputs')"
/>
</div>
<div class="q-mt-xl text-right">
<q-btn flat class="q-mr-sm" :label="$t('buttons.close')" @click="hideModal('rescan')" />
<q-btn color="primary" :label="$t('buttons.rescan')" @click="rescanWallet()" />
<q-btn
flat
class="q-mr-sm"
:label="$t('buttons.close')"
@click="hideModal('rescan')"
/>
<q-btn
color="primary"
:label="$t('buttons.rescan')"
@click="rescanWallet()"
/>
</div>
</div>
</div>
</q-dialog>
<!-- KEY IMAGE MODAL -->
<q-dialog v-model="modals.key_image.visible" class="key-image-modal" minimized>
<q-dialog
v-model="modals.key_image.visible"
class="key-image-modal"
minimized
>
<div class="modal key-image-modal">
<div class="modal-header">
<!-- Export/Import key images -->
{{
$t("dialog.keyImages.title", {
type: $t(`dialog.keyImages.${modals.key_image.type.toLowerCase()}`)
type: $t(
`dialog.keyImages.${modals.key_image.type.toLowerCase()}`
)
})
}}
</div>
<div class="q-ma-lg">
<div class="q-ma-md">
<div class="row q-mb-md">
<div class="q-mr-xl">
<q-radio v-model="modals.key_image.type" val="Export" :label="$t('dialog.keyImages.export')" />
<q-radio
v-model="modals.key_image.type"
val="Export"
:label="$t('dialog.keyImages.export')"
/>
</div>
<div>
<q-radio v-model="modals.key_image.type" val="Import" :label="$t('dialog.keyImages.import')" />
<q-radio
v-model="modals.key_image.type"
val="Import"
:label="$t('dialog.keyImages.import')"
/>
</div>
</div>
<template v-if="modals.key_image.type == 'Export'">
<LokiField class="q-mt-lg" :label="$t('fieldLabels.keyImages.exportDirectory')" disable-hover>
<q-input v-model="modals.key_image.export_path" disable borderless />
<OxenField
class="q-mt-lg"
:label="$t('fieldLabels.keyImages.exportDirectory')"
disable-hover
>
<q-input
v-model="modals.key_image.export_path"
disable
borderless
/>
<input
id="keyImageExportPath"
ref="keyImageExportSelect"
@ -162,12 +271,22 @@
hidden
@change="setKeyImageExportPath"
/>
<q-btn color="secondary" @click="selectKeyImageExportPath">{{ $t("buttons.browse") }}</q-btn>
</LokiField>
<q-btn color="primary" @click="selectKeyImageExportPath">{{
$t("buttons.browse")
}}</q-btn>
</OxenField>
</template>
<template v-if="modals.key_image.type == 'Import'">
<LokiField class="q-mt-lg" :label="$t('fieldLabels.keyImages.importFile')" disable-hover>
<q-input v-model="modals.key_image.import_path" disable borderless />
<OxenField
class="q-mt-lg"
:label="$t('fieldLabels.keyImages.importFile')"
disable-hover
>
<q-input
v-model="modals.key_image.import_path"
disable
borderless
/>
<input
id="keyImageImportPath"
ref="keyImageImportSelect"
@ -176,12 +295,19 @@
hidden
@change="setKeyImageImportPath"
/>
<q-btn color="secondary" @click="selectKeyImageImportPath">{{ $t("buttons.browse") }}</q-btn>
</LokiField>
<q-btn color="primary" @click="selectKeyImageImportPath">{{
$t("buttons.browse")
}}</q-btn>
</OxenField>
</template>
<div class="q-mt-lg text-right">
<q-btn flat class="q-mr-sm" :label="$t('buttons.close')" @click="hideModal('key_image')" />
<q-btn
flat
class="q-mr-sm"
:label="$t('buttons.close')"
@click="hideModal('key_image')"
/>
<q-btn
color="primary"
:label="$t('buttons.' + modals.key_image.type.toLowerCase())"
@ -193,33 +319,98 @@
</q-dialog>
<!-- CHANGE PASSWORD MODAL -->
<q-dialog v-model="modals.change_password.visible" minimized @hide="clearChangePassword()">
<q-dialog
v-model="modals.change_password.visible"
minimized
@hide="clearChangePassword()"
>
<div class="modal password-modal">
<div class="modal-header">{{ $t("titles.changePassword") }}</div>
<div class="q-ma-lg">
<div class="q-ma-md">
<q-input
v-model="modals.change_password.old_password"
type="password"
:label="$t('fieldLabels.oldPassword')"
:dark="theme == 'dark'"
/>
<q-input
v-model="modals.change_password.new_password"
type="password"
:label="$t('fieldLabels.newPassword')"
:dark="theme == 'dark'"
/>
<q-input
v-model="modals.change_password.new_password_confirm"
type="password"
:label="$t('fieldLabels.confirmNewPassword')"
:dark="theme == 'dark'"
/>
<div class="q-mt-xl text-right">
<q-btn flat class="q-mr-sm" :label="$t('buttons.close')" @click="hideModal('change_password')" />
<q-btn color="primary" :label="$t('buttons.change')" @click="doChangePassword()" />
<q-btn
flat
class="q-mr-sm"
:label="$t('buttons.close')"
@click="hideModal('change_password')"
/>
<q-btn
color="primary"
:label="$t('buttons.change')"
@click="doChangePassword()"
/>
</div>
</div>
</div>
</q-dialog>
<!-- EXPORT TRANSFERS MODAL -->
<q-dialog
v-model="modals.export_transfers.visible"
class="export-transfers-modal"
minimized
>
<div class="modal export-transfers-modal">
<div class="modal-header">
<!-- Export Transfers as CSV -->
{{ $t("dialog.exportTransfers.title") }}
</div>
<div class="q-ma-md">
<template>
<OxenField
class="q-mt-lg"
:label="$t('fieldLabels.exportTransfers.exportDirectory')"
disable-hover
>
<q-input
v-model="modals.export_transfers.export_path"
disable
borderless
/>
<input
id="exportTransfersExportPath"
ref="exportTransfersExportSelect"
class="export-transfers-path"
type="file"
webkitdirectory
directory
hidden
@change="setExportTransfersExportPath"
/>
<q-btn color="primary" @click="selectExportTransfersExportPath">{{
$t("buttons.browse")
}}</q-btn>
</OxenField>
</template>
<div class="q-mt-lg text-right">
<q-btn
flat
class="q-mr-sm"
:label="$t('buttons.close')"
@click="hideModal('export_transfers')"
/>
<q-btn
color="primary"
:label="$t('buttons.export')"
@click="doExportTransfers()"
/>
</div>
</div>
</div>
@ -231,12 +422,12 @@
const { clipboard } = require("electron");
import { mapState } from "vuex";
import WalletPassword from "src/mixins/wallet_password";
import LokiField from "components/loki_field";
import OxenField from "components/oxen_field";
export default {
name: "WalletSettings",
components: {
LokiField
OxenField
},
mixins: [WalletPassword],
data() {
@ -255,6 +446,10 @@ export default {
export_path: "",
import_path: ""
},
export_transfers: {
visible: false,
export_path: ""
},
change_password: {
visible: false,
old_password: "",
@ -307,8 +502,22 @@ export default {
},
created() {
const path = require("upath");
this.modals.key_image.export_path = path.join(this.wallet_data_dir, "images", this.info.name);
this.modals.key_image.import_path = path.join(this.wallet_data_dir, "images", this.info.name, "key_image_export");
this.modals.key_image.export_path = path.join(
this.wallet_data_dir,
"images",
this.info.name
);
this.modals.key_image.import_path = path.join(
this.wallet_data_dir,
"images",
this.info.name,
"key_image_export"
);
this.modals.export_transfers.export_path = path.join(
this.wallet_data_dir,
"CSV",
this.info.name
);
},
methods: {
showModal(which) {
@ -355,8 +564,7 @@ export default {
ok: {
label: this.$t("dialog.buttons.ok"),
color: "primary"
},
dark: this.theme === "dark"
}
})
.onDismiss(() => null)
.onCancel(() => null)
@ -379,8 +587,11 @@ export default {
label: this.$t("dialog.showPrivateKeys.ok"),
color: "primary"
},
dark: this.theme == "dark",
color: this.theme == "dark" ? "white" : "dark"
cancel: {
color: "tertiary",
flat: true
},
color: "white"
});
passwordDialog
.onOk(password => {
@ -418,10 +629,8 @@ export default {
},
cancel: {
flat: true,
label: this.$t("dialog.buttons.cancel"),
color: this.theme == "dark" ? "white" : "dark"
},
dark: this.theme == "dark"
label: this.$t("dialog.buttons.cancel")
}
})
.onOk(() => {
this.$gateway.send("wallet", "rescan_blockchain");
@ -444,10 +653,18 @@ export default {
setKeyImageImportPath(file) {
this.modals.key_image.import_path = file.target.files[0].path;
},
selectExportTransfersExportPath() {
this.$refs.exportTransfersExportSelect.click();
},
setExportTransfersExportPath(file) {
this.modals.export_transfers.export_path = file.target.files[0].path;
},
async doKeyImages() {
this.hideModal("key_image");
const type = this.$t(`dialog.keyImages.${this.modals.key_image.type.toLowerCase()}`);
const type = this.$t(
`dialog.keyImages.${this.modals.key_image.type.toLowerCase()}`
);
let passwordDialog = await this.showPasswordConfirmation({
title: this.$t("dialog.keyImages.title", { type }),
@ -458,8 +675,11 @@ export default {
label: type.toLocaleUpperCase(this.locale),
color: "primary"
},
dark: this.theme == "dark",
color: this.theme == "dark" ? "white" : "dark"
color: this.theme == "dark" ? "dark" : "white",
cancel: {
color: "tertiary",
flat: true
}
});
passwordDialog
.onOk(password => {
@ -482,7 +702,8 @@ export default {
doChangePassword() {
let old_password = this.modals.change_password.old_password;
let new_password = this.modals.change_password.new_password;
let new_password_confirm = this.modals.change_password.new_password_confirm;
let new_password_confirm = this.modals.change_password
.new_password_confirm;
if (new_password == old_password) {
this.$q.notify({
@ -509,6 +730,34 @@ export default {
this.modals.change_password.new_password = "";
this.modals.change_password.new_password_confirm = "";
},
async doExportTransfers() {
this.hideModal("export_transfers");
let passwordDialog = await this.showPasswordConfirmation({
title: this.$t("dialog.exportTransfers.title"),
noPasswordMessage: this.$t("dialog.exportTransfers.message"),
ok: {
label: "export".toLocaleUpperCase(this.locale),
color: "primary"
},
color: this.theme == "dark" ? "dark" : "white",
cancel: {
color: "tertiary",
flat: true
}
});
passwordDialog
.onOk(password => {
// if no password set
password = password || "";
this.$gateway.send("wallet", "export_transfers", {
password: password,
path: this.modals.export_transfers.export_path
});
})
.onCancel(() => {})
.onDismiss(() => {});
},
deleteWallet() {
if (!this.is_ready) return;
this.$q
@ -524,7 +773,7 @@ export default {
label: this.$t("dialog.buttons.cancel"),
color: this.theme == "dark" ? "white" : "dark"
},
dark: this.theme == "dark"
color: "#1F1C47"
})
.onOk(async () => {
const hasPassword = await this.hasPassword();
@ -569,17 +818,20 @@ export default {
};
</script>
.menu-list { }
<style lang="scss">
.wallet-settings {
.q-btn {
color: white;
.password-modal {
min-width: 400px;
background: white;
color: #1f1c47;
> * {
color: #1f1c47;
}
}
.password-modal {
min-width: 400px;
.rescan-modal {
background: white;
color: #1f1c47;
}
.image-path {
@ -588,8 +840,25 @@ export default {
}
.key-image-modal {
color: #1f1c47;
background: white;
label * {
color: #cecece !important;
color: #1f1c47 !important;
text-overflow: ellipsis;
overflow: hidden;
}
input {
overflow: ellipsis;
}
}
.export-transfers-modal {
color: #1f1c47;
background: white;
min-width: 500px;
label * {
color: #1f1c47 !important;
text-overflow: ellipsis;
overflow: hidden;
}
@ -598,7 +867,15 @@ export default {
}
}
.export-transfers-path {
opacity: 0;
overflow: hidden;
}
.private-key-modal {
background: white;
color: #1f1c47;
.copy-btn {
margin-left: 8px;
}
@ -608,7 +885,7 @@ export default {
min-width: 400px;
width: 45vw;
.loki-field {
.oxen-field {
flex: 1;
}
}

View File

@ -1,6 +1,6 @@
<template>
<div class="lns-input">
<LNSInputForm
<div class="ons-input">
<ONSInputForm
ref="form"
:submit-label="submit_label"
:disable-name="updating || renewing"
@ -11,7 +11,7 @@
@onSubmit="onSubmit"
@onClear="onClear"
/>
<q-inner-loading :showing="lns_status.sending" :dark="theme == 'dark'">
<q-inner-loading :showing="ons_status.sending" :dark="theme == 'dark'">
<q-spinner color="primary" size="30" />
</q-inner-loading>
</div>
@ -19,14 +19,14 @@
<script>
import { mapState } from "vuex";
import LNSInputForm from "./lns_input_form";
import ONSInputForm from "./ons_input_form";
import WalletPassword from "src/mixins/wallet_password";
const objectAssignDeep = require("object-assign-deep");
export default {
name: "LNSInput",
name: "ONSInput",
components: {
LNSInputForm
ONSInputForm
},
mixins: [WalletPassword],
data() {
@ -37,10 +37,10 @@ export default {
},
computed: mapState({
theme: state => state.gateway.app.config.appearance.theme,
lns_status: state => state.gateway.lns_status,
ons_status: state => state.gateway.ons_status,
unlocked_balance: state => state.gateway.wallet.info.unlocked_balance,
disable_submit_button() {
const minBalance = this.updating ? 0.05 : 21;
const minBalance = this.updating ? 0.05 : 7.1;
return this.unlocked_balance < minBalance * 1e9;
},
submit_label() {
@ -55,7 +55,7 @@ export default {
}),
watch: {
lns_status: {
ons_status: {
handler(val, old) {
if (val.code == old.code) return;
const { code, message } = val;
@ -119,28 +119,27 @@ export default {
};
let passwordDialog = await this.showPasswordConfirmation({
title: this.$t("dialog.lnsUpdate.title"),
noPasswordMessage: this.$t("dialog.lnsUpdate.message"),
title: this.$t("dialog.onsUpdate.title"),
noPasswordMessage: this.$t("dialog.onsUpdate.message"),
ok: {
label: this.$t("dialog.lnsUpdate.ok"),
label: this.$t("dialog.onsUpdate.ok"),
color: "primary"
},
dark: this.theme == "dark",
color: this.theme == "dark" ? "white" : "dark"
color: "#1F1C47"
});
passwordDialog
.onOk(password => {
// if no password set
password = password || "";
this.$store.commit("gateway/set_lns_status", {
this.$store.commit("gateway/set_ons_status", {
code: 1,
message: "Sending transaction",
sending: true
});
const lns = objectAssignDeep.noMutate(updatedRecord, {
const ons = objectAssignDeep.noMutate(updatedRecord, {
password
});
this.$gateway.send("wallet", "update_lns_mapping", lns);
this.$gateway.send("wallet", "update_ons_mapping", ons);
})
.onDismiss(() => {})
.onCancel(() => {});
@ -152,23 +151,21 @@ export default {
ok: {
label: this.$t("dialog.purchase.ok"),
color: "primary"
},
dark: this.theme == "dark",
color: this.theme == "dark" ? "white" : "dark"
}
});
passwordDialog
.onOk(password => {
// if no password set
password = password || "";
this.$store.commit("gateway/set_lns_status", {
this.$store.commit("gateway/set_ons_status", {
code: 1,
message: "Sending transaction",
sending: true
});
const lns = objectAssignDeep.noMutate(record, {
const ons = objectAssignDeep.noMutate(record, {
password
});
this.$gateway.send("wallet", "purchase_lns", lns);
this.$gateway.send("wallet", "purchase_ons", ons);
})
.onDismiss(() => {})
.onCancel(() => {});
@ -188,7 +185,7 @@ export default {
.onOk(password => {
// if no password set
password = password || "";
this.$store.commit("gateway/set_lns_status", {
this.$store.commit("gateway/set_ons_status", {
code: 1,
message: "Sending renew mapping transaction",
sending: true
@ -198,7 +195,7 @@ export default {
name: record.name,
password
};
this.$gateway.send("wallet", "lns_renew_mapping", params);
this.$gateway.send("wallet", "ons_renew_mapping", params);
})
.onDismiss(() => {})
.onCancel(() => {});

View File

@ -1,23 +1,22 @@
<template>
<div class="lns-input-form">
<div class="ons-input-form">
<!-- Type -->
<div class="col q-mt-sm">
<LokiField :label="$t('fieldLabels.lnsType')" :disable="updating">
<OxenField :label="$t('fieldLabels.onsType')" :disable="updating">
<q-select
v-model.trim="record.type"
emit-value
map-options
:options="renewing ? lokinetOptions : typeOptions"
:dark="theme == 'dark'"
:disable="updating"
borderless
dense
/>
</LokiField>
</OxenField>
</div>
<!-- Name -->
<div class="col q-mt-sm">
<LokiField
<OxenField
:label="$t('fieldLabels.name')"
:disable="disableName"
:error="$v.record.name.$error"
@ -25,19 +24,21 @@
<q-input
v-model.trim="record.name"
:dark="theme == 'dark'"
:placeholder="$t('placeholders.lnsName')"
:placeholder="$t('placeholders.onsName')"
:disable="disableName"
borderless
dense
:suffix="record.type === 'session' ? '' : '.loki'"
:suffix="
record.type === 'session' || record.type === 'wallet' ? '' : '.loki'
"
@blur="$v.record.name.$touch"
/>
</LokiField>
</OxenField>
</div>
<!-- Value (Session ID, Wallet Address or .loki address) -->
<div class="col q-mt-sm">
<LokiField
<OxenField
class="q-mt-md"
:label="value_field_label"
:error="$v.record.value.$error"
@ -49,15 +50,17 @@
borderless
dense
:disable="renewing"
:suffix="record.type === 'session' ? '' : '.loki'"
:suffix="
record.type === 'session' || record.type === 'wallet' ? '' : '.loki'
"
@blur="$v.record.value.$touch"
/>
</LokiField>
</OxenField>
</div>
<!-- Owner -->
<div class="col q-mt-sm">
<LokiField
<OxenField
class="q-mt-md"
:label="$t('fieldLabels.owner')"
:error="$v.record.owner.$error"
@ -72,12 +75,12 @@
:disable="renewing"
@blur="$v.record.owner.$touch"
/>
</LokiField>
</OxenField>
</div>
<!-- Backup owner -->
<div class="col q-mt-sm">
<LokiField
<OxenField
class="q-mt-md"
:label="$t('fieldLabels.backupOwner')"
:error="$v.record.backup_owner.$error"
@ -86,13 +89,13 @@
<q-input
v-model.trim="record.backup_owner"
:dark="theme == 'dark'"
:placeholder="$t('placeholders.lnsBackupOwner')"
:placeholder="$t('placeholders.onsBackupOwner')"
:disable="renewing"
borderless
dense
@blur="$v.record.backup_owner.$touch"
/>
</LokiField>
</OxenField>
</div>
<div class="buttons">
<q-btn
@ -103,7 +106,7 @@
/>
<q-btn
v-if="showClearButton"
color="secondary"
color="accent"
:label="$t('buttons.clear')"
@click="clear()"
/>
@ -120,13 +123,13 @@ import {
lokinet_name,
session_name
} from "src/validators/common";
import LokiField from "components/loki_field";
import OxenField from "components/oxen_field";
import WalletPassword from "src/mixins/wallet_password";
export default {
name: "LNSInputForm",
name: "ONSInputForm",
components: {
LokiField
OxenField
},
mixins: [WalletPassword],
props: {
@ -166,28 +169,31 @@ export default {
},
data() {
let sessionOptions = [
{ label: this.$t("strings.lns.sessionID"), value: "session" }
{ label: this.$t("strings.ons.sessionID"), value: "session" }
];
let walletOptions = [
{ label: this.$t("strings.ons.wallet"), value: "wallet" }
];
let lokinetOptions = [
{ label: this.$t("strings.lns.lokinetName1Year"), value: "lokinet_1y" },
{ label: this.$t("strings.ons.lokinetName1Year"), value: "lokinet_1y" },
{
label: this.$t("strings.lns.lokinetNameXYears", { years: 2 }),
label: this.$t("strings.ons.lokinetNameXYears", { years: 2 }),
value: "lokinet_2y"
},
{
label: this.$t("strings.lns.lokinetNameXYears", { years: 5 }),
label: this.$t("strings.ons.lokinetNameXYears", { years: 5 }),
value: "lokinet_5y"
},
{
label: this.$t("strings.lns.lokinetNameXYears", { years: 10 }),
label: this.$t("strings.ons.lokinetNameXYears", { years: 10 }),
value: "lokinet_10y"
}
];
let typeOptions = [...sessionOptions, ...lokinetOptions];
let typeOptions = [...sessionOptions, ...walletOptions, ...lokinetOptions];
const initialRecord = {
// Lokinet 1 year is valid on renew or purchase
type: typeOptions[1].value,
type: typeOptions[2].value,
name: "",
value: "",
owner: "",
@ -208,6 +214,8 @@ export default {
value_field_label() {
if (this.record.type === "session") {
return this.$t("fieldLabels.sessionId");
} else if (this.record.type === "wallet") {
return this.$t("fieldLabels.walletAddress");
} else {
return this.$t("fieldLabels.lokinetFullAddress");
}
@ -232,6 +240,8 @@ export default {
value_placeholder() {
if (this.record.type === "session") {
return this.$t("placeholders.sessionId");
} else if (this.record.type === "wallet") {
return this.$t("placeholders.walletAddress");
} else {
return this.$t("placeholders.lokinetFullAddress");
}
@ -334,7 +344,10 @@ export default {
const submitRecord = {
...this.record,
name: this.record.name.toLowerCase(),
value: this.record.value.toLowerCase()
value:
this.record.type === "wallet"
? this.record.value
: this.record.value.toLowerCase()
};
// Send up the submission with the record
this.$emit("onSubmit", submitRecord);
@ -357,7 +370,7 @@ export default {
if (this.record.type === "session") {
return session_name(_value);
} else {
// shortened lokinet LNS name
// shortened lokinet ONS name
return lokinet_name(_value);
}
}
@ -373,6 +386,8 @@ export default {
const _value = value.toLowerCase();
if (this.record.type === "session") {
return session_id(_value);
} else if (this.record.type === "wallet") {
return this.isAddress(value);
} else {
// full lokinet address
return lokinet_address(_value);
@ -390,7 +405,7 @@ export default {
</script>
<style lang="scss">
.lns-input-form {
.ons-input-form {
.buttons {
margin-top: 6px;

View File

@ -1,21 +1,21 @@
<template>
<div class="my-lns">
<div class="my-ons">
<div class="q-px-md q-pt-md">
<div class="description">
{{ $t("strings.lnsDescription") }}
<div class="tab-desc">
{{ $t("strings.onsDescription") }}
</div>
<LNSRecords @onUpdate="onUpdate" @onRenew="onRenew" />
<ONSRecords @onUpdate="onUpdate" @onRenew="onRenew" />
</div>
</div>
</template>
<script>
import LNSRecords from "./lns_records";
import ONSRecords from "./ons_records";
export default {
name: "MyLNS",
name: "MyONS",
components: {
LNSRecords
ONSRecords
},
methods: {
onUpdate(record) {
@ -29,7 +29,7 @@ export default {
</script>
<style lang="scss">
.my-lns {
.my-ons {
.description {
white-space: pre-line;
color: #cecece;

View File

@ -0,0 +1,72 @@
<template>
<div class="ons-purchase">
<div class="q-mb-lg q-px-md q-pt-md">
<div class="tab-desc">
{{ $t("strings.onsPurchaseDescription") }}
</div>
<div class="prices">
{{ $t("strings.ons.prices") }}
<table>
<tr>
<td>{{ $t("strings.ons.sessionID") }}:</td>
<td>7 OXEN</td>
</tr>
<tr>
<td>{{ $t("strings.ons.wallet") }}:</td>
<td>7 OXEN</td>
</tr>
<tr>
<td>{{ $t("strings.ons.lokinetName1Year") }}:</td>
<td>7 OXEN</td>
</tr>
<tr>
<td>{{ $t("strings.ons.lokinetNameXYears", { years: 2 }) }}:</td>
<td>14 OXEN</td>
</tr>
<tr>
<td>{{ $t("strings.ons.lokinetNameXYears", { years: 5 }) }}:</td>
<td>28 OXEN</td>
</tr>
<tr>
<td>{{ $t("strings.ons.lokinetNameXYears", { years: 10 }) }}:</td>
<td>42 OXEN</td>
</tr>
</table>
</div>
<ONSInput ref="input" />
</div>
</div>
</template>
<script>
import ONSInput from "./ons_input";
export default {
name: "ONSPurchase",
components: {
ONSInput
},
methods: {
startUpdating(record) {
this.$refs.input.startUpdating(record);
},
startRenewing(record) {
this.$refs.input.startRenewing(record);
}
}
};
</script>
<style lang="scss">
.ons-purchase {
.description {
white-space: pre-line;
// oxen-navy
color: #1f1c47;
}
.prices {
// oxen-navy
margin-top: 20px;
color: #1f1c47;
}
}
</style>

View File

@ -1,9 +1,9 @@
<template>
<q-list link no-border :dark="theme == 'dark'" class="lns-record-list">
<q-list link no-border class="ons-record-list">
<q-item
v-for="record in recordList"
:key="record.name_hash"
class="loki-list-item"
class="oxen-list-item"
>
<q-item-section class="type" avatar>
<q-icon :name="isLocked(record) ? 'lock' : 'lock_open'" size="24px" />
@ -12,7 +12,9 @@
<q-item-label :class="bindClass(record)">{{
isLocked(record) ? record.name_hash : record.name
}}</q-item-label>
<q-item-label v-if="!isLocked(record)">{{ record.value }}</q-item-label>
<q-item-label v-if="!isLocked(record)" class="truncate-item">{{
record.value
}}</q-item-label>
</q-item-section>
<q-item-section side class="height">
<template v-if="isLocked(record)">{{
@ -22,13 +24,13 @@
<q-item-section>
<div class="row update-renew-buttons">
<q-btn
color="secondary"
color="primary"
:label="$t('buttons.update')"
@click="onUpdate(record)"
/>
<q-btn
v-if="isLokinet"
color="secondary"
color="primary"
:label="$t('buttons.renew')"
@click="onRenew(record)"
/>
@ -37,7 +39,7 @@
</template>
</q-item-section>
<q-item-section v-if="!isLocked(record)" side>
<span v-if="record.type === 'session'">{{
<span v-if="record.type === 'session' || record.type === 'wallet'">{{
record.update_height | blockHeight
}}</span>
<span v-else class="lokinet-expiration">{{
@ -67,7 +69,7 @@ import ContextMenu from "components/menus/contextmenu";
const { clipboard } = require("electron");
export default {
name: "LNSRecordList",
name: "ONSRecordList",
components: {
ContextMenu
},
@ -108,7 +110,7 @@ export default {
this.$emit("onRenew", record);
},
copyNameI18nLabel(record) {
if (record.type === "session") {
if (record.type === "session" || record.type === "wallet") {
return "menuItems.copyName";
} else {
return "menuItems.copyLokinetName";
@ -146,6 +148,9 @@ export default {
if (record.type === "session") {
message = this.$t("notification.positive.sessionIdCopied");
}
if (record.type === "wallet") {
message = this.$t("notification.positive.walletCopied");
}
this.copy(record.value, message);
},
copy(value, message) {
@ -161,36 +166,4 @@ export default {
};
</script>
<style lang="scss">
.lokinet-expiration {
width: 180px;
}
.lns-record-list {
.q-item {
cursor: pointer;
background: #313131;
-webkit-transition: background-color 0.2s ease-in;
transition: background-color 0.2s ease-in;
border-radius: 3px;
+ .q-item {
margin-top: 10px;
}
}
.q-item-sublabel {
color: #313131;
}
.q-item:hover {
background: rgba(117, 117, 117, 0.3);
}
}
.update-renew-buttons {
.q-btn:not(:first-child) {
margin-left: 8px;
}
}
</style>
<style lang="scss"></style>

View File

@ -1,7 +1,7 @@
<template>
<div class="lns-record-list">
<div class="ons-record-list">
<div v-if="needsDecryption" class="decrypt row justify-between items-end">
<LokiField
<OxenField
:label="$t('fieldLabels.decryptRecord')"
:disable="decrypting"
:error="$v.name.$error"
@ -11,11 +11,11 @@
:dark="theme == 'dark'"
borderless
dense
:placeholder="$t('placeholders.lnsDecryptName')"
:placeholder="$t('placeholders.onsDecryptName')"
:disable="decrypting"
@blur="$v.name.$touch"
/>
</LokiField>
</OxenField>
<div class="btn-wrapper q-ml-md row items-center">
<q-btn
color="primary"
@ -27,19 +27,27 @@
</div>
<div v-if="session_records.length > 0" class="records-group">
<span class="record-type-title">{{
$t("titles.lnsSessionRecords")
$t("titles.onsSessionRecords")
}}</span>
<LNSRecordList
<ONSRecordList
:record-list="session_records"
:is-lokinet="false"
@onUpdate="onUpdate"
/>
</div>
<div v-if="wallet_records.length > 0" class="records-group">
<span class="record-type-title">{{ $t("titles.onsWalletRecords") }}</span>
<ONSRecordList
:record-list="wallet_records"
:is-lokinet="false"
@onUpdate="onUpdate"
/>
</div>
<div v-if="lokinet_records.length > 0" class="records-group">
<span class="record-type-title">{{
$t("titles.lnsLokinetRecords")
$t("titles.onsLokinetRecords")
}}</span>
<LNSRecordList
<ONSRecordList
:record-list="lokinet_records"
:is-lokinet="true"
@onUpdate="onUpdate"
@ -51,15 +59,15 @@
<script>
import { mapState } from "vuex";
import LokiField from "components/loki_field";
import OxenField from "components/oxen_field";
import { session_name_or_lokinet_name } from "src/validators/common";
import LNSRecordList from "./lns_record_list";
import ONSRecordList from "./ons_record_list";
export default {
name: "LNSRecords",
name: "ONSRecords",
components: {
LokiField,
LNSRecordList
OxenField,
ONSRecordList
},
data() {
return {
@ -68,7 +76,7 @@ export default {
};
},
mounted() {
this.$gateway.send("wallet", "lns_known_names");
this.$gateway.send("wallet", "ons_known_names");
},
computed: mapState({
theme: state => state.gateway.app.config.appearance.theme,
@ -81,11 +89,18 @@ export default {
session_records(state) {
return this.records_of_type(state, "session");
},
wallet_records(state) {
return this.records_of_type(state, "wallet");
},
lokinet_records(state) {
return this.records_of_type(state, "lokinet");
},
needsDecryption() {
const records = [...this.lokinet_records, ...this.session_records];
const records = [
...this.lokinet_records,
...this.session_records,
...this.wallet_records
];
return records.find(r => this.isLocked(r));
}
}),
@ -93,7 +108,7 @@ export default {
records_of_type(state, type) {
// receives the type and returns the records of that type
const ourAddresses = this.ourAddresses;
const records = state.gateway.wallet.lnsRecords;
const records = state.gateway.wallet.onsRecords;
const ourRecords = records.filter(record => {
return (
record.type === type &&
@ -151,7 +166,7 @@ export default {
this.$q.notify({
type: "positive",
timeout: 2000,
message: this.$t("notification.positive.decryptedLNSRecord", {
message: this.$t("notification.positive.decryptedONSRecord", {
name
})
});
@ -160,7 +175,7 @@ export default {
this.$q.notify({
type: "negative",
timeout: 3000,
message: this.$t("notification.errors.decryptLNSRecord", { name })
message: this.$t("notification.errors.decryptONSRecord", { name })
});
}
this.decrypting = false;
@ -189,7 +204,7 @@ export default {
</script>
<style lang="scss">
.lns-record-list {
.ons-record-list {
.height {
font-size: 0.9em;
}
@ -197,7 +212,7 @@ export default {
cursor: default;
}
.loki-field {
.oxen-field {
flex: 1;
}
@ -209,14 +224,4 @@ export default {
}
}
}
.record-type-title {
font-weight: bold;
margin-bottom: 40px;
padding-bottom: 40px;
}
.records-group {
padding-bottom: 40px;
}
</style>

View File

@ -1,8 +1,10 @@
<template>
<div class="loki-field" :class="{ disable, 'disable-hover': disableHover }">
<div class="oxen-field" :class="{ disable, 'disable-hover': disableHover }">
<div v-if="label" class="label row items-center" :disabled="disable">
{{ label }}
<span v-if="optional" class="optional">({{ $t("fieldLabels.optional") }})</span>
<span v-if="optional" class="optional"
>({{ $t("fieldLabels.optional") }})</span
>
</div>
<div class="content row items-center" :class="{ error }">
<slot></slot>
@ -15,7 +17,7 @@
<script>
export default {
name: "LokiField",
name: "OxenField",
props: {
label: {
type: String,
@ -55,7 +57,7 @@ export default {
</script>
<style lang="scss">
.loki-field {
.oxen-field {
.label {
margin: 6px 0;
font-weight: bold;
@ -90,7 +92,14 @@ export default {
margin: 0;
* {
color: white;
// Oxen navy, can't use vars here :(
color: #1f1c47;
}
}
.q-select {
.row {
color: red;
}
}

View File

@ -1,20 +1,34 @@
<template>
<q-list class="loki-list-item" no-border @click.native="details(address)">
<q-list class="oxen-list-item" no-border @click.native="details(address)">
<q-item>
<q-item-section class="flex">
<q-item-label class="ellipsis">{{ address.address }}</q-item-label>
<q-item-label v-if="sublabel" caption class="non-selectable">{{ sublabel }}</q-item-label>
<q-item-label v-if="sublabel" caption class="non-selectable">{{
sublabel
}}</q-item-label>
</q-item-section>
<q-item-section side>
<div class="row">
<q-btn style="margin-right: 4px;" flat padding="xs" size="md" @click="showQR(address.address, $event)">
<q-btn
style="margin-right: 4px;"
flat
padding="xs"
size="md"
@click="showQR(address.address, $event)"
>
<!-- height of 24 makes it equal size as copy -->
<img :src="qrImage" height="24" />
<q-tooltip anchor="bottom right" self="top right" :offset="[0, 5]">
{{ $t("menuItems.showQRCode") }}
</q-tooltip>
</q-btn>
<q-btn flat padding="xs" size="md" icon="file_copy" @click="copyAddress(address.address, $event)">
<q-btn
flat
padding="xs"
size="md"
icon="file_copy"
@click="copyAddress(address.address, $event)"
>
<q-tooltip anchor="bottom right" self="top right" :offset="[0, 5]">
{{ $t("menuItems.copyAddress") }}
</q-tooltip>
@ -28,19 +42,23 @@
<q-item-section>
<div class="row info-section">
<span class="col-sm-4">
<span>{{ $t("strings.lokiBalance") }}</span>
<span>{{ $t("strings.oxenBalance") }}</span>
<br />
<span class="value">{{ address.balance | currency }}</span>
</span>
<span class="col-sm-4">
<span>{{ $t("strings.lokiUnlockedBalance") }}</span>
<span>{{ $t("strings.oxenUnlockedBalance") }}</span>
<br />
<span class="value">{{ address.unlocked_balance | currency }}</span>
<span class="value">{{
address.unlocked_balance | currency
}}</span>
</span>
<span class="col-sm-4">
<span>{{ $t("strings.unspentOutputs") }}</span>
<br />
<span class="value">{{ address.num_unspent_outputs | toString }}</span>
<span class="value">{{
address.num_unspent_outputs | toString
}}</span>
</span>
</div>
</q-item-section>

View File

@ -23,7 +23,7 @@
/>
</q-toolbar>
</q-header>
<q-page-container>
<q-page-container class="detail-page">
<div class="layout-padding">
<h6 class="q-mt-xs q-mb-none text-weight-light">
{{ $t("strings.serviceNodeDetails.serviceNodeKey") }}
@ -40,7 +40,7 @@
</div>
<div class="value">
<span
><FormatLoki :amount="node.staking_requirement" raw-value
><FormatOxen :amount="node.staking_requirement" raw-value
/></span>
</div>
</div>
@ -54,7 +54,7 @@
</div>
<div class="value">
<span
><FormatLoki :amount="node.total_contributed" raw-value
><FormatOxen :amount="node.total_contributed" raw-value
/></span>
</div>
</div>
@ -120,7 +120,7 @@
</div>
</div>
</div>
<q-list no-border :dark="theme == 'dark'" class="loki-list">
<q-list no-border :dark="theme == 'dark'" class="oxen-list">
<q-item-label class="contributors-title"
>{{
$t("strings.serviceNodeDetails.contributors")
@ -129,7 +129,7 @@
<q-item
v-for="contributor in contributors"
:key="contributor.address"
class="loki-list-item"
class="oxen-list-item"
clickable
@click="openUserWalletInfo(contributor.address)"
>
@ -150,7 +150,7 @@
>{{ $t("strings.operator") }}
</span>
{{ $t("strings.contribution") }}:
<FormatLoki :amount="contributor.amount" raw-value />
<FormatOxen :amount="contributor.amount" raw-value />
</q-item-label>
</q-item-label>
<ContextMenu
@ -175,12 +175,12 @@
const { clipboard } = require("electron");
import { mapState } from "vuex";
import { date } from "quasar";
import FormatLoki from "components/format_loki";
import FormatOxen from "components/format_oxen";
import ContextMenu from "components/menus/contextmenu";
export default {
name: "ServiceNodeDetails",
components: {
FormatLoki,
FormatOxen,
ContextMenu
},
props: {
@ -245,7 +245,7 @@ export default {
}),
methods: {
openUserWalletInfo(contributorAddress) {
const url = `https://www.lokisn.com/user/${contributorAddress}`;
const url = `https://www.oxensn.com/user/${contributorAddress}`;
this.$gateway.send("core", "open_url", {
url
});
@ -280,6 +280,7 @@ export default {
<style lang="scss">
.contributors-title {
margin-bottom: 12px;
color: #1f1c47;
}
.serviceNodeDetails {

View File

@ -16,7 +16,7 @@
<span v-if="getRole(node)">{{ getRole(node) }} </span>
<span>
{{ $t("strings.contribution") }}:
<FormatLoki :amount="node.ourContributionAmount" />
<FormatOxen :amount="node.ourContributionAmount" />
</span>
</span>
<!-- you only have a contribution amount of 0 if you are a "contributor"
@ -28,18 +28,16 @@
>
{{ $t("strings.serviceNodeDetails.reserved") }}
</span>
<span v-if="node.awaitingContribution">
<span v-if="node.awaitingContribution" class="contrib-amounts">
{{ $t("strings.serviceNodeDetails.minContribution") }}:
{{ getMinContribution(node) }} LOKI
{{ getMinContribution(node, our_address) }} OXEN
{{ $t("strings.serviceNodeDetails.maxContribution") }}:
{{ openForContributionLoki(node) }} LOKI
{{ openForContributionOxen(node, our_address) }} OXEN
</span>
</q-item-label>
</q-item-section>
<q-item-section side>
<span style="font-size: 16px; color: #cecece">{{
getFee(node)
}}</span>
<span class="fee">{{ getFee(node) }}</span>
</q-item-section>
<q-item-section side>
<q-btn
@ -72,7 +70,7 @@
<script>
import { clipboard } from "electron";
import ContextMenu from "components/menus/contextmenu";
import FormatLoki from "components/format_loki";
import FormatOxen from "components/format_oxen";
import ServiceNodeMixin from "src/mixins/service_node_mixin";
import { mapState } from "vuex";
@ -80,7 +78,7 @@ export default {
name: "ServiceNodeList",
components: {
ContextMenu,
FormatLoki
FormatOxen
},
mixins: [ServiceNodeMixin],
props: {
@ -120,7 +118,7 @@ export default {
nodeWithMinContribution(node) {
const nodeWithMinContribution = {
...node,
minContribution: this.getMinContribution(node)
minContribution: this.getMinContribution(node, this.our_address)
};
return nodeWithMinContribution;
},
@ -168,4 +166,4 @@ export default {
};
</script>
<style></style>
<style lang="scss"></style>

View File

@ -1,11 +1,15 @@
<template>
<div class="service-node-registration">
<div class="q-pa-md">
<i18n path="strings.serviceNodeRegistrationDescription" tag="div" class="description q-mb-lg">
<i18n
path="strings.serviceNodeRegistrationDescription"
tag="div"
class="tab-desc q-mb-lg"
>
<b place="registerCommand">register_service_node</b>
<b place="prepareCommand">prepare_registration</b>
</i18n>
<LokiField
<OxenField
:label="$t('fieldLabels.serviceNodeCommand')"
:error="$v.registration_string.$error"
:disabled="registration_status.sending"
@ -13,8 +17,7 @@
<q-input
v-model.trim="registration_string"
type="textarea"
:dark="theme == 'dark'"
class="full-width text-area-loki"
class="full-width text-area-oxen"
placeholder="register_service_node ..."
:disabled="registration_status.sending"
borderless
@ -22,7 +25,7 @@
@blur="$v.registration_string.$touch"
@paste="onPaste"
/>
</LokiField>
</OxenField>
<q-btn
class="register-button"
color="primary"
@ -32,7 +35,7 @@
/>
</div>
<q-inner-loading :showing="registration_status.sending" :dark="theme == 'dark'">
<q-inner-loading :showing="registration_status.sending">
<q-spinner color="primary" size="30" />
</q-inner-loading>
</div>
@ -41,13 +44,13 @@
<script>
import { mapState } from "vuex";
import { required } from "vuelidate/lib/validators";
import LokiField from "components/loki_field";
import OxenField from "components/oxen_field";
import WalletPassword from "src/mixins/wallet_password";
export default {
name: "ServiceNodeRegistration",
components: {
LokiField
OxenField
},
mixins: [WalletPassword],
data() {

View File

@ -1,15 +1,15 @@
<template>
<div class="service-node-staking">
<div class="q-px-md q-pt-md">
<p style="color: #cecece">
<p class="tab-desc">
{{ $t("strings.serviceNodeContributionDescription") }}
<span
style="cursor: pointer; text-decoration: underline;"
@click="lokiWebsite"
>Loki {{ $t("strings.website") }}.</span
@click="oxenWebsite"
>Oxen {{ $t("strings.website") }}.</span
>
</p>
<LokiField
<OxenField
:label="$t('fieldLabels.serviceNodeKey')"
:error="$v.service_node.key.$error"
>
@ -21,8 +21,8 @@
dense
@blur="$v.service_node.key.$touch"
/>
</LokiField>
<LokiField
</OxenField>
<OxenField
:label="$t('fieldLabels.amount')"
class="q-mt-md"
:error="$v.service_node.amount.$error"
@ -39,20 +39,20 @@
@blur="$v.service_node.amount.$touch"
/>
<q-btn
color="secondary"
color="primary"
:text-color="theme == 'dark' ? 'white' : 'dark'"
:label="$t('buttons.min')"
:disable="!areButtonsEnabled()"
@click="service_node.amount = minStake(service_node.key)"
/>
<q-btn
color="secondary"
color="primary"
:text-color="theme == 'dark' ? 'white' : 'dark'"
:label="$t('buttons.max')"
:disable="!areButtonsEnabled()"
@click="service_node.amount = maxStake(service_node.key)"
/>
</LokiField>
</OxenField>
<div class="submit-button">
<q-btn
:disable="!is_able_to_send"
@ -62,7 +62,7 @@
/>
<q-btn
:disable="!is_able_to_send"
color="secondary"
color="accent"
:label="$t('buttons.sweepAll')"
@click="sweepAllWarning()"
/>
@ -84,7 +84,6 @@
/>
<q-inner-loading
:showing="stake_status.sending || sweep_all_status.sending"
:dark="theme == 'dark'"
>
<q-spinner color="primary" size="30" />
</q-inner-loading>
@ -96,7 +95,7 @@ const objectAssignDeep = require("object-assign-deep");
import { mapState } from "vuex";
import { required, decimal } from "vuelidate/lib/validators";
import { service_node_key, greater_than_zero } from "src/validators/common";
import LokiField from "components/loki_field";
import OxenField from "components/oxen_field";
import WalletPassword from "src/mixins/wallet_password";
import ConfirmDialogMixin from "src/mixins/confirm_dialog_mixin";
import ServiceNodeContribute from "./service_node_contribute";
@ -108,7 +107,7 @@ const DO_NOTHING = 10;
export default {
name: "ServiceNodeStaking",
components: {
LokiField,
OxenField,
ServiceNodeContribute,
ConfirmTransactionDialog
},
@ -152,17 +151,24 @@ export default {
},
awaiting_service_nodes(state) {
const nodes = state.gateway.daemon.service_nodes.nodes;
// a reserved node is one on which someone is a "contributor" of amount = 0
const getOurContribution = node =>
node.contributors.find(
c => c.address === this.our_address && c.amount > 0
c => c.address === this.award_address && c.amount > 0
);
// a reserved node is one on which someone is a "contributor" of amount = 0
const reservedForUs = node =>
node.contributors.find(
c => c.address === this.award_address && c.amount == 0
);
const isAwaitingContribution = node =>
!node.active && !node.funded && node.requested_unlock_height === 0;
!node.active && !node.funded;
const isAwaitingContributionNonReserved = node =>
isAwaitingContribution(node) && !getOurContribution(node);
node.requested_unlock_height === 0 &&
isAwaitingContribution(node) &&
!getOurContribution(node) &&
this.openForContribution(node) > 0;
const isAwaitingContributionReserved = node =>
isAwaitingContribution(node) && getOurContribution(node);
isAwaitingContribution(node) && reservedForUs(node);
// we want the reserved nodes sorted by fee at the top
const awaitingContributionNodesReserved = nodes
@ -278,8 +284,8 @@ export default {
}
},
methods: {
lokiWebsite() {
const url = "https://loki.network/service-nodes/";
oxenWebsite() {
const url = "https://oxen.io/";
this.$gateway.send("core", "open_url", {
url
});
@ -290,11 +296,11 @@ export default {
},
minStake() {
const node = this.getNodeWithPubKey();
return this.getMinContribution(node);
return this.getMinContribution(node, this.award_address);
},
maxStake() {
const node = this.getNodeWithPubKey();
return this.openForContributionLoki(node);
return this.openForContributionOxen(node, this.award_address);
},
getFeeDecimal(node) {
const operatorPortion = node.portions_for_operator;
@ -353,9 +359,8 @@ export default {
cancel: {
flat: true,
label: this.$t("dialog.buttons.cancel"),
color: this.theme === "dark" ? "white" : "dark"
},
dark: this.theme === "dark"
color: "negative"
}
})
.onOk(() => {
this.sweepAll();
@ -387,10 +392,8 @@ export default {
noPasswordMessage: this.$t("dialog.sweepAll.message"),
ok: {
label: this.$t("dialog.sweepAll.ok"),
color: "primary"
},
dark: this.theme == "dark",
color: this.theme == "dark" ? "white" : "dark"
color: "#12C7BA"
}
});
passwordDialog
.onOk(password => {

View File

@ -72,13 +72,20 @@ export default {
node.contributors.find(
c => c.address === this.our_address && c.amount > 0
);
return nodes.filter(getOurContribution).map(n => {
const ourContribution = getOurContribution(n);
return {
...n,
ourContributionAmount: ourContribution.amount
};
});
return nodes
.filter(getOurContribution)
.sort((a, b) => {
if (a.service_node_pubkey < b.service_node_pubkey) return -1;
if (a.service_node_pubkey > b.service_node_pubkey) return 1;
return 0;
})
.map(n => {
const ourContribution = getOurContribution(n);
return {
...n,
ourContributionAmount: ourContribution.amount
};
});
},
fetching: state => state.gateway.daemon.service_nodes.fetching
}),

View File

@ -12,7 +12,7 @@
<q-btn-toggle
v-model="page"
toggle-color="primary"
color="tertiary"
color="accent"
size="md"
:options="tabs"
/>
@ -29,7 +29,7 @@
</div>
<div v-if="page == 'peers'">
<q-list :dark="theme == 'dark'" no-border>
<q-list no-border>
<q-item-label header>{{ $t("strings.peerList") }}</q-item-label>
<q-item
v-for="entry in daemon.connections"

View File

@ -24,19 +24,19 @@
</div>
</div>
<p v-if="config_daemon.type == 'local_remote'">
<p v-if="config_daemon.type == 'local_remote'" class="tab-desc">
{{ $t("strings.daemon.localRemote.description") }}
</p>
<p v-if="config_daemon.type == 'local'">
<p v-if="config_daemon.type == 'local'" class="tab-desc">
{{ $t("strings.daemon.local.description") }}
</p>
<p v-if="is_remote">
<p v-if="is_remote" class="tab-desc">
{{ $t("strings.daemon.remote.description") }}
</p>
<template v-if="config_daemon.type != 'remote'">
<div class="row pl-sm">
<LokiField
<OxenField
class="col-8"
:label="$t('fieldLabels.localDaemonIP')"
disable
@ -44,13 +44,12 @@
<q-input
v-model="config_daemon.rpc_bind_ip"
:placeholder="daemon_defaults.rpc_bind_ip"
:dark="theme == 'dark'"
disable
borderless
dense
/>
</LokiField>
<LokiField
</OxenField>
<OxenField
class="col-4"
:label="$t('fieldLabels.localDaemonPort') + '(RPC)'"
>
@ -62,21 +61,19 @@
:step="1"
min="1024"
max="65535"
:dark="theme == 'dark'"
borderless
dense
/>
</LokiField>
</OxenField>
</div>
</template>
<template v-if="config_daemon.type != 'local'">
<div class="row q-mt-md pl-sm">
<LokiField class="col-8" :label="$t('fieldLabels.remoteNodeHost')">
<OxenField class="col-8" :label="$t('fieldLabels.remoteNodeHost')">
<q-input
v-model="config_daemon.remote_host"
:placeholder="daemon_defaults.remote_host"
:dark="theme == 'dark'"
borderless
dense
/>
@ -86,7 +83,7 @@
class="remote-dropdown"
flat
>
<q-list link dark no-border>
<q-list link no-border>
<q-item
v-for="option in remotes"
:key="option.host"
@ -101,8 +98,8 @@
</q-item>
</q-list>
</q-btn-dropdown>
</LokiField>
<LokiField class="col-4" :label="$t('fieldLabels.remoteNodePort')">
</OxenField>
<OxenField class="col-4" :label="$t('fieldLabels.remoteNodePort')">
<q-input
v-model="config_daemon.remote_port"
:placeholder="toString(daemon_defaults.remote_port)"
@ -115,12 +112,12 @@
borderless
dense
/>
</LokiField>
</OxenField>
</div>
</template>
<div class="col q-mt-md pt-sm">
<LokiField :label="$t('fieldLabels.dataStoragePath')" disable-hover>
<OxenField :label="$t('fieldLabels.dataStoragePath')" disable-hover>
<q-input
v-model="config.app.data_dir"
disable
@ -138,13 +135,13 @@
@change="setDataPath"
/>
<q-btn
color="secondary"
color="primary"
:text-color="theme == 'dark' ? 'white' : 'dark'"
@click="selectPath('data')"
>{{ $t("buttons.selectLocation") }}</q-btn
>
</LokiField>
<LokiField :label="$t('fieldLabels.walletStoragePath')" disable-hover>
</OxenField>
<OxenField :label="$t('fieldLabels.walletStoragePath')" disable-hover>
<q-input
v-model="config.app.wallet_data_dir"
disable
@ -162,12 +159,12 @@
@change="setWalletDataPath"
/>
<q-btn
color="secondary"
color="primary"
:text-color="theme == 'dark' ? 'white' : 'dark'"
@click="selectPath('wallet')"
>{{ $t("buttons.selectLocation") }}</q-btn
>
</LokiField>
</OxenField>
</div>
<q-expansion-item
@ -175,7 +172,7 @@
header-class="q-mt-sm non-selectable row reverse advanced-options-label"
>
<div class="row pl-sm q-mt-sm">
<LokiField
<OxenField
class="col-6"
:label="$t('fieldLabels.daemonLogLevel')"
:disable="is_remote"
@ -193,8 +190,8 @@
borderless
dense
/>
</LokiField>
<LokiField class="col-6" :label="$t('fieldLabels.walletLogLevel')">
</OxenField>
<OxenField class="col-6" :label="$t('fieldLabels.walletLogLevel')">
<q-input
v-model="config.wallet.log_level"
:placeholder="toString(defaults.wallet.log_level)"
@ -207,12 +204,12 @@
borderless
dense
/>
</LokiField>
</OxenField>
</div>
<div class="row pl-sm q-mt-md">
<!-- TODO: Can be generalised to a "port" (or similar) field -->
<LokiField
<OxenField
class="col-3"
:label="$t('fieldLabels.maxIncomingPeers')"
:disable="is_remote"
@ -230,8 +227,8 @@
borderless
dense
/>
</LokiField>
<LokiField
</OxenField>
<OxenField
class="col-3"
:label="$t('fieldLabels.maxOutgoingPeers')"
:disable="is_remote"
@ -249,8 +246,8 @@
borderless
dense
/>
</LokiField>
<LokiField
</OxenField>
<OxenField
class="col-3"
:label="$t('fieldLabels.limitUploadRate')"
:disable="is_remote"
@ -269,8 +266,8 @@
borderless
dense
/>
</LokiField>
<LokiField
</OxenField>
<OxenField
class="col-3"
:label="$t('fieldLabels.limitDownloadRate')"
:disable="is_remote"
@ -289,10 +286,10 @@
borderless
dense
/>
</LokiField>
</OxenField>
</div>
<div class="row pl-sm q-mt-md">
<LokiField
<OxenField
class="col-3"
:label="$t('fieldLabels.daemonP2pPort')"
:disable="is_remote"
@ -311,8 +308,8 @@
borderless
dense
/>
</LokiField>
<LokiField class="col-3" :label="$t('fieldLabels.internalWalletPort')">
</OxenField>
<OxenField class="col-3" :label="$t('fieldLabels.internalWalletPort')">
<q-input
v-model="config.app.ws_bind_port"
:placeholder="toString(defaults.app.ws_bind_port)"
@ -326,8 +323,8 @@
borderless
dense
/>
</LokiField>
<LokiField
</OxenField>
<OxenField
class="col-3"
:label="$t('fieldLabels.walletRPCPort')"
:disable="is_remote"
@ -346,9 +343,9 @@
borderless
dense
/>
</LokiField>
</OxenField>
</div>
<LokiField
<OxenField
:helper="$t('fieldLabels.chooseNetwork')"
:label="$t('fieldLabels.network')"
class="network-group-field"
@ -362,18 +359,18 @@
{ label: 'Test Net', value: 'testnet' }
]"
/>
</LokiField>
</OxenField>
</q-expansion-item>
</div>
</template>
<script>
import { mapState } from "vuex";
import LokiField from "components/loki_field";
import OxenField from "components/oxen_field";
export default {
name: "SettingsGeneral",
components: {
LokiField
OxenField
},
props: {
randomiseRemote: {

View File

@ -21,7 +21,7 @@
/>
</q-toolbar>
</q-header>
<q-page-container>
<q-page-container class="detail-page">
<div class="layout-padding">
<div class="row items-center non-selectable">
<div class="q-mr-sm">
@ -72,7 +72,7 @@
<span>{{ $t("strings.transactions.amount") }}</span>
</div>
<div class="value">
<span><FormatLoki :amount="tx.amount" raw-value/></span>
<span><FormatOxen :amount="tx.amount" raw-value/></span>
</div>
</div>
</div>
@ -88,7 +88,7 @@
</span>
</div>
<div class="value">
<span><FormatLoki :amount="tx.fee" raw-value/></span>
<span><FormatOxen :amount="tx.fee" raw-value/></span>
</div>
</div>
</div>
@ -176,7 +176,7 @@
destination.address
}}</q-item-label>
<q-item-label
><FormatLoki :amount="destination.amount"
><FormatOxen :amount="destination.amount"
/></q-item-label>
</q-item-label>
<ContextMenu
@ -200,8 +200,6 @@
<q-input
v-model="txNotes"
:label="$t('fieldLabels.transactionNotes')"
:dark="theme == 'dark'"
:text-color="theme == 'dark' ? 'white' : 'dark'"
type="textarea"
rows="2"
dense
@ -225,13 +223,13 @@ const { clipboard } = require("electron");
import { mapState } from "vuex";
import { date } from "quasar";
import TxTypeIcon from "components/tx_type_icon";
import FormatLoki from "components/format_loki";
import FormatOxen from "components/format_oxen";
import ContextMenu from "components/menus/contextmenu";
export default {
name: "TxDetails",
components: {
TxTypeIcon,
FormatLoki,
FormatOxen,
ContextMenu
},
data() {
@ -327,8 +325,8 @@ export default {
label: this.$t("dialog.transactionDetails.ok"),
color: "primary"
},
dark: this.theme == "dark",
style: "min-width: 500px; overflow-wrap: break-word;"
style: "min-width: 500px; overflow-wrap: break-word;",
color: "#1F1C47"
})
.onOk(() => {})
.onCancel(() => {})

View File

@ -1,7 +1,7 @@
<template>
<div class="tx-list">
<template v-if="tx_list_paged.length === 0">
<p class="q-pa-md q-mb-none">
<p class="q-pa-md q-mb-none tab-desc">
{{ $t("strings.noTransactionsFound") }}
</p>
</template>
@ -12,12 +12,12 @@
link
no-border
:dark="theme == 'dark'"
class="loki-list tx-list"
class="oxen-list tx-list"
>
<q-item
v-for="(tx, i) in tx_list_paged"
:key="`${tx.txid}-${tx.type}-${i}`"
class="loki-list-item transaction"
class="oxen-list-item transaction"
:class="'tx-' + tx.type"
@click.native="details(tx)"
>
@ -26,7 +26,7 @@
</q-item-section>
<q-item-label class="main">
<q-item-label class="amount">
<FormatLoki :amount="tx.amount || 0" />
<FormatOxen :amount="tx.amount || 0" />
</q-item-label>
<q-item-label caption>{{ tx.txid }}</q-item-label>
</q-item-label>
@ -61,7 +61,7 @@ const { clipboard } = require("electron");
import { mapState } from "vuex";
import { QSpinnerDots } from "quasar";
import TxDetails from "components/tx_details";
import FormatLoki from "components/format_loki";
import FormatOxen from "components/format_oxen";
import { i18n } from "boot/i18n";
import ContextMenu from "components/menus/contextmenu";
@ -95,7 +95,7 @@ export default {
components: {
QSpinnerDots,
TxDetails,
FormatLoki,
FormatOxen,
ContextMenu
},
props: {
@ -328,7 +328,7 @@ export default {
<style lang="scss">
.tx-list {
.loki-list-item {
.oxen-list-item {
padding-top: 0;
padding-bottom: 0;
}

View File

@ -1,22 +1,52 @@
<template>
<div class="column wallet-info">
<div class="row justify-between items-center wallet-header loki-green">
<div class="row justify-between items-center wallet-header">
<div class="title">{{ info.name }}</div>
<WalletSettings />
</div>
<div class="wallet-content">
<div class="wallet-content oxen-navy">
<div class="row justify-center">
<div class="funds column items-center">
<div class="balance">
<div class="text">
<span>{{ $t("strings.lokiBalance") }}</span>
</div>
<q-btn-toggle
v-model="balancestakeselector"
text-color="white"
toggle-text-color="primary"
flat
:options="[
{
label: $t('strings.oxenBalance'),
value: 'balance'
},
{
label: $t('strings.stake'),
value: 'stake'
}
]"
/>
<div class="value">
<span><FormatLoki :amount="info.balance"/></span>
<span><FormatOxen :amount="info.balance"/></span>
</div>
</div>
<div class="row unlocked">
<span>{{ $t("strings.lokiUnlockedShort") }}: <FormatLoki :amount="info.unlocked_balance"/></span>
<div v-if="balancestakeselector != 'stake'" class="row unlocked">
<span
>{{ $t("strings.oxenUnlockedShort") }}:
<FormatOxen :amount="info.unlocked_balance"
/></span>
</div>
<div v-if="balancestakeselector == 'stake'" class="row unlocked">
<span v-if="info.accrued_balance > 0"
>{{ $t("strings.oxenAccumulatedRewards") }}:
<FormatOxen :amount="info.accrued_balance" />
{{ $t("strings.nextPayout") }}:
<FormatNextPayout
:payout-block="info.accrued_balance_next_payout"
:current-block="info.height"
/>
</span>
<span v-if="info.accrued_balance == 0">
No accumulated rewards from staking
</span>
</div>
</div>
</div>
@ -30,20 +60,27 @@
<script>
import { mapState } from "vuex";
import FormatLoki from "components/format_loki";
import FormatOxen from "components/format_oxen";
import FormatNextPayout from "components/format_next_payout";
import WalletSettings from "components/menus/wallet_settings";
import CopyIcon from "components/icons/copy_icon";
export default {
name: "WalletDetails",
components: {
FormatLoki,
FormatOxen,
FormatNextPayout,
WalletSettings,
CopyIcon
},
computed: mapState({
theme: state => state.gateway.app.config.appearance.theme,
info: state => state.gateway.wallet.info
})
}),
data() {
return {
balancestakeselector: "balance"
};
}
};
</script>
@ -58,7 +95,6 @@ export default {
.wallet-content {
text-align: center;
background-color: #0a0a0a;
padding: 2em;
.balance {

View File

@ -0,0 +1,86 @@
<template>
<q-item @click.native="openWallet(wallet)">
<q-item-section avatar>
<q-icon class="wallet-icon">
<svg
width="48"
viewBox="0 0 17 16"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="si-glyph si-glyph-wallet"
>
<defs class="si-glyph-fill"></defs>
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(1.000000, 0.000000)" fill="#434343">
<path
d="M7.988,10.635 L7.988,8.327 C7.988,7.578 8.561,6.969 9.267,6.969 L13.964,6.969 L13.964,5.531 C13.964,4.849 13.56,4.279 13.007,4.093 L13.007,4.094 L11.356,4.08 L11.336,4.022 L3.925,4.022 L3.784,4.07 L1.17,4.068 L1.165,4.047 C0.529,4.167 0.017,4.743 0.017,5.484 L0.017,13.437 C0.017,14.269 0.665,14.992 1.408,14.992 L12.622,14.992 C13.365,14.992 13.965,14.316 13.965,13.484 L13.965,12.031 L9.268,12.031 C8.562,12.031 7.988,11.384 7.988,10.635 L7.988,10.635 Z"
class="si-glyph-fill"
></path>
<path
d="M14.996,8.061 L14.947,8.061 L9.989,8.061 C9.46,8.061 9.031,8.529 9.031,9.106 L9.031,9.922 C9.031,10.498 9.46,10.966 9.989,10.966 L14.947,10.966 L14.996,10.966 C15.525,10.966 15.955,10.498 15.955,9.922 L15.955,9.106 C15.955,8.528 15.525,8.061 14.996,8.061 L14.996,8.061 Z M12.031,10.016 L9.969,10.016 L9.969,9 L12.031,9 L12.031,10.016 L12.031,10.016 Z"
class="si-glyph-fill"
></path>
<path
d="M3.926,4.022 L10.557,1.753 L11.337,4.022 L12.622,4.022 C12.757,4.022 12.885,4.051 13.008,4.092 L11.619,0.051 L1.049,3.572 L1.166,4.048 C1.245,4.033 1.326,4.023 1.408,4.023 L3.926,4.023 L3.926,4.022 Z"
class="si-glyph-fill"
></path>
</g>
</g>
</svg>
</q-icon>
</q-item-section>
<q-item-section>
<q-item-label class="wallet-name" caption>{{ wallet.name }}</q-item-label>
<q-item-label class="monospace ellipsis" caption>{{
wallet.address
}}</q-item-label>
</q-item-section>
<ContextMenu
:menu-items="menuItems"
@openWallet="openWallet(wallet)"
@copyAddress="copyAddress(wallet.address)"
/>
</q-item>
</template>
<script>
const { clipboard } = require("electron");
import { mapState } from "vuex";
export default {
name: "WalletListItem",
props: {
wallet: {
type: Object,
required: true
},
openWallet: {
type: Function,
required: true
}
},
computed: mapState({
theme: state => state.gateway.app.config.appearance.theme,
info: state => state.gateway.wallet.info
}),
methods: {
copyAddress() {
event.stopPropagation();
for (let i = 0; i < event.path.length; i++) {
if (event.path[i].tagName == "BUTTON") {
event.path[i].blur();
break;
}
}
clipboard.writeText(this.wallet.address);
this.$q.notify({
type: "positive",
timeout: 1000,
message: this.$t("notification.positive.addressCopied")
});
}
}
};
</script>
<style lang="scss"></style>

View File

@ -55,6 +55,7 @@ footer,
font-family: 'RobotoMono-Light', monospace
}
.break-all {
word-break: break-all
}
@ -72,7 +73,7 @@ footer,
}
}
.text-area-loki {
.text-area-oxen {
margin-top: 0px;
margin-bottom: 0px;
}
@ -100,8 +101,8 @@ footer,
}
.q-layout, .app-content {
background: $loki-black-80;
color:white;
background: white;
color:$oxen-navy;
}
.q-header {
@ -138,7 +139,7 @@ footer,
}
.q-toolbar-inverted {
background: none;
background: $oxen-navy;
padding-top: 0;
}
@ -153,9 +154,9 @@ footer,
.q-tab {
text-transform: none;
&:hover {
background-color: rgba(12,12,12,0.15)
}
// &:hover {
// background-color: rgba(12,12,12,0.15)
// }
.q-icon {
font-size: 22px;
@ -196,19 +197,22 @@ footer,
.text {
font-size: 14px;
margin-top: 8px;
color: #cecece;
color: $oxen-navy;
}
.value {
font-size: 24px;
margin-top: 4px;
font-weight: 800;
color: #cecece;
font-weight:300;
font-weight: 300;
color: $oxen-navy;
}
}
}
.q-checkbox {
color: $oxen-navy
}
.q-loading + .modal {
z-index: 9999 !important;
}
@ -220,7 +224,7 @@ footer,
}
.q-footer.status-footer {
background: #000000;
background: $oxen-navy;
color: rgba(255, 255, 255, 0.51);
border-top: 1px solid #333;
padding-top: 2px;
@ -242,7 +246,7 @@ footer,
}
.ready {
color: $loki-green-solid;
color: $positive;
}
.scanning, .syncing {
@ -271,11 +275,12 @@ footer,
}
div:first-child {
background-color: $loki-green-dark-solid;
// TODO: should first and last child be different, where even is this?
background-color: $positive;
}
div:last-child {
background-color: $loki-green-solid;
background-color: $positive;
}
}
@ -315,10 +320,10 @@ footer,
.q-item.tx-snode,
.q-item.tx-gov {
.amount span {
color: #43bd43;
color: $oxen-navy;
&:before {
content: "+";
color: #43bd43;
color: $oxen-navy;
}
}
}
@ -332,11 +337,11 @@ footer,
.q-item.tx-out,
.q-item.tx-pending {
.amount span {
color: white;
// color: white;
&:before {
content: "-";
font-weight: bold;
color: white;
// color: white;
}
}
}
@ -360,8 +365,12 @@ footer,
}
.q-menu {
background: #222;
color:#cecece;
background: white;
color: $oxen-navy !important;
* {
color: $oxen-navy;
}
.q-list-separator > .q-item-division + .q-item-division, .q-item-division + .q-separator {
border-top: 1px solid #333;
@ -369,15 +378,25 @@ footer,
}
.menu-list {
.q-item * {
color: white;
}
background: white;
.q-item * {
color: $oxen-navy;
}
}
.context-menu-list {
min-width: 150px;
max-height: 300px;
}
.confirm-tx-card {
color: "primary";
width: 450px;
max-width: 450x;
color: $oxen-navy !important;
.confirm-list {
.q-item {
max-height: 100%;
@ -389,7 +408,7 @@ footer,
}
.label {
color: #cecece;
color: $oxen-purple;
padding-right: 6px;
}
.address-value {
@ -398,20 +417,14 @@ footer,
.confirm-send-btn {
color: white;
background: $positive;
background: $primary;
}
}
.header-popover {
background: $primary;
color: white;
background: white;
max-width: 250px !important;
// padding-top: 4px;
// .q-list-separator > .q-item-division + .q-item-division, .q-item-division + .q-separator {
// border-top: 1px solid $loki-green-dark-solid;
// }
}
.modal.minimized {
@ -427,7 +440,6 @@ footer,
.modal {
color: #cecece;
background-color: $dark;
.modal-header {
margin-top: 6px;
@ -436,10 +448,6 @@ footer,
font-size: 24px;
font-weight: bold;
}
.modal-content,
.modal-body {
background: $loki-black-80;
}
.q-header {
@ -452,9 +460,22 @@ footer,
}
.q-toolbar {
background-color: $oxen-navy;
}
.header {
color: $oxen-navy;
}
.detail-page {
color: $oxen-navy;
}
.q-radio {
color: $oxen-navy;
svg {
color: #cecece;
color: $oxen-navy;
}
}
@ -463,20 +484,30 @@ footer,
color: white;
.qr-code-card {
background-color: $dark;
background-color: white;
}
}
.loki-green {
background: $loki-green;
.oxen-light-teal {
background: $oxen-light-teal;
color: white;
}
.oxen-navy {
background: $oxen-navy;
color: white;
}
.oxen-teal {
background: $oxen-teal;
color: white;
}
.startup-icons {
.solid {
color:$loki-green-solid;
color:$oxen-teal;
g,path {
fill: $loki-green-solid;
fill: $oxen-teal;
}
}
}
@ -489,37 +520,52 @@ footer,
.navigation {
.q-btn {
background: $oxen-navy;
color: white;
background: $loki-black-90;
.q-icon {
color: $oxen-teal;
}
}
.router-link-exact-active > .q-btn {
background: $loki-green;
color: $oxen-navy
background: $oxen-teal;
.q-icon {
color: $oxen-navy;
}
}
}
.truncate-item {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.wallet-list {
.q-item-label {
color: white;
}
.q-item {
background: $secondary;
background: $oxen-navy;
.wallet-icon {
color: $tertiary;
color: $oxen-teal;
g,path {
fill: $tertiary;
fill: $oxen-teal;
}
}
}
.q-item:hover, .q-item.selected {
background: $primary !important;
background: $oxen-purple !important;
.wallet-icon {
color:$loki-green-solid;
color: $oxen-teal;
g,path {
fill: $loki-green-solid;
fill: $oxen-teal;
}
}
@ -529,21 +575,33 @@ footer,
}
}
.wallet-settings {
.q-btn {
color: $oxen-navy;
}
}
.receive {
.q-list-header {
color: #FFFFFF;
.list-header {
color: $oxen-navy;
}
.q-separator-component {
background-color: $secondary;
background-color: white;
opacity: 0.4;
}
.primary-address {
background: #3eb13e !important;
background: $oxen-teal !important;
border: none !important;
.q-item, .q-item-side {
color: white;
.text-caption {
color: white;
}
}
.q-btn {
@ -552,83 +610,153 @@ footer,
}
.primary-address:hover {
background: $loki-green-solid !important;
background: #0ca69b !important;
}
}
.loki-list {
.loki-list-item {
background: #313131;
.oxen-list {
.oxen-list-item {
background: white;
-webkit-transition: background-color 0.2s ease-in;
transition: background-color 0.2s ease-in;
font-size: 16px;
color: #cecece;
color: $oxen-navy;
padding-top: 6px;
padding-bottom: 6px;
margin: 0 16px;
border-radius: 3px;
border: 1px solid $oxen-navy;
border-radius: 4px;
+ .loki-list-item {
+ .oxen-list-item {
margin-top: 10px;
}
}
.loki-list-item:hover {
background: rgba(117,117,117,0.3);
.text-caption {
color: $oxen-navy;
}
.oxen-list-item:hover {
background: $off-grey;
cursor: pointer;
}
}
.lns-record-list {
.q-item-label.unlocked {
color: white;
}
.record-type-title {
font-weight: bold;
margin-bottom: 40px;
padding-bottom: 40px;
color: $oxen-navy;
}
.records-group {
padding-bottom: 40px;
}
.service-node-list {
.q-item {
cursor: pointer;
background: #313131;
color: $oxen-navy;
background: white;
-webkit-transition: background-color 0.2s ease-in;
transition: background-color 0.2s ease-in;
border-radius: 3px;
border: 1px solid $oxen-navy;
border-radius: 4px;
+ .q-item {
margin-top: 10px;
}
.contrib-amounts {
color: $oxen-navy;
}
}
.q-item-label {
color: $oxen-navy;
}
.q-item-sublabel {
color: $loki-black-50;
.fee {
color: $oxen-navy;
}
.q-item:hover {
background: rgba(117,117,117,0.3);
background: $off-grey;
}
}
.lokinet-expiration {
width: 180px;
}
.ons-record-list {
.q-item {
cursor: pointer;
color: $oxen-navy;
-webkit-transition: background-color 0.2s ease-in;
transition: background-color 0.2s ease-in;
border: 1px solid $oxen-navy;
border-radius: 4px;
.height {
color: $oxen-navy;
}
// we don't want to select button text (also a span) as well
// this is the height text
div > span {
color: #1f1c47;
}
+ .q-item {
margin-top: 10px;
}
}
.q-item:hover {
background: $off-grey;
}
}
.update-renew-buttons {
.q-btn:not(:first-child) {
margin-left: 8px;
}
}
.tx-list {
.transaction .main {
border-left: 1px solid #252525;
}
}
.loki-field {
.tab-desc {
color: $oxen-navy;
font-style: normal;
b {
color: $oxen-teal;
font-style: italic;
}
}
.oxen-field {
.content {
border: 1px solid #484848;
background: white;
-webkit-transition: background-color 0.2s ease-in, border-color 0.2s ease-in;
transition: background-color 0.2s ease-in, border-color 0.2s ease-in;
}
&:not(.disable):not(.disable-hover) {
.content:hover {
background: #2e2e2e;
background: white;
}
}
@ -643,7 +771,7 @@ footer,
}
.label {
color: white;
color: $oxen-navy;
.optional {
color: #7C7C7C;
@ -651,23 +779,16 @@ footer,
}
}
.service-node-registration, .prove-transaction, .check-transaction {
.description{
color: #b7b7b7;
font-style: normal;
b {
color: white;
font-style: italic;
}
}
}
.welcome {
.q-footer {
background: $secondary
}
}
.welcome-container {
color: $oxen-navy;
}
.address-book, .address-header {
.q-item-label, .header {
color: white;
@ -683,6 +804,10 @@ footer,
}
}
.signature-dialog {
color: $oxen-navy;
}
.check-transaction {
.good {
color: #43bd43;
@ -712,3 +837,46 @@ footer,
.non-selectable {
color: #979797;
}
.created {
color: $oxen-navy;
.wallet h6 {
text-align: center;
}
.address {
text-align: center;
word-break: break-all;
}
.seed-box {
border: 1px solid white;
border-radius: 3px;
margin: 16px;
padding: 16px;
div,
h6 {
text-align: center;
}
.seed {
font-size: 24px;
font-weight: 600;
}
.warning {
color: goldenrod;
}
}
h6 {
font-size: 18px;
margin: 8px 0;
font-weight: 450;
}
.advanced-options-label {
padding-left: 0;
padding-right: 0;
}
}

View File

@ -15,9 +15,9 @@
// It"s highly recommended to change the default colors
// to match your app"s branding.
$primary = $loki-green
$secondary = $loki-black-90
$tertiary = $loki-black-80
$primary = $oxen-teal
$secondary = $oxen-light-teal
$accent = $oxen-navy
$neutral = #E0E1E2
$positive = #21BA45
@ -26,10 +26,12 @@ $info = #31CCEC
$warning = #F2C037
$loki-green = linear-gradient(180deg, #419B41 0%, #43BD43 100%)
$loki-green-solid = #5BCA5B;
$loki-green-dark-solid = #419B41;
$loki-black-90 = #0A0A0A
$loki-black-80 = #252525
$loki-black-60 = #313131
$loki-black-50 = #7E7E7E;
$oxen-navy = #1F1C47;
$oxen-teal = #12C7BA;
$oxen-light-teal = #DBF7F5;
$oxen-purple = #654192;
$off-grey = #E8E8E8;
$loki-black-60 = #313131;

View File

@ -14,21 +14,23 @@
// It"s highly recommended to change the default colors
// to match your app"s branding.
$primary = $loki-green
$secondary = $loki-black-90
$tertiary = $loki-black-80
// $primary = $loki-green
// $secondary = $loki-black-90
// $tertiary = $loki-black-80
$neutral = #E0E1E2
$positive = #21BA45
$negative = #DB2828
$info = #31CCEC
$warning = #F2C037
// $neutral = #E0E1E2
// $positive = #21BA45
// $negative = #DB2828
// $info = #31CCEC
// $warning = #F2C037
$loki-green = linear-gradient(180deg, #419B41 0%, #43BD43 100%)
$loki-green-solid = #5BCA5B;
$loki-green-dark-solid = #419B41;
// $loki-green = linear-gradient(180deg, #419B41 0%, #43BD43 100%)
$loki-black-90 = #0A0A0A
$loki-black-80 = #252525
$loki-black-60 = #313131
$loki-black-50 = #7E7E7E;
// $oxen-navy = #1F1C47;
// $oxen-teal = #12C7BA;
// $oxen-light-teal = #DBF7F5;
// $loki-black-90 = #0A0A0A
// $loki-black-80 = #252525
// $loki-black-60 = #313131
// $loki-black-50 = #7E7E7E;

View File

@ -20,7 +20,7 @@ export class Gateway extends EventEmitter {
let theme = LocalStorage.has("theme")
? LocalStorage.getItem("theme")
: "dark";
: "light";
this.app.store.commit("gateway/set_app_data", {
config: {
appearance: {
@ -89,17 +89,13 @@ export class Gateway extends EventEmitter {
message: msg,
ok: {
label: i18n.t(`dialog.${key}.ok`),
color: "positive"
color: "primary"
},
cancel: {
flat: true,
label: i18n.t("dialog.buttons.cancel"),
color:
this.app.store.state.gateway.app.config.appearance.theme === "dark"
? "white"
: "dark"
},
dark: this.app.store.state.gateway.app.config.appearance.theme === "dark"
color: "grey"
}
})
.onOk(() => {
this.closeDialog = false;
@ -194,13 +190,13 @@ export class Gateway extends EventEmitter {
break;
}
case "set_lns_status": {
case "set_ons_status": {
const data = { ...decrypted_data.data };
if (data.i18n) {
data.message = this.geti18n(data.i18n);
}
this.app.store.commit("gateway/set_lns_status", data);
this.app.store.commit("gateway/set_ons_status", data);
break;
}
@ -237,6 +233,14 @@ export class Gateway extends EventEmitter {
this.app.store.commit("gateway/set_check_transaction_status", data);
break;
}
case "set_sign_status": {
this.app.store.commit("gateway/set_sign_status", decrypted_data.data);
break;
}
case "set_verify_status": {
this.app.store.commit("gateway/set_verify_status", decrypted_data.data);
break;
}
case "set_old_gui_import_status":
this.app.store.commit(
"gateway/set_old_gui_import_status",

View File

@ -243,7 +243,7 @@ export default {
copyViewKey: "View Key kopieren",
createNewWallet: "Neue Wallet erstellen",
deleteWallet: "Wallet löschen",
exit: "Loki GUI Wallet schliessen",
exit: "Oxen GUI Wallet schliessen",
importOldGUIWallet: "Wallets von alter GUI importieren",
manageKeyImages: "Key Images verwalten",
openWallet: "Wallet öffnen",
@ -332,7 +332,7 @@ export default {
noKeyImageExport: "Keine Key Images zum Exportieren gefunden",
usingLocalNode:
"Zugang zur Remote Node nicht möglich, wechsle zur lokalen Node",
usingRemoteNode: "llokid nicht gefunden, benutze eine Remote Node"
usingRemoteNode: "loxend nicht gefunden, benutze eine Remote Node"
}
},
placeholders: {
@ -408,9 +408,9 @@ export default {
destinationUnknown: "Ziel unbekannt",
editAddressBookEntry: "Adressbucheintrag bearbeiten",
loadingSettings: "Einstellungen werden geladen",
lokiBalance: "Guthaben",
lokiUnlockedBalance: "frei verfügbares Guthaben",
lokiUnlockedShort: "frei verfügbar",
oxenBalance: "Guthaben",
oxenUnlockedBalance: "frei verfügbares Guthaben",
oxenUnlockedShort: "frei verfügbar",
noTransactionsFound: "Keine Transaktionen gefunden",
notes: "Notizen",
numberOfUnspentOutputs: "Anzahl der unspent outputs",

View File

@ -12,6 +12,7 @@ export default {
close: "CLOSE",
contacts: "CONTACTS",
copyAddress: "COPY ADDRESS",
copyData: "COPY DATA",
copySignature: "COPY SIGNATURE",
createWallet: "CREATE WALLET",
decrypt: "DECRYPT",
@ -21,7 +22,7 @@ export default {
generate: "GENERATE",
import: "IMPORT",
importWallet: "IMPORT WALLET | IMPORT WALLETS",
lns: "LOKI NAME SERVICE",
ons: "OXEN NAME SERVICE",
max: "MAX",
min: "MIN",
next: "NEXT",
@ -42,10 +43,12 @@ export default {
settings: "SETTINGS",
showQRCode: "SHOW QR CODE",
showTxDetails: "SHOW TX DETAILS",
sign: "SIGN",
stake: "STAKE",
sweepAll: "SWEEP ALL",
unlock: "UNLOCK",
update: "UPDATE",
verify: "VERIFY",
viewOnExplorer: "VIEW ON EXPLORER"
},
dialog: {
@ -88,15 +91,20 @@ export default {
message: "Are you sure you want to exit?",
ok: "EXIT"
},
exportTransfers: {
title: "Export Transfers to CSV",
message: "Do you want to export transfers?",
export: "Export"
},
keyImages: {
title: "{type} key images",
message: "Do you want to {type} key images?",
export: "Export",
import: "Import"
},
lnsUpdate: {
title: "Update LNS record",
message: "Do you want to update the LNS record?",
onsUpdate: {
title: "Update ONS record",
message: "Do you want to update the ONS record?",
ok: "UPDATE"
},
noPassword: {
@ -139,6 +147,11 @@ export default {
message: "Do you want to view your private keys?",
ok: "SHOW"
},
signature: {
title: "Signature",
message:
"Copy the data signed by your primary address's private key below"
},
stake: {
title: "Stake",
message: "Do you want to stake?",
@ -202,8 +215,12 @@ export default {
confirmPassword: "CONFIRM PASSWORD",
daemonLogLevel: "DAEMON LOG LEVEL",
daemonP2pPort: "DAEMON P2P PORT",
data: "DATA",
dataStoragePath: "DATA STORAGE PATH",
decryptRecord: "DECRYPT RECORD",
exportTransfers: {
exportDirectory: "CSV EXPORT DIRECTORY"
},
filter: "FILTER",
filterTransactionType: "FILTER BY TRANSACTION TYPE",
internalWalletPort: "INTERNAL WALLET PORT",
@ -213,7 +230,7 @@ export default {
},
limitDownloadRate: "LIMIT DOWNLOAD RATE",
limitUploadRate: "LIMIT UPLOAD RATE",
lnsType: "LNS RECORD TYPE",
onsType: "ONS RECORD TYPE",
localDaemonIP: "LOCAL DAEMON IP",
localDaemonPort: "LOCAL DAEMON PORT",
lokinetFullAddress: "LOKINET FULL ADDRESS",
@ -239,6 +256,7 @@ export default {
sessionId: "SESSION ID",
signature: "SIGNATURE",
transactionId: "TRANSACTION ID",
walletAddress: "WALLET ADDRESS",
walletFile: "WALLET FILE",
walletLogLevel: "WALLET LOG LEVEL",
walletName: "WALLET NAME",
@ -282,7 +300,8 @@ export default {
copyViewKey: "Copy view key",
createNewWallet: "Create new wallet",
deleteWallet: "Delete Wallet",
exit: "Exit Loki GUI Wallet",
exportTransfers: "Export Transfers",
exit: "Exit Oxen GUI Wallet",
importOldGUIWallet: "Import wallets from old GUI",
manageKeyImages: "Manage Key Images",
openWallet: "Open wallet",
@ -304,13 +323,14 @@ export default {
backupOwnerCopied: "Backup owner copied to clipboard",
bannedPeer: "Banned {host} until {time}",
copied: "{item} copied to clipboard",
decryptedLNSRecord: "Successfully decrypted LNS Record for {name}",
decryptedONSRecord: "Successfully decrypted ONS Record for {name}",
exportTransfers: "Transfers exported to {filename}",
itemSaved: "{item} saved to {filename}",
keyImages: {
exported: "Key images exported to {filename}",
imported: "Key images imported"
},
lnsRecordUpdated: "LNS Record was successfully updated",
onsRecordUpdated: "ONS Record was successfully updated",
lokinetAddressCopied: "Full lokinet address copied",
lokinetNameCopied: "Lokinet name copied",
passwordUpdated: "Password updated",
@ -324,8 +344,10 @@ export default {
serviceNodeInfoFilled: "Service node key and min amount filled",
sessionIdCopied: "Session ID copied to clipboard",
signatureCopied: "Signature copied to clipboard",
signatureVerified: "Signature verified",
stakeSuccess: "Successfully staked",
transactionNotesSaved: "Transaction notes saved"
transactionNotesSaved: "Transaction notes saved",
walletCopied: "Wallet address copied to clipboard"
},
errors: {
banningPeer: "Error banning peer",
@ -335,7 +357,7 @@ export default {
copyWalletFail: "Failed to copy wallet",
copyingPrivateKeys: "Error copying private keys",
dataPathNotFound: "Data storage path not found",
decryptLNSRecord: "Failed to decrypt LNS Record for {name}",
decryptONSRecord: "Failed to decrypt ONS Record for {name}",
differentNetType: "Remote node is using a different nettype",
enterSeedWords: "Enter seed words",
enterTransactionId: "Enter transaction ID",
@ -343,6 +365,7 @@ export default {
enterWalletName: "Enter a wallet name",
enterName: "Enter a name",
errorSavingItem: "Error saving {item}",
exportTransfers: "Error exporting transfers",
failedServiceNodeUnlock: "Failed to unlock service node",
failedToSetLanguage: "Failed to set language: {lang}",
failedWalletImport: "Failed to import wallet",
@ -370,6 +393,7 @@ export default {
"Please enter the service node registration command",
invalidServiceNodeKey: "Service node key not valid",
invalidSessionId: "Session ID not valid",
invalidSignature: "Invalid signature",
invalidWalletPath: "Invalid wallet path",
keyImages: {
exporting: "Error exporting key images",
@ -389,21 +413,24 @@ export default {
zeroAmount: "Amount must be greater than zero"
},
warnings: {
noExportTransfers: "No transfers found to export",
noKeyImageExport: "No key images found to export",
usingLocalNode: "Could not access remote node, switching to local only",
usingRemoteNode: "lokid not found, using remote node"
usingRemoteNode: "oxend not found, using remote node"
}
},
placeholders: {
additionalNotes: "Additional notes",
addressBookName: "Name that belongs to this address",
addressOfSigner: "Public wallet address of signer",
dataToSign: "Data you want to sign with your primary address's private key",
filterTx: "Enter an ID, name, address or amount",
hexCharacters: "{count} hexadecimal characters",
lnsName: "The name to purchase via Loki Name Service",
lnsBackupOwner: "The wallet address of the backup owner",
lnsDecryptName: "A LNS name that belongs to you",
onsName: "The name to purchase via Oxen Name Service",
onsBackupOwner: "The wallet address of the backup owner",
onsDecryptName: "A ONS name that belongs to you",
lokinetFullAddress:
"Full lokinet address to map LNS name to (without .loki)",
"Full lokinet address to map ONS name to (without .loki)",
mnemonicSeed: "25 (or 24) word mnemonic seed",
pasteTransactionId: "Paste transaction ID",
pasteTransactionProof: "Paste transaction proof",
@ -411,8 +438,11 @@ export default {
"Optional message against which the signature is signed",
recipientWalletAddress: "Recipient's wallet address",
selectAFile: "Please select a file",
sessionId: "The Session ID to link to Loki Name Service",
sessionId: "The Session ID to link to Oxen Name Service",
signature: "Signature to verify",
transactionNotes: "Additional notes to locally attach to the transaction",
unsignedData: "The data as it should look before it was signed",
walletAddress: "Wallet address to map ONS name to",
walletName: "A name for your wallet",
walletPassword: "An optional password for the wallet"
},
@ -435,6 +465,7 @@ export default {
bannedUntil: "Banned until {time}"
},
blockHeight: "Height",
cannotSign: "You cannot sign with a view only wallet.",
checkTransaction: {
description:
"Verify that funds were paid to an address by supplying the transaction ID, the recipient address, the message used for signing and the signature.\nFor a 'Spend Proof' you dont need to provide the recipient address.",
@ -473,22 +504,27 @@ export default {
destinationUnknown: "Destination Unknown",
editAddressBookEntry: "Edit address book entry",
expirationHeight: "Expiration height",
lns: {
nextPayout: "Next payout",
ons: {
sessionID: "Session ID",
wallet: "Wallet Address",
lokinetName1Year: "Lokinet Name 1 year",
lokinetNameXYears: "Lokinet Name {years} years",
prices: "LNS Prices:"
prices: "ONS Prices:"
},
lnsPurchaseDescription:
"Purchase or update an LNS record. If you purchase a name, it may take a minute or two for it to show up in the list.",
lnsDescription:
"Here you can find all the LNS names owned by this wallet. Decrypting a record you own will return the name and value of that LNS record.",
onsPurchaseDescription:
"Purchase or update an ONS record. If you purchase a name, it may take a minute or two for it to show up in the list.",
onsDescription:
"Here you can find all the ONS names owned by this wallet. Decrypting a record you own will return the name and value of that ONS record.",
hardwareWallet: "Hardware wallet",
hardwareWallets: "Hardware wallets",
loadingSettings: "Loading settings",
lokiBalance: "Balance",
oxenBalance: "Balance",
lokinetNameDescription:
"Purchase or update a name on Lokinet. If you purchase a name it may take a minute or two for it to show up in the list. To learn more about lokinet visit: ",
lokiUnlockedBalance: "Unlocked balance",
lokiUnlockedShort: "Unlocked",
oxenAccumulatedRewards: "Accumulated rewards",
oxenUnlockedBalance: "Unlocked balance",
oxenUnlockedShort: "Unlocked",
me: "Me",
noTransactionsFound: "No transactions found",
notes: "Notes",
@ -511,6 +547,7 @@ export default {
recentIncomingTransactionsToAddress:
"Recent incoming transactions to this address",
recentTransactionsWithAddress: "Recent transactions with this address",
regularWallets: "Regular wallets",
rescanModalDescription:
"Select full rescan or rescan of spent outputs only.",
saveSeedWarning: "Please copy and save these in a secure location!",
@ -518,7 +555,7 @@ export default {
seedWords: "Seed words",
selectLanguage: "Select language",
serviceNodeContributionDescription:
"Staking contributes to the safety of the Loki network. For your contribution, you earn LOKI. Once staked, you will have to wait either 15 or 30 days to have your Loki unlocked, depending on if a stake was unlocked by a contributor or the node was deregistered. To learn more about staking, please visit the",
"Staking contributes to the safety of the Oxen network. For your contribution, you earn OXEN. Once staked, you will have to wait either 15 or 30 days to have your OXEN unlocked, depending on if a stake was unlocked by a contributor or the node was deregistered. To learn more about staking, please visit the documentation on the",
serviceNodeRegistrationDescription:
'Enter the {registerCommand} command produced by the daemon that is registering to become a Service Node using the "{prepareCommand}" command',
serviceNodeStartStakingDescription:
@ -540,7 +577,10 @@ export default {
stakingRequirement: "Staking requirement",
totalContributed: "Total contributed"
},
signAndVerifyDescription:
"Sign data with your primary address's private key or verify a signature against a public address.",
spendKey: "Spend key",
stake: "Staking",
startingDaemon: "Starting daemon",
startingWallet: "Starting wallet",
switchToDateSelect: "Switch to date select",
@ -585,20 +625,24 @@ export default {
addressDetails: "Address details",
advanced: {
checkTransaction: "CHECK TRANSACTION",
prove: "PROVE"
prove: "PROVE",
signAndVerify: "SIGN/VERIFY",
sign: "Sign",
verify: "Verify"
},
availableForContribution: "Service nodes available for contribution",
changePassword: "Change password",
configure: "Configure",
currentlyStakedNodes: "Currently staked nodes",
lnsRecordDetails: "LNS record details",
lnsSessionRecords: "Session records",
lnsLokinetRecords: "Lokinet records",
onsRecordDetails: "ONS record details",
onsSessionRecords: "Session records",
onsLokinetRecords: "Lokinet records",
onsWalletRecords: "Wallet records",
privateKeys: "Private keys",
rescanWallet: "Rescan wallet",
lns: {
ons: {
purchase: "PURCHASE",
myLns: "MY LNS"
myOns: "MY ONS"
},
serviceNode: {
registration: "REGISTRATION",

View File

@ -242,7 +242,7 @@ export default {
copyViewKey: "Copiar clave de visualización",
createNewWallet: "Crear nuevo monedero",
deleteWallet: "Eliminar monedero",
exit: "Cerrar la interfaz del monedero Loki",
exit: "Cerrar la interfaz del monedero Oxen",
importOldGUIWallet: "Importar monedero de una interfaz gráfica antigua",
manageKeyImages: "Administrar Imágenes de Clave",
openWallet: "Abrir monedero",
@ -330,7 +330,7 @@ export default {
noKeyImageExport: "No se han encontrado claves para exportar",
usingLocalNode:
"No se ha podido acceder al nodo remoto, volviendo al modo local",
usingRemoteNode: "lokid no encontrado, utilizando nodo remoto"
usingRemoteNode: "oxend no encontrado, utilizando nodo remoto"
}
},
placeholders: {
@ -403,9 +403,9 @@ export default {
destinationUnknown: "Destino Desconocido",
editAddressBookEntry: "Modificar un registro de la libreta de direcciones",
loadingSettings: "Cargando configuración",
lokiBalance: "Saldo",
lokiUnlockedBalance: "Saldo libre",
lokiUnlockedShort: "Libre",
oxenBalance: "Saldo",
oxenUnlockedBalance: "Saldo libre",
oxenUnlockedShort: "Libre",
noTransactionsFound: "No se han encontrado transacciones",
notes: "Notas",
numberOfUnspentOutputs: "Número de salidas no gastadas",
@ -431,7 +431,7 @@ export default {
seedWords: "Palabras semilla",
selectLanguage: "Escoja un idioma",
serviceNodeRegistrationDescription:
'Introduzca la orden {registerCommand} generada por el servicio (lokid) que se está intentado registrar como Nodo de Servicio usando la instrucción "{prepareCommand}"',
'Introduzca la orden {registerCommand} generada por el servicio (oxend) que se está intentado registrar como Nodo de Servicio usando la instrucción "{prepareCommand}"',
spendKey: "Clave de gasto",
startingDaemon: "Iniciando servicio",
startingWallet: "Iniciando monedero",

View File

@ -244,7 +244,7 @@ export default {
copyViewKey: "Copier la clé de visibilité",
createNewWallet: "Créer un nouveau portefeuille",
deleteWallet: "Supprimer le portefeuille",
exit: "Quitter le portefeuille Loki GUI",
exit: "Quitter le portefeuille Oxen GUI",
importOldGUIWallet: "Importer le portefeuille depuis lancien GUI",
manageKeyImages: "Gérer les images clés",
openWallet: "Ouvrir le portefeuille",
@ -334,7 +334,7 @@ export default {
noKeyImageExport: "Aucune clé image n'a été trouvé pour l'export",
usingLocalNode:
"Impossible d'accéder au nœud distant, basculement en local uniquement",
usingRemoteNode: "lokid introuvable, utilisation du nœud distant"
usingRemoteNode: "oxend introuvable, utilisation du nœud distant"
}
},
placeholders: {
@ -410,9 +410,9 @@ export default {
destinationUnknown: "Destination inconnue",
editAddressBookEntry: "Modifiez l'entrée du carnet d'adresses",
loadingSettings: "Chargement des réglages",
lokiBalance: "Solde",
lokiUnlockedBalance: "Solde débloqué",
lokiUnlockedShort: "Débloqué",
oxenBalance: "Solde",
oxenUnlockedBalance: "Solde débloqué",
oxenUnlockedShort: "Débloqué",
noTransactionsFound: "Aucune transaction trouvée",
notes: "Notes",
numberOfUnspentOutputs: "Nombre de sorties non dépensées",

View File

@ -330,7 +330,7 @@ export default {
noKeyImageExport: "Nenhuma chave de imagem encontrada para exportar",
usingLocalNode:
"Não foi possível aceder ao nódulo remoto, mudando para nódulo local apenas",
usingRemoteNode: "lokid não encontrado, utilizando nódulo remoto"
usingRemoteNode: "oxend não encontrado, utilizando nódulo remoto"
}
},
placeholders: {
@ -404,9 +404,9 @@ export default {
destinationUnknown: "Destino Desconhecido",
editAddressBookEntry: "Editar registo do livro de endereços",
loadingSettings: "Carregando configurações",
lokiBalance: "Saldo",
lokiUnlockedBalance: "Saldo desbloqueado",
lokiUnlockedShort: "Desbloqueado",
oxenBalance: "Saldo",
oxenUnlockedBalance: "Saldo desbloqueado",
oxenUnlockedShort: "Desbloqueado",
noTransactionsFound: "Nenhuma transação encontrada",
notes: "Notas",
numberOfUnspentOutputs: "Número de outputs não gastos",

View File

@ -241,7 +241,7 @@ export default {
copyViewKey: "Копировать Ключ Просмотра",
createNewWallet: "Создать новый кошелек",
deleteWallet: "Удалить Кошелек",
exit: "Закрыть Кошелек Loki",
exit: "Закрыть Кошелек Oxen",
importOldGUIWallet: "Импортировать кошельки из старого GUI",
manageKeyImages: "Управлять Ключевыми Образами",
openWallet: "Открыть кошелек",
@ -329,7 +329,7 @@ export default {
noKeyImageExport: "Не найдено ключевых образов для экспорта",
usingLocalNode:
"Не удалось подключиться к удаленной ноде, переключаемся на локальную ноду",
usingRemoteNode: "Не найден файл lokid, используется удаленная нода"
usingRemoteNode: "Не найден файл oxend, используется удаленная нода"
}
},
placeholders: {
@ -403,9 +403,9 @@ export default {
destinationUnknown: "Назначение Неизвестно",
editAddressBookEntry: "Редактировать запись адресной книги",
loadingSettings: "Загрузка настроек",
lokiBalance: "Баланс",
lokiUnlockedBalance: "Разблокированый баланс",
lokiUnlockedShort: "Разблокировано",
oxenBalance: "Баланс",
oxenUnlockedBalance: "Разблокированый баланс",
oxenUnlockedShort: "Разблокировано",
noTransactionsFound: "Транзакции не найдены",
notes: "Заметки",
numberOfUnspentOutputs: "Количество непотраченных выходов",

View File

@ -6,12 +6,24 @@
<MainMenu :disable-switch-wallet="true" />
</template>
<template v-else>
<q-btn class="cancel" icon="reply" flat round dense @click="cancel()" />
<q-btn
class="cancel"
icon="reply"
flat
round
dense
@click="cancel()"
/>
</template>
<q-toolbar-title v-if="page_title == 'Loki'" class="flex items-center justify-center">
<img src="loki.svg" height="32" />
<q-toolbar-title
v-if="page_title == 'Oxen'"
class="flex items-center justify-center"
>
<img src="oxen-white.svg" height="32" />
</q-toolbar-title>
<q-toolbar-title v-else class="flex items-center justify-center">{{ page_title }}</q-toolbar-title>
<q-toolbar-title v-else class="flex items-center justify-center">{{
page_title
}}</q-toolbar-title>
</q-toolbar>
</q-header>
@ -58,7 +70,7 @@ export default {
default:
case "wallet-select":
return "Loki";
return "Oxen";
}
}
},

View File

@ -2,9 +2,9 @@
<q-layout view="hHh Lpr lFf">
<q-header class="shift-title">
<MainMenu />
<q-toolbar-title>
<div class="flex items-center justify-center" style="margin-top:7px">
<img src="loki.svg" height="32" />
<q-toolbar-title style="background: white;">
<div class="flex items-center justify-center" style="margin:8px">
<img src="oxen.svg" height="32" />
</div>
</q-toolbar-title>
</q-header>
@ -27,16 +27,40 @@
/>
</router-link>
<router-link to="/wallet/receive">
<q-btn class="large-btn" :label="$t('buttons.receive')" size="md" icon-right="save_alt" align="between" />
<q-btn
class="large-btn"
:label="$t('buttons.receive')"
size="md"
icon-right="save_alt"
align="between"
/>
</router-link>
<router-link to="/wallet/servicenode">
<q-btn class="large-btn" :label="$t('buttons.serviceNode')" size="md" icon-right="router" align="between" />
<q-btn
class="large-btn"
:label="$t('buttons.serviceNode')"
size="md"
icon-right="router"
align="between"
/>
</router-link>
<router-link to="/wallet/lns">
<q-btn class="large-btn" :label="$t('buttons.lns')" size="md" icon-right="text_fields" align="between" />
<router-link to="/wallet/ons">
<q-btn
class="large-btn"
:label="$t('buttons.ons')"
size="md"
icon-right="text_fields"
align="between"
/>
</router-link>
<router-link to="/wallet/advanced">
<q-btn class="large-btn" :label="$t('buttons.advanced')" size="md" icon-right="tune" align="between" />
<q-btn
class="large-btn"
:label="$t('buttons.advanced')"
size="md"
icon-right="tune"
align="between"
/>
</router-link>
<router-link to="/wallet/addressbook" class="address">
<q-btn class="single-icon" size="md" icon="person" />

View File

@ -1,27 +1,52 @@
export default {
methods: {
getMinContribution(node) {
const MAX_NUMBER_OF_CONTRIBUTORS = 4;
// This is calculated in the same way it is calculated on the LokiBlocks site
const openContributionRemaining = this.openForContribution(node);
const minContributionAtomicUnits =
!node.funded && node.contributors.length < MAX_NUMBER_OF_CONTRIBUTORS
? openContributionRemaining /
(MAX_NUMBER_OF_CONTRIBUTORS - node.contributors.length)
: 0;
const minContributionLoki = minContributionAtomicUnits / 1e9;
// ceiling to 4 decimal places
return minContributionLoki.toFixed(4);
// Returns the atomic, unfilled, reserved contributions for the given wallet
getUnfilledReservedContribution(node, addr) {
for (const contributor of node.contributors) {
if (contributor.address === addr && contributor.amount === 0 && contributor.reserved > 0) {
return contributor.reserved;
}
}
return 0;
},
openForContribution(node) {
const openContributionRemaining =
getMinContribution(node, myaddr) {
if (node.funded) {
return 0;
}
const MAX_NUMBER_OF_CONTRIBUTORS = 10;
// If we have a reserved spot then that is our minimum:
let minContributionAtomicUnits = this.getUnfilledReservedContribution(node, myaddr);
// Otherwise we can contribute our fair share of whatever amount is left (i.e. REMAINING/N
// when there are N available spots).
if (minContributionAtomicUnits === 0 && node.contributors.length < MAX_NUMBER_OF_CONTRIBUTORS) {
const openContributionRemaining = this.openForContribution(node);
let contributors_length = 0;
for (const contributor of node.contributors) {
contributors_length = contributors_length + contributor.locked_contributions.length;
}
minContributionAtomicUnits = openContributionRemaining /
(MAX_NUMBER_OF_CONTRIBUTORS - contributors_length);
}
const minContributionOxen = minContributionAtomicUnits / 1e9;
// ceiling to 4 decimal places
return minContributionOxen.toFixed(4);
},
openForContribution(node, addr = null) {
let openContributionRemaining =
node.staking_requirement > node.total_reserved
? node.staking_requirement - node.total_reserved
: 0;
if (addr) {
openContributionRemaining += this.getUnfilledReservedContribution(node, addr);
}
return openContributionRemaining;
},
openForContributionLoki(node) {
return (this.openForContribution(node) / 1e9).toFixed(4);
openForContributionOxen(node, addr = null) {
return (this.openForContribution(node, addr) / 1e9).toFixed(4);
}
}
};

View File

@ -2,7 +2,7 @@
<q-page>
<div class="init-screen-page text-center">
<div class="absolute-center">
<img src="loki.svg" width="400" class="q-mb-md" />
<img src="oxen.svg" width="400" class="q-mb-md" />
<div class="startup-icons q-mt-xl q-mb-lg">
<div ref="backend">
@ -62,10 +62,34 @@
d="M7.494,14.958 C3.361,14.958 0,11.622 0,7.52 C0,3.418 3.361,0.082 7.494,0.082 C11.627,0.082 14.989,3.418 14.989,7.52 C14.989,11.622 11.627,14.958 7.494,14.958 L7.494,14.958 Z M7.51,0.938 C3.887,0.938 0.938,3.886 0.938,7.51 C0.938,11.135 3.887,14.083 7.51,14.083 C11.135,14.083 14.083,11.135 14.083,7.51 C14.083,3.886 11.135,0.938 7.51,0.938 L7.51,0.938 Z"
class="si-glyph-fill"
></path>
<rect x="7" y="1" width="0.922" height="14.084" class="si-glyph-fill"></rect>
<rect x="0" y="7" width="13.96" height="0.922" class="si-glyph-fill"></rect>
<rect x="1" y="4" width="12.406" height="0.906" class="si-glyph-fill"></rect>
<rect x="1" y="10" width="12.406" height="0.922" class="si-glyph-fill"></rect>
<rect
x="7"
y="1"
width="0.922"
height="14.084"
class="si-glyph-fill"
></rect>
<rect
x="0"
y="7"
width="13.96"
height="0.922"
class="si-glyph-fill"
></rect>
<rect
x="1"
y="4"
width="12.406"
height="0.906"
class="si-glyph-fill"
></rect>
<rect
x="1"
y="10"
width="12.406"
height="0.922"
class="si-glyph-fill"
></rect>
<path
d="M7.317,14.854 C4.72,13.581 3.043,10.662 3.043,7.417 C3.043,4.247 4.666,1.355 7.181,0.05 L7.642,0.937 C5.455,2.074 4.043,4.617 4.043,7.417 C4.043,10.282 5.502,12.849 7.757,13.955 L7.317,14.854 L7.317,14.854 Z"
class="si-glyph-fill"
@ -112,7 +136,9 @@
{{ message }}
</div>
<div v-if="daemonStatus" class="q-mt-xs">{{ $t("strings.syncingDaemon") }}: {{ daemonStatus }}</div>
<div v-if="daemonStatus" class="q-mt-xs">
{{ $t("strings.syncingDaemon") }}: {{ daemonStatus }}
</div>
</div>
<div class="absolute-bottom">
@ -144,7 +170,10 @@ export default {
if (this.status.code < 3 || !this.isLocalDaemon) return null;
const currentHeight = this.daemon.info.height_without_bootstrap;
const targetHeight = Math.max(this.daemon.info.height, this.daemon.info.target_height);
const targetHeight = Math.max(
this.daemon.info.height,
this.daemon.info.target_height
);
const percentage = ((100 * currentHeight) / targetHeight).toFixed(1);
if (targetHeight === 0 || currentHeight >= targetHeight) return null;
@ -200,7 +229,8 @@ export default {
this.$q.notify({
type: "warning",
timeout: 2000,
message: "Warning: " + this.$t("notification.warnings.usingRemoteNode")
message:
"Warning: " + this.$t("notification.warnings.usingRemoteNode")
});
break;
case 6:
@ -224,6 +254,9 @@ export default {
</script>
<style lang="scss">
.message {
color: #1f1c47;
}
.init-screen-page {
height: 100vh;
position: relative;

View File

@ -2,7 +2,7 @@
<q-page>
<div class="init-screen-page text-center">
<div class="absolute-center">
<img src="loki.svg" width="400" class="q-mb-md" />
<img src="oxen.svg" width="400" class="q-mb-md" />
<div class="q-mt-xl q-mb-lg">
<q-spinner color="primary" :size="30" />

View File

@ -8,7 +8,7 @@
class="first-step"
>
<div class="welcome-container">
<img src="loki.svg" height="100" class="q-mb-md" />
<img src="oxen.svg" height="100" class="q-mb-md" />
<div>Wallet Version: v{{ version }}</div>
<div>Daemon Version: {{ daemonVersion }}</div>
<LanguageSelect class="q-mt-lg" @select="onLanguageSelected" />
@ -105,7 +105,6 @@ export default {
.welcome-stepper {
height: 100%;
// the Loki lighter grey is behind it
background: transparent;
}

View File

@ -1,7 +1,7 @@
<template>
<q-page class="create-wallet">
<div class="fields q-mx-md q-mt-md">
<LokiField
<OxenField
:label="$t('fieldLabels.walletName')"
:error="$v.wallet.name.$error"
>
@ -14,21 +14,20 @@
@keyup.enter="create"
@blur="$v.wallet.name.$touch"
/>
</LokiField>
</OxenField>
<LokiField :label="$t('fieldLabels.seedLanguage')">
<OxenField :label="$t('fieldLabels.seedLanguage')">
<q-select
v-model="wallet.language"
:options="languageOptions"
:dark="theme == 'dark'"
borderless
dense
emit-value
map-options
/>
</LokiField>
</OxenField>
<LokiField :label="$t('fieldLabels.password')" optional>
<OxenField :label="$t('fieldLabels.password')" optional>
<q-input
v-model="wallet.password"
type="password"
@ -38,9 +37,9 @@
dense
@keyup.enter="create"
/>
</LokiField>
</OxenField>
<LokiField :label="$t('fieldLabels.confirmPassword')">
<OxenField :label="$t('fieldLabels.confirmPassword')">
<q-input
v-model="wallet.password_confirm"
type="password"
@ -49,14 +48,34 @@
dense
@keyup.enter="create"
/>
</LokiField>
</OxenField>
<q-btn
class="submit-button"
color="primary"
:label="$t('buttons.createWallet')"
@click="create"
/>
<q-field class="q-pb-sm">
<q-checkbox
v-model="wallet.hardware_wallet"
:label="$t('strings.hardwareWallet')"
/>
</q-field>
<OxenField
v-if="!wallet.hardware_wallet"
:label="$t('fieldLabels.seedLanguage')"
>
<q-select
v-model="wallet.language"
:options="languageOptions"
:dark="theme == 'dark'"
hide-underline
/>
</OxenField>
<q-field>
<q-btn
color="primary"
:label="$t('buttons.createWallet')"
@click="create"
/>
</q-field>
</div>
</q-page>
</template>
@ -64,10 +83,10 @@
<script>
import { required } from "vuelidate/lib/validators";
import { mapState } from "vuex";
import LokiField from "components/loki_field";
import OxenField from "components/oxen_field";
export default {
components: {
LokiField
OxenField
},
data() {
const languageOptions = [
@ -89,7 +108,8 @@ export default {
name: "",
language: languageOptions[0].value,
password: "",
password_confirm: ""
password_confirm: "",
hardware_wallet: false
},
languageOptions
};
@ -167,11 +187,9 @@ export default {
},
cancel: {
flat: true,
label: this.$t("dialog.buttons.cancel"),
color: this.theme === "dark" ? "white" : "dark"
label: this.$t("dialog.buttons.cancel")
},
dark: this.theme == "dark",
color: "positive"
color: "primary"
});
passwordPromise
.onOk(() => {

View File

@ -7,8 +7,18 @@
{{ info.address }}
</div>
<div class="q-item-side">
<q-btn color="primary" padding="xs" size="sm" icon="file_copy" @click="copyAddress">
<q-tooltip anchor="center left" self="center right" :offset="[5, 10]">
<q-btn
color="primary"
padding="xs"
size="sm"
icon="file_copy"
@click="copyAddress"
>
<q-tooltip
anchor="center left"
self="center right"
:offset="[5, 10]"
>
{{ $t("menuItems.copyAddress") }}
</q-tooltip>
</q-btn>
@ -37,7 +47,10 @@
</div>
</template>
<q-expansion-item label="Advanced" header-class="q-mt-sm non-selectable row reverse advanced-options-label">
<q-expansion-item
label="Advanced"
header-class="q-mt-sm non-selectable row reverse advanced-options-label"
>
<template v-if="secret.view_key != secret.spend_key">
<h6 class="q-mb-xs title">{{ $t("strings.viewKey") }}</h6>
<div class="row">
@ -45,8 +58,18 @@
{{ secret.view_key }}
</div>
<div class="q-item-side">
<q-btn color="primary" padding="xs" size="sm" icon="file_copy" @click="copyPrivateKey('view_key', $event)">
<q-tooltip anchor="center left" self="center right" :offset="[5, 10]">
<q-btn
color="primary"
padding="xs"
size="sm"
icon="file_copy"
@click="copyPrivateKey('view_key', $event)"
>
<q-tooltip
anchor="center left"
self="center right"
:offset="[5, 10]"
>
{{ $t("menuItems.copyViewKey") }}
</q-tooltip>
</q-btn>
@ -61,8 +84,18 @@
{{ secret.spend_key }}
</div>
<div class="q-item-side">
<q-btn color="primary" padding="xs" size="sm" icon="file_copy" @click="copyPrivateKey('spend_key', $event)">
<q-tooltip anchor="center left" self="center right" :offset="[5, 10]">
<q-btn
color="primary"
padding="xs"
size="sm"
icon="file_copy"
@click="copyPrivateKey('spend_key', $event)"
>
<q-tooltip
anchor="center left"
self="center right"
:offset="[5, 10]"
>
{{ $t("menuItems.copySpendKey") }}
</q-tooltip>
</q-btn>
@ -71,7 +104,12 @@
</template>
</q-expansion-item>
<q-btn class="q-mt-lg" color="primary" :label="$t('buttons.openWallet')" @click="open" />
<q-btn
class="q-mt-lg"
color="primary"
:label="$t('buttons.openWallet')"
@click="open"
/>
</q-page>
</template>
@ -137,9 +175,7 @@ export default {
ok: {
label: this.$t("dialog.buttons.ok"),
color: "primary"
},
color: this.theme === "dark" ? "white" : "dark",
dark: this.theme === "dark"
}
})
.onDismiss(() => null)
.onCancel(() => null)
@ -166,45 +202,4 @@ export default {
};
</script>
<style lang="scss">
.created {
.wallet h6 {
text-align: center;
}
.address {
text-align: center;
word-break: break-all;
}
.seed-box {
border: 1px solid white;
border-radius: 3px;
margin: 16px;
padding: 16px;
div,
h6 {
text-align: center;
}
.seed {
font-size: 24px;
font-weight: 600;
}
.warning {
color: goldenrod;
}
}
h6 {
font-size: 18px;
margin: 8px 0;
font-weight: 450;
}
.advanced-options-label {
padding-left: 0;
padding-right: 0;
}
}
</style>
<style lang="scss"></style>

View File

@ -2,13 +2,20 @@
<q-page>
<div class="q-mx-md import-old-gui">
<q-list link dark no-border class="wallet-list">
<q-item v-for="state in directory_state" :key="state.directory" :class="{ selected: state.selected }">
<q-item
v-for="state in directory_state"
:key="state.directory"
:class="{ selected: state.selected }"
>
<q-item-section>
<div class="row items-center">
<q-item-label class="items-center">
<q-checkbox v-model="state.selected" dark color="dark" />
</q-item-label>
<q-item-label class="wallet-name" @click.native="state.selected = !state.selected">
<q-item-label
class="wallet-name"
@click.native="state.selected = !state.selected"
>
{{ state.directory }}
</q-item-label>
</div>
@ -19,7 +26,7 @@
v-model="state.type"
hide-underline
dark
class="q-ma-none full-width"
class="q-ma-none full-width nettype-select"
:options="selectOptions"
emit-value
map-options
@ -93,7 +100,9 @@ export default {
this.$q.notify({
type: "negative",
timeout: 3000,
message: this.$t("notification.errors.failedWalletImport") + `: ${wallet}`
message:
this.$t("notification.errors.failedWalletImport") +
`: ${wallet}`
});
});
}
@ -109,7 +118,9 @@ export default {
methods: {
populate_state() {
// Keep any directories that intersect
const new_state = this.directory_state.filter(state => this.directories.includes(state.directory));
const new_state = this.directory_state.filter(state =>
this.directories.includes(state.directory)
);
// Add in new directories
this.directories

View File

@ -1,7 +1,10 @@
<template>
<q-page>
<div class="q-mx-md import-wallet">
<LokiField :label="$t('fieldLabels.newWalletName')" :error="$v.wallet.name.$error">
<OxenField
:label="$t('fieldLabels.newWalletName')"
:error="$v.wallet.name.$error"
>
<q-input
v-model="wallet.name"
:placeholder="$t('placeholders.walletName')"
@ -11,9 +14,13 @@
@keyup.enter="import_wallet"
@blur="$v.wallet.name.$touch"
/>
</LokiField>
</OxenField>
<LokiField :label="$t('fieldLabels.walletFile')" disable-hover :error="$v.wallet.path.$error">
<OxenField
:label="$t('fieldLabels.walletFile')"
disable-hover
:error="$v.wallet.path.$error"
>
<q-input
v-model="wallet.path"
:placeholder="$t('placeholders.selectAFile')"
@ -22,16 +29,22 @@
borderless
dense
/>
<input id="walletPath" ref="fileInput" type="file" hidden @change="setWalletPath" />
<input
id="walletPath"
ref="fileInput"
type="file"
hidden
@change="setWalletPath"
/>
<q-btn
color="secondary"
color="primary"
:label="$t('buttons.selectWalletFile')"
:text-color="theme == 'dark' ? 'white' : 'dark'"
@click="selectFile"
/>
</LokiField>
</OxenField>
<LokiField :label="$t('fieldLabels.password')">
<OxenField :label="$t('fieldLabels.password')">
<q-input
v-model="wallet.password"
:placeholder="$t('placeholders.walletPassword')"
@ -41,9 +54,9 @@
dense
@keyup.enter="import_wallet"
/>
</LokiField>
</OxenField>
<LokiField :label="$t('fieldLabels.confirmPassword')">
<OxenField :label="$t('fieldLabels.confirmPassword')">
<q-input
v-model="wallet.password_confirm"
type="password"
@ -52,8 +65,13 @@
dense
@keyup.enter="import_wallet"
/>
</LokiField>
<q-btn class="submit-button" color="primary" :label="$tc('buttons.importWallet', 1)" @click="import_wallet" />
</OxenField>
<q-btn
class="submit-button"
color="primary"
:label="$tc('buttons.importWallet', 1)"
@click="import_wallet"
/>
</div>
</q-page>
</template>
@ -61,10 +79,10 @@
<script>
import { required } from "vuelidate/lib/validators";
import { mapState } from "vuex";
import LokiField from "components/loki_field";
import OxenField from "components/oxen_field";
export default {
components: {
LokiField
OxenField
},
data() {
return {
@ -172,7 +190,7 @@ export default {
}
}
.loki-field {
.oxen-field {
margin-top: 16px;
}
}

View File

@ -1,15 +1,26 @@
<template>
<q-page>
<q-list class="wallet-list" no-border :dark="theme == 'dark'">
<template v-if="wallets.list.length">
<q-list class="wallet-list" link no-border :dark="theme == 'dark'">
<template v-if="wallet_list.length">
<div class="header row justify-between items-center">
<div class="header-title">
{{ $t("titles.yourWallets") }}
</div>
<q-btn v-if="wallets.list.length" class="add" icon="add" size="md" color="primary">
<q-btn
v-if="wallets.list.length"
class="add"
icon="add"
size="md"
color="primary"
>
<q-menu class="header-popover" :content-class="'header-popover'">
<q-list separator>
<q-item v-for="action in actions" :key="action.name" clickable @click.native="action.handler">
<q-item
v-for="action in actions"
:key="action.name"
clickable
@click.native="action.handler"
>
<q-item-section>
{{ action.name }}
</q-item-section>
@ -19,55 +30,43 @@
</q-btn>
</div>
<div class="hr-separator" />
<q-item
v-for="wallet in wallets.list"
<!-- Hardware wallets -->
<q-list-header v-if="hardware_wallets.length">
<div class="header row justify-between items-center">
<div class="header-title">
{{ $t("strings.hardwareWallets") }}
</div>
</div>
</q-list-header>
<WalletListItem
v-for="wallet in hardware_wallets"
:key="`${wallet.address}-${wallet.name}`"
@click.native="openWallet(wallet)"
>
<q-item-section avatar>
<q-icon class="wallet-icon">
<svg
width="48"
viewBox="0 0 17 16"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="si-glyph si-glyph-wallet"
>
<defs class="si-glyph-fill"></defs>
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(1.000000, 0.000000)" fill="#434343">
<path
d="M7.988,10.635 L7.988,8.327 C7.988,7.578 8.561,6.969 9.267,6.969 L13.964,6.969 L13.964,5.531 C13.964,4.849 13.56,4.279 13.007,4.093 L13.007,4.094 L11.356,4.08 L11.336,4.022 L3.925,4.022 L3.784,4.07 L1.17,4.068 L1.165,4.047 C0.529,4.167 0.017,4.743 0.017,5.484 L0.017,13.437 C0.017,14.269 0.665,14.992 1.408,14.992 L12.622,14.992 C13.365,14.992 13.965,14.316 13.965,13.484 L13.965,12.031 L9.268,12.031 C8.562,12.031 7.988,11.384 7.988,10.635 L7.988,10.635 Z"
class="si-glyph-fill"
></path>
<path
d="M14.996,8.061 L14.947,8.061 L9.989,8.061 C9.46,8.061 9.031,8.529 9.031,9.106 L9.031,9.922 C9.031,10.498 9.46,10.966 9.989,10.966 L14.947,10.966 L14.996,10.966 C15.525,10.966 15.955,10.498 15.955,9.922 L15.955,9.106 C15.955,8.528 15.525,8.061 14.996,8.061 L14.996,8.061 Z M12.031,10.016 L9.969,10.016 L9.969,9 L12.031,9 L12.031,10.016 L12.031,10.016 Z"
class="si-glyph-fill"
></path>
<path
d="M3.926,4.022 L10.557,1.753 L11.337,4.022 L12.622,4.022 C12.757,4.022 12.885,4.051 13.008,4.092 L11.619,0.051 L1.049,3.572 L1.166,4.048 C1.245,4.033 1.326,4.023 1.408,4.023 L3.926,4.023 L3.926,4.022 Z"
class="si-glyph-fill"
></path>
</g>
</g>
</svg>
</q-icon>
</q-item-section>
<q-item-section>
<q-item-label class="wallet-name" caption>{{ wallet.name }}</q-item-label>
<q-item-label class="monospace ellipsis" caption>{{ wallet.address }}</q-item-label>
</q-item-section>
<ContextMenu
:menu-items="menuItems"
@openWallet="openWallet(wallet)"
@copyAddress="copyAddress(wallet.address)"
/>
</q-item>
<q-separator />
:wallet="wallet"
:open-wallet="openWallet"
/>
<!-- Regular wallets -->
<q-list-header v-if="hardware_wallets.length">
<div class="header row justify-between items-center">
<div class="header-title">
{{ $t("strings.regularWallets") }}
</div>
</div>
</q-list-header>
<WalletListItem
v-for="wallet in regular_wallets"
:key="`${wallet.address}-${wallet.name}`"
:wallet="wallet"
:open-wallet="openWallet"
/>
<q-item-separator />
</template>
<template v-else>
<q-item v-for="action in actions" :key="action.name" @click.native="action.handler">
<q-item
v-for="action in actions"
:key="action.name"
@click.native="action.handler"
>
<q-item-section>
{{ action.name }}
</q-item-section>
@ -78,27 +77,24 @@
</template>
<script>
const { clipboard } = require("electron");
import { mapState } from "vuex";
import ContextMenu from "components/menus/contextmenu";
import WalletListItem from "components/wallet_list_item";
export default {
components: {
ContextMenu
},
data() {
const menuItems = [
{ action: "openWallet", i18n: "menuItems.openWallet" },
{ action: "copyAddress", i18n: "menuItems.copyAddress" }
];
return {
menuItems
};
WalletListItem
},
computed: mapState({
theme: state => state.gateway.app.config.appearance.theme,
wallets: state => state.gateway.wallets,
wallet_list: state => state.gateway.wallets.list,
status: state => state.gateway.wallet.status,
hardware_wallets() {
return this.wallet_list.filter(w => w.hardware_wallet);
},
regular_wallets() {
return this.wallet_list.filter(w => !w.hardware_wallet);
},
actions() {
// TODO: Add this in once LOKI has the functionality
// <q-item @click.native="restoreViewWallet()">
@ -173,15 +169,15 @@ export default {
type: "password"
},
ok: {
label: this.$t("dialog.buttons.open")
label: this.$t("dialog.buttons.open"),
color: "primary"
},
cancel: {
flat: true,
label: this.$t("dialog.buttons.cancel"),
color: this.theme == "dark" ? "white" : "dark"
label: this.$t("dialog.buttons.cancel")
},
dark: this.theme == "dark",
color: "positive"
color: "#1F1C47",
persistent: true
})
.onOk(password => {
this.$q.loading.show({
@ -221,14 +217,6 @@ export default {
},
importLegacyWallet() {
this.$router.replace({ path: "wallet-select/import-legacy" });
},
copyAddress(address) {
clipboard.writeText(address);
this.$q.notify({
type: "positive",
timeout: 1000,
message: this.$t("notification.positive.addressCopied")
});
}
}
};

View File

@ -1,7 +1,7 @@
<template>
<q-page>
<div class="q-mx-md">
<LokiField
<OxenField
class="q-mt-md"
:label="$t('fieldLabels.walletName')"
:error="$v.wallet.name.$error"
@ -9,34 +9,32 @@
<q-input
v-model="wallet.name"
:placeholder="$t('placeholders.walletName')"
:dark="theme == 'dark'"
borderless
dense
@keyup.enter="restore_wallet"
@blur="$v.wallet.name.$touch"
/>
</LokiField>
</OxenField>
<LokiField
<OxenField
class="q-mt-md"
:label="$t('fieldLabels.mnemonicSeed')"
:error="$v.wallet.seed.$error"
>
<q-input
v-model="wallet.seed"
class="full-width text-area-loki"
class="full-width text-area-oxen"
:placeholder="$t('placeholders.mnemonicSeed')"
type="textarea"
:dark="theme == 'dark'"
borderless
dense
@blur="$v.wallet.seed.$touch"
/>
</LokiField>
</OxenField>
<div class="row items-end q-mt-md">
<div class="col-md-9 col-sm-8">
<LokiField
<OxenField
v-if="wallet.refresh_type == 'date'"
:label="$t('fieldLabels.restoreFromDate')"
>
@ -59,7 +57,6 @@
>
<q-date
v-model="wallet.refresh_start_date"
:dark="theme == 'dark'"
:options="dateRangeOptions"
>
<div class="row items-center justify-end">
@ -75,8 +72,8 @@
</q-icon>
</template>
</q-input>
</LokiField>
<LokiField
</OxenField>
<OxenField
v-else-if="wallet.refresh_type == 'height'"
:label="$t('fieldLabels.restoreFromBlockHeight')"
:error="$v.wallet.refresh_start_height.$error"
@ -90,13 +87,12 @@
dense
@blur="$v.wallet.refresh_start_height.$touch"
/>
</LokiField>
</OxenField>
</div>
<div class="col-sm-4 col-md-3">
<template v-if="wallet.refresh_type == 'date'">
<q-btn
class="restore-from-button"
:text-color="theme == 'dark' ? 'white' : 'dark'"
flat
@click="wallet.refresh_type = 'height'"
>
@ -109,7 +105,6 @@
<template v-else-if="wallet.refresh_type == 'height'">
<q-btn
class="restore-from-button"
:text-color="theme == 'dark' ? 'white' : 'dark'"
flat
@click="wallet.refresh_type = 'date'"
>
@ -122,7 +117,7 @@
</div>
</div>
<LokiField class="q-mt-md" :label="$t('fieldLabels.password')">
<OxenField class="q-mt-md" :label="$t('fieldLabels.password')">
<q-input
v-model="wallet.password"
:placeholder="$t('placeholders.walletPassword')"
@ -132,9 +127,9 @@
dense
@keyup.enter="restore_wallet"
/>
</LokiField>
</OxenField>
<LokiField class="q-mt-md" :label="$t('fieldLabels.confirmPassword')">
<OxenField class="q-mt-md" :label="$t('fieldLabels.confirmPassword')">
<q-input
v-model="wallet.password_confirm"
type="password"
@ -143,7 +138,7 @@
dense
@keyup.enter="restore_wallet"
/>
</LokiField>
</OxenField>
<q-btn
class="submit-button"
color="primary"
@ -157,7 +152,7 @@
<script>
import { required, numeric } from "vuelidate/lib/validators";
import { mapState } from "vuex";
import LokiField from "components/loki_field";
import OxenField from "components/oxen_field";
import { date } from "quasar";
import _ from "lodash";
@ -167,7 +162,7 @@ let dateFirstBlock = date.formatDate(timeStampFirstBlock, qDateFormat);
export default {
components: {
LokiField
OxenField
},
data() {
return {

View File

@ -7,11 +7,11 @@
</div>
<template v-if="address_book_combined.length">
<q-list link no-border :dark="theme == 'dark'" class="loki-list">
<q-list link no-border :dark="theme == 'dark'" class="oxen-list">
<q-item
v-for="(entry, index) in address_book_combined"
:key="`${entry.address}-${entry.name}-${index}`"
class="loki-list-item"
class="oxen-list-item"
@click.native="details(entry)"
>
<q-item-section>
@ -27,7 +27,7 @@
:name="entry.starred ? 'star' : 'star_border'"
/>
<q-btn
color="secondary"
color="primary"
style="margin-left: 10px;"
:label="$t('buttons.send')"
:disabled="view_only"
@ -45,7 +45,7 @@
</q-list>
</template>
<template v-else>
<p class="q-ma-md">{{ $t("strings.addressBookIsEmpty") }}</p>
<p class="q-ma-md tab-desc">{{ $t("strings.addressBookIsEmpty") }}</p>
</template>
<q-page-sticky position="bottom-right" :offset="[18, 18]">
@ -126,9 +126,10 @@ export default {
.header {
font-size: 14px;
font-weight: 500;
color: #1f1c47;
}
.loki-list-item {
.oxen-list-item {
cursor: pointer;
padding-top: 12px;
padding-bottom: 12px;

View File

@ -4,28 +4,36 @@
<q-btn-toggle
v-model="screen"
toggle-color="primary"
color="secondary"
color="accent"
:options="[
{ label: $t('titles.advanced.prove'), value: 'prove' },
{
label: $t('titles.advanced.checkTransaction'),
value: 'check'
},
{
label: $t('titles.advanced.signAndVerify'),
value: 'signAndVerify'
}
]"
/>
</div>
<ProveTransaction v-if="screen === 'prove'" />
<CheckTransaction v-if="screen === 'check'" />
<SignAndVerify v-if="screen === 'signAndVerify'" />
</q-page>
</template>
<script>
import ProveTransaction from "components/prove_transaction";
import CheckTransaction from "components/check_transaction";
import ProveTransaction from "components/advanced/prove_transaction";
import CheckTransaction from "components/advanced/check_transaction";
import SignAndVerify from "components/advanced/sign_and_verify";
export default {
components: {
ProveTransaction,
CheckTransaction
CheckTransaction,
SignAndVerify
},
data() {
return {

View File

@ -1,37 +1,37 @@
<template>
<q-page class="lns-page">
<q-page class="ons-page">
<div class="header row items-center justify-center q-pt-md">
<q-btn-toggle
v-model="screen"
toggle-color="primary"
color="secondary"
color="accent"
:options="[
{
label: $t('titles.lns.purchase'),
label: $t('titles.ons.purchase'),
value: 'purchase'
},
{
label: $t('titles.lns.myLns'),
value: 'my_lns'
label: $t('titles.ons.myOns'),
value: 'my_ons'
}
]"
/>
</div>
<LNSPurchase v-if="screen === 'purchase'" ref="purchase" />
<MyLNS v-if="screen === 'my_lns'" @onUpdate="onUpdate" @onRenew="onRenew" />
<ONSPurchase v-if="screen === 'purchase'" ref="purchase" />
<MyONS v-if="screen === 'my_ons'" @onUpdate="onUpdate" @onRenew="onRenew" />
</q-page>
</template>
<script>
import LNSPurchase from "components/lns/lns_purchase";
import MyLNS from "components/lns/lns_mylns";
import ONSPurchase from "components/ons/ons_purchase";
import MyONS from "components/ons/ons_myons";
import Vue from "vue";
import _ from "lodash";
export default {
components: {
MyLNS,
LNSPurchase
MyONS,
ONSPurchase
},
data() {
return {

View File

@ -1,7 +1,9 @@
<template>
<q-page class="receive">
<q-list link no-border :dark="theme == 'dark'" class="loki-list">
<q-item-label header>{{ $t("strings.addresses.myPrimaryAddress") }}</q-item-label>
<q-list link no-border :dark="theme == 'dark'" class="oxen-list">
<q-item-label header class="list-header">{{
$t("strings.addresses.myPrimaryAddress")
}}</q-item-label>
<ReceiveItem
v-for="address in address_list.primary"
:key="address.address"
@ -15,15 +17,20 @@
/>
<template v-if="address_list.used.length">
<q-item-label header>{{ $t("strings.addresses.myUsedAddresses") }}</q-item-label>
<q-item-label header class="list-header">{{
$t("strings.addresses.myUsedAddresses")
}}</q-item-label>
<ReceiveItem
v-for="address in address_list.used"
:key="address.address"
:address="address"
:sublabel="
`${$t('strings.addresses.subAddress')} (${$t('strings.addresses.subAddressIndex', {
index: address.address_index
})})`
`${$t('strings.addresses.subAddress')} (${$t(
'strings.addresses.subAddressIndex',
{
index: address.address_index
}
)})`
"
:show-q-r="showQR"
:copy-address="copyAddress"
@ -32,15 +39,20 @@
</template>
<template v-if="address_list.unused.length">
<q-item-label header>{{ $t("strings.addresses.myUnusedAddresses") }}</q-item-label>
<q-item-label header class="list-header">{{
$t("strings.addresses.myUnusedAddresses")
}}</q-item-label>
<ReceiveItem
v-for="address in address_list.unused"
:key="address.address"
:address="address"
:sublabel="
`${$t('strings.addresses.subAddress')} (${$t('strings.addresses.subAddressIndex', {
index: address.address_index
})})`
`${$t('strings.addresses.subAddress')} (${$t(
'strings.addresses.subAddressIndex',
{
index: address.address_index
}
)})`
"
:show-q-r="showQR"
:copy-address="copyAddress"
@ -57,10 +69,18 @@
<q-card class="qr-code-card">
<div class="text-center q-mb-sm q-pa-md" style="background: white;">
<QrcodeVue ref="qr" :value="QR.address" size="240"> </QrcodeVue>
<ContextMenu :menu-items="menuItems" @copyQR="copyQR()" @saveQR="saveQR()" />
<ContextMenu
:menu-items="menuItems"
@copyQR="copyQR()"
@saveQR="saveQR()"
/>
</div>
<q-card-actions>
<q-btn color="primary" :label="$t('buttons.close')" @click="QR.visible = false" />
<q-btn
color="primary"
:label="$t('buttons.close')"
@click="QR.visible = false"
/>
</q-card-actions>
</q-card>
</q-dialog>
@ -161,7 +181,7 @@ export default {
font-weight: 400;
}
.loki-list-item {
.oxen-list-item {
cursor: pointer;
.q-item-section {

View File

@ -10,13 +10,12 @@
<div class="row gutter-md">
<!-- Amount -->
<div class="col-6 amount">
<LokiField
<OxenField
:label="$t('fieldLabels.amount')"
:error="$v.newTx.amount.$error"
>
<q-input
v-model="newTx.amount"
:dark="theme == 'dark'"
type="number"
min="0"
:max="unlocked_balance / 1e9"
@ -26,100 +25,88 @@
@blur="$v.newTx.amount.$touch"
/>
<q-btn
color="secondary"
:text-color="theme == 'dark' ? 'white' : 'dark'"
color="primary"
@click="newTx.amount = unlocked_balance / 1e9"
>
{{ $t("buttons.all") }}
</q-btn>
</LokiField>
</OxenField>
</div>
<!-- Priority -->
<div class="col-6 priority">
<LokiField :label="$t('fieldLabels.priority')">
<OxenField :label="$t('fieldLabels.priority')">
<q-select
v-model="newTx.priority"
emit-value
map-options
:dark="theme == 'dark'"
:options="priorityOptions"
borderless
dense
/>
</LokiField>
</OxenField>
</div>
</div>
<!-- Address -->
<div class="col q-mt-sm">
<LokiField
<OxenField
:label="$t('fieldLabels.address')"
:error="$v.newTx.address.$error"
>
<q-input
v-model.trim="newTx.address"
:dark="theme == 'dark'"
:placeholder="address_placeholder"
borderless
dense
@blur="$v.newTx.address.$touch"
/>
<q-btn
color="secondary"
:text-color="theme == 'dark' ? 'white' : 'dark'"
to="addressbook"
>
<q-btn color="primary" to="addressbook">
{{ $t("buttons.contacts") }}
</q-btn>
</LokiField>
</OxenField>
</div>
<!-- Notes -->
<div class="col q-mt-sm">
<LokiField :label="$t('fieldLabels.notes')" optional>
<OxenField :label="$t('fieldLabels.notes')" optional>
<q-input
v-model="newTx.note"
class="full-width text-area-loki"
class="full-width text-area-oxen"
type="textarea"
:dark="theme == 'dark'"
:placeholder="$t('placeholders.transactionNotes')"
borderless
dense
/>
</LokiField>
</OxenField>
</div>
<q-checkbox
v-model="newTx.address_book.save"
:label="$t('strings.saveToAddressBook')"
:dark="theme == 'dark'"
color="dark"
/>
<div v-if="newTx.address_book.save">
<LokiField :label="$t('fieldLabels.name')" optional>
<OxenField :label="$t('fieldLabels.name')" optional>
<q-input
v-model="newTx.address_book.name"
:dark="theme == 'dark'"
:placeholder="$t('placeholders.addressBookName')"
borderless
dense
/>
</LokiField>
<LokiField class="q-mt-sm" :label="$t('fieldLabels.notes')" optional>
</OxenField>
<OxenField class="q-mt-sm" :label="$t('fieldLabels.notes')" optional>
<q-input
v-model="newTx.address_book.description"
type="textarea"
class="full-width text-area-loki"
class="full-width text-area-oxen"
rows="2"
:dark="theme == 'dark'"
:placeholder="$t('placeholders.additionalNotes')"
borderless
dense
/>
</LokiField>
</OxenField>
</div>
<!-- div required so button below checkbox -->
<!-- div required so the button falls below the checkbox -->
<div>
<q-btn
class="send-btn"
@ -139,7 +126,7 @@
:on-confirm-transaction="onConfirmTransaction"
:on-cancel-transaction="onCancelTransaction"
/>
<q-inner-loading :showing="tx_status.sending" :dark="theme == 'dark'">
<q-inner-loading :showing="tx_status.sending">
<q-spinner color="primary" size="30" />
</q-inner-loading>
</template>
@ -150,7 +137,7 @@
import { mapState } from "vuex";
import { required, decimal } from "vuelidate/lib/validators";
import { address, greater_than_zero } from "src/validators/common";
import LokiField from "components/loki_field";
import OxenField from "components/oxen_field";
import WalletPassword from "src/mixins/wallet_password";
import ConfirmDialogMixin from "src/mixins/confirm_dialog_mixin";
import ConfirmTransactionDialog from "components/confirm_tx_dialog";
@ -161,7 +148,7 @@ const DO_NOTHING = 10;
export default {
components: {
LokiField,
OxenField,
ConfirmTransactionDialog
},
mixins: [WalletPassword, ConfirmDialogMixin],
@ -383,9 +370,7 @@ export default {
ok: {
label: this.$t("dialog.transfer.ok"),
color: "primary"
},
dark: this.theme == "dark",
color: this.theme == "dark" ? "white" : "dark"
}
});
passwordDialog
.onOk(password => {

View File

@ -4,7 +4,7 @@
<q-btn-toggle
v-model="screen"
toggle-color="primary"
color="secondary"
color="accent"
:options="[
{
label: $t('titles.serviceNode.myStakes'),

View File

@ -5,27 +5,25 @@
{{ $t("titles.transactions") }}
</div>
<LokiField class="col-5 q-px-sm" :label="$t('fieldLabels.filter')">
<OxenField class="col-5 q-px-sm" :label="$t('fieldLabels.filter')">
<q-input
v-model="tx_filter"
:dark="theme == 'dark'"
:placeholder="$t('placeholders.filterTx')"
borderless
dense
/>
</LokiField>
</OxenField>
<LokiField class="col-2" :label="$t('fieldLabels.filterTransactionType')">
<OxenField class="col-2" :label="$t('fieldLabels.filterTransactionType')">
<q-select
v-model="tx_type"
:dark="theme == 'dark'"
:options="tx_type_options"
borderless
dense
emit-value
map-options
/>
</LokiField>
</OxenField>
</div>
<TxList :type="tx_type" :filter="tx_filter" />
</q-page>
@ -34,11 +32,11 @@
<script>
import { mapState } from "vuex";
import TxList from "components/tx_list";
import LokiField from "components/loki_field";
import OxenField from "components/oxen_field";
export default {
components: {
TxList,
LokiField
OxenField
},
data() {
return {

View File

@ -94,8 +94,8 @@ export default [
component: () => import("pages/wallet/service-node")
},
{
path: "lns",
component: () => import("pages/wallet/lns")
path: "ons",
component: () => import("pages/wallet/ons")
},
{
path: "advanced",

View File

@ -10,6 +10,8 @@ export const resetWalletData = state => {
height: 0,
balance: 0,
unlocked_balance: 0,
accrued_balance: 0,
accrued_balance_next_payout: 0,
view_only: false
},
secret: {

View File

@ -39,8 +39,23 @@ export const set_check_transaction_status = (state, data) => {
...data
};
};
export const set_lns_status = (state, data) => {
state.lns_status = data;
export const set_sign_status = (state, data) => {
state.sign_status = {
...state.sign_status,
...data
};
};
export const set_verify_status = (state, data) => {
state.verify_status = {
...state.verify_status,
...data
};
};
export const set_ons_status = (state, data) => {
state.ons_status = data;
};
export const set_update_required = (state, data) => {

View File

@ -33,7 +33,11 @@ export default {
height: 0,
balance: 0,
unlocked_balance: 0,
view_only: false
view_only: false,
hardware_wallet: false,
accrued_balance: 0,
accrued_balance_next_payout: 0
},
secret: {
mnemonic: "",
@ -48,7 +52,7 @@ export default {
unused: [],
address_book: []
},
lnsRecords: [],
onsRecords: [],
isRPCSyncing: false
},
tx_status: {
@ -96,7 +100,20 @@ export default {
i18n: "",
state: {}
},
lns_status: {
sign_status: {
code: 0,
message: "",
i18n: "",
signature: "",
sending: false
},
verify_status: {
code: 0,
message: "",
i18n: "",
sending: false
},
ons_status: {
code: 0,
message: "",
i18n: "",

View File

@ -1945,11 +1945,11 @@ return{_strlen:lb,_ge_mul8:Va,_keccak:db,_ge_scalarmult:Ta,_ge_fromfe_frombytes_
var lokiConfig = {
var oxenConfig = {
coinUnitPlaces: 12,
coinSymbol: 'LOKI',
coinName: 'Loki',
coinUriPrefix: 'loki:',
coinSymbol: 'OXEN',
coinName: 'Oxen',
coinUriPrefix: 'oxen:',
addressPrefix: 114,
};
@ -2386,7 +2386,7 @@ var cnUtilGen = function(initConfig) {
return this;
};
var cnUtil = cnUtilGen(lokiConfig);
var cnUtil = cnUtilGen(oxenConfig);
/*
mnemonic.js : Converts between 4-byte aligned strings and a human-readable
sequence of words. Uses 1626 common words taken from wikipedia article:

View File

@ -17,7 +17,7 @@ export const session_id = input => {
return input.length === 66 && /^05[0-9A-Za-z]+$/.test(input);
};
// shortened Lokinet LNS name
// shortened Lokinet ONS name
export const lokinet_name = (input, lokiExt = false) => {
let inputSafe = input || "";
let maxLength = 32;

Some files were not shown because too many files have changed in this diff Show More