diff --git a/CALCKEY.md b/CALCKEY.md index 55d37a56e..d1585adc3 100644 --- a/CALCKEY.md +++ b/CALCKEY.md @@ -11,7 +11,7 @@ - Federate with note edits - User "choices" (recommended users) like Mastodon and Soapbox - Join Reason system like Mastodon/Pleroma - - Option to publicize instance blocks + - Option to publicize server blocks - Build flag to remove NSFW/AI stuff - Filter notifications by user - Exclude self from antenna @@ -19,7 +19,7 @@ - MFM button - Personal notes for all accounts - Fully revamp non-logged-in screen - - Lookup/details for post/file/instance + - Lookup/details for post/file/server - [Rat mode?](https://stop.voring.me/notes/933fx97bmd) ## Work in progress @@ -43,7 +43,7 @@ - Upgrade packages with security vunrabilities - Saner defaults - Fediverse account migration -- Recommended instances timeline +- Recommended servers timeline - OCR image captioning - Improve mobile UX - Swipe through pages on mobile @@ -71,7 +71,7 @@ - Better welcome screen (not logged in) - vue-plyr as video/audio player - Ability to turn off "Connection lost" message -- Raw instance info only for moderators +- Raw server info only for moderators - New spinner animation - Spinner instead of "Loading..." - SearchX instead of Google @@ -98,7 +98,7 @@ - Obliteration of Ai-chan - Switch to [Calckey.js](https://codeberg.org/calckey/calckey.js) - Woozy mode 🥴 -- Improve blocking instances +- Improve blocking servers - Release notes - New post style - Admins set default reaction emoji @@ -117,7 +117,7 @@ - Sonic search - Popular color schemes, including Nord, Gruvbox, and Catppuccin - Non-nyaify cat mode -- Post imports from other Calckey/Misskey/Mastodon/Pleroma/Akkoma instances +- Post imports from other Calckey/Misskey/Mastodon/Pleroma/Akkoma servers - Improve Classic mode - Proper Helm/Kubernetes config - Multiple boost visibilities diff --git a/README.md b/README.md index 5dcdc4861..f4d7c4150 100644 --- a/README.md +++ b/README.md @@ -23,15 +23,15 @@ # ✨ About Calckey - Calckey is based off of Misskey, a powerful microblogging server on ActivityPub with features such as emoji reactions, a customizable web UI, rich chatting, and much more! -- Calckey adds many quality of life changes and bug fixes for users and instance admins alike. +- Calckey adds many quality of life changes and bug fixes for users and server admins alike. - Read **[this document](./CALCKEY.md)** all for current and future differences. - Notable differences: - Improved UI/UX (especially on mobile) - Improved notifications - - Improved instance security + - Improved server security - Improved accessibility - Improved threads - - Recommended Instances timeline + - Recommended Servers timeline - OCR image captioning - New and improved Groups - Better intro tutorial @@ -50,10 +50,10 @@ - 💸 OpenCollective: - 💸 Liberapay: - Donate publicly to get your name on the Patron list! -- 🚢 Flagship instance: +- 🚢 Flagship server: - 📣 Official account: - 💁 Matrix support room: -- 📜 Instance list: +- 📜 Server list: - 📖 JoinFediverse Wiki: - 🐋 Docker Hub: - ✍️ Weblate: @@ -177,13 +177,13 @@ Please don't use ElasticSearch unless you already have an ElasticSearch setup an ## 💅 Customize - To add custom CSS for all users, edit `./custom/assets/instance.css`. -- To add static assets (such as images for the splash screen), place them in the `./custom/assets/` directory. They'll then be available on `https://yourinstance.tld/static-assets/filename.ext`. +- To add static assets (such as images for the splash screen), place them in the `./custom/assets/` directory. They'll then be available on `https://yourserver.tld/static-assets/filename.ext`. - To add custom locales, place them in the `./custom/locales/` directory. If you name your custom locale the same as an existing locale, it will overwrite it. If you give it a unique name, it will be added to the list. Also make sure that the first part of the filename matches the locale you're basing it on. (Example: `en-FOO.yml`) - To add custom error images, place them in the `./custom/assets/badges` directory, replacing the files already there. - To add custom sounds, place only mp3 files in the `./custom/assets/sounds` directory. - To update custom assets without rebuilding, just run `pnpm run gulp`. -## 🧑‍🔬 Configuring a new instance +## 🧑‍🔬 Configuring a new server - Run `cp .config/example.yml .config/default.yml` - Edit `.config/default.yml`, making sure to fill out required fields. @@ -198,7 +198,7 @@ For migrating from Misskey v13, Misskey v12, and Foundkey, read [this document]( ### 🍀 Nginx (recommended) - Run `sudo cp ./calckey.nginx.conf /etc/nginx/sites-available/ && cd /etc/nginx/sites-available/` -- Edit `calckey.nginx.conf` to reflect your instance properly +- Edit `calckey.nginx.conf` to reflect your server properly - Run `sudo ln -s ./calckey.nginx.conf ../sites-enabled/calckey.nginx.conf` - Run `sudo nginx -t` to validate that the config is valid, then restart the NGINX service. @@ -218,7 +218,7 @@ example.tld { > Apache has some known problems with Calckey. Only use it if you have to. - Run `sudo cp ./calckey.apache.conf /etc/apache2/sites-available/ && cd /etc/apache2/sites-available/` -- Edit `calckey.apache.conf` to reflect your instance properly +- Edit `calckey.apache.conf` to reflect your server properly - Run `sudo a2ensite calckey.apache` to enable the site - Run `sudo service apache2 restart` to reload apache2 configuration ## 🚀 Build and launch! diff --git a/docs/docker.md b/docs/docker.md index 8c42ee54d..0c625a4b3 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -1,4 +1,4 @@ -# 🐳 Running a Calckey instance with Docker +# 🐳 Running a Calckey server with Docker ## Pre-built docker container [thatonecalculator/calckey](https://hub.docker.com/r/thatonecalculator/calckey) @@ -8,7 +8,7 @@ There is a `docker-compose.yml` in the root of the project that you can use to build the container from source - .config/docker.env (**db config settings**) -- .config/default.yml (**calckey instance settings**) +- .config/default.yml (**calckey server settings**) ## Configuring @@ -20,7 +20,7 @@ Rename the files: then edit them according to your environment. You can configure `docker.env` with anything you like, but you will have to pay attention to the `default.yml` file: -- `url` should be set to the URL you will be hosting the web interface for the instance at. +- `url` should be set to the URL you will be hosting the web interface for the server at. - `host`, `db`, `user`, `pass` will have to be configured in the `PostgreSQL configuration` section - `host` is the name of the postgres container (eg: *calckey_db_1*), and the others should match your `docker.env`. - `host`will need to be configured in the *Redis configuration* section - it is the name of the redis container (eg: *calckey_redis_1*) - `auth` will need to be configured in the *Sonic* section - cannot be the default `SecretPassword` @@ -36,7 +36,7 @@ Copy `docker-compose.yml` and the `config/` to a directory, then run the **docke NOTE: This will take some time to come fully online, even after download and extracting the container images, and it may emit some error messages before completing successfully. Specifically, the `db` container needs to initialize and so isn't available to the `web` container right away. Only once the `db` container comes online does the `web` container start building and initializing the calckey tables. -Once the instance is up you can use a web browser to access the web interface at `http://serverip:3000` (where `serverip` is the IP of the server you are running the calckey instance on). +Once the server is up you can use a web browser to access the web interface at `http://serverip:3000` (where `serverip` is the IP of the server you are running the calckey server on). ## Docker for development diff --git a/docs/kubernetes.md b/docs/kubernetes.md index 710d0dee0..5cb6e5d83 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -1,4 +1,4 @@ -# Running a Calckey instance with Kubernetes and Helm +# Running a Calckey server with Kubernetes and Helm This is a [Helm](https://helm.sh/) chart directory in the root of the project that you can use to deploy calckey to a Kubernetes cluster @@ -27,7 +27,7 @@ helm upgrade \ -f .config/helm_values.yml ``` -4. Watch your calckey instance spin up: +4. Watch your calckey server spin up: ```shell kubectl -n calckey get po -w ``` diff --git a/locales/en-US.yml b/locales/en-US.yml index e26f6ed2b..5b6abcc35 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -598,6 +598,8 @@ scratchpadDescription: "The scratchpad provides an environment for AiScript expe output: "Output" script: "Script" disablePagesScript: "Disable AiScript on Pages" +expandOnNoteClick: "Open post on click" +expandOnNoteClickDesc: "If disabled, you can still open posts in the right-click menu or by clicking the timestamp." updateRemoteUser: "Update remote user information" deleteAllFiles: "Delete all files" deleteAllFilesConfirm: "Are you sure that you want to delete all files?" diff --git a/packages/backend/src/queue/processors/db/import-custom-emojis.ts b/packages/backend/src/queue/processors/db/import-custom-emojis.ts index e2454405f..9e8b3b174 100644 --- a/packages/backend/src/queue/processors/db/import-custom-emojis.ts +++ b/packages/backend/src/queue/processors/db/import-custom-emojis.ts @@ -11,6 +11,7 @@ import { addFile } from "@/services/drive/add-file.js"; import { genId } from "@/misc/gen-id.js"; import { db } from "@/db/postgre.js"; import probeImageSize from "probe-image-size"; +import * as path from "path"; const logger = queueLogger.createSubLogger("import-custom-emojis"); @@ -29,11 +30,11 @@ export async function importCustomEmojis( return; } - const [path, cleanup] = await createTempDir(); + const [tempPath, cleanup] = await createTempDir(); - logger.info(`Temp dir is ${path}`); + logger.info(`Temp dir is ${tempPath}`); - const destPath = `${path}/emojis.zip`; + const destPath = `${tempPath}/emojis.zip`; try { fs.writeFileSync(destPath, "", "binary"); @@ -46,44 +47,96 @@ export async function importCustomEmojis( throw e; } - const outputPath = `${path}/emojis`; + const outputPath = `${tempPath}/emojis`; const unzipStream = fs.createReadStream(destPath); const zip = new AdmZip(destPath); zip.extractAllToAsync(outputPath, true, false, async (error) => { if (error) throw error; - const metaRaw = fs.readFileSync(`${outputPath}/meta.json`, "utf-8"); - const meta = JSON.parse(metaRaw); - for (const record of meta.emojis) { - if (!record.downloaded) continue; - const emojiInfo = record.emoji; - const emojiPath = `${outputPath}/${record.fileName}`; - await Emojis.delete({ - name: emojiInfo.name, - }); - const driveFile = await addFile({ - user: null, - path: emojiPath, - name: record.fileName, - force: true, - }); - const file = fs.createReadStream(emojiPath); - const size = await probeImageSize(file); - file.destroy(); - await Emojis.insert({ - id: genId(), - updatedAt: new Date(), - name: emojiInfo.name, - category: emojiInfo.category, - host: null, - aliases: emojiInfo.aliases, - originalUrl: driveFile.url, - publicUrl: driveFile.webpublicUrl ?? driveFile.url, - type: driveFile.webpublicType ?? driveFile.type, - license: emojiInfo.license, - width: size.width || null, - height: size.height || null, - }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); + if (fs.existsSync(`${outputPath}/meta.json`)) { + logger.info("starting emoji import with metadata"); + const metaRaw = fs.readFileSync(`${outputPath}/meta.json`, "utf-8"); + const meta = JSON.parse(metaRaw); + + for (const record of meta.emojis) { + if (!record.downloaded) continue; + const emojiInfo = record.emoji; + const emojiPath = `${outputPath}/${record.fileName}`; + await Emojis.delete({ + name: emojiInfo.name, + }); + const driveFile = await addFile({ + user: null, + path: emojiPath, + name: record.fileName, + force: true, + }); + const file = fs.createReadStream(emojiPath); + const size = await probeImageSize(file); + file.destroy(); + await Emojis.insert({ + id: genId(), + updatedAt: new Date(), + name: emojiInfo.name, + category: emojiInfo.category, + host: null, + aliases: emojiInfo.aliases, + originalUrl: driveFile.url, + publicUrl: driveFile.webpublicUrl ?? driveFile.url, + type: driveFile.webpublicType ?? driveFile.type, + license: emojiInfo.license, + width: size.width || null, + height: size.height || null, + }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); + } + } else { + logger.info("starting emoji import without metadata"); + // Since we lack metadata, we import into a randomized category name instead + let categoryName = genId(); + + let containedEmojis = fs.readdirSync(outputPath); + + // Filter out accidental JSON files + containedEmojis = containedEmojis.filter( + (emoji) => !emoji.match(/\.(json)$/i), + ); + + for (const emojiFilename of containedEmojis) { + // strip extension and get filename to use as name + const name = path.basename(emojiFilename, path.extname(emojiFilename)); + const emojiPath = `${outputPath}/${emojiFilename}`; + + logger.info(`importing ${name}`); + + await Emojis.delete({ + name: name, + }); + const driveFile = await addFile({ + user: null, + path: emojiPath, + name: path.basename(emojiFilename), + force: true, + }); + const file = fs.createReadStream(emojiPath); + const size = await probeImageSize(file); + file.destroy(); + logger.info(`emoji size: ${size.width}x${size.height}`); + + await Emojis.insert({ + id: genId(), + updatedAt: new Date(), + name: name, + category: categoryName, + host: null, + aliases: [], + originalUrl: driveFile.url, + publicUrl: driveFile.webpublicUrl ?? driveFile.url, + type: driveFile.webpublicType ?? driveFile.type, + license: null, + width: size.width || null, + height: size.height || null, + }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); + } } await db.queryResultCache!.remove(["meta_emojis"]); diff --git a/packages/client/src/components/MkNote.vue b/packages/client/src/components/MkNote.vue index 589b83a51..669fd67ef 100644 --- a/packages/client/src/components/MkNote.vue +++ b/packages/client/src/components/MkNote.vue @@ -68,6 +68,7 @@ class="article" @contextmenu.stop="onContextmenu" @click="noteClick" + :style="{ cursor: expandOnNoteClick && !detailedView ? 'pointer' : '' }" >
@@ -313,6 +314,7 @@ const muted = ref(getWordSoftMute(note, $i, defaultStore.state.mutedWords)); const translation = ref(null); const translating = ref(false); const enableEmojiReactions = defaultStore.state.enableEmojiReactions; +const expandOnNoteClick = defaultStore.state.expandOnNoteClick; const keymap = { r: () => reply(true), @@ -501,7 +503,7 @@ function scrollIntoView() { } function noteClick(e) { - if (document.getSelection().type === "Range" || props.detailedView) { + if (document.getSelection().type === "Range" || props.detailedView || !expandOnNoteClick) { e.stopPropagation(); } else { router.push(notePage(appearNote)); @@ -704,7 +706,6 @@ defineExpose({ position: relative; overflow: clip; padding: 4px 32px 10px; - cursor: pointer; &:first-child, &:nth-child(2) { diff --git a/packages/client/src/components/MkNoteDetailed.vue b/packages/client/src/components/MkNoteDetailed.vue index 68e33d701..ff8264a60 100644 --- a/packages/client/src/components/MkNoteDetailed.vue +++ b/packages/client/src/components/MkNoteDetailed.vue @@ -534,12 +534,8 @@ onUnmounted(() => { > .reply { border-top: solid 0.5px var(--divider); - cursor: pointer; padding-top: 24px; padding-bottom: 10px; - @media (pointer: coarse) { - cursor: default; - } } // Hover diff --git a/packages/client/src/components/MkNoteSub.vue b/packages/client/src/components/MkNoteSub.vue index dd69145a0..60fbbe11e 100644 --- a/packages/client/src/components/MkNoteSub.vue +++ b/packages/client/src/components/MkNoteSub.vue @@ -14,7 +14,10 @@ @contextmenu.stop="onContextmenu" >
-
+
.main { display: flex; - cursor: pointer; > .avatar-container { margin-right: 8px; diff --git a/packages/client/src/components/MkSubNoteContent.vue b/packages/client/src/components/MkSubNoteContent.vue index 0b6f920cb..5c965972a 100644 --- a/packages/client/src/components/MkSubNoteContent.vue +++ b/packages/client/src/components/MkSubNoteContent.vue @@ -168,10 +168,10 @@ {{ i18n.ts._mfm.stop }} -
+ >
-->
diff --git a/packages/client/src/pages/settings/general.vue b/packages/client/src/pages/settings/general.vue index f120551b8..5486503b1 100644 --- a/packages/client/src/pages/settings/general.vue +++ b/packages/client/src/pages/settings/general.vue @@ -54,6 +54,10 @@ {{ i18n.ts.disablePagesScript }} + {{ + i18n.ts.expandOnNoteClick + }} + {{ i18n.ts.flagShowTimelineReplies }}