diff --git a/.env.example b/.env.example index b99ca071..b60c2769 100644 --- a/.env.example +++ b/.env.example @@ -10,3 +10,7 @@ NUXT_STORAGE_DRIVER= NUXT_STORAGE_FS_BASE= NUXT_PUBLIC_DISABLE_VERSION_CHECK= + +NUXT_GITHUB_CLIENT_ID= +NUXT_GITHUB_CLIENT_SECRET= +NUXT_GITHUB_INVITE_TOKEN= diff --git a/.env.mock b/.env.mock index e45acc79..9e2d8b22 100644 --- a/.env.mock +++ b/.env.mock @@ -1 +1 @@ -MOCK_USER='{"user":{"server":"universeodon.com","token":"yZcpj0FmnsEkUvBiXSCb_KQnccl2IU0kx9TfDbcxPJY","vapidKey":"BJwtUVlyCabpMnLI6HOyu-qMfJswxEq_c8pgRymxjTN_vCzMWfGrRHrwNczj9LIokAHtxh6Ziw1Kq7_ERDoriz0=","account":{"id":"109424142224653388","username":"elkdev","acct":"elkdev@universeodon.com","displayName":"Elk Dev Team","locked":false,"bot":false,"discoverable":null,"group":false,"createdAt":"2022-11-28T00:00:00.000Z","note":"","url":"https://universeodon.com/@elkdev","avatar":"https://universeodon.com/avatars/original/missing.png","avatarStatic":"https://universeodon.com/avatars/original/missing.png","header":"https://universeodon.com/headers/original/missing.png","headerStatic":"https://universeodon.com/headers/original/missing.png","followersCount":3,"followingCount":4,"statusesCount":20,"lastStatusAt":"2022-12-13","noindex":false,"source":{"privacy":"public","sensitive":false,"language":null,"note":"","fields":[],"followRequestsCount":0},"emojis":[],"fields":[],"role":{"id":"-99","name":"","permissions":"65536","color":"","highlighted":false}}},"server":{"109424142224653388":{"uri":"universeodon.com","title":"Universeodon","shortDescription":"Be one with the fediverse.","description":"","email":"novae@universeodon.com","version":"4.0.2","urls":{"streamingApi":"wss://universeodon.com"},"stats":{"userCount":57026,"statusCount":283364,"domainCount":11515},"thumbnail":"https://media.universeodon.com/site_uploads/files/000/000/003/@1x/9de6fc1bbd150b05.png","languages":["en"],"registrations":true,"approvalRequired":false,"invitesEnabled":true,"configuration":{"accounts":{"maxFeaturedTags":10},"statuses":{"maxCharacters":500,"maxMediaAttachments":4,"charactersReservedPerUrl":23},"mediaAttachments":{"supportedMimeTypes":["image/jpeg","image/png","image/gif","image/heic","image/heif","image/webp","image/avif","video/webm","video/mp4","video/quicktime","video/ogg","audio/wave","audio/wav","audio/x-wav","audio/x-pn-wave","audio/vnd.wave","audio/ogg","audio/vorbis","audio/mpeg","audio/mp3","audio/webm","audio/flac","audio/aac","audio/m4a","audio/x-m4a","audio/mp4","audio/3gpp","video/x-ms-asf"],"imageSizeLimit":10485760,"imageMatrixLimit":16777216,"videoSizeLimit":41943040,"videoFrameRateLimit":60,"videoMatrixLimit":2304000},"polls":{"maxOptions":4,"maxCharactersPerOption":50,"minExpiration":300,"maxExpiration":2629746}},"contactAccount":{"id":"109287809647205395","username":"supernovae","acct":"supernovae","displayName":"Supernovae","locked":false,"bot":false,"discoverable":true,"group":false,"createdAt":"2022-11-04T00:00:00.000Z","note":"","url":"https://universeodon.com/@supernovae","avatar":"https://media.universeodon.com/accounts/avatars/109/287/809/647/205/395/original/551eafba585d19e5.jpg","avatarStatic":"https://media.universeodon.com/accounts/avatars/109/287/809/647/205/395/original/551eafba585d19e5.jpg","header":"https://media.universeodon.com/accounts/headers/109/287/809/647/205/395/original/5de388c5945925c5.jpg","headerStatic":"https://media.universeodon.com/accounts/headers/109/287/809/647/205/395/original/5de388c5945925c5.jpg","followersCount":6387,"followingCount":305,"statusesCount":1753,"lastStatusAt":"2022-11-28","noindex":false,"emojis":[],"fields":[]},"rules":[]}}}' +MOCK_USER='{"user":{"server":"universeodon.com","token":"yZcpj0FmnsEkUvBiXSCb_KQnccl2IU0kx9TfDbcxPJY","vapidKey":"BJwtUVlyCabpMnLI6HOyu-qMfJswxEq_c8pgRymxjTN_vCzMWfGrRHrwNczj9LIokAHtxh6Ziw1Kq7_ERDoriz0=","account":{"id":"109424142224653388","username":"elkdev","acct":"elkdev@universeodon.com","displayName":"Elk Dev Team","locked":false,"bot":false,"discoverable":null,"group":false,"createdAt":"2022-11-28T00:00:00.000Z","note":"","url":"https://universeodon.com/@elkdev","avatar":"https://universeodon.com/avatars/original/missing.png","avatarStatic":"https://universeodon.com/avatars/original/missing.png","header":"https://universeodon.com/headers/original/missing.png","headerStatic":"https://universeodon.com/headers/original/missing.png","followersCount":3,"followingCount":4,"statusesCount":20,"lastStatusAt":"2022-12-13","noindex":false,"source":{"privacy":"public","sensitive":false,"language":null,"note":"","fields":[],"followRequestsCount":0},"emojis":[],"fields":[],"role":{"id":"-99","name":"","permissions":"65536","color":"","highlighted":false}}},"server":{"universeodon.com":{"uri":"universeodon.com","title":"Universeodon","shortDescription":"Be one with the fediverse.","description":"","email":"novae@universeodon.com","version":"4.0.2","urls":{"streamingApi":"wss://universeodon.com"},"stats":{"userCount":57026,"statusCount":283364,"domainCount":11515},"thumbnail":"https://media.universeodon.com/site_uploads/files/000/000/003/@1x/9de6fc1bbd150b05.png","languages":["en"],"registrations":true,"approvalRequired":false,"invitesEnabled":true,"configuration":{"accounts":{"maxFeaturedTags":10},"statuses":{"maxCharacters":500,"maxMediaAttachments":4,"charactersReservedPerUrl":23},"mediaAttachments":{"supportedMimeTypes":["image/jpeg","image/png","image/gif","image/heic","image/heif","image/webp","image/avif","video/webm","video/mp4","video/quicktime","video/ogg","audio/wave","audio/wav","audio/x-wav","audio/x-pn-wave","audio/vnd.wave","audio/ogg","audio/vorbis","audio/mpeg","audio/mp3","audio/webm","audio/flac","audio/aac","audio/m4a","audio/x-m4a","audio/mp4","audio/3gpp","video/x-ms-asf"],"imageSizeLimit":10485760,"imageMatrixLimit":16777216,"videoSizeLimit":41943040,"videoFrameRateLimit":60,"videoMatrixLimit":2304000},"polls":{"maxOptions":4,"maxCharactersPerOption":50,"minExpiration":300,"maxExpiration":2629746}},"contactAccount":{"id":"109287809647205395","username":"supernovae","acct":"supernovae","displayName":"Supernovae","locked":false,"bot":false,"discoverable":true,"group":false,"createdAt":"2022-11-04T00:00:00.000Z","note":"","url":"https://universeodon.com/@supernovae","avatar":"https://media.universeodon.com/accounts/avatars/109/287/809/647/205/395/original/551eafba585d19e5.jpg","avatarStatic":"https://media.universeodon.com/accounts/avatars/109/287/809/647/205/395/original/551eafba585d19e5.jpg","header":"https://media.universeodon.com/accounts/headers/109/287/809/647/205/395/original/5de388c5945925c5.jpg","headerStatic":"https://media.universeodon.com/accounts/headers/109/287/809/647/205/395/original/5de388c5945925c5.jpg","followersCount":6387,"followingCount":305,"statusesCount":1753,"lastStatusAt":"2022-11-28","noindex":false,"emojis":[],"fields":[]},"rules":[]}}}' diff --git a/.gitignore b/.gitignore index a48a3cd7..c4c69b12 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ dist .netlify/ public/shiki +public/emojis *~ *swp diff --git a/.vscode/settings.json b/.vscode/settings.json index 0987a409..6c710a8c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,5 +19,9 @@ "i18n-ally.keystyle": "nested", "i18n-ally.sourceLanguage": "en-US", "i18n-ally.preferredDelimiter": "_", - "i18n-ally.sortKeys": true + "i18n-ally.sortKeys": true, + "i18n-ally.keysInUse": [ + "time_ago_options.*", + "visibility.*" + ] } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..52e1b3f6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,147 @@ +# Contributing Guide + +Hi! We are really excited that you are interested in contributing to Elk. Before submitting your contribution, please make sure to take a moment and read through the following guide. + +Refer also to https://github.com/antfu/contribute. + +## Set up your local development environment + +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). + +To develop and test the Elk package: + +1. Fork the Elk repository to your own GitHub account and then clone it to your local device. + +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`. + +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 + +6. 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. + +### Running PWA on dev server + +In order to run Elk with PWA enabled, run `pnpm run dev:pwa` in Elk's root folder to start dev server or `pnpm dev:pwa:mocked` to start dev server with `@elkdev@universeodon.com` user. + +You should test the Elk PWA application on private browsing mode on any Chromium based browser: will not work on Firefox and Safari. + +If not using private browsing mode, you will need to uninstall the PWA application from your browser once you finish testing: +- Open `Dev Tools` (`Option + ⌘ + J` on MacOS, `Shift + CTRL + J` on Windows/Linux) +- Go to `Application > Storage`, you should check following checkboxes: + - Application: [x] Unregister service worker + - Storage: [x] IndexedDB and [x] Local and session storage + - Cache: [x] Cache storage and [x] Application cache +- Click on `Clear site data` button +- Go to `Application > Service Workers` and check the current `service worker` is missing or has the state `deleted` or `redundant` + +## CI errors + +Sometimes when you push your changes, the CI can fail, but we cannot check the logs to see what went wrong, run the following commands on your local environment: +- `pnpm test:unit` to run unit tests, maybe you also need to update snapshots +- `pnpm test:typecheck` to run TypeScript checks run on CI + +## RTL Support + +Elk supports `right-to-left` languages, we need to make sure that the UI is working correctly in both directions. + +Simple approach used by most websites of relying on direction set in HTML element does not work because direction for various items, such as timeline, does not always match direction set in HTML. + +We've added some `UnoCSS` utilities styles to help you with that: +- Do not use `left/right` padding and margin: for example `pl-1`. Use `padding-inline-start/end` instead. So `pl-1` should be `ps-1`, `pr-1` should be `pe-1`. Same rules applies for margin. +- Do not use `rtl-` classes, such as `rtl-left-0`. +- For icons that should be rotated for RTL, add `class="rtl-flip"`. This can only be used for icons outside of elements with `dir="auto"`, such as timeline, and is the only exception from rule above. For icons inside timeline it might not work as expected. +- For absolute positioned elements, don't use `left/right`: for example `left-0`. Use `inset-inline-start/end` instead. `UnoCSS` shortcuts are `inset-is` for `inset-inline-start` and `inset-ie` for `inset-inline-end`. Example: `left-0` should be replaced with `inset-is-0`. +- If you need to change border radius for an entire left or right side, use `border-inline-start/end`. `UnoCSS` shortcuts are `rounded-is` for left side, `rounded-ie` for right side. Example: `rounded-l-5` should be replaced with `rounded-ie-5`. +- If you need to change border radius for one corner, use `border-start-end-radius` and similar rules. `UnoCSS` shortcuts are `rounded` + top/bottom as either `-bs` (top) or `-be` (bottom) + left/right as either `-is` (left) or `-ie` (right). Example: `rounded-tl-0` should be replaced with `rounded-bs-is-0`. + +## Internalization + +We are using [vue-i18n](https://vue-i18n.intlify.dev/) via [nuxt-i18n](https://i18n.nuxtjs.org/) to handle internalization. + +### Adding a new language + +1. Add a new file in [locales](../locales) folder with the language code as the filename. +2. Copy [en-US](../locales/en-US.json) and translate the strings. +3. Add the language to the `locales` array in [config/i18n.ts](../config/i18n.ts#L13) +4. If the language is `right-to-left`, add `dir` option with `rtl` value, for example, for [ar-EG](../config/i18n.ts#L63) +5. If the language requires special pluralization rules, add `pluralRule` callback option, for example, for [ar-EG](../config/i18n.ts#L64) + +Check [Pluralization rule callback](https://vue-i18n.intlify.dev/guide/essentials/pluralization.html#custom-pluralization) for more info. + +### Messages interpolation + +Most of the messages used in Elk do not require any interpolation, however, there are some messages that require interpolation: check [Message Format Syntax](https://vue-i18n.intlify.dev/guide/essentials/syntax.html) for more info. + +We're using these types of interpolation: +- [List interpolation](https://vue-i18n.intlify.dev/guide/essentials/syntax.html#list-interpolation) +- [Named interpolation](https://vue-i18n.intlify.dev/guide/essentials/syntax.html#interpolations) +- [Linked messages](https://vue-i18n.intlify.dev/guide/essentials/syntax.html#linked-messages) +- [Literal interpolation](https://vue-i18n.intlify.dev/guide/essentials/syntax.html#literal-interpolation) + +#### List interpolation + +You can access the elements of the list using the object notation using the index: for example, `{0}` for the first element, `{1}` for the second, `{2}` for the third and so on. + +#### Named interpolation + +Elk will use named interpolation only to handle plurals for number formatting. We have 2 scenarios for this: +- using `plural` **with** `i18n-t` component +- using `plural` **without** `i18n-t` component + +Check [Custom Plural Number Formatting Entries](#custom-plural-number-formatting-entries) for custom plural entries in Elk with available values for interpolation. + +When using plural number formatting, we'll have always `{n}` available in the message, for example, `You have {n} new notifications|You have {n} new notification|You have {n} new notifications` or `You have no new notifications|You have 1 new notification|You have {n} new notifications`. + +We've included `v` named parameter, it will be used to pass the formatted number using [Intl.NumberFormat::format](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/format): will be the number with separators symbols. The exception to previous rule is when we're using `plural` **with** `i18n-t` component, in this case, we'll need to use `{0}` instead `{v}` to access the number. + +Additionally, Elk will use [compact notation for numbers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#parameters) for some entries, check `notation` and `compactDisplay` options: for example, `1K` for `1000`, `1M` for `1000000`, `1B` for `1000000000` and so on. That entry will be available in the message using `{v}` named parameter (or `{0}` if using the message **with** `i18n-t` component). + +You can run this code in your browser console to see how it works: +```ts +[1, 12, 123, 1234, 12345, 123456, 1234567].forEach((n) => { + const acc = {} + + Array.from(['en-US', 'en-GB', 'de-DE', 'zh-CN', 'ja-JP', 'es-ES', 'fr-FR', 'cs-CZ', 'ar-EG']).forEach((l) => { + const nf = new Intl.NumberFormat(l, { + style: 'decimal', + maximumFractionDigits: 0, + }) + const nf2 = new Intl.NumberFormat(l, { + notation: 'compact', + compactDisplay: 'short', + maximumFractionDigits: 1, + }) + acc[l] = { + number: n, + format: nf.format(n), + compact: nf2.format(n), + } + }) + console.table(acc) +}) +``` + +#### Custom Plural Number Formatting Entries + +**Warning**: +Either **{0}**, **{v}** or **{followers}** should be used with the exception being custom plurals entries using the `{n}` placeholder. + +This is the full list of entries that will be available for number formatting in Elk: +- `action.boost_count` (no need to be included, we should use always `en-US` entry): `{0}` for formatted number and `{n}` for raw number - **{0} should be use** +- `action.favourite_count` (no need to be included, we should use always `en-US` entry): `{0}` for formatted number and `{n}` for raw number - **{0} should be use** +- `action.reply_count` (no need to be included, we should use always `en-US` entry): `{0}` for formatted number and `{n}` for raw number - **{0} should be use** +- `account.followers_count`: `{0}` for formatted number and `{n}` for raw number - **{0} should be use** +- `account.following_count`: `{0}` for formatted number and `{n}` for raw number - **{0} should be use** +- `account.posts_count`: `{0}` for formatted number and `{n}` for raw number - **{0} should be use** +- `notification.followed_you_count`: `{followers}` for formatted number and `{n}` for raw number - **{followers} should be use** +- `status.poll.count`: `{0}` for formatted number and `{n}` for raw number - **{0} should be use** +- `time_ago_options.*`: `{0}` for formatted number and `{n}` for raw number - **{0} should be use**: since numbers will be always small, we can also use `{n}` +- `timeline.show_new_items`: `{v}` for formatted number and `{n}` for raw number - **{v} should be use** diff --git a/README.md b/README.md index c2348740..14db59a1 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ *A nimble Mastodon web client*

