Merge branch 'develop' of https://codeberg.org/calckey/calckey into upstream/develop

This commit is contained in:
Freeplay 2023-06-05 20:15:21 -04:00
commit 9424ea416b
32 changed files with 396 additions and 314 deletions

View File

@ -1,12 +1,12 @@
import { defineConfig } from 'cypress'
import { defineConfig } from "cypress";
export default defineConfig({
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
baseUrl: 'http://localhost:61812',
},
})
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require("./cypress/plugins/index.js")(on, config);
},
baseUrl: "http://localhost:61812",
},
});

View File

@ -1,4 +1,4 @@
describe('Before setup instance', () => {
describe("Before setup instance", () => {
beforeEach(() => {
cy.resetState();
});
@ -9,31 +9,31 @@ describe('Before setup instance', () => {
cy.wait(1000);
});
it('successfully loads', () => {
cy.visit('/');
});
it("successfully loads", () => {
cy.visit("/");
});
it('setup instance', () => {
cy.visit('/');
it("setup instance", () => {
cy.visit("/");
cy.intercept('POST', '/api/admin/accounts/create').as('signup');
cy.get('[data-cy-admin-username] input').type('admin');
cy.get('[data-cy-admin-password] input').type('admin1234');
cy.get('[data-cy-admin-ok]').click();
cy.intercept("POST", "/api/admin/accounts/create").as("signup");
cy.get("[data-cy-admin-username] input").type("admin");
cy.get("[data-cy-admin-password] input").type("admin1234");
cy.get("[data-cy-admin-ok]").click();
// なぜか動かない
//cy.wait('@signup').should('have.property', 'response.statusCode');
cy.wait('@signup');
});
cy.wait("@signup");
});
});
describe('After setup instance', () => {
describe("After setup instance", () => {
beforeEach(() => {
cy.resetState();
// インスタンス初期セットアップ
cy.registerUser('admin', 'pass', true);
cy.registerUser("admin", "pass", true);
});
afterEach(() => {
@ -42,34 +42,34 @@ describe('After setup instance', () => {
cy.wait(1000);
});
it('successfully loads', () => {
cy.visit('/');
});
it("successfully loads", () => {
cy.visit("/");
});
it('signup', () => {
cy.visit('/');
it("signup", () => {
cy.visit("/");
cy.intercept('POST', '/api/signup').as('signup');
cy.intercept("POST", "/api/signup").as("signup");
cy.get('[data-cy-signup]').click();
cy.get('[data-cy-signup-username] input').type('alice');
cy.get('[data-cy-signup-password] input').type('alice1234');
cy.get('[data-cy-signup-password-retype] input').type('alice1234');
cy.get('[data-cy-signup-submit]').click();
cy.get("[data-cy-signup]").click();
cy.get("[data-cy-signup-username] input").type("alice");
cy.get("[data-cy-signup-password] input").type("alice1234");
cy.get("[data-cy-signup-password-retype] input").type("alice1234");
cy.get("[data-cy-signup-submit]").click();
cy.wait('@signup');
});
cy.wait("@signup");
});
});
describe('After user signup', () => {
describe("After user signup", () => {
beforeEach(() => {
cy.resetState();
// インスタンス初期セットアップ
cy.registerUser('admin', 'pass', true);
cy.registerUser("admin", "pass", true);
// ユーザー作成
cy.registerUser('alice', 'alice1234');
cy.registerUser("alice", "alice1234");
});
afterEach(() => {
@ -78,51 +78,53 @@ describe('After user signup', () => {
cy.wait(1000);
});
it('successfully loads', () => {
cy.visit('/');
});
it("successfully loads", () => {
cy.visit("/");
});
it('signin', () => {
cy.visit('/');
it("signin", () => {
cy.visit("/");
cy.intercept('POST', '/api/signin').as('signin');
cy.intercept("POST", "/api/signin").as("signin");
cy.get('[data-cy-signin]').click();
cy.get('[data-cy-signin-username] input').type('alice');
cy.get("[data-cy-signin]").click();
cy.get("[data-cy-signin-username] input").type("alice");
// Enterキーでサインインできるかの確認も兼ねる
cy.get('[data-cy-signin-password] input').type('alice1234{enter}');
cy.get("[data-cy-signin-password] input").type("alice1234{enter}");
cy.wait('@signin');
});
cy.wait("@signin");
});
it('suspend', function() {
cy.request('POST', '/api/admin/suspend-user', {
it("suspend", function () {
cy.request("POST", "/api/admin/suspend-user", {
i: this.admin.token,
userId: this.alice.id,
});
cy.visit('/');
cy.visit("/");
cy.get('[data-cy-signin]').click();
cy.get('[data-cy-signin-username] input').type('alice');
cy.get('[data-cy-signin-password] input').type('alice1234{enter}');
cy.get("[data-cy-signin]").click();
cy.get("[data-cy-signin-username] input").type("alice");
cy.get("[data-cy-signin-password] input").type("alice1234{enter}");
// TODO: cypressにブラウザの言語指定できる機能が実装され次第英語のみテストするようにする
cy.contains(/アカウントが凍結されています|This account has been suspended due to/gi);
cy.contains(
/アカウントが凍結されています|This account has been suspended due to/gi,
);
});
});
describe('After user singed in', () => {
describe("After user singed in", () => {
beforeEach(() => {
cy.resetState();
// インスタンス初期セットアップ
cy.registerUser('admin', 'pass', true);
cy.registerUser("admin", "pass", true);
// ユーザー作成
cy.registerUser('alice', 'alice1234');
cy.registerUser("alice", "alice1234");
cy.login('alice', 'alice1234');
cy.login("alice", "alice1234");
});
afterEach(() => {
@ -131,17 +133,17 @@ describe('After user singed in', () => {
cy.wait(1000);
});
it('successfully loads', () => {
cy.get('[data-cy-open-post-form]').should('be.visible');
});
it("successfully loads", () => {
cy.get("[data-cy-open-post-form]").should("be.visible");
});
it('note', () => {
cy.get('[data-cy-open-post-form]').click();
cy.get('[data-cy-post-form-text]').type('Hello, Misskey!');
cy.get('[data-cy-open-post-form-submit]').click();
it("note", () => {
cy.get("[data-cy-open-post-form]").click();
cy.get("[data-cy-post-form-text]").type("Hello, Misskey!");
cy.get("[data-cy-open-post-form-submit]").click();
cy.contains('Hello, Misskey!');
});
cy.contains("Hello, Misskey!");
});
});
// TODO: 投稿フォームの公開範囲指定のテスト

View File

@ -1,14 +1,14 @@
describe('After user signed in', () => {
describe("After user signed in", () => {
beforeEach(() => {
cy.resetState();
cy.viewport('macbook-16');
cy.viewport("macbook-16");
// インスタンス初期セットアップ
cy.registerUser('admin', 'pass', true);
cy.registerUser("admin", "pass", true);
// ユーザー作成
cy.registerUser('alice', 'alice1234');
cy.registerUser("alice", "alice1234");
cy.login('alice', 'alice1234');
cy.login("alice", "alice1234");
});
afterEach(() => {
@ -17,47 +17,47 @@ describe('After user signed in', () => {
cy.wait(1000);
});
it('widget edit toggle is visible', () => {
cy.get('.mk-widget-edit').should('be.visible');
});
it("widget edit toggle is visible", () => {
cy.get(".mk-widget-edit").should("be.visible");
});
it('widget select should be visible in edit mode', () => {
cy.get('.mk-widget-edit').click();
cy.get('.mk-widget-select').should('be.visible');
});
it("widget select should be visible in edit mode", () => {
cy.get(".mk-widget-edit").click();
cy.get(".mk-widget-select").should("be.visible");
});
it('first widget should be removed', () => {
cy.get('.mk-widget-edit').click();
cy.get('.customize-container:first-child .remove._button').click();
cy.get('.customize-container').should('have.length', 2);
it("first widget should be removed", () => {
cy.get(".mk-widget-edit").click();
cy.get(".customize-container:first-child .remove._button").click();
cy.get(".customize-container").should("have.length", 2);
});
function buildWidgetTest(widgetName) {
it(`${widgetName} widget should get added`, () => {
cy.get('.mk-widget-edit').click();
cy.get('.mk-widget-select select').select(widgetName, { force: true });
cy.get('.bg._modalBg.transparent').click({ multiple: true, force: true });
cy.get('.mk-widget-add').click({ force: true });
cy.get(`.mkw-${widgetName}`).should('exist');
cy.get(".mk-widget-edit").click();
cy.get(".mk-widget-select select").select(widgetName, { force: true });
cy.get(".bg._modalBg.transparent").click({ multiple: true, force: true });
cy.get(".mk-widget-add").click({ force: true });
cy.get(`.mkw-${widgetName}`).should("exist");
});
}
buildWidgetTest('memo');
buildWidgetTest('notifications');
buildWidgetTest('timeline');
buildWidgetTest('calendar');
buildWidgetTest('rss');
buildWidgetTest('trends');
buildWidgetTest('clock');
buildWidgetTest('activity');
buildWidgetTest('photos');
buildWidgetTest('digitalClock');
buildWidgetTest('federation');
buildWidgetTest('postForm');
buildWidgetTest('slideshow');
buildWidgetTest('serverMetric');
buildWidgetTest('onlineUsers');
buildWidgetTest('jobQueue');
buildWidgetTest('button');
buildWidgetTest('aiscript');
buildWidgetTest("memo");
buildWidgetTest("notifications");
buildWidgetTest("timeline");
buildWidgetTest("calendar");
buildWidgetTest("rss");
buildWidgetTest("trends");
buildWidgetTest("clock");
buildWidgetTest("activity");
buildWidgetTest("photos");
buildWidgetTest("digitalClock");
buildWidgetTest("federation");
buildWidgetTest("postForm");
buildWidgetTest("slideshow");
buildWidgetTest("serverMetric");
buildWidgetTest("onlineUsers");
buildWidgetTest("jobQueue");
buildWidgetTest("button");
buildWidgetTest("aiscript");
});

View File

@ -16,6 +16,6 @@
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
};

View File

@ -24,32 +24,34 @@
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
Cypress.Commands.add('resetState', () => {
cy.window(win => {
win.indexedDB.deleteDatabase('keyval-store');
Cypress.Commands.add("resetState", () => {
cy.window((win) => {
win.indexedDB.deleteDatabase("keyval-store");
});
cy.request('POST', '/api/reset-db').as('reset');
cy.get('@reset').its('status').should('equal', 204);
cy.request("POST", "/api/reset-db").as("reset");
cy.get("@reset").its("status").should("equal", 204);
cy.reload(true);
});
Cypress.Commands.add('registerUser', (username, password, isAdmin = false) => {
const route = isAdmin ? '/api/admin/accounts/create' : '/api/signup';
Cypress.Commands.add("registerUser", (username, password, isAdmin = false) => {
const route = isAdmin ? "/api/admin/accounts/create" : "/api/signup";
cy.request('POST', route, {
cy.request("POST", route, {
username: username,
password: password,
}).its('body').as(username);
})
.its("body")
.as(username);
});
Cypress.Commands.add('login', (username, password) => {
cy.visit('/');
Cypress.Commands.add("login", (username, password) => {
cy.visit("/");
cy.intercept('POST', '/api/signin').as('signin');
cy.intercept("POST", "/api/signin").as("signin");
cy.get('[data-cy-signin]').click();
cy.get('[data-cy-signin-username] input').type(username);
cy.get('[data-cy-signin-password] input').type(`${password}{enter}`);
cy.get("[data-cy-signin]").click();
cy.get("[data-cy-signin-username] input").type(username);
cy.get("[data-cy-signin-password] input").type(`${password}{enter}`);
cy.wait('@signin').as('signedIn');
cy.wait("@signin").as("signedIn");
});

View File

@ -14,19 +14,21 @@
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
import "./commands";
// Alternatively you can use CommonJS syntax:
// require('./commands')
Cypress.on('uncaught:exception', (err, runnable) => {
if ([
// Chrome
'ResizeObserver loop limit exceeded',
Cypress.on("uncaught:exception", (err, runnable) => {
if (
[
// Chrome
"ResizeObserver loop limit exceeded",
// Firefox
'ResizeObserver loop completed with undelivered notifications',
].some(msg => err.message.includes(msg))) {
// Firefox
"ResizeObserver loop completed with undelivered notifications",
].some((msg) => err.message.includes(msg))
) {
return false;
}
});

View File

@ -2,59 +2,90 @@
* Languages Loader
*/
const fs = require('fs');
const yaml = require('js-yaml');
let languages = []
let languages_custom = []
const merge = (...args) => args.reduce((a, c) => ({
...a,
...c,
...Object.entries(a)
.filter(([k]) => c && typeof c[k] === 'object')
.reduce((a, [k, v]) => (a[k] = merge(v, c[k]), a), {})
}), {});
const fs = require("fs");
const yaml = require("js-yaml");
let languages = [];
let languages_custom = [];
const merge = (...args) =>
args.reduce(
(a, c) => ({
...a,
...c,
...Object.entries(a)
.filter(([k]) => c && typeof c[k] === "object")
.reduce((a, [k, v]) => ((a[k] = merge(v, c[k])), a), {}),
}),
{},
);
fs.readdirSync(__dirname).forEach((file) => {
if (file.includes('.yml')){
file = file.slice(0, file.indexOf('.'))
if (file.includes(".yml")) {
file = file.slice(0, file.indexOf("."));
languages.push(file);
}
})
});
fs.readdirSync(__dirname + '/../custom/locales').forEach((file) => {
if (file.includes('.yml')){
file = file.slice(0, file.indexOf('.'))
fs.readdirSync(__dirname + "/../custom/locales").forEach((file) => {
if (file.includes(".yml")) {
file = file.slice(0, file.indexOf("."));
languages_custom.push(file);
}
})
});
const primaries = {
'en': 'US',
'ja': 'JP',
'zh': 'CN',
en: "US",
ja: "JP",
zh: "CN",
};
// 何故か文字列にバックスペース文字が混入することがあり、YAMLが壊れるので取り除く
const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g'), '');
const clean = (text) =>
text.replace(new RegExp(String.fromCodePoint(0x08), "g"), "");
const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(`${__dirname}/${c}.yml`, 'utf-8'))) || {}, a), {});
const locales_custom = languages_custom.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(`${__dirname}/../custom/locales/${c}.yml`, 'utf-8'))) || {}, a), {});
Object.assign(locales, locales_custom)
const locales = languages.reduce(
(a, c) => (
(a[c] =
yaml.load(clean(fs.readFileSync(`${__dirname}/${c}.yml`, "utf-8"))) ||
{}),
a
),
{},
);
const locales_custom = languages_custom.reduce(
(a, c) => (
(a[c] =
yaml.load(
clean(
fs.readFileSync(`${__dirname}/../custom/locales/${c}.yml`, "utf-8"),
),
) || {}),
a
),
{},
);
Object.assign(locales, locales_custom);
module.exports = Object.entries(locales)
.reduce((a, [k ,v]) => (a[k] = (() => {
const [lang] = k.split('-');
switch (k) {
case 'ja-JP': return v;
case 'ja-KS':
case 'en-US': return merge(locales['ja-JP'], v);
default: return merge(
locales['ja-JP'],
locales['en-US'],
locales[`${lang}-${primaries[lang]}`] || {},
v
);
}
})(), a), {});
module.exports = Object.entries(locales).reduce(
(a, [k, v]) => (
(a[k] = (() => {
const [lang] = k.split("-");
switch (k) {
case "ja-JP":
return v;
case "ja-KS":
case "en-US":
return merge(locales["ja-JP"], v);
default:
return merge(
locales["ja-JP"],
locales["en-US"],
locales[`${lang}-${primaries[lang]}`] || {},
v,
);
}
})()),
a
),
{},
);

View File

@ -1,6 +1,6 @@
{
"name": "calckey",
"version": "14.0.0-dev40",
"version": "14.0.0-dev44",
"codename": "aqua",
"repository": {
"type": "git",
@ -27,7 +27,7 @@
"e2e": "start-server-and-test start:test http://localhost:61812 cy:run",
"mocha": "pnpm --filter backend run mocha",
"test": "pnpm run mocha",
"format": "pnpm rome format packages/**/* --write && pnpm -r run format",
"format": "pnpm -r run format",
"clean": "pnpm node ./scripts/clean.js",
"clean-all": "pnpm node ./scripts/clean-all.js",
"cleanall": "pnpm run clean-all"

View File

@ -47,6 +47,6 @@ export class noteRepliesFunction1658656633972 {
}
async down(queryRunner) {
await queryRunner.query(`DROP FUNCTION note_replies`);
await queryRunner.query("DROP FUNCTION note_replies");
}
}

View File

@ -12,7 +12,7 @@ export class addFkAbuseUserReportTargetUserIdToUserId1671199573000 {
async down(queryRunner) {
await queryRunner.query(
`ALTER TABLE abuse_user_report DROP CONSTRAINT fk_7f4e851a35d81b64dda28eee0`,
"ALTER TABLE abuse_user_report DROP CONSTRAINT fk_7f4e851a35d81b64dda28eee0",
);
}
}

View File

@ -4,22 +4,22 @@ export class CleanCharts1680375641101 {
}
async up(queryRunner) {
await queryRunner.query(
`delete from __chart__hashtag where ___local_users = 0 and ___remote_users = 0;`,
"delete from __chart__hashtag where ___local_users = 0 and ___remote_users = 0;",
);
await queryRunner.query(
`delete from __chart_day__hashtag where ___local_users = 0 and ___remote_users = 0;`,
"delete from __chart_day__hashtag where ___local_users = 0 and ___remote_users = 0;",
);
await queryRunner.query(`COMMIT;`);
await queryRunner.query(`vacuum __chart__hashtag;`);
await queryRunner.query(`vacuum __chart_day__hashtag;`);
await queryRunner.query(`COMMIT;`);
await queryRunner.query("COMMIT;");
await queryRunner.query("vacuum __chart__hashtag;");
await queryRunner.query("vacuum __chart_day__hashtag;");
await queryRunner.query("COMMIT;");
}
async down(queryRunner) {
await queryRunner.query(
`delete from __chart__hashtag where ___local_users = 0 and ___remote_users = 0;`,
"delete from __chart__hashtag where ___local_users = 0 and ___remote_users = 0;",
);
await queryRunner.query(
`delete from __chart_day__hashtag where ___local_users = 0 and ___remote_users = 0;`,
"delete from __chart_day__hashtag where ___local_users = 0 and ___remote_users = 0;",
);
}
}

View File

@ -17,7 +17,8 @@
"watch": "pnpm swc src -d built -D -w",
"lint": "pnpm rome check \"src/**/*.ts\"",
"mocha": "cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha",
"test": "pnpm run mocha"
"test": "pnpm run mocha",
"format": "pnpm rome format * --write && pnpm rome check --apply-suggested *"
},
"resolutions": {
"chokidar": "^3.3.1"
@ -112,7 +113,7 @@
"random-seed": "0.3.0",
"ratelimiter": "3.4.1",
"re2": "1.19.0",
"redis-lock": "1.0.0",
"redis-lock": "0.1.4",
"reflect-metadata": "0.1.13",
"rename": "1.0.4",
"rndstr": "1.0.0",

View File

@ -93,7 +93,7 @@ export async function masterMain() {
true,
);
if (!envOption.noDaemons && !config.onlyQueueProcessor) {
if (!(envOption.noDaemons || config.onlyQueueProcessor)) {
import("../daemons/server-stats.js").then((x) => x.default());
import("../daemons/queue-stats.js").then((x) => x.default());
import("../daemons/janitor.js").then((x) => x.default());

View File

@ -70,13 +70,13 @@ export class LdSignature {
...options,
"@context": "https://w3id.org/identity/v1",
};
delete transformedOptions["type"];
delete transformedOptions["id"];
delete transformedOptions["signatureValue"];
transformedOptions["type"] = undefined;
transformedOptions["id"] = undefined;
transformedOptions["signatureValue"] = undefined;
const canonizedOptions = await this.normalize(transformedOptions);
const optionsHash = this.sha256(canonizedOptions);
const transformedData = { ...data };
delete transformedData["signature"];
transformedData["signature"] = undefined;
const cannonidedData = await this.normalize(transformedData);
if (this.debug) console.debug(`cannonidedData: ${cannonidedData}`);
const documentHash = this.sha256(cannonidedData);

View File

@ -29,8 +29,8 @@ export async function createImage(
throw new Error("invalid image: url not privided");
}
if (!image.url.startsWith("https://") && !image.url.startsWith("http://")) {
throw new Error("invalid image: unexpected shcema of url: " + image.url);
if (!(image.url.startsWith("https://") || image.url.startsWith("http://"))) {
throw new Error(`invalid image: unexpected shcema of url: ${image.url}`);
}
logger.info(`Creating the Image: ${image.url}`);

View File

@ -529,8 +529,8 @@ export default define(meta, paramDef, async (ps, me) => {
github: instance.enableGithubIntegration,
discord: instance.enableDiscordIntegration,
serviceWorker: instance.enableServiceWorker,
postEditing: instance.experimentalFeatures?.postEditing || false,
postImports: instance.experimentalFeatures?.postImports || false,
postEditing: instance.experimentalFeatures?.postEditing,
postImports: instance.experimentalFeatures?.postImports,
miauth: true,
};
}

View File

@ -614,13 +614,13 @@ export default define(meta, paramDef, async (ps, user) => {
}
// Post is a reply and remote user is the contributor of the original post
if (note.reply && note.reply.userHost !== null) {
if (note.reply?.userHost !== null) {
const u = await Users.findOneBy({ id: note.reply.userId });
if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u);
}
// Post is a renote and remote user is the contributor of the original post
if (note.renote && note.renote.userHost !== null) {
if (note.renote?.userHost !== null) {
const u = await Users.findOneBy({ id: note.renote.userId });
if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u);
}

View File

@ -49,7 +49,7 @@ export function toTextWithReaction(status: Entity.Status[], host: string) {
if (t.reblog) t.reblog = toTextWithReaction([t.reblog], host)[0];
const reactions = t.emoji_reactions.map((r) => {
const emojiNotation = r.url ? `:${r.name.replace("@.", "")}:` : r.name;
return `${emojiNotation} (${r.count}${r.me ? `* ` : ""})`;
return `${emojiNotation} (${r.count}${r.me ? "* " : ""})`;
});
const reaction = t.emoji_reactions as Entity.Reaction[];
const emoji = t.emojis || [];

View File

@ -97,7 +97,7 @@
const fontSize = localStorage.getItem("fontSize");
if (fontSize) {
document.documentElement.classList.add("f-" + fontSize);
document.documentElement.classList.add(`f-${fontSize}`);
}
const useSystemFont = localStorage.getItem("useSystemFont");
@ -292,7 +292,7 @@
const meta = await res.json();
if (meta.version != v) {
if (meta.version !== v) {
localStorage.setItem("v", meta.version);
refresh();
}

View File

@ -564,13 +564,13 @@ export default async (
}
// 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送
if (data.reply && data.reply.userHost !== null) {
if (data.reply?.userHost !== null) {
const u = await Users.findOneBy({ id: data.reply.userId });
if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u);
}
// 投稿がRenoteかつ投稿者がローカルユーザーかつRenote元の投稿の投稿者がリモートユーザーなら配送
if (data.renote && data.renote.userHost !== null) {
if (data.renote?.userHost !== null) {
const u = await Users.findOneBy({ id: data.renote.userId });
if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u);
}
@ -671,7 +671,7 @@ async function insertNote(
tags: tags.map((tag) => normalizeForSearch(tag)),
emojis,
userId: user.id,
localOnly: data.localOnly || false,
localOnly: data.localOnly,
visibility: data.visibility as any,
visibleUserIds:
data.visibility === "specified"

View File

@ -462,21 +462,21 @@ describe("API visibility", () => {
it("[HTL] public-post が 自分が見れる", async(async () => {
const res = await request("/notes/timeline", { limit: 100 }, alice);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id == pub.id);
const notes = res.body.filter((n: any) => n.id === pub.id);
assert.strictEqual(notes[0].text, "x");
}));
it("[HTL] public-post が 非フォロワーから見れない", async(async () => {
const res = await request("/notes/timeline", { limit: 100 }, other);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id == pub.id);
const notes = res.body.filter((n: any) => n.id === pub.id);
assert.strictEqual(notes.length, 0);
}));
it("[HTL] followers-post が フォロワーから見れる", async(async () => {
const res = await request("/notes/timeline", { limit: 100 }, follower);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id == fol.id);
const notes = res.body.filter((n: any) => n.id === fol.id);
assert.strictEqual(notes[0].text, "x");
}));
//#endregion
@ -489,7 +489,7 @@ describe("API visibility", () => {
follower,
);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id == folR.id);
const notes = res.body.filter((n: any) => n.id === folR.id);
assert.strictEqual(notes[0].text, "x");
}));
@ -500,7 +500,7 @@ describe("API visibility", () => {
other,
);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id == folR.id);
const notes = res.body.filter((n: any) => n.id === folR.id);
assert.strictEqual(notes.length, 0);
}));
@ -511,7 +511,7 @@ describe("API visibility", () => {
target,
);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id == folR.id);
const notes = res.body.filter((n: any) => n.id === folR.id);
assert.strictEqual(notes[0].text, "x");
}));
//#endregion
@ -520,14 +520,14 @@ describe("API visibility", () => {
it("[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる", async(async () => {
const res = await request("/notes/mentions", { limit: 100 }, target);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id == folR.id);
const notes = res.body.filter((n: any) => n.id === folR.id);
assert.strictEqual(notes[0].text, "x");
}));
it("[mentions] followers-mention が 非フォロワー (メンション先である) から見れる", async(async () => {
const res = await request("/notes/mentions", { limit: 100 }, target);
assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id == folM.id);
const notes = res.body.filter((n: any) => n.id === folM.id);
assert.strictEqual(notes[0].text, "@target x");
}));
//#endregion

View File

@ -13,10 +13,10 @@ describe("Get file info", () => {
const info = (await getFileInfo(path, {
skipSensitiveDetection: true,
})) as any;
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
info.warnings = undefined;
info.blurhash = undefined;
info.sensitive = undefined;
info.porn = undefined;
assert.deepStrictEqual(info, {
size: 0,
md5: "d41d8cd98f00b204e9800998ecf8427e",
@ -35,10 +35,10 @@ describe("Get file info", () => {
const info = (await getFileInfo(path, {
skipSensitiveDetection: true,
})) as any;
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
info.warnings = undefined;
info.blurhash = undefined;
info.sensitive = undefined;
info.porn = undefined;
assert.deepStrictEqual(info, {
size: 25360,
md5: "091b3f259662aa31e2ffef4519951168",
@ -57,10 +57,10 @@ describe("Get file info", () => {
const info = (await getFileInfo(path, {
skipSensitiveDetection: true,
})) as any;
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
info.warnings = undefined;
info.blurhash = undefined;
info.sensitive = undefined;
info.porn = undefined;
assert.deepStrictEqual(info, {
size: 1868,
md5: "08189c607bea3b952704676bb3c979e0",
@ -79,10 +79,10 @@ describe("Get file info", () => {
const info = (await getFileInfo(path, {
skipSensitiveDetection: true,
})) as any;
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
info.warnings = undefined;
info.blurhash = undefined;
info.sensitive = undefined;
info.porn = undefined;
assert.deepStrictEqual(info, {
size: 2248,
md5: "32c47a11555675d9267aee1a86571e7e",
@ -101,10 +101,10 @@ describe("Get file info", () => {
const info = (await getFileInfo(path, {
skipSensitiveDetection: true,
})) as any;
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
info.warnings = undefined;
info.blurhash = undefined;
info.sensitive = undefined;
info.porn = undefined;
assert.deepStrictEqual(info, {
size: 3772,
md5: "f73535c3e1e27508885b69b10cf6e991",
@ -123,10 +123,10 @@ describe("Get file info", () => {
const info = (await getFileInfo(path, {
skipSensitiveDetection: true,
})) as any;
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
info.warnings = undefined;
info.blurhash = undefined;
info.sensitive = undefined;
info.porn = undefined;
assert.deepStrictEqual(info, {
size: 505,
md5: "b6f52b4b021e7b92cdd04509c7267965",
@ -146,10 +146,10 @@ describe("Get file info", () => {
const info = (await getFileInfo(path, {
skipSensitiveDetection: true,
})) as any;
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
info.warnings = undefined;
info.blurhash = undefined;
info.sensitive = undefined;
info.porn = undefined;
assert.deepStrictEqual(info, {
size: 544,
md5: "4b7a346cde9ccbeb267e812567e33397",
@ -168,10 +168,10 @@ describe("Get file info", () => {
const info = (await getFileInfo(path, {
skipSensitiveDetection: true,
})) as any;
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
info.warnings = undefined;
info.blurhash = undefined;
info.sensitive = undefined;
info.porn = undefined;
assert.deepStrictEqual(info, {
size: 75933,
md5: "268c5dde99e17cf8fe09f1ab3f97df56",
@ -190,10 +190,10 @@ describe("Get file info", () => {
const info = (await getFileInfo(path, {
skipSensitiveDetection: true,
})) as any;
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
info.warnings = undefined;
info.blurhash = undefined;
info.sensitive = undefined;
info.porn = undefined;
assert.deepStrictEqual(info, {
size: 12624,
md5: "68d5b2d8d1d1acbbce99203e3ec3857e",

View File

@ -599,7 +599,7 @@ describe("Streaming", () => {
chitose,
"hashtag",
({ type, body }) => {
if (type == "note") {
if (type === "note") {
assert.deepStrictEqual(body.text, "#foo");
ws.close();
done();
@ -625,7 +625,7 @@ describe("Streaming", () => {
chitose,
"hashtag",
({ type, body }) => {
if (type == "note") {
if (type === "note") {
if (body.text === "#foo") fooCount++;
if (body.text === "#bar") barCount++;
if (body.text === "#foo #bar") fooBarCount++;
@ -668,7 +668,7 @@ describe("Streaming", () => {
chitose,
"hashtag",
({ type, body }) => {
if (type == "note") {
if (type === "note") {
if (body.text === "#foo") fooCount++;
if (body.text === "#bar") barCount++;
if (body.text === "#foo #bar") fooBarCount++;
@ -718,7 +718,7 @@ describe("Streaming", () => {
chitose,
"hashtag",
({ type, body }) => {
if (type == "note") {
if (type === "note") {
if (body.text === "#foo") fooCount++;
if (body.text === "#bar") barCount++;
if (body.text === "#foo #bar") fooBarCount++;

View File

@ -53,7 +53,7 @@ export const api = async (endpoint: string, params: any, me?: any) => {
beforeError: [
(error) => {
const { response } = error;
if (response && response.body) console.warn(response.body);
if (response?.body) console.warn(response.body);
return error;
},
],
@ -316,7 +316,7 @@ export function launchServer(
moreProcess: () => Promise<void> = async () => {},
) {
return (done: (err?: Error) => any) => {
const p = childProcess.spawn("node", [_dirname + "/../index.js"], {
const p = childProcess.spawn("node", [`${_dirname}/../index.js`], {
stdio: ["inherit", "inherit", "inherit", "ipc"],
env: { NODE_ENV: "test", PATH: process.env.PATH },
});
@ -340,8 +340,8 @@ export async function initTestDb(justBorrow = false, initEntities?: any[]) {
username: config.db.user,
password: config.db.pass,
database: config.db.db,
synchronize: true && !justBorrow,
dropSchema: true && !justBorrow,
synchronize: !justBorrow,
dropSchema: !justBorrow,
entities: initEntities || entities,
});
@ -359,7 +359,7 @@ export function startServer(
rej("timeout to start");
}, timeout);
const p = childProcess.spawn("node", [_dirname + "/../built/index.js"], {
const p = childProcess.spawn("node", [`${_dirname}/../built/index.js`], {
stdio: ["inherit", "inherit", "inherit", "ipc"],
env: { NODE_ENV: "test", PATH: process.env.PATH },
});

View File

@ -42,7 +42,7 @@
isLong,
manyImages: note.files.length > 4,
showContent: note.cw && !showContent,
animatedMfm: !disableMfm
animatedMfm: !disableMfm,
}"
>
<XShowMoreButton

View File

@ -7,9 +7,9 @@
:customEmojis="customEmojis"
:isNote="isNote"
class="mfm-object"
:class="{
nowrap,
advancedMfm: defaultStore.state.advancedMfm
:class="{
nowrap,
advancedMfm: defaultStore.state.advancedMfm,
}"
/>
</template>

View File

@ -57,6 +57,12 @@ export default defineComponent({
if (t == null) return null;
return t.match(/^[0-9.]+s$/) ? t : null;
};
// const validEase = (e: string | null | undefined) => {
// if (e == null) return null;
// return e.match(/(steps)?\(-?[0-9.]+,-?[0-9.]+,-?[0-9.]+,-?[0-9.]+\)/)
// ? (e.startsWith("steps") ? e : "cubic-bezier" + e)
// : null
// }
const genEl = (ast: mfm.MfmNode[]) =>
concat(
@ -102,22 +108,27 @@ export default defineComponent({
switch (token.props.name) {
case "tada": {
const speed = validTime(token.props.args.speed) || "1s";
style = `font-size: 150%; animation: tada ${speed} linear infinite both;`;
const delay = validTime(token.props.args.delay) || "0s";
// const ease = validEase(token.props.args.ease) || "linear";
style = `font-size: 150%; animation: tada ${speed} ${delay} linear infinite both;`;
break;
}
case "jelly": {
const speed = validTime(token.props.args.speed) || "1s";
style = `animation: mfm-rubberBand ${speed} linear infinite both;`;
const delay = validTime(token.props.args.delay) || "0s";
style = `animation: mfm-rubberBand ${speed} ${delay} linear infinite both;`;
break;
}
case "twitch": {
const speed = validTime(token.props.args.speed) || "0.5s";
style = `animation: mfm-twitch ${speed} ease infinite;`;
const delay = validTime(token.props.args.delay) || "0s";
style = `animation: mfm-twitch ${speed} ${delay} ease infinite;`;
break;
}
case "shake": {
const speed = validTime(token.props.args.speed) || "0.5s";
style = `animation: mfm-shake ${speed} ease infinite;`;
const delay = validTime(token.props.args.delay) || "0s";
style = `animation: mfm-shake ${speed} ${delay} ease infinite;`;
break;
}
case "spin": {
@ -132,22 +143,26 @@ export default defineComponent({
? "mfm-spinY"
: "mfm-spin";
const speed = validTime(token.props.args.speed) || "1.5s";
style = `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction};`;
const delay = validTime(token.props.args.delay) || "0s";
style = `animation: ${anime} ${speed} ${delay} linear infinite; animation-direction: ${direction};`;
break;
}
case "jump": {
const speed = validTime(token.props.args.speed) || "0.75s";
style = `animation: mfm-jump ${speed} linear infinite;`;
const delay = validTime(token.props.args.delay) || "0s";
style = `animation: mfm-jump ${speed} ${delay} linear infinite;`;
break;
}
case "bounce": {
const speed = validTime(token.props.args.speed) || "0.75s";
style = `animation: mfm-bounce ${speed} linear infinite; transform-origin: center bottom;`;
const delay = validTime(token.props.args.delay) || "0s";
style = `animation: mfm-bounce ${speed} ${delay} linear infinite; transform-origin: center bottom;`;
break;
}
case "rainbow": {
const speed = validTime(token.props.args.speed) || "1s";
style = `animation: mfm-rainbow ${speed} linear infinite;`;
const delay = validTime(token.props.args.delay) || "0s";
style = `animation: mfm-rainbow ${speed} ${delay} linear infinite;`;
break;
}
case "sparkle": {
@ -161,7 +176,8 @@ export default defineComponent({
? "alternate-reverse"
: "alternate";
const speed = validTime(token.props.args.speed) || "1.5s";
style = `animation: mfm-fade ${speed} linear infinite; animation-direction: ${direction};`;
const delay = validTime(token.props.args.delay) || "0s";
style = `animation: mfm-fade ${speed} ${delay} linear infinite; animation-direction: ${direction};`;
break;
}
case "flip": {

View File

@ -4,7 +4,8 @@
"scripts": {
"build": "webpack",
"watch": "pnpm swc src -d built -D -w",
"lint": "pnpm rome check \"src/**/*.ts\""
"lint": "pnpm rome check \"src/**/*.ts\"",
"format": "pnpm rome format * --write && pnpm rome check --apply-suggested *"
},
"devDependencies": {
"@swc/cli": "^0.1.62",

View File

@ -331,8 +331,8 @@ importers:
specifier: 1.19.0
version: 1.19.0
redis-lock:
specifier: 1.0.0
version: 1.0.0
specifier: 0.1.4
version: 0.1.4
reflect-metadata:
specifier: 0.1.13
version: 0.1.13
@ -13210,9 +13210,9 @@ packages:
lodash: 4.17.21
dev: false
/redis-lock@1.0.0:
resolution: {integrity: sha512-zfI+Il36jXwRT/W8SBsG132Bc2yp3tMuf3KTGjSzXimadI17NEGBvb/KrDkCuAC2hzVxW5uR5ns/rxuqiWeV3Q==}
engines: {node: '>=12'}
/redis-lock@0.1.4:
resolution: {integrity: sha512-7/+zu86XVQfJVx1nHTzux5reglDiyUCDwmW7TSlvVezfhH2YLc/Rc8NE0ejQG+8/0lwKzm29/u/4+ogKeLosiA==}
engines: {node: '>=0.6'}
dev: false
/redis-parser@3.0.0:

View File

@ -1,21 +1,39 @@
const fs = require('fs');
const execa = require('execa');
const fs = require("fs");
const execa = require("execa");
(async () => {
fs.rmSync(__dirname + '/../packages/backend/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/backend/node_modules', { recursive: true, force: true });
fs.rmSync(`${__dirname}/../packages/backend/built`, {
recursive: true,
force: true,
});
fs.rmSync(`${__dirname}/../packages/backend/node_modules`, {
recursive: true,
force: true,
});
fs.rmSync(__dirname + '/../packages/client/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/client/node_modules', { recursive: true, force: true });
fs.rmSync(`${__dirname}/../packages/client/built`, {
recursive: true,
force: true,
});
fs.rmSync(`${__dirname}/../packages/client/node_modules`, {
recursive: true,
force: true,
});
fs.rmSync(__dirname + '/../packages/sw/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/sw/node_modules', { recursive: true, force: true });
fs.rmSync(`${__dirname}/../packages/sw/built`, {
recursive: true,
force: true,
});
fs.rmSync(`${__dirname}/../packages/sw/node_modules`, {
recursive: true,
force: true,
});
fs.rmSync(__dirname + '/../built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../node_modules', { recursive: true, force: true });
fs.rmSync(`${__dirname}/../built`, { recursive: true, force: true });
fs.rmSync(`${__dirname}/../node_modules`, { recursive: true, force: true });
execa('pnpm', ['store', 'prune'], {
cwd: __dirname + '/../',
stdio: 'inherit'
execa("pnpm", ["store", "prune"], {
cwd: `${__dirname}/../`,
stdio: "inherit",
});
})();

View File

@ -1,8 +1,17 @@
const fs = require('fs');
const fs = require("fs");
(async () => {
fs.rmSync(__dirname + '/../packages/backend/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/client/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/sw/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../built', { recursive: true, force: true });
fs.rmSync(`${__dirname}/../packages/backend/built`, {
recursive: true,
force: true,
});
fs.rmSync(`${__dirname}/../packages/client/built`, {
recursive: true,
force: true,
});
fs.rmSync(`${__dirname}/../packages/sw/built`, {
recursive: true,
force: true,
});
fs.rmSync(`${__dirname}/../built`, { recursive: true, force: true });
})();

View File

@ -1,45 +1,45 @@
const execa = require('execa');
const execa = require("execa");
(async () => {
await execa('pnpm', ['clean'], {
cwd: __dirname + '/../',
await execa("pnpm", ["clean"], {
cwd: `${__dirname}/../`,
stdout: process.stdout,
stderr: process.stderr,
});
execa('pnpm', ['dlx', 'gulp', 'watch'], {
cwd: __dirname + '/../',
execa("pnpm", ["dlx", "gulp", "watch"], {
cwd: `${__dirname}/../`,
stdout: process.stdout,
stderr: process.stderr,
});
execa('pnpm', ['--filter', 'backend', 'watch'], {
cwd: __dirname + '/../',
execa("pnpm", ["--filter", "backend", "watch"], {
cwd: `${__dirname}/../`,
stdout: process.stdout,
stderr: process.stderr,
});
execa('pnpm', ['--filter', 'client', 'watch'], {
cwd: __dirname + '/../',
execa("pnpm", ["--filter", "client", "watch"], {
cwd: `${__dirname}/../`,
stdout: process.stdout,
stderr: process.stderr,
});
execa('pnpm', ['--filter', 'sw', 'watch'], {
cwd: __dirname + '/../',
execa("pnpm", ["--filter", "sw", "watch"], {
cwd: `${__dirname}/../`,
stdout: process.stdout,
stderr: process.stderr,
});
const start = async () => {
try {
await execa('pnpm', ['start'], {
cwd: __dirname + '/../',
await execa("pnpm", ["start"], {
cwd: `${__dirname}/../`,
stdout: process.stdout,
stderr: process.stderr,
});
} catch (e) {
await new Promise(resolve => setTimeout(resolve, 3000));
await new Promise((resolve) => setTimeout(resolve, 3000));
start();
}
};