Skip to content

Commit

Permalink
modularize pages to components and introduce messaging app (#88)
Browse files Browse the repository at this point in the history
* makw wallet a walletTab component

* fixes

* cleanup

* move notes to pinia store

* move note fetching back to index

* hotswapping to gov, swap, messaging

* make teerdays a self-sustained tab too

* directory structure refactoring

* messaging app works

* fixes
  • Loading branch information
brenzi authored Nov 15, 2024
1 parent 7d1b6b7 commit 38593e2
Show file tree
Hide file tree
Showing 22 changed files with 2,804 additions and 2,008 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@ import {
connectExtension,
extensionAccounts,
injectorForAddress,
} from "@/lib/signerExtensionUtils";
import OverlayDialog from "@/components/ui/OverlayDialog.vue";
} from "~/lib/signerExtensionUtils";
import OverlayDialog from "~/components/overlays/OverlayDialog.vue";
import { defineProps, computed, ref, watch } from "vue";
import { useAccount } from "@/store/account.ts";
import { useAccount } from "~/store/account.ts";
const accountStore = useAccount();
const selectedExtensionAccount = ref("");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,14 @@
</template>

<script setup lang="ts">
import OverlayDialog from "@/components/ui/OverlayDialog.vue";
import OverlayDialog from "~/components/overlays/OverlayDialog.vue";
import { computed, defineProps, ref, watch } from "vue";
import { Health, useSystemHealth } from "@/store/systemHealth";
import { Health, useSystemHealth } from "~/store/systemHealth";
import { useInterval } from "@vueuse/core";
import { useRouter } from "vue-router";
import { useIncognitee } from "@/store/incognitee.ts";
import { chainConfigs } from "@/configs/chains.ts";
import { shieldingTarget, incogniteeShard } from "@/lib/environmentConfig";
import { useIncognitee } from "~/store/incognitee.ts";
import { chainConfigs } from "~/configs/chains.ts";
import { shieldingTarget, incogniteeShard } from "~/lib/environmentConfig";

const router = useRouter();
const systemHealth = useSystemHealth();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@
</template>
<script setup lang="ts">
import OverlayDialog from "@/components/ui/OverlayDialog.vue";
import OverlayDialog from "~/components/overlays/OverlayDialog.vue";
import { defineProps } from "vue";
import { Note, NoteDirection } from "@/lib/notes";
import { formatMoment, formatDate } from "@/helpers/date";
import { Note, NoteDirection } from "~/lib/notes";
import { formatDate } from "~/helpers/date";
import { divideBigIntToFloat } from "~/helpers/numbers";
import { useAccount } from "@/store/account.ts";
import { useAccount } from "~/store/account.ts";
const accountStore = useAccount();
Expand All @@ -46,8 +46,10 @@ const props = defineProps({
required: true,
},
note: {
type: Note,
required: true,
type: Object,
required: false,
default: null,
validator: (value) => value === null || value instanceof Note,
},
});
</script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
</template>

