Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Staking #192

Merged
merged 21 commits into from
Jul 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/abi-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,24 @@ export class REXDeposit extends Struct {
@Struct.field('asset') amount!: Asset
}

@Struct.type('rexwithdraw')
export class REXWithdraw extends Struct {
@Struct.field('name') owner!: Name
@Struct.field('asset') amount!: Asset
}

@Struct.type('rexbuyrex')
export class REXBUYREX extends Struct {
@Struct.field('name') from!: Name
@Struct.field('asset') amount!: Asset
}

@Struct.type('rexsellrex')
export class REXSELLREX extends Struct {
@Struct.field('name') from!: Name
@Struct.field('asset') rex!: Asset
}

@Struct.type('rexrentcpu')
export class REXRentCPU extends Struct {
@Struct.field('name') from!: Name
Expand Down
4 changes: 4 additions & 0 deletions src/app.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import Login from '~/pages/login.svelte'
import Dashboard from '~/pages/dashboard/index.svelte'
import Earn from '~/pages/earn/index.svelte'
import Request from '~/pages/request/index.svelte'
import Send from '~/pages/send/index.svelte'
import TokensPurchase from '~/pages/tokens/purchase/index.svelte'
Expand Down Expand Up @@ -178,6 +179,9 @@
<Route path="/">
<Dashboard />
</Route>
<Route path="/earn">
<Earn />
</Route>
<Route path="/send">
<Send />
</Route>
Expand Down
1 change: 0 additions & 1 deletion src/components/elements/input/token/selector.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script>
import {writable} from 'svelte/store'
import {Asset} from 'anchor-link'
import {activeBlockchain} from '~/store'
import type {Token} from '~/stores/tokens'
Expand Down
7 changes: 6 additions & 1 deletion src/components/elements/input/token/selector/row.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@
let balance

$: {
balance = $balances && $balances.find((balance) => balance.tokenKey === token.key)?.quantity
if (token.balance) {
balance = token.balance
} else {
balance =
$balances && $balances.find((balance) => balance.tokenKey === token.key)?.quantity
}

if (typeof balance === 'string') {
formattedTokenBalance = balance
Expand Down
310 changes: 310 additions & 0 deletions src/components/elements/slider.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
<script>
import {createEventDispatcher} from 'svelte'
import {fly, fade} from 'svelte/transition'

// Props
export let min = 0
export let max = 100
export let initialValue = 0
export let id: any = null
export let value = typeof initialValue === 'string' ? parseInt(initialValue) : initialValue

// Node Bindings
let container = null
let thumb = null
let progressBar = null
let element = null

// Internal State
let elementX = null
let currentThumb = null
let holding = false
let thumbHover = false
let keydownAcceleration = 0
let accelerationTimer = null

// Dispatch 'change' events
const dispatch = createEventDispatcher()

// Mouse shield used onMouseDown to prevent any mouse events penetrating other elements,
// ie. hover events on other elements while dragging. Especially for Safari
const mouseEventShield = document.createElement('div')
mouseEventShield.setAttribute('class', 'mouse-over-shield')
mouseEventShield.addEventListener('mouseover', (e) => {
e.preventDefault()
e.stopPropagation()
})

function resizeWindow() {
elementX = element.getBoundingClientRect().left
}

// Allows both bind:value and on:change for parent value retrieval
function setValue(val) {
value = val
dispatch('change', {value})
}

function onTrackEvent(e) {
// Update value immediately before beginning drag
updateValueOnEvent(e)
onDragStart(e)
}

function onHover(e) {
thumbHover = thumbHover ? false : true
}

function onDragStart(e) {
// If mouse event add a pointer events shield
if (e.type === 'mousedown') document.body.append(mouseEventShield)
currentThumb = thumb
}

function onDragEnd(e) {
// If using mouse - remove pointer event shield
if (e.type === 'mouseup') {
if (document.body.contains(mouseEventShield))
document.body.removeChild(mouseEventShield)
// Needed to check whether thumb and mouse overlap after shield removed
if (isMouseInElement(e, thumb)) thumbHover = true
}
currentThumb = null
}

// Check if mouse event cords overlay with an element's area
function isMouseInElement(event, element) {
if (element === null) {
return false
}
let rect = element.getBoundingClientRect()
let {clientX: x, clientY: y} = event
if (x < rect.left || x >= rect.right) return false
if (y < rect.top || y >= rect.bottom) return false
return true
}

// Accessible keypress handling
function onKeyPress(e) {
// Max out at +/- 10 to value per event (50 events / 5)
// 100 below is to increase the amount of events required to reach max velocity
if (keydownAcceleration < 50) keydownAcceleration++
let throttled = Math.ceil(keydownAcceleration / 5)

if (e.key === 'ArrowUp' || e.key === 'ArrowRight') {
if (value + throttled > max || value >= max) {
setValue(max)
} else {
setValue(value + throttled)
}
}
if (e.key === 'ArrowDown' || e.key === 'ArrowLeft') {
if (value - throttled < min || value <= min) {
setValue(min)
} else {
setValue(value - throttled)
}
}

// Reset acceleration after 100ms of no events
clearTimeout(accelerationTimer)
accelerationTimer = setTimeout(() => (keydownAcceleration = 1), 100)
}

function calculateNewValue(clientX) {
// Find distance between cursor and element's left cord (20px / 2 = 10px) - Center of thumb
let delta = clientX - (elementX + 10)

// Use width of the container minus (5px * 2 sides) offset for percent calc
let percent = (delta * 100) / (container.clientWidth - 10)

// Limit percent 0 -> 100
percent = percent < 0 ? 0 : percent > 100 ? 100 : percent

// Limit value min -> max
setValue(parseInt((percent * (max - min)) / 100) + min)
}

// Handles both dragging of touch/mouse as well as simple one-off click/touches
function updateValueOnEvent(e) {
// touchstart && mousedown are one-off updates, otherwise expect a currentPointer node
if (!currentThumb && e.type !== 'touchstart' && e.type !== 'mousedown') return false

if (e.stopPropagation) e.stopPropagation()
if (e.preventDefault) e.preventDefault()

// Get client's x cord either touch or mouse
const clientX =
e.type === 'touchmove' || e.type === 'touchstart' ? e.touches[0].clientX : e.clientX

calculateNewValue(clientX)
}

// React to left position of element relative to window
$: if (element) elementX = element.getBoundingClientRect().left

// Set a class based on if dragging
$: holding = Boolean(currentThumb)

// Update progressbar and thumb styles to represent value
$: if (progressBar && thumb) {
// Limit value min -> max
value = value > min ? value : min
value = value < max ? value : max

let percent = ((value - min) * 100) / (max - min)
let offsetLeft = (container.clientWidth - 10) * (percent / 100) + 5

// Update thumb position + active slider track width
thumb.style.left = `${offsetLeft}px`
progressBar.style.width = `${offsetLeft}px`
}
</script>

<style>
.slider {
position: relative;
flex: 1;
}

.slider__wrapper {
min-width: 100%;
position: relative;
padding: 0.5rem;
box-sizing: border-box;
outline: none;
}

.slider__wrapper:focus-visible > .slider__track {
box-shadow: 0 0 0 2px white, 0 0 0 3px var(--main-blue);
}

.slider__track {
height: 6px;
background-color: var(--cultured);
border-radius: 999px;
}

.slider__track--highlighted {
background-color: var(--main-blue);
width: 0;
height: 6px;
position: absolute;
border-radius: 999px;
}

.slider__thumb {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
width: 20px;
height: 20px;
background-color: var(--thumb-bgcolor, white);
cursor: pointer;
border-radius: 999px;
margin-top: -8px;
transition: box-shadow 100ms;
user-select: none;
box-shadow: var(
--thumb-boxshadow,
0 1px 1px 0 rgba(0, 0, 0, 0.14),
0 0px 2px 1px rgba(0, 0, 0, 0.2)
);
}

.slider__thumb--holding {
box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 2px 1px rgba(0, 0, 0, 0.2),
0 0 0 6px var(--thumb-holding-outline, rgba(113, 119, 250, 0.3));
}

.slider__tooltip {
pointer-events: none;
position: absolute;
top: -33px;
color: var(--tooltip-text, white);
width: 38px;
padding: 4px 0;
border-radius: 4px;
text-align: center;
background-color: var(--main-blue);
}

.slider__tooltip::after {
content: '';
display: block;
position: absolute;
height: 7px;
width: 7px;
background-color: var(--main-blue);
bottom: -3px;
left: calc(50% - 3px);
clip-path: polygon(0% 0%, 100% 100%, 0% 100%);
transform: rotate(-45deg);
border-radius: 0 0 0 3px;
}
</style>

<svelte:window
on:touchmove|nonpassive={updateValueOnEvent}
on:touchcancel={onDragEnd}
on:touchend={onDragEnd}
on:mousemove={updateValueOnEvent}
on:mouseup={onDragEnd}
on:resize={resizeWindow}
/>
<div class="slider">
<div
class="slider__wrapper"
tabindex="0"
on:keydown={onKeyPress}
bind:this={element}
role="slider"
aria-valuemin={min}
aria-valuemax={max}
aria-valuenow={value}
{id}
on:mousedown={onTrackEvent}
on:touchstart={onTrackEvent}
>
<div class="slider__track" bind:this={container}>
<div class="slider__track--highlighted" bind:this={progressBar} />
<div
class="slider__thumb"
class:slider__thumb--holding={holding}
bind:this={thumb}
on:touchstart={onDragStart}
on:mousedown={onDragStart}
on:mouseover={() => (thumbHover = true)}
on:focus={() => (thumbHover = true)}
on:mouseout={() => (thumbHover = false)}
on:blur={() => (thumbHover = false)}
>
{#if holding || thumbHover}
<div
class="slider__tooltip"
in:fly={{y: 7, duration: 200}}
out:fade={{duration: 100}}
>
{value + ' %'}
</div>
{/if}
</div>
</div>
</div>
</div>

<svelte:head>
<style>
.mouse-over-shield {
position: fixed;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
background-color: rgba(255, 0, 0, 0);
z-index: 10000;
cursor: grabbing;
}
</style>
</svelte:head>
11 changes: 11 additions & 0 deletions src/components/layout/navigation/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import {activeBlockchain, preferences} from '~/store'
import type {NavigationItem} from '~/ui-types'
import {banxaIsAvailable} from '~/lib/banxa'
import {rexIsAvailable} from '~/lib/rex'

import MediaQuery from '~/components/utils/media-query.svelte'
import NavigationContent from '~/components/layout/navigation/content.svelte'
Expand All @@ -30,6 +31,16 @@
name: 'Transfer',
path: '/transfer',
},
...(rexIsAvailable($activeBlockchain)
? [
{
icon: 'dollar-sign',
name: 'Earn',
path: '/earn',
},
]
: []),

...(banxaIsAvailable($activeBlockchain)
? [
{
Expand Down
Loading
Loading