Skip to content

Commit 018b531

Browse files
AchoArnoldCopilot
andcommitted
feat(web): port all components (Toast, LoadingDashboard, LoadingButton, BackButton, CopyButton, FixedHeader, BlogAuthorBio, BlogInfo, NuxtLogo, FirebaseAuth, MessageThread, MessageThreadHeader)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 30f5369 commit 018b531

12 files changed

Lines changed: 712 additions & 0 deletions

web/app/components/BackButton.vue

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<script setup lang="ts">
2+
import { useDisplay } from "vuetify";
3+
import { mdiArrowLeft } from "@mdi/js";
4+
import type { RouteLocationRaw } from "vue-router";
5+
6+
const props = withDefaults(
7+
defineProps<{
8+
route?: RouteLocationRaw;
9+
block?: boolean;
10+
}>(),
11+
{
12+
block: false,
13+
},
14+
);
15+
16+
const router = useRouter();
17+
const { smAndDown } = useDisplay();
18+
19+
function goBack() {
20+
if (props.route) {
21+
router.push(props.route);
22+
return;
23+
}
24+
if (window.history.length > 1) {
25+
router.back();
26+
return;
27+
}
28+
router.push({ name: "index" });
29+
}
30+
</script>
31+
32+
<template>
33+
<v-btn
34+
color="default"
35+
:size="smAndDown ? 'small' : 'default'"
36+
:block="block"
37+
@click="goBack"
38+
>
39+
<v-icon :icon="mdiArrowLeft" />
40+
Go Back
41+
</v-btn>
42+
</template>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<script setup lang="ts">
2+
import { mdiTwitter, mdiGithub } from "@mdi/js";
3+
</script>
4+
5+
<template>
6+
<div class="d-flex mb-6 mt-8">
7+
<v-avatar image="/img/arnold.png" />
8+
<div class="ml-2">
9+
<p class="text-subtitle-1 mb-n1">Acho Arnold</p>
10+
<a
11+
class="mb-n4 text-decoration-none text-on-surface"
12+
href="https://twitter.com/acho_arnold"
13+
>
14+
<v-icon color="#1DA1F2" :icon="mdiTwitter" />
15+
</a>
16+
<a
17+
class="ml-2 text-decoration-none text-on-surface"
18+
href="https://github.com/AchoArnold"
19+
>
20+
<v-icon color="#FFFFFF" :icon="mdiGithub" />
21+
</a>
22+
</div>
23+
</div>
24+
</template>

web/app/components/BlogInfo.vue

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<script setup lang="ts">
2+
import { mdiBookOpenVariant } from "@mdi/js";
3+
4+
const appStore = useAppStore();
5+
</script>
6+
7+
<template>
8+
<div>
9+
<NuxtLink to="/" class="text-decoration-none d-flex">
10+
<v-avatar :image="'/img/logo.svg'" :size="33" class="mt-1" />
11+
<h3 class="text-h4 text-on-surface ml-1">httpSMS</h3>
12+
</NuxtLink>
13+
<p>
14+
httpSMS is an
15+
<a
16+
class="text-decoration-none"
17+
href="https://github.com/NdoleStudio/httpsms"
18+
>open source</a
19+
>
20+
application that converts your android phone into an SMS gateway so you
21+
can send and receive SMS messages using a simple HTTP API.
22+
</p>
23+
<v-btn :href="appStore.appData.documentationUrl">
24+
<v-icon start :icon="mdiBookOpenVariant" />
25+
Documentation
26+
</v-btn>
27+
</div>
28+
</template>