- - Vite logo + + Elk logo


@@ -13,26 +13,45 @@


-Elk is in early alpha, but it is already quite usable. We would love your feedback and contributions. +# Elk is in early alpha ⚠️ -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 learn more and get involved! +It is already quite usable, but it isn't ready for wide adoption yet. We recommend you to use if if you would like to help us building 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 to [elk.zone](https://elk.zone), you can share screenshots on social media but avoid sharing this URL or the discord server until we open the repo. +The client is deployed to [elk.zone](https://elk.zone), 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 friedns and invite others you think could be interested in helping to improve Elk. -> **Note** -> If you would like to contribute, until the repo is open, please create branches in the main repository and send a PR from there. +## Sponsors -# Contributing +We want to thanks the generous sponsoring and help of: -Hi! We're really excited that you're interested in contributing to Elk! Before submitting your contribution, please read through the following guide. + + NuxtLabs + +

+ + StackBlitz + +

-## Online +And all the companies and individuals sponsoring Elk Team members. If you're enjoying the app, consider sponsoring our team: -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. +- [Anthony Fu](https://github.com/sponsors/antfu) +- [Daniel Roe](https://github.com/sponsors/danielroe) +- [三咲智子 Kevin Deng](https://github.com/sponsors/sxzz) +- [Patak](https://github.com/sponsors/patak-dev) + +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. + +## Contributing + +We're really excited that you're interested in contributing to Elk! Before submitting your contribution, please read through the following guide. + +### Online + +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 +### Local Setup Clone the repository and run on the root folder: @@ -41,6 +60,8 @@ pnpm i pnpm run dev ``` +`Warning`: you will need `corepack` enabled, check out the [Elk Contributing Guide](./CONTRIBUTING.md) for a detailed guide on how to set up the project locally. + 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: ``` @@ -48,7 +69,7 @@ ni nr dev ``` -## Testing +### Testing Elk uses [Vitest](https://vitest.dev). You can run the test suite with: @@ -56,7 +77,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 @@ -70,6 +91,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 +[MIT](./LICENSE) © 2022-PRESENT Elk contributors diff --git a/app.vue b/app.vue index b7379cb6..1022586e 100644 --- a/app.vue +++ b/app.vue @@ -1,17 +1,15 @@ diff --git a/components/PWAPrompt.client.vue b/components/PWAPrompt.client.vue deleted file mode 100644 index 056cd2e2..00000000 --- a/components/PWAPrompt.client.vue +++ /dev/null @@ -1,39 +0,0 @@ - - - - diff --git a/components/account/AccountAvatar.vue b/components/account/AccountAvatar.vue index 920b15e9..44db7eb8 100644 --- a/components/account/AccountAvatar.vue +++ b/components/account/AccountAvatar.vue @@ -12,6 +12,8 @@ const error = $ref(false)