<script setup lang="ts">
import OverlayDialog from "@/components/ui/OverlayDialog.vue";
import OverlayDialog from "~/components/overlays/OverlayDialog.vue";
import { defineProps } from "vue";
const props = defineProps({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import {
TransitionRoot,
} from "@headlessui/vue";
import { defineProps } from "vue";
import TransitionChildSootGlass from "@/components/ui/TransitionChildSootGlass.vue";
import TransitionChildSootGlass from "~/components/ui/TransitionChildSootGlass.vue";
const props = defineProps({
close: {
Expand Down
File renamed without changes.
4 changes: 0 additions & 4 deletions pages/gov.vue → components/tabs/GovTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@
</div>
</template>

<script setup lang="ts">
// Import your components and add your setup logic here
</script>

<style scoped>
h1 {
font-size: 2em; /* Adjust as needed */
Expand Down
296 changes: 296 additions & 0 deletions components/tabs/MessagingTab.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
<template>
<form class="mt-5" @submit.prevent="submitSendForm">
<div class="flex flex-col">
<label
for="recipientAddress"
class="text-sm font-medium leading-6 text-white text-left"
>Recipient</label
>
<div class="relative flex items-center rounded-lg">
<input
id="recipientAddress"
v-model="recipientAddress"
type="text"
required
class="w-full text-sm rounded-lg flex-grow py-2 bg-cool-900 text-white placeholder-gray-500 border border-green-500 truncate-input pr-12"
style="border-color: #24ad7c"
placeholder="Recipient"
/>
<div class="absolute right-3 flex space-x-2">
<div @click="openScanOverlay" class="cursor-pointer">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="h-6 w-6 text-white"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M3.75 4.875c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5A1.125 1.125 0 0 1 3.75 9.375v-4.5ZM3.75 14.625c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5a1.125 1.125 0 0 1-1.125-1.125v-4.5ZM13.5 4.875c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5A1.125 1.125 0 0 1 13.5 9.375v-4.5Z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M6.75 6.75h.75v.75h-.75v-.75ZM6.75 16.5h.75v.75h-.75v-.75ZM16.5 6.75h.75v.75h-.75v-.75ZM13.5 13.5h.75v.75h-.75v-.75ZM13.5 19.5h.75v.75h-.75v-.75ZM19.5 13.5h.75v.75h-.75v-.75ZM19.5 19.5h.75v.75h-.75v-.75ZM16.5 16.5h.75v.75h-.75v-.75Z"
/>
</svg>
</div>
</div>
</div>
</div>

<!-- Messages -->
<div class="flex flex-col">
<label
for="paymentNote"
class="text-sm font-medium leading-6 text-white text-left"
>Note</label
>
<div class="relative flex items-center rounded-lg">
<textarea
id="messages"
v-model="sendPrivateNote"
rows="2"
ref="messageTextarea"
name="messages"
placeholder="Enter a private note for the recipient"
:maxlength="140"
class="w-full text-sm rounded-lg flex-grow py-2 bg-cool-900 text-white placeholder-gray-500 border border-green-500 truncate-input pr-12"
></textarea>
</div>
</div>

<div class="mt-8 bottom-0 left-0 w-full bg-gray-800">
<button
type="submit"
class="btn btn_gradient inline-flex w-full justify-center rounded-md px-3 py-2 text-sm font-semibold text-white shadow-sm"
>
Send
</button>
</div>
<div>{{ txStatus }}</div>
</form>
<PrivateMessageHistory :show="true" />
<!-- Scan QR -->
<OverlayDialog
:show="showScanOverlay"
:close="closeScanOverlay"
title="Scan recipient's QR code"
>
<div class="mt-6 qrcode-container">
<qrcode-stream @detect="onDecode"></qrcode-stream>
</div>
</OverlayDialog>
</template>

<script setup lang="ts">
import PrivateMessageHistory from "~/components/ui/PrivateMessageHistory.vue";
import { incogniteeSidechain } from "~/lib/environmentConfig";
import { formatDecimalBalance } from "~/helpers/numbers";
import { INCOGNITEE_TX_FEE } from "~/configs/incognitee";
import { Health, useSystemHealth } from "~/store/systemHealth";
import { TypeRegistry, u32 } from "@polkadot/types";
import { defineProps, ref, watch } from "vue";
import { useAccount } from "~/store/account";
import { useIncognitee } from "~/store/incognitee";
import OverlayDialog from "~/components/overlays/OverlayDialog.vue";
import { QrcodeStream } from "vue-qrcode-reader";
import { ApiPromise } from "@polkadot/api";
import { useInterval } from "@vueuse/core";
const recipientAddress = ref("");
const sendPrivateNote = ref("");
const txStatus = ref("");
const accountStore = useAccount();
const incogniteeStore = useIncognitee();
const systemHealth = useSystemHealth();
const pollCounter = useInterval(2000);
watch(pollCounter, async () => {
console.debug("polling for new incognitee notes");
await props.updateNotes();
});
const submitSendForm = () => {
if (systemHealth.getSidechainSystemHealth.overall() !== Health.Healthy) {
alert(
"Sidechain health currently can't be assessed. Please wait for a green health indicator and try again",
);
return;
}
sendPrivately();
};
const sendPrivately = async () => {
console.log("sending message on incognitee");
txStatus.value = "⌛ sending message privately on incognitee";
const amount = BigInt(0);
const account = accountStore.account;
const encoder = new TextEncoder();
const byteLength = encoder.encode(sendPrivateNote.value).length;
// fixme: https://github.com/encointer/encointer-js/issues/123
if (byteLength > 161) {
alert(
"Note is too long when encoded to UTF-8. Please keep it under 162 bytes.",
);
return;
}
const note = sendPrivateNote.value.length > 0 ? sendPrivateNote.value : null;
const nonce = new u32(
new TypeRegistry(),
accountStore.nonce[incogniteeSidechain.value],
);
console.log(
`sending message from ${account.address} privately to ${recipientAddress.value} with nonce ${nonce} and note: ${note}`,
);
await incogniteeStore.api
.trustedBalanceTransfer(
account,
incogniteeStore.shard,
incogniteeStore.fingerprint,
accountStore.getAddress,
recipientAddress.value,
amount,
note,
{
signer: accountStore.injector?.signer,
nonce: nonce,
},
)
.then((result) => handleTopResult(result, "😀 message sent successfully"))
.catch((err) => handleTopError(err));
//todo: manually inc nonce locally avoiding clashes with fetchIncogniteeBalance
};
const handleTopResult = (result, successMsg?) => {
console.log("TOP result: " + result);
if (result) {
if (result.status.isInSidechainBlock) {
if (successMsg) {
txStatus.value = successMsg;
} else {
txStatus.value =
"😀 included in sidechain block: " + result.status.asInSidechainBlock;
}
//update history to see successfuly action immediately
props.updateNotes();
return;
}
if (result.status.isInvalid) {
txStatus.value = "😞 Invalid (unspecified reason)";
return;
}
}
console.error(`unknown result: ${result}`);
txStatus.value = "😞 Unknown Result";
};
const handleTopError = (err) => {
console.error(`error: ${err}`);
txStatus.value = `😞 Submission Failed: ${err}`;
};
const scanResult = ref("No QR code data yet");
const onDecode = (decodeResult) => {
console.log("QR scan decoded: " + decodeResult[0].rawValue);
scanResult.value = decodeResult[0].rawValue;
recipientAddress.value = decodeResult[0].rawValue;
closeScanOverlay();
};
const showScanOverlay = ref(false);
const openScanOverlay = () => {
scanResult.value = "No QR code data yet";
showScanOverlay.value = true;
};
const closeScanOverlay = () => {
console.debug("closeScanOverlay");
showScanOverlay.value = false;
};
const props = defineProps({
isMobile: {
type: Boolean,
required: true,
},
updateNotes: {
type: Function,
required: true,
},
});
</script>

<style scoped>
.border-green-500 {
border-color: #24ad7c;
}
.bg-gray-800 {
background-color: #1f2937;
}
.text-white {
color: #ffffff;
}
.truncate-input {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
h1 {
font-size: 1em; /* Adjust as needed */
font-weight: bold; /* Makes the text bold */
color: #999; /* Change color as needed */
text-align: center; /* Centers the text */
margin-bottom: 20px; /* Adds space below the heading */
}
hr {
border: none;
border-top: 1px #222; /* Change color as needed */
color: #111; /* Change color as needed */
background-color: #333; /* Change color as needed */
height: 1px; /* Adjust as needed */
}
.qrcode-container {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
.form-container input {
background-color: #333;
color: #fff; /* You might want to change the text color to ensure it's readable against the dark background */
}
.form-container input#amount {
font-size: 2em; /* Make the font size twice as large */
text-align: center; /* Center the text */
width: 50%; /* Reduce the width by 50% */
}
.spinner {
border: 2px solid #f3f3f3; /* Light grey */
border-top: 2px solid #3498db; /* Blue */
border-radius: 50%;
width: 1em; /* Adjust the size here */
height: 1em; /* Adjust the size here */
animation: spin 2s linear infinite;
vertical-align: middle; /* Align with the text */
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
Loading

0 comments on commit 38593e2

Please sign in to comment.