web/app/components/CopyButton.vue

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<script setup lang="ts">
2+
import { useDisplay } from "vuetify";
3+
import { mdiContentCopy } from "@mdi/js";
4+
5+
const props = withDefaults(
6+
defineProps<{
7+
value: string;
8+
color?: string;
9+
block?: boolean;
10+
large?: boolean;
11+
copyText?: string;
12+
notificationText?: string;
13+
}>(),
14+
{
15+
color: "default",
16+
block: false,
17+
large: false,
18+
copyText: "Copy",
19+
notificationText: "Copied",
20+
},
21+
);
22+
23+
const { smAndDown } = useDisplay();
24+
const notificationsStore = useNotificationsStore();
25+
const disabled = ref(false);
26+
27+
async function copy() {
28+
disabled.value = true;
29+
await navigator.clipboard.writeText(props.value);
30+
notificationsStore.addNotification({
31+
message: props.notificationText,
32+
type: "success",
33+
});
34+
setTimeout(() => {
35+
disabled.value = false;
36+
}, 5000);
37+
}
38+
</script>
39+
40+
<template>
41+
<v-btn
42+
:disabled="disabled"
43+
:color="color"
44+
:size="smAndDown ? 'small' : large ? 'large' : 'default'"
45+
:block="block"
46+
@click="copy"
47+
>
48+
<v-icon start :icon="mdiContentCopy" />
49+
{{ copyText }}
50+
</v-btn>
51+
</template>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<script setup lang="ts">
2+
import {
3+
getAuth,
4+
GoogleAuthProvider,
5+
GithubAuthProvider,
6+
EmailAuthProvider,
7+
} from "firebase/auth";
8+
9+
const props = withDefaults(
10+
defineProps<{
11+
to?: string;
12+
}>(),
13+
{ to: "/" },
14+
);
15+
16+
const router = useRouter();
17+
const authStore = useAuthStore();
18+
const notificationsStore = useNotificationsStore();
19+
const appStore = useAppStore();
20+
const authContainer = ref<HTMLElement | null>(null);
21+
const firebaseUIInitialized = ref(false);
22+
let ui: any = null;
23+
24+
onMounted(async () => {
25+
if (!import.meta.client) return;
26+
27+
const firebaseui = await import("firebaseui");
28+
await import("firebaseui/dist/firebaseui.css");
29+
30+
const auth = getAuth();
31+
ui = new firebaseui.auth.AuthUI(auth);
32+
ui.start("#firebaseui-auth-container", {
33+
callbacks: {
34+
signInSuccessWithAuthResult: (authResult: any) => {
35+
notificationsStore.addNotification({
36+
message: "Login successful!",
37+
type: "success",
38+
});
39+
authStore.onAuthStateChanged(authResult.user);
40+
router.push({ path: props.to });
41+
return false;
42+
},
43+
uiShown: () => {
44+
firebaseUIInitialized.value = true;
45+
if (authContainer.value) {
46+
Array.from(
47+
authContainer.value.getElementsByClassName(
48+
"firebaseui-idp-text-long",
49+
),
50+
).forEach((item: Element) => {
51+
item.textContent =
52+
item.textContent?.replace("Sign in with", "Continue with") ||
53+
null;
54+
});
55+
}
56+
},
57+
},
58+
signInFlow: "popup",
59+
signInSuccessUrl: window.location.href,
60+
signInOptions: [
61+
GoogleAuthProvider.PROVIDER_ID,
62+
GithubAuthProvider.PROVIDER_ID,
63+
EmailAuthProvider.PROVIDER_ID,
64+
],
65+
tosUrl: appStore.appData.url + "/terms-and-conditions",
66+
privacyPolicyUrl: appStore.appData.url + "/privacy-policy",
67+
});
68+
});
69+
70+
onBeforeUnmount(() => {
71+
if (ui) ui.delete();
72+
});
73+
</script>
74+
75+
<template>
76+
<div>
77+
<div id="firebaseui-auth-container" ref="authContainer" />
78+
<v-progress-circular
79+
v-if="!firebaseUIInitialized"
80+
class="mx-auto d-block my-16"
81+
:size="80"
82+
:width="5"
83+
color="primary"
84+
indeterminate
85+
/>
86+
</div>
87+
</template>

