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

modularize pages to components and introduce messaging app #88

Merged
merged 10 commits into from
Nov 15, 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
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
Loading