Convert all remaining components into script-setup format

This commit is contained in:
silverpill 2022-08-13 20:49:28 +00:00
parent 5f2506c170
commit 78dfbfc396
4 changed files with 250 additions and 277 deletions

View file

@ -10,7 +10,7 @@
<div class="textarea-group">
<textarea
id="content"
ref="postFormContent"
ref="postFormContentRef"
v-model="content"
rows="1"
required
@ -29,7 +29,7 @@
<img :src="require('@/assets/feather/paperclip.svg')">
<input
type="file"
ref="attachmentUploadInput"
ref="attachmentUploadInputRef"
accept="image/*"
style="display: none;"
@change="onAttachmentUpload($event)"
@ -85,9 +85,9 @@
</form>
</template>
<script lang="ts">
import { Options, Vue, setup } from "vue-class-component"
import { Prop } from "vue-property-decorator"
<script setup lang="ts">
import { nextTick, onMounted } from "vue"
import { $, $computed, $ref } from "vue/macros"
import {
Visibility,
@ -107,127 +107,119 @@ import { setupAutoResize, triggerResize } from "@/utils/autoresize"
import { renderMarkdownLite } from "@/utils/markdown"
import { fileToDataUrl, dataUrlToBase64 } from "@/utils/upload"
@Options({
components: {
Avatar,
VisibilityIcon,
},
const visibilityMap = Object.entries(VISIBILITY_MAP)
const { currentUser, ensureAuthToken } = $(useCurrentUser())
const { instance, getActorAddress } = $(useInstanceInfo())
/* eslint-disable-next-line no-undef */
const props = defineProps<{
inReplyTo: Post | null,
}>()
/* eslint-disable-next-line no-undef, func-call-spacing */
const emit = defineEmits<{
(event: "post-created", post: Post): void,
}>()
const postFormContentRef = $ref<HTMLTextAreaElement | null>(null)
const attachmentUploadInputRef = $ref<HTMLInputElement | null>(null)
let content = $ref("")
let attachment = $ref<Attachment | null>(null)
let visibility = $ref(Visibility.Public)
let visibilityMenuVisible = $ref(false)
let errorMessage = $ref<string | null>(null)
const author = $computed<User | null>(() => {
return currentUser
})
export default class PostEditor extends Vue {
@Prop()
inReplyTo: Post | null = null
if (props.inReplyTo) {
const mentions: Mention[] = [
props.inReplyTo.account,
...props.inReplyTo.mentions,
]
content = mentions
.filter(mention => mention.id !== currentUser?.id)
.map(mention => "@" + getActorAddress(mention))
// Remove duplicates
.filter((mention, index, mentions) => mentions.indexOf(mention) === index)
.join(" ")
}
if (props.inReplyTo && props.inReplyTo.visibility !== Visibility.Public) {
visibility = Visibility.Direct
}
content = ""
attachment: Attachment | null = null
visibility = Visibility.Public
mentions: string[] = []
onMounted(() => {
if (postFormContentRef) {
setupAutoResize(postFormContentRef)
}
})
function selectAttachment() {
if (attachmentUploadInputRef) {
attachmentUploadInputRef.click()
}
}
async function onAttachmentUpload(event: Event) {
const files = (event.target as HTMLInputElement).files
if (!files) {
return
}
const imageDataUrl = await fileToDataUrl(files[0])
const imageBase64 = dataUrlToBase64(imageDataUrl)
attachment = await uploadAttachment(
ensureAuthToken(),
imageBase64,
)
}
function toggleVisibilityMenu() {
visibilityMenuVisible = !visibilityMenuVisible
}
function hideVisibilityMenu() {
visibilityMenuVisible = false
errorMessage: string | null = null
}
$refs!: {
postFormContent: HTMLTextAreaElement,
attachmentUploadInput: HTMLInputElement,
function getCharacterCount(): number {
if (!instance) {
return 0
}
return (instance.post_character_limit - content.length)
}
private store = setup(() => {
const { currentUser, ensureAuthToken } = useCurrentUser()
const { instance, getActorAddress } = useInstanceInfo()
return { currentUser, ensureAuthToken, instance, getActorAddress }
})
get author(): User | null {
return this.store.currentUser
async function publish() {
const contentRendered = renderMarkdownLite(content)
const postData = {
content: contentRendered,
in_reply_to_id: props.inReplyTo ? props.inReplyTo.id : null,
visibility: visibility,
mentions: [],
}
created() {
if (this.inReplyTo) {
const mentions: Mention[] = [
this.inReplyTo.account,
...this.inReplyTo.mentions,
]
this.content = mentions
.filter(mention => mention.id !== this.store.currentUser?.id)
.map(mention => "@" + this.store.getActorAddress(mention))
// Remove duplicates
.filter((mention, index, mentions) => mentions.indexOf(mention) === index)
.join(" ")
}
if (this.inReplyTo && this.inReplyTo.visibility !== Visibility.Public) {
this.visibility = Visibility.Direct
}
}
mounted() {
setupAutoResize(this.$refs.postFormContent)
}
selectAttachment() {
this.$refs.attachmentUploadInput.click()
}
async onAttachmentUpload(event: Event) {
const files = (event.target as HTMLInputElement).files
if (!files) {
return
}
const imageDataUrl = await fileToDataUrl(files[0])
const imageBase64 = dataUrlToBase64(imageDataUrl)
this.attachment = await uploadAttachment(
this.store.ensureAuthToken(),
imageBase64,
let post
try {
post = await createPost(
ensureAuthToken(),
postData,
attachment,
)
} catch (error: any) {
errorMessage = error.message
return
}
get visibilityMap() {
return Object.entries(VISIBILITY_MAP)
// Refresh editor
errorMessage = null
attachment = null
content = ""
if (postFormContentRef) {
await nextTick()
triggerResize(postFormContentRef)
}
toggleVisibilityMenu() {
this.visibilityMenuVisible = !this.visibilityMenuVisible
}
hideVisibilityMenu() {
this.visibilityMenuVisible = false
}
getCharacterCount(): number {
if (!this.store.instance) {
return 0
}
return (this.store.instance.post_character_limit - this.content.length)
}
async publish() {
const content = renderMarkdownLite(this.content)
const postData = {
content,
in_reply_to_id: this.inReplyTo ? this.inReplyTo.id : null,
visibility: this.visibility,
mentions: this.mentions,
}
let post
try {
post = await createPost(
this.store.ensureAuthToken(),
postData,
this.attachment,
)
} catch (error: any) {
this.errorMessage = error.message
return
}
// Refresh editor
this.errorMessage = null
this.attachment = null
this.content = ""
this.$nextTick(() => {
triggerResize(this.$refs.postFormContent)
})
this.$emit("post-created", post)
}
emit("post-created", post)
}
</script>

View file

@ -1,7 +1,7 @@
<template>
<sidebar-layout>
<template #content>
<post-editor @post-created="insertPost"></post-editor>
<post-editor :in-reply-to="null" @post-created="insertPost"></post-editor>
<post-list :posts="posts" @load-next-page="loadNextPage"></post-list>
</template>
</sidebar-layout>

View file

@ -20,7 +20,12 @@
<div class="form-title">Want to join?</div>
<div class="form-control">
<div class="input-group">
<input id="username" v-model="username" required placeholder="Username">
<input
id="username"
v-model="username"
required
placeholder="Username"
>
<div class="addon">@{{ instance.uri }}</div>
</div>
<div class="form-message">Only letters, numbers and underscores are allowed.</div>
@ -48,111 +53,97 @@
</div>
</template>
<script lang="ts">
import { Options, Vue, setup } from "vue-class-component"
<script setup lang="ts">
import { $, $ref } from "vue/macros"
import { useRouter } from "vue-router"
import {
createUser,
getAccessToken,
getCurrentUser,
} from "@/api/users"
import { InstanceInfo } from "@/api/instance"
import Loader from "@/components/Loader.vue"
import { useInstanceInfo } from "@/store/instance"
import { useCurrentUser } from "@/store/user"
import { createEip4361_SignedMessage, getWallet } from "@/utils/ethereum"
@Options({
components: { Loader },
})
export default class LandingPage extends Vue {
const router = useRouter()
const { setCurrentUser, setAuthToken } = useCurrentUser()
const { instance } = $(useInstanceInfo())
username = ""
inviteCode: string | null = null
const username = $ref("")
const inviteCode = $ref<string | null>(null)
let isLoading = $ref(false)
let loginErrorMessage = $ref<string | null>(null)
let registrationErrorMessage = $ref<string | null>(null)
async function register() {
registrationErrorMessage = null
if (!instance) {
return
}
const instanceHost = instance.uri
const loginMessage = instance.login_message
const signer = await getWallet()
if (!signer) {
return
}
const { message, signature } = await createEip4361_SignedMessage(
signer,
instanceHost,
loginMessage,
)
isLoading = true
let user
let authToken
try {
user = await createUser({
username: username,
message,
signature,
invite_code: inviteCode,
})
authToken = await getAccessToken(message, signature)
} catch (error: any) {
isLoading = false
registrationErrorMessage = error.message
return
}
setCurrentUser(user)
setAuthToken(authToken)
isLoading = false
loginErrorMessage: string | null = null
registrationErrorMessage: string | null = null
private store = setup(() => {
const { setCurrentUser, setAuthToken } = useCurrentUser()
const { instance } = useInstanceInfo()
return { setCurrentUser, setAuthToken, instance }
})
get instance(): InstanceInfo | null {
return this.store.instance
}
async register() {
this.registrationErrorMessage = null
if (!this.store.instance) {
return
}
const instanceHost = this.store.instance.uri
const loginMessage = this.store.instance.login_message
const signer = await getWallet()
if (!signer) {
return
}
const { message, signature } = await createEip4361_SignedMessage(
signer,
instanceHost,
loginMessage,
)
this.isLoading = true
let user
let authToken
try {
user = await createUser({
username: this.username,
message,
signature,
invite_code: this.inviteCode,
})
authToken = await getAccessToken(message, signature)
} catch (error: any) {
this.isLoading = false
this.registrationErrorMessage = error.message
return
}
this.store.setCurrentUser(user)
this.store.setAuthToken(authToken)
this.isLoading = false
this.$router.push({ name: "home" })
}
async login() {
this.loginErrorMessage = null
if (!this.store.instance) {
return
}
const instanceHost = this.store.instance.uri
const loginMessage = this.store.instance.login_message
const signer = await getWallet()
if (!signer) {
return
}
const { message, signature } = await createEip4361_SignedMessage(
signer,
instanceHost,
loginMessage,
)
let user
let authToken
try {
authToken = await getAccessToken(message, signature)
user = await getCurrentUser(authToken)
} catch (error: any) {
this.loginErrorMessage = error.message
return
}
this.store.setCurrentUser(user)
this.store.setAuthToken(authToken)
this.$router.push({ name: "home" })
}
router.push({ name: "home" })
}
async function login() {
loginErrorMessage = null
if (!instance) {
return
}
const instanceHost = instance.uri
const loginMessage = instance.login_message
const signer = await getWallet()
if (!signer) {
return
}
const { message, signature } = await createEip4361_SignedMessage(
signer,
instanceHost,
loginMessage,
)
let user
let authToken
try {
authToken = await getAccessToken(message, signature)
user = await getCurrentUser(authToken)
} catch (error: any) {
loginErrorMessage = error.message
return
}
setCurrentUser(user)
setAuthToken(authToken)
router.push({ name: "home" })
}
</script>
<style scoped lang="scss">

View file

@ -50,8 +50,10 @@
</div>
</template>
<script lang="ts">
import { Options, Vue, setup } from "vue-class-component"
<script setup lang="ts">
import { onMounted } from "vue"
import { $, $ref, $computed } from "vue/macros"
import { useRoute, useRouter } from "vue-router"
import { DateTime } from "luxon"
@ -61,79 +63,67 @@ import Avatar from "@/components/Avatar.vue"
import { useInstanceInfo } from "@/store/instance"
import { useCurrentUser } from "@/store/user"
@Options({
components: {
Avatar,
},
const route = useRoute()
const router = useRouter()
const { currentUser, authToken } = $(useCurrentUser())
const { instance, getActorAddress } = $(useInstanceInfo())
let post = $ref<Post | null>(null)
let token = $ref<TokenMetadata | null>(null)
onMounted(async () => {
post = await getPost(
authToken,
route.params.postId as string,
)
if (metadataUrl) {
token = await getTokenMetadata(metadataUrl)
}
})
export default class PostOverlay extends Vue {
post: Post | null = null
token: TokenMetadata | null = null
private store = setup(() => {
const { currentUser, authToken } = useCurrentUser()
const { instance, getActorAddress } = useInstanceInfo()
return { currentUser, authToken, instance, getActorAddress }
})
async created() {
this.post = await getPost(
this.store.authToken,
this.$route.params.postId as string,
)
const metadataUrl = this.metadataUrl
if (metadataUrl) {
this.token = await getTokenMetadata(metadataUrl)
}
}
canGoBack(): boolean {
return this.store.currentUser !== null
}
goBack() {
this.$router.back()
}
get actorAddress(): string {
if (!this.post) {
return ""
}
return this.store.getActorAddress(this.post.account)
}
get transactionUrl(): string | null {
const explorerUrl = this.store.instance?.blockchain_explorer_url
if (!explorerUrl || !this.post?.token_tx_id) {
return null
}
return `${explorerUrl}/tx/0x${this.post.token_tx_id}`
}
get metadataUrl(): string | null {
const gatewayUrl = this.store.instance?.ipfs_gateway_url
if (!gatewayUrl || !this.post) {
return null
}
return `${gatewayUrl}/ipfs/${this.post.ipfs_cid}`
}
get imageUrl(): string | null {
const gatewayUrl = this.store.instance?.ipfs_gateway_url
if (!gatewayUrl || !this.token) {
return null
}
return this.token.image.replace("ipfs://", `${gatewayUrl}/ipfs/`)
}
formatDate(isoDate: string): string {
const date = DateTime.fromISO(isoDate)
return date.toLocaleString(DateTime.DATE_FULL)
}
function canGoBack(): boolean {
return currentUser !== null
}
function goBack() {
router.back()
}
const actorAddress = $computed<string>(() => {
if (!post) {
return ""
}
return getActorAddress(post.account)
})
const transactionUrl = $computed<string | null>(() => {
const explorerUrl = instance?.blockchain_explorer_url
if (!explorerUrl || !post?.token_tx_id) {
return null
}
return `${explorerUrl}/tx/0x${post.token_tx_id}`
})
const metadataUrl = $computed<string | null>(() => {
const gatewayUrl = instance?.ipfs_gateway_url
if (!gatewayUrl || !post) {
return null
}
return `${gatewayUrl}/ipfs/${post.ipfs_cid}`
})
const imageUrl = $computed<string | null>(() => {
const gatewayUrl = instance?.ipfs_gateway_url
if (!gatewayUrl || !token) {
return null
}
return token.image.replace("ipfs://", `${gatewayUrl}/ipfs/`)
})
function formatDate(isoDate: string): string {
const date = DateTime.fromISO(isoDate)
return date.toLocaleString(DateTime.DATE_FULL)
}
</script>
<style scoped lang="scss">