web/app/components/FixedHeader.vue

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<script setup lang="ts">
2+
import { useDisplay } from "vuetify";
3+
4+
const { lgAndUp, mdAndDown } = useDisplay();
5+
</script>
6+
7+
<template>
8+
<v-app-bar elevation="0" color="#121212" height="70">
9+
<v-container>
10+
<v-row>
11+
<v-col class="w-full d-flex">
12+
<NuxtLink
13+
:to="{ name: 'index' }"
14+
class="text-on-surface text-h4 text-decoration-none"
15+
:class="{ 'mt-5': mdAndDown, 'mt-4': !mdAndDown }"
16+
>
17+
<v-avatar v-if="lgAndUp" :image="'/img/logo.svg'" :size="30" />
18+
HTTP SMS
19+
</NuxtLink>
20+
<v-spacer />
21+
<v-btn
22+
color="primary"
23+
class="mt-5 mb-5"
24+
:size="lgAndUp ? 'large' : 'default'"
25+
:to="{ name: 'login' }"
26+
>
27+
Get Started
28+
<span v-if="lgAndUp">&nbsp;For Free</span>
29+
</v-btn>
30+
</v-col>
31+
</v-row>
32+
</v-container>
33+
</v-app-bar>
34+
</template>
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<script setup lang="ts">
2+
const props = withDefaults(
3+
defineProps<{
4+
type?: string;
5+
block?: boolean;
6+
large?: boolean;
7+
xLarge?: boolean;
8+
tile?: boolean;
9+
text?: boolean;
10+
small?: boolean;
11+
color?: string;
12+
icon?: string | null;
13+
loading: boolean;
14+
}>(),
15+
{
16+
type: "submit",
17+
block: false,
18+
large: false,
19+
xLarge: false,
20+
tile: false,
21+
text: false,
22+
small: false,
23+
color: "primary",
24+
icon: null,
25+
},
26+
);
27+
28+
const emit = defineEmits<{
29+
click: [];
30+
"update:loading": [value: boolean];
31+
}>();
32+
33+
const isClicked = ref(false);
34+
35+
watch(
36+
() => props.loading,
37+
(submitting) => {
38+
if (!submitting && isClicked.value) {
39+
isClicked.value = false;
40+
}
41+
},
42+
);
43+
44+
function onClick() {
45+
isClicked.value = true;
46+
emit("click");
47+
}
48+
49+
const size = computed(() => {
50+
if (props.xLarge) return "x-large";
51+
if (props.large) return "large";
52+
if (props.small) return "small";
53+
return "default";
54+
});
55+
</script>
56+
57+
<template>
58+
<v-btn
59+
:block="block"
60+
:type="type"
61+
:size="size"
62+
:color="color"
63+
:variant="text ? 'text' : 'elevated'"
64+
:disabled="loading"
65+
@click.prevent="onClick"
66+
>
67+
<v-progress-circular
68+
v-if="isClicked"
69+
:size="small ? 20 : 25"
70+
color="grey"
71+
class="mr-2"
72+
indeterminate
73+
/>
74+
<v-icon v-if="icon && !loading" start :icon="icon" />
75+
<slot />
76+
</v-btn>
77+
</template>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<script setup lang="ts">
2+
import { useDisplay } from "vuetify";
3+
const { mdAndDown } = useDisplay();
4+
</script>
5+
6+
<template>
7+
<v-main>
8+
<v-container fluid class="fill-height">
9+
<v-row align="center" justify="center">
10+
<v-col
11+
cols="12"
12+
md="5"
13+
xl="3"
14+
class="text-center mt-16"
15+
:class="{ 'px-6': mdAndDown, 'px-16': !mdAndDown }"
16+
>
17+
<h2 class="text-h4 text-medium-emphasis mt-16 mb-4">
18+
<img
19+
class="mx-auto d-inline-block"
20+
src="/img/logo.svg"
21+
style="max-width: 32px"
22+
alt="httpSMS Logo"
23+
/>
24+
Loading the httpSMS dashboard
25+
</h2>
26+
<v-progress-circular
27+
indeterminate
28+
size="160"
29+
class="mt-8"
30+
color="primary"
31+
/>
32+
</v-col>
33+
</v-row>
34+
</v-container>
35+
</v-main>
36+
</template>

0 commit comments

Comments
 (0)