Merge branch 'main' of github.com:elk-zone/elk into feat/interface-font-selector-ui

This commit is contained in:
wheatjs 2023-01-11 19:13:58 -05:00
commit 799d157424
24 changed files with 189 additions and 117 deletions

3
.github/FUNDING.yml vendored
View file

@ -1 +1,2 @@
github: [antfu, patak-dev, sxzz, danielroe]
github: [elk-zone]
open_collective: elk

View file

@ -4,9 +4,13 @@ Hi! We are really excited that you are interested in contributing to Elk. Before
Refer also to https://github.com/antfu/contribute.
## Set up your local development environment
### Online
The package manager used to install and link dependencies must be [pnpm](https://pnpm.io/) (Note: on Linux in a standard Node 16+ environment, you should follow the instructions to install via Node's `corepack` rather than using the `curl` command).
You can use [StackBlitz Codeflow](https://stackblitz.com/codeflow) to fix bugs or implement features. You'll also see a Codeflow button on PRs to review them without a local setup. Once the elk repo has been cloned in Codeflow, the dev server will start automatically and print the URL to open the App. You should receive a prompt in the bottom-right suggesting to open it in the Editor or in another Tab. To learn more, check out the [Codeflow docs](https://developer.stackblitz.com/codeflow/what-is-codeflow).
[![Open in Codeflow](https://developer.stackblitz.com/img/open_in_codeflow.svg)](https://pr.new/elk-zone/elk)
### Local Setup
To develop and test the Elk package:
@ -14,18 +18,33 @@ To develop and test the Elk package:
2. Ensure using the latest Node.js (16.x)
3. Elk uses pnpm v7, you must enable [Corepack](https://github.com/nodejs/corepack) by running `corepack enable`.
3. The package manager used to install and link dependencies must be [pnpm](https://pnpm.io/) v7. To use it you must first enable [Corepack](https://github.com/nodejs/corepack) by running `corepack enable`. (Note: on Linux in a standard Node 16+ environment, you should follow the instructions to install via Node's `corepack` rather than using the `curl` command)
4. Check out a branch where you can work and commit your changes:
```shell
git checkout -b my-new-branch
```
5. Run `pnpm i` in Elk's root folder
1. Run `pnpm i` in Elk's root folder
6. Run `pnpm nuxi prepare` in Elk's root folder
2. Run `pnpm nuxi prepare` in Elk's root folder
7. Run `pnpm dev` in Elk's root folder to start dev server or `pnpm dev:mocked` to start dev server with `@elkdev@universeodon.com` user.
3. Run `pnpm dev` in Elk's root folder to start dev server or `pnpm dev:mocked` to start dev server with `@elkdev@universeodon.com` user.
We recommend installing [ni](https://github.com/antfu/ni#ni), that will use the right package manager in each of your projects. If `ni` is installed, you can instead run:
```
ni
nr dev
```
### Testing
Elk uses [Vitest](https://vitest.dev). You can run the test suite with:
```
nr test
```
### Running PWA on dev server

View file

@ -1,11 +1,15 @@
# Elk
*A nimble Mastodon web client*
<p align="center">
<a href="https://elk.zone" target="_blank" rel="noopener noreferrer">
<img width="180" height="180" src="./elk.svg" alt="Elk logo">
<img width="160" height="160" src="./public/logo.svg" alt="Elk logo">
</a>
</p>
<h1 align="center"/>Elk <sup><em>alpha</em></sup></h1>
<p align="center">
A nimble Mastodon web client
</p>
<br/>
<p align="center">
<a href="https://chat.elk.zone"><img src="https://img.shields.io/badge/chat-discord-blue?style=flat&logo=discord" alt="discord chat"></a>
@ -13,9 +17,15 @@
</p>
<br/>
# Elk is in early alpha ⚠️
<p align="center">
<a href="https://elk.zone/" target="_blank" rel="noopener noreferrer" >
<img src="./public/elk-og.png" alt="Elk screenshots" height="300">
</a>
</p>
It is already quite usable, but it isn't ready for wide adoption yet. We recommend you to use it if you would like to help us build it. We appreciate your feedback and contributions. Check out the [Open Issues](https://github.com/elk-zone/elk/issues) and jump in the action. Join the [Elk discord server](https://chat.elk.zone) to chat with us and learn more about the project.
## ⚠️ Elk is in Alpha
It is already quite usable, but it isn't ready for wide adoption yet. We recommend you use it if you would like to help us build it. We appreciate your feedback and contributions. Check out the [Open Issues](https://github.com/elk-zone/elk/issues) and jump in the action. Join the [Elk discord server](https://chat.elk.zone) to chat with us and learn more about the project.
The client is deployed on:
@ -24,9 +34,9 @@ The client is deployed on:
You can share screenshots on social media but we prefer you avoid sharing this URL directly until the app is more polished. Feel free to share the URL with your friends and invite others you think could be interested in helping to improve Elk.
## Sponsors
## 💖 Sponsors
We want to thanks the generous sponsoring and help of:
We are grateful for the generous sponsorship and help of:
<a href="https://nuxtlabs.com/" target="_blank" rel="noopener noreferrer" >
<img src="./images/nuxtlabs.svg" alt="NuxtLabs" height="85">
@ -37,7 +47,11 @@ We want to thanks the generous sponsoring and help of:
</a>
<br><br>
And all the companies and individuals sponsoring Elk Team members. If you're enjoying the app, consider sponsoring our team:
And all the companies and individuals sponsoring Elk Team and the members. If you're enjoying the app, consider sponsoring us:
- [Elk Team's GitHub Sponsors](https://github.com/sponsors/elk-zone)
Or you can sponsor our core team members individually:
- [Anthony Fu](https://github.com/sponsors/antfu)
- [Daniel Roe](https://github.com/sponsors/danielroe)
@ -46,11 +60,11 @@ And all the companies and individuals sponsoring Elk Team members. If you're enj
We would also appreciate sponsoring other contributors to the Elk project. If someone helps you solve an issue or implement a feature you wanted, supporting them would help make this project and OS more sustainable.
## Roadmap
## 📍 Roadmap
[Open board on Volta](https://volta.net/elk-zone/elk)
## Contributing
## 🧑‍💻 Contributing
We're really excited that you're interested in contributing to Elk! Before submitting your contribution, please read through the following guide.
@ -86,7 +100,7 @@ Elk uses [Vitest](https://vitest.dev). You can run the test suite with:
nr test
```
## Stack
## 🦄 Stack
- [Vite](https://vitejs.dev/) - Next Generation Frontend Tooling
- [Nuxt](https://nuxt.com/) - The Intuitive Web Framework
@ -100,6 +114,6 @@ nr test
- [shiki](https://shiki.matsu.io/) - A beautiful Syntax Highlighter
- [vite-plugin-pwa](https://github.com/vite-pwa/vite-plugin-pwa) - Prompt for update and push notifications
## License
## 📄 License
[MIT](./LICENSE) &copy; 2022-PRESENT Elk contributors

View file

@ -2,6 +2,15 @@
setupPageHeader()
provideGlobalCommands()
const route = useRoute()
if (process.server && !route.path.startsWith('/settings')) {
useHead({
meta: [
{ property: 'og:url', content: `https://main.elk.zone${route.path}` },
],
})
}
// We want to trigger rerendering the page when account changes
const key = computed(() => `${currentUser.value?.server ?? currentServer.value}:${currentUser.value?.account.id || ''}`)
</script>

View file

@ -26,6 +26,16 @@ function toggleDark() {
@click="userSettings.zenMode = !userSettings.zenMode"
/>
</CommonTooltip>
<CommonTooltip :content="$t('settings.about.sponsor_action')">
<NuxtLink
flex
text-lg
i-ri-heart-3-line hover="i-ri-heart-3-fill text-rose"
:aria-label="$t('settings.about.sponsor_action')"
href="https://github.com/sponsors/elk-zone"
target="_blank"
/>
</CommonTooltip>
</div>
<div>
<i18n-t v-if="isHydrated" keypath="nav.built_at">

View file

@ -62,7 +62,7 @@ useCommand({
/>
</slot>
</div>
<div space-y-1>
<div flex="~ col gap-0.5">
<p>
<slot>
<span>{{ text }}</span>

File diff suppressed because one or more lines are too long

View file

@ -55,7 +55,10 @@ export function parseMastodonHTML(
// Handle code blocks
html = html
.replace(/>(```|~~~)(\w*)([\s\S]+?)\1/g, (_1, _2, lang: string, raw: string) => {
const code = htmlToText(raw).replace(/</g, '&lt;').replace(/>/g, '&gt;')
const code = htmlToText(raw)
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/`/, '&#96;')
const classes = lang ? ` class="language-${lang}"` : ''
return `><pre><code${classes}>${code}</code></pre>`
})

View file

@ -37,6 +37,20 @@ export function onReactivated(hook: Function, target?: ComponentInternalInstance
// TODO: Workaround for Nuxt bug: https://github.com/elk-zone/elk/pull/199#issuecomment-1329771961
export function useHeadFixed<T extends HeadAugmentations>(input: UseHeadInput<T>, options?: HeadEntryOptions): ActiveHeadEntry<UseHeadInput<T>> | void {
const deactivated = useDeactivated()
if (input && typeof input === 'object' && !('value' in input)) {
const title = 'title' in input ? input.title : undefined
if (process.server && title) {
input.meta = input.meta || []
if (Array.isArray(input.meta)) {
input.meta.push(
{ property: 'og:title', content: (typeof input.title === 'function' ? input.title() : input.title) as string },
)
}
}
else if (title) {
(input as any).title = () => isHydrated.value ? typeof title === 'function' ? title() : title : ''
}
}
return useHead(() => {
if (deactivated.value)
return {}

View file

@ -236,7 +236,13 @@
"settings": {
"about": {
"label": "About",
"meet_the_team": "Meet the team"
"meet_the_team": "Meet the team",
"sponsor_action": "Sponsor us",
"sponsor_action_desc": "To support the team developing Elk",
"sponsors": "Sponsors",
"sponsors_body_1": "Elk is made possible thanks the generous sponsoring and help of:",
"sponsors_body_2": "And all the companies and individuals sponsoring Elk Team and the members.",
"sponsors_body_3": "If you're enjoying the app, consider sponsoring us:"
},
"account_settings": {
"description": "Edit your account settings in Mastodon UI",

View file

@ -5,7 +5,6 @@ import {
toNodeListener,
} from 'h3'
import { createFetch } from 'ofetch'
import { parseURL } from 'ufo'
import {
createCall,
createFetch as createLocalFetch,
@ -26,13 +25,10 @@ const handlers = [
},
]
const { protocol, host } = parseURL(window.location.href)
// @ts-expect-error undeclared global window property
window.__NUXT__.config = {
// @ts-expect-error undeclared global window property
...window.__NUXT__.config,
deployUrl: `${protocol}//${host}`,
storage: {},
}

View file

@ -71,25 +71,11 @@ export default defineNuxtConfig({
},
},
runtimeConfig: {
deployUrl: !isCI
? 'http://localhost:5314'
: isPreview
? process.env.DEPLOY_PRIME_URL
: 'https://elk.zone',
cloudflare: {
accountId: '',
namespaceId: '',
apiToken: '',
},
discord: {
inviteUrl: 'https://chat.elk.zone',
},
github: {
// oauth flow
clientId: '',
clientSecret: '',
inviteToken: '',
},
public: {
env: '', // set in build-env module
buildInfo: {} as BuildInfo, // set in build-env module
@ -132,6 +118,16 @@ export default defineNuxtConfig({
],
meta: [
{ name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' },
// open graph social image
{ property: 'og:title', content: 'Elk' },
{ property: 'og:description', content: 'A nimble Mastodon web client' },
{ property: 'og:type', content: 'website' },
{ property: 'og:image', content: 'https://main.elk.zone/elk-og.png' },
{ property: 'og:image:width', content: '3800' },
{ property: 'og:image:height', content: '1900' },
{ property: 'og:site_name', content: 'Elk' },
{ property: 'twitter:site', content: '@elk_zone' },
{ property: 'twitter:card', content: 'summary_large_image' },
],
},
},

View file

@ -8,7 +8,7 @@ const paginator = useMasto().v1.trends.listStatuses()
const hideNewsTips = useLocalStorage(STORAGE_KEY_HIDE_EXPLORE_POSTS_TIPS, false)
useHeadFixed({
title: () => isHydrated.value ? `${t('tab.posts')} | ${t('nav.explore')}` : '',
title: () => `${t('tab.posts')} | ${t('nav.explore')}`,
})
</script>

View file

@ -8,7 +8,7 @@ const paginator = useMasto().v1.trends.listLinks()
const hideNewsTips = useLocalStorage(STORAGE_KEY_HIDE_EXPLORE_NEWS_TIPS, false)
useHeadFixed({
title: () => isHydrated.value ? `${t('tab.news')} | ${t('nav.explore')}` : '',
title: () => `${t('tab.news')} | ${t('nav.explore')}`,
})
</script>

View file

@ -11,7 +11,7 @@ const paginator = masto.v1.trends.listTags({
const hideTagsTips = useLocalStorage(STORAGE_KEY_HIDE_EXPLORE_TAGS_TIPS, false)
useHeadFixed({
title: () => isHydrated.value ? `${t('tab.hashtags')} | ${t('nav.explore')}` : '',
title: () => `${t('tab.hashtags')} | ${t('nav.explore')}`,
})
</script>

View file

@ -5,7 +5,7 @@ const { t } = useI18n()
const paginator = useMasto().v2.suggestions.list({ limit: 20 })
useHeadFixed({
title: () => isHydrated.value ? `${t('tab.for_you')} | ${t('nav.explore')}` : '',
title: () => `${t('tab.for_you')} | ${t('nav.explore')}`,
})
</script>

View file

@ -1,7 +1,7 @@
<script setup lang="ts">
const { t } = useI18n()
useHeadFixed({
title: () => isHydrated.value ? `${t('tab.notifications_all')} | ${t('nav.notifications')}` : '',
title: () => `${t('tab.notifications_all')} | ${t('nav.notifications')}`,
})
</script>

View file

@ -1,7 +1,7 @@
<script setup lang="ts">
const { t } = useI18n()
useHeadFixed({
title: () => isHydrated.value ? `${t('tab.notifications_mention')} | ${t('nav.notifications')}` : '',
title: () => `${t('tab.notifications_mention')} | ${t('nav.notifications')}`,
})
</script>

View file

@ -54,7 +54,7 @@ const handleShowCommit = () => {
<SettingsItem
:text="$t('nav.show_intro')"
icon="i-ri:article-line"
cursor-pointer
cursor-pointer large
@click="openPreviewHelp"
/>
@ -62,27 +62,58 @@ const handleShowCommit = () => {
text="Mastodon"
icon="i-ri:mastodon-line"
to="/m.webtoo.ls/@elk"
external target="_blank"
external large target="_blank"
/>
<SettingsItem
text="Discord"
icon="i-ri:discord-fill"
to="https://chat.elk.zone"
external target="_blank"
external large target="_blank"
/>
<SettingsItem
text="GitHub"
icon="i-ri:github-fill"
to="https://github.com/elk-zone"
external target="_blank"
external large target="_blank"
/>
<div h-1px bg-border my2 />
<p px5 py3 font-bold text-lg>
{{ $t('settings.about.sponsors') }}
</p>
<p px5 text-secondary>
{{ $t('settings.about.sponsors_body_1') }}
</p>
<LazySettingsSponsorsList />
<p px5 mb1 text-secondary>
{{ $t('settings.about.sponsors_body_2') }}
</p>
<p px5 mb2 text-secondary>
{{ $t('settings.about.sponsors_body_3') }}
</p>
<SettingsItem
:text="$t('settings.about.sponsor_action')"
to="https://github.com/sponsors/elk-zone"
:description="$t('settings.about.sponsor_action_desc')"
external large target="_blank"
>
<template #icon>
<div i-ri-heart-3-fill text-rose rounded-full w-8 h-8 height="32" width="32" />
</template>
</SettingsItem>
<div h-1px bg-border my2 />
<template v-if="isHydrated">
<p px5 py3 font-bold text-lg>
{{ $t('settings.about.meet_the_team') }}
</p>
<SettingsItem
v-for="team in teams" :key="team.github"
:text="team.display"

View file

@ -1,4 +1,5 @@
<script lang="ts" setup>
/* eslint-disable no-alert */
import { fileOpen } from 'browser-fs-access'
import type { UserLogin } from '~/types'

BIN
public/elk-og.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 KiB

View file

@ -1,66 +0,0 @@
const query = (accessToken: string, query: string) =>
$fetch<{ data: any }>('https://api.github.com/graphql', {
method: 'POST',
headers: { Authorization: `Bearer ${accessToken}` },
body: { query },
})
export default defineEventHandler(async (event) => {
const { code } = getQuery(event)
const config = useRuntimeConfig()
if (!code) {
const redirect = `&redirect_uri=${config.deployUrl}/invite`
const loginURL = `https://github.com/login/oauth/authorize?client_id=${config.github.clientId}${redirect}`
await sendRedirect(event, loginURL)
return
}
const { access_token } = await $fetch<{ access_token: string }>(
'https://github.com/login/oauth/access_token',
{
method: 'POST',
body: {
client_id: config.github.clientId,
client_secret: config.github.clientSecret,
code,
},
},
)
if (!access_token) {
throw createError({
statusCode: 422,
statusMessage: 'Authorisation code invalid.',
})
}
const id = await query(access_token, '{ viewer { databaseId } }')
.then(r => r.data?.viewer.databaseId)
if (!id) {
throw createError({
statusCode: 422,
statusMessage: 'Access code invalid.',
})
}
await $fetch(
'https://api.github.com/orgs/elk-zone/invitations',
{
method: 'POST',
body: { invitee_id: id, role: 'direct_member', team_ids: [7042932] },
headers: {
'Accept': 'application/vnd.github+json',
'Authorization': `Bearer ${config.github.inviteToken}`,
'X-GitHub-Api-Version': '2022-11-28',
},
},
)
return sendRedirect(
event,
config.discord.inviteUrl,
)
})

View file

@ -1,5 +1,7 @@
// Vitest Snapshot v1
exports[`content-rich > block with backticks 1`] = `"<p><pre>[(\`number string) (\`tag string)]</pre></p>"`;
exports[`content-rich > code frame 1`] = `
"<p>Testing code block</p><p></p><p><pre lang=\\"ts\\">import { useMouse, usePreferredDark } from &#39;@vueuse/core&#39;
// tracks mouse position

View file

@ -20,6 +20,11 @@ describe('content-rich', () => {
expect(formatted).toMatchSnapshot()
})
it ('block with backticks', async () => {
const { formatted } = await render('<p>```<br />[(`number string) (`tag string)]<br />```</p>')
expect(formatted).toMatchSnapshot()
})
it('group mention', async () => {
const { formatted } = await render('<p><span class="h-card"><a href="https://lemmy.ml/c/pilipinas" class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@<span>pilipinas</span></a></span></p>', undefined, [{ id: '', username: 'pilipinas', url: 'https://lemmy.ml/c/pilipinas', acct: 'pilipinas@lemmy.ml' }])
expect(formatted).toMatchSnapshot('html')