From 05b8111c1906c1285c9ddde758eda45b83792244 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 29 Apr 2019 09:11:57 +0900 Subject: Pages (#4811) * wip * wip * wip * Update page-editor.vue * wip * wip * wip * wip * wip * wip * wip * Update page-editor.variable.core.vue * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * Update aiscript.ts * wip * Update package.json * wip * wip * wip * wip * wip * Update page.vue * wip * wip * wip * wip * more info * wip fn * wip * wip * wip --- src/client/app/common/scripts/aiscript.ts | 470 +++++++++++++++++++++ src/client/app/common/scripts/collect-page-vars.ts | 24 ++ src/client/app/common/views/components/dialog.vue | 11 +- .../app/common/views/components/media-image.vue | 2 +- .../components/page-editor/page-editor.block.vue | 25 ++ .../components/page-editor/page-editor.button.vue | 54 +++ .../page-editor/page-editor.container.vue | 135 ++++++ .../components/page-editor/page-editor.image.vue | 78 ++++ .../components/page-editor/page-editor.input.vue | 54 +++ .../page-editor/page-editor.script-block.vue | 263 ++++++++++++ .../components/page-editor/page-editor.section.vue | 133 ++++++ .../components/page-editor/page-editor.switch.vue | 48 +++ .../components/page-editor/page-editor.text.vue | 57 +++ .../views/components/page-editor/page-editor.vue | 452 ++++++++++++++++++++ .../app/common/views/components/page-preview.vue | 141 +++++++ .../app/common/views/pages/page/page.block.vue | 34 ++ .../app/common/views/pages/page/page.button.vue | 42 ++ .../app/common/views/pages/page/page.image.vue | 36 ++ .../app/common/views/pages/page/page.input.vue | 43 ++ .../app/common/views/pages/page/page.section.vue | 55 +++ .../app/common/views/pages/page/page.switch.vue | 33 ++ .../app/common/views/pages/page/page.text.vue | 35 ++ src/client/app/common/views/pages/page/page.vue | 143 +++++++ src/client/app/desktop/script.ts | 4 + .../desktop/views/components/ui.header.account.vue | 27 +- src/client/app/desktop/views/home/pages.vue | 92 ++++ src/client/app/desktop/views/pages/page-editor.vue | 32 ++ src/client/app/desktop/views/pages/page.vue | 36 ++ src/client/app/mobile/script.ts | 4 + src/client/app/mobile/views/components/ui.nav.vue | 5 +- src/client/app/mobile/views/pages/page-editor.vue | 32 ++ src/client/app/mobile/views/pages/page.vue | 36 ++ src/client/app/mobile/views/pages/pages.vue | 94 +++++ src/client/themes/dark.json5 | 3 + src/client/themes/light.json5 | 3 + 35 files changed, 2721 insertions(+), 15 deletions(-) create mode 100644 src/client/app/common/scripts/aiscript.ts create mode 100644 src/client/app/common/scripts/collect-page-vars.ts create mode 100644 src/client/app/common/views/components/page-editor/page-editor.block.vue create mode 100644 src/client/app/common/views/components/page-editor/page-editor.button.vue create mode 100644 src/client/app/common/views/components/page-editor/page-editor.container.vue create mode 100644 src/client/app/common/views/components/page-editor/page-editor.image.vue create mode 100644 src/client/app/common/views/components/page-editor/page-editor.input.vue create mode 100644 src/client/app/common/views/components/page-editor/page-editor.script-block.vue create mode 100644 src/client/app/common/views/components/page-editor/page-editor.section.vue create mode 100644 src/client/app/common/views/components/page-editor/page-editor.switch.vue create mode 100644 src/client/app/common/views/components/page-editor/page-editor.text.vue create mode 100644 src/client/app/common/views/components/page-editor/page-editor.vue create mode 100644 src/client/app/common/views/components/page-preview.vue create mode 100644 src/client/app/common/views/pages/page/page.block.vue create mode 100644 src/client/app/common/views/pages/page/page.button.vue create mode 100644 src/client/app/common/views/pages/page/page.image.vue create mode 100644 src/client/app/common/views/pages/page/page.input.vue create mode 100644 src/client/app/common/views/pages/page/page.section.vue create mode 100644 src/client/app/common/views/pages/page/page.switch.vue create mode 100644 src/client/app/common/views/pages/page/page.text.vue create mode 100644 src/client/app/common/views/pages/page/page.vue create mode 100644 src/client/app/desktop/views/home/pages.vue create mode 100644 src/client/app/desktop/views/pages/page-editor.vue create mode 100644 src/client/app/desktop/views/pages/page.vue create mode 100644 src/client/app/mobile/views/pages/page-editor.vue create mode 100644 src/client/app/mobile/views/pages/page.vue create mode 100644 src/client/app/mobile/views/pages/pages.vue (limited to 'src/client') diff --git a/src/client/app/common/scripts/aiscript.ts b/src/client/app/common/scripts/aiscript.ts new file mode 100644 index 0000000000..4ef21f9943 --- /dev/null +++ b/src/client/app/common/scripts/aiscript.ts @@ -0,0 +1,470 @@ +/** + * AiScript + * evaluator & type checker + */ + +import autobind from 'autobind-decorator'; +import * as seedrandom from 'seedrandom'; + +import { + faSuperscript, + faAlignLeft, + faShareAlt, + faSquareRootAlt, + faPlus, + faMinus, + faTimes, + faDivide, + faList, + faQuoteRight, + faEquals, + faGreaterThan, + faLessThan, + faGreaterThanEqual, + faLessThanEqual, + faExclamation, + faNotEqual, + faDice, + faSortNumericUp, +} from '@fortawesome/free-solid-svg-icons'; +import { faFlag } from '@fortawesome/free-regular-svg-icons'; + +import { version } from '../../config'; + +export type Block = { + id: string; + type: string; + args: Block[]; + value: any; +}; + +export type Variable = Block & { + name: string; +}; + +type Type = 'string' | 'number' | 'boolean' | 'stringArray'; + +type TypeError = { + arg: number; + expect: Type; + actual: Type; +}; + +const funcDefs = { + if: { in: ['boolean', 0, 0], out: 0, category: 'flow', icon: faShareAlt, }, + not: { in: ['boolean'], out: 'boolean', category: 'logical', icon: faFlag, }, + or: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: faFlag, }, + and: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: faFlag, }, + add: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faPlus, }, + subtract: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faMinus, }, + multiply: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faTimes, }, + divide: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, }, + eq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faEquals, }, + notEq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faNotEqual, }, + gt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faGreaterThan, }, + lt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faLessThan, }, + gtEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faGreaterThanEqual, }, + ltEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faLessThanEqual, }, + rannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, }, + random: { in: ['number'], out: 'boolean', category: 'random', icon: faDice, }, + randomPick: { in: [0], out: 0, category: 'random', icon: faDice, }, + dailyRannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, }, + dailyRandom: { in: ['number'], out: 'boolean', category: 'random', icon: faDice, }, + dailyRandomPick: { in: [0], out: 0, category: 'random', icon: faDice, }, +}; + +const blockDefs = [ + { type: 'text', out: 'string', category: 'value', icon: faQuoteRight, }, + { type: 'multiLineText', out: 'string', category: 'value', icon: faAlignLeft, }, + { type: 'textList', out: 'stringArray', category: 'value', icon: faList, }, + { type: 'number', out: 'number', category: 'value', icon: faSortNumericUp, }, + { type: 'ref', out: null, category: 'value', icon: faSuperscript, }, + { type: 'in', out: null, category: 'value', icon: faSuperscript, }, + { type: 'fn', out: 'function', category: 'value', icon: faSuperscript, }, + ...Object.entries(funcDefs).map(([k, v]) => ({ + type: k, out: v.out || null, category: v.category, icon: v.icon + })) +]; + +type PageVar = { name: string; value: any; type: Type; }; + +const envVarsDef = { + AI: 'string', + VERSION: 'string', + LOGIN: 'boolean', + NAME: 'string', + USERNAME: 'string', + USERID: 'string', + NOTES_COUNT: 'number', + FOLLOWERS_COUNT: 'number', + FOLLOWING_COUNT: 'number', + IS_CAT: 'boolean', + MY_NOTES_COUNT: 'number', + MY_FOLLOWERS_COUNT: 'number', + MY_FOLLOWING_COUNT: 'number', +}; + +export class AiScript { + private variables: Variable[]; + private pageVars: PageVar[]; + private envVars: Record; + + public static envVarsDef = envVarsDef; + public static blockDefs = blockDefs; + public static funcDefs = funcDefs; + private opts: { + randomSeed?: string; user?: any; visitor?: any; + }; + + constructor(variables: Variable[] = [], pageVars: PageVar[] = [], opts: AiScript['opts'] = {}) { + this.variables = variables; + this.pageVars = pageVars; + this.opts = opts; + + this.envVars = { + AI: 'kawaii', + VERSION: version, + LOGIN: opts.visitor != null, + NAME: opts.visitor ? opts.visitor.name : '', + USERNAME: opts.visitor ? opts.visitor.username : '', + USERID: opts.visitor ? opts.visitor.id : '', + NOTES_COUNT: opts.visitor ? opts.visitor.notesCount : 0, + FOLLOWERS_COUNT: opts.visitor ? opts.visitor.followersCount : 0, + FOLLOWING_COUNT: opts.visitor ? opts.visitor.followingCount : 0, + IS_CAT: opts.visitor ? opts.visitor.isCat : false, + MY_NOTES_COUNT: opts.user ? opts.user.notesCount : 0, + MY_FOLLOWERS_COUNT: opts.user ? opts.user.followersCount : 0, + MY_FOLLOWING_COUNT: opts.user ? opts.user.followingCount : 0, + }; + } + + @autobind + public injectVars(vars: Variable[]) { + this.variables = vars; + } + + @autobind + public injectPageVars(pageVars: PageVar[]) { + this.pageVars = pageVars; + } + + @autobind + public updatePageVar(name: string, value: any) { + this.pageVars.find(v => v.name === name).value = value; + } + + @autobind + public updateRandomSeed(seed: string) { + this.opts.randomSeed = seed; + } + + @autobind + public static isLiteralBlock(v: Block) { + if (v.type === null) return true; + if (v.type === 'text') return true; + if (v.type === 'multiLineText') return true; + if (v.type === 'textList') return true; + if (v.type === 'number') return true; + if (v.type === 'ref') return true; + if (v.type === 'fn') return true; + if (v.type === 'in') return true; + return false; + } + + @autobind + public typeCheck(v: Block): TypeError | null { + if (AiScript.isLiteralBlock(v)) return null; + + const def = AiScript.funcDefs[v.type]; + if (def == null) { + throw new Error('Unknown type: ' + v.type); + } + + const generic: Type[] = []; + + for (let i = 0; i < def.in.length; i++) { + const arg = def.in[i]; + const type = this.typeInference(v.args[i]); + if (type === null) continue; + + if (typeof arg === 'number') { + if (generic[arg] === undefined) { + generic[arg] = type; + } else if (type !== generic[arg]) { + return { + arg: i, + expect: generic[arg], + actual: type + }; + } + } else if (type !== arg) { + return { + arg: i, + expect: arg, + actual: type + }; + } + } + + return null; + } + + @autobind + public getExpectedType(v: Block, slot: number): Type | null { + const def = AiScript.funcDefs[v.type]; + if (def == null) { + throw new Error('Unknown type: ' + v.type); + } + + const generic: Type[] = []; + + for (let i = 0; i < def.in.length; i++) { + const arg = def.in[i]; + const type = this.typeInference(v.args[i]); + if (type === null) continue; + + if (typeof arg === 'number') { + if (generic[arg] === undefined) { + generic[arg] = type; + } + } + } + + if (typeof def.in[slot] === 'number') { + return generic[def.in[slot]] || null; + } else { + return def.in[slot]; + } + } + + @autobind + public typeInference(v: Block): Type | null { + if (v.type === null) return null; + if (v.type === 'text') return 'string'; + if (v.type === 'multiLineText') return 'string'; + if (v.type === 'textList') return 'stringArray'; + if (v.type === 'number') return 'number'; + if (v.type === 'ref') { + const variable = this.variables.find(va => va.name === v.value); + if (variable) { + return this.typeInference(variable); + } + + const pageVar = this.pageVars.find(va => va.name === v.value); + if (pageVar) { + return pageVar.type; + } + + const envVar = AiScript.envVarsDef[v.value]; + if (envVar) { + return envVar; + } + + return null; + } + if (v.type === 'fn') return null; // todo + if (v.type === 'in') return null; // todo + + const generic: Type[] = []; + + const def = AiScript.funcDefs[v.type]; + + for (let i = 0; i < def.in.length; i++) { + const arg = def.in[i]; + if (typeof arg === 'number') { + const type = this.typeInference(v.args[i]); + + if (generic[arg] === undefined) { + generic[arg] = type; + } else { + if (type !== generic[arg]) { + generic[arg] = null; + } + } + } + } + + if (typeof def.out === 'number') { + return generic[def.out]; + } else { + return def.out; + } + } + + @autobind + public getVarsByType(type: Type | null): Variable[] { + if (type == null) return this.variables; + return this.variables.filter(x => (this.typeInference(x) === null) || (this.typeInference(x) === type)); + } + + @autobind + public getVarByName(name: string): Variable { + return this.variables.find(x => x.name === name); + } + + @autobind + public getEnvVarsByType(type: Type | null): string[] { + if (type == null) return Object.keys(AiScript.envVarsDef); + return Object.entries(AiScript.envVarsDef).filter(([k, v]) => type === v).map(([k, v]) => k); + } + + @autobind + public getPageVarsByType(type: Type | null): string[] { + if (type == null) return this.pageVars.map(v => v.name); + return this.pageVars.filter(v => type === v.type).map(v => v.name); + } + + @autobind + private interpolate(str: string, values: { name: string, value: any }[]) { + return str.replace(/\{(.+?)\}/g, match => + (this.getVariableValue(match.slice(1, -1).trim(), values) || '').toString()); + } + + @autobind + public evaluateVars() { + const values: { name: string, value: any }[] = []; + + for (const v of this.variables) { + values.push({ + name: v.name, + value: this.evaluate(v, values) + }); + } + + for (const v of this.pageVars) { + values.push({ + name: v.name, + value: v.value + }); + } + + for (const [k, v] of Object.entries(this.envVars)) { + values.push({ + name: k, + value: v + }); + } + + return values; + } + + @autobind + private evaluate(block: Block, values: { name: string, value: any }[], slotArg: Record = {}): any { + if (block.type === null) { + return null; + } + + if (block.type === 'number') { + return parseInt(block.value, 10); + } + + if (block.type === 'text' || block.type === 'multiLineText') { + return this.interpolate(block.value, values); + } + + if (block.type === 'textList') { + return block.value.trim().split('\n'); + } + + if (block.type === 'ref') { + return this.getVariableValue(block.value, values); + } + + if (block.type === 'in') { + return slotArg[block.value]; + } + + if (block.type === 'fn') { // ユーザー関数定義 + return { + slots: block.value.slots, + exec: slotArg => this.evaluate(block.value.expression, values, slotArg) + }; + } + + if (block.type.startsWith('fn:')) { // ユーザー関数呼び出し + const fnName = block.type.split(':')[1]; + const fn = this.getVariableValue(fnName, values); + for (let i = 0; i < fn.slots.length; i++) { + const name = fn.slots[i]; + slotArg[name] = this.evaluate(block.args[i], values); + } + return fn.exec(slotArg); + } + + if (block.args === undefined) return null; + + const date = new Date(); + const day = `${this.opts.visitor ? this.opts.visitor.id : ''} ${date.getFullYear()}/${date.getMonth()}/${date.getDate()}`; + + const funcs: { [p in keyof typeof funcDefs]: any } = { + not: (a) => !a, + eq: (a, b) => a === b, + notEq: (a, b) => a !== b, + gt: (a, b) => a > b, + lt: (a, b) => a < b, + gtEq: (a, b) => a >= b, + ltEq: (a, b) => a <= b, + or: (a, b) => a || b, + and: (a, b) => a && b, + if: (bool, a, b) => bool ? a : b, + add: (a, b) => a + b, + subtract: (a, b) => a - b, + multiply: (a, b) => a * b, + divide: (a, b) => a / b, + random: (probability) => Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * 100) < probability, + rannum: (min, max) => min + Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * (max - min + 1)), + randomPick: (list) => list[Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * list.length)], + dailyRandom: (probability) => Math.floor(seedrandom(`${day}:${block.id}`)() * 100) < probability, + dailyRannum: (min, max) => min + Math.floor(seedrandom(`${day}:${block.id}`)() * (max - min + 1)), + dailyRandomPick: (list) => list[Math.floor(seedrandom(`${day}:${block.id}`)() * list.length)], + }; + + const fnName = block.type; + + const fn = funcs[fnName]; + if (fn == null) { + console.error('Unknown function: ' + fnName); + throw new Error('Unknown function: ' + fnName); + } + + const args = block.args.map(x => this.evaluate(x, values, slotArg)); + + return fn(...args); + } + + @autobind + private getVariableValue(name: string, values: { name: string, value: any }[]): any { + const v = values.find(v => v.name === name); + if (v) { + return v.value; + } + + const pageVar = this.pageVars.find(v => v.name === name); + if (pageVar) { + return pageVar.value; + } + + if (AiScript.envVarsDef[name]) { + return this.envVars[name].value; + } + + throw new Error(`Script: No such variable '${name}'`); + } + + @autobind + public isUsedName(name: string) { + if (this.variables.some(v => v.name === name)) { + return true; + } + + if (this.pageVars.some(v => v.name === name)) { + return true; + } + + if (AiScript.envVarsDef[name]) { + return true; + } + + return false; + } +} diff --git a/src/client/app/common/scripts/collect-page-vars.ts b/src/client/app/common/scripts/collect-page-vars.ts new file mode 100644 index 0000000000..86687e21f4 --- /dev/null +++ b/src/client/app/common/scripts/collect-page-vars.ts @@ -0,0 +1,24 @@ +export function collectPageVars(content) { + const pageVars = []; + const collect = (xs: any[]) => { + for (const x of xs) { + if (x.type === 'input') { + pageVars.push({ + name: x.name, + type: x.inputType, + value: x.default + }); + } else if (x.type === 'switch') { + pageVars.push({ + name: x.name, + type: 'boolean', + value: x.default + }); + } else if (x.children) { + collect(x.children); + } + } + }; + collect(content); + return pageVars; +} diff --git a/src/client/app/common/views/components/dialog.vue b/src/client/app/common/views/components/dialog.vue index c1ee7958c0..020c88f699 100644 --- a/src/client/app/common/views/components/dialog.vue +++ b/src/client/app/common/views/components/dialog.vue @@ -22,7 +22,14 @@ - + + {{ (showCancelButton || input || select || user) ? $t('@.ok') : $t('@.got-it') }} @@ -230,7 +237,7 @@ export default Vue.extend({ font-size 32px &.success - color #37ec92 + color #85da5a &.error color #ec4137 diff --git a/src/client/app/common/views/components/media-image.vue b/src/client/app/common/views/components/media-image.vue index 2559907512..6db4b40dd8 100644 --- a/src/client/app/common/views/components/media-image.vue +++ b/src/client/app/common/views/components/media-image.vue @@ -36,7 +36,7 @@ export default Vue.extend({ return { hide: true }; - } + }, computed: { style(): any { let url = `url(${ diff --git a/src/client/app/common/views/components/page-editor/page-editor.block.vue b/src/client/app/common/views/components/page-editor/page-editor.block.vue new file mode 100644 index 0000000000..a3e1488d1b --- /dev/null +++ b/src/client/app/common/views/components/page-editor/page-editor.block.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/client/app/common/views/components/page-editor/page-editor.button.vue b/src/client/app/common/views/components/page-editor/page-editor.button.vue new file mode 100644 index 0000000000..d5fc243818 --- /dev/null +++ b/src/client/app/common/views/components/page-editor/page-editor.button.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/src/client/app/common/views/components/page-editor/page-editor.container.vue b/src/client/app/common/views/components/page-editor/page-editor.container.vue new file mode 100644 index 0000000000..698fdfee45 --- /dev/null +++ b/src/client/app/common/views/components/page-editor/page-editor.container.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/src/client/app/common/views/components/page-editor/page-editor.image.vue b/src/client/app/common/views/components/page-editor/page-editor.image.vue new file mode 100644 index 0000000000..0bc1816e8d --- /dev/null +++ b/src/client/app/common/views/components/page-editor/page-editor.image.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/src/client/app/common/views/components/page-editor/page-editor.input.vue b/src/client/app/common/views/components/page-editor/page-editor.input.vue new file mode 100644 index 0000000000..1f3754252b --- /dev/null +++ b/src/client/app/common/views/components/page-editor/page-editor.input.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/src/client/app/common/views/components/page-editor/page-editor.script-block.vue b/src/client/app/common/views/components/page-editor/page-editor.script-block.vue new file mode 100644 index 0000000000..3122832030 --- /dev/null +++ b/src/client/app/common/views/components/page-editor/page-editor.script-block.vue @@ -0,0 +1,263 @@ + + + + + diff --git a/src/client/app/common/views/components/page-editor/page-editor.section.vue b/src/client/app/common/views/components/page-editor/page-editor.section.vue new file mode 100644 index 0000000000..d7a247b0b1 --- /dev/null +++ b/src/client/app/common/views/components/page-editor/page-editor.section.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/src/client/app/common/views/components/page-editor/page-editor.switch.vue b/src/client/app/common/views/components/page-editor/page-editor.switch.vue new file mode 100644 index 0000000000..a9cfa2844f --- /dev/null +++ b/src/client/app/common/views/components/page-editor/page-editor.switch.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/src/client/app/common/views/components/page-editor/page-editor.text.vue b/src/client/app/common/views/components/page-editor/page-editor.text.vue new file mode 100644 index 0000000000..7368931b2f --- /dev/null +++ b/src/client/app/common/views/components/page-editor/page-editor.text.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/src/client/app/common/views/components/page-editor/page-editor.vue b/src/client/app/common/views/components/page-editor/page-editor.vue new file mode 100644 index 0000000000..1bcaaa0330 --- /dev/null +++ b/src/client/app/common/views/components/page-editor/page-editor.vue @@ -0,0 +1,452 @@ + + + + + diff --git a/src/client/app/common/views/components/page-preview.vue b/src/client/app/common/views/components/page-preview.vue new file mode 100644 index 0000000000..d8fdbf4b04 --- /dev/null +++ b/src/client/app/common/views/components/page-preview.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/src/client/app/common/views/pages/page/page.block.vue b/src/client/app/common/views/pages/page/page.block.vue new file mode 100644 index 0000000000..48a89f9de7 --- /dev/null +++ b/src/client/app/common/views/pages/page/page.block.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/client/app/common/views/pages/page/page.button.vue b/src/client/app/common/views/pages/page/page.button.vue new file mode 100644 index 0000000000..5063d27122 --- /dev/null +++ b/src/client/app/common/views/pages/page/page.button.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/src/client/app/common/views/pages/page/page.image.vue b/src/client/app/common/views/pages/page/page.image.vue new file mode 100644 index 0000000000..1285445eb0 --- /dev/null +++ b/src/client/app/common/views/pages/page/page.image.vue @@ -0,0 +1,36 @@ + + + + + diff --git a/src/client/app/common/views/pages/page/page.input.vue b/src/client/app/common/views/pages/page/page.input.vue new file mode 100644 index 0000000000..cda5550337 --- /dev/null +++ b/src/client/app/common/views/pages/page/page.input.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/src/client/app/common/views/pages/page/page.section.vue b/src/client/app/common/views/pages/page/page.section.vue new file mode 100644 index 0000000000..03c009d9c3 --- /dev/null +++ b/src/client/app/common/views/pages/page/page.section.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/src/client/app/common/views/pages/page/page.switch.vue b/src/client/app/common/views/pages/page/page.switch.vue new file mode 100644 index 0000000000..715a2fee6e --- /dev/null +++ b/src/client/app/common/views/pages/page/page.switch.vue @@ -0,0 +1,33 @@ + + + diff --git a/src/client/app/common/views/pages/page/page.text.vue b/src/client/app/common/views/pages/page/page.text.vue new file mode 100644 index 0000000000..eadc6f0aed --- /dev/null +++ b/src/client/app/common/views/pages/page/page.text.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/src/client/app/common/views/pages/page/page.vue b/src/client/app/common/views/pages/page/page.vue new file mode 100644 index 0000000000..5ca58a6a4e --- /dev/null +++ b/src/client/app/common/views/pages/page/page.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/src/client/app/desktop/script.ts b/src/client/app/desktop/script.ts index 8d292ce324..00ba5db23a 100644 --- a/src/client/app/desktop/script.ts +++ b/src/client/app/desktop/script.ts @@ -156,7 +156,11 @@ init(async (launch, os) => { { path: '/explore', name: 'explore', component: () => import('../common/views/pages/explore.vue').then(m => m.default) }, { path: '/explore/tags/:tag', name: 'explore-tag', props: true, component: () => import('../common/views/pages/explore.vue').then(m => m.default) }, { path: '/i/favorites', component: () => import('./views/home/favorites.vue').then(m => m.default) }, + { path: '/i/pages', component: () => import('./views/home/pages.vue').then(m => m.default) }, ]}, + { path: '/@:user/pages/:page', props: true, component: () => import('./views/pages/page.vue').then(m => m.default) }, + { path: '/i/pages/new', component: () => import('./views/pages/page-editor.vue').then(m => m.default) }, + { path: '/i/pages/edit/:page', props: true, component: () => import('./views/pages/page-editor.vue').then(m => m.default) }, { path: '/i/messaging/:user', component: MkMessagingRoom }, { path: '/i/drive', component: MkDrive }, { path: '/i/drive/folder/:folder', component: MkDrive }, diff --git a/src/client/app/desktop/views/components/ui.header.account.vue b/src/client/app/desktop/views/components/ui.header.account.vue index 7f9decfdcd..05692667b7 100644 --- a/src/client/app/desktop/views/components/ui.header.account.vue +++ b/src/client/app/desktop/views/components/ui.header.account.vue @@ -9,35 +9,42 @@
  • - + {{ $t('profile') }}
  • - + {{ $t('@.drive') }}

  • - + {{ $t('@.favorites') }}
  • - + {{ $t('lists') }}

  • +
  • + + + {{ $t('@.pages') }} + + +
  • - + {{ $t('follow-requests') }}{{ $store.state.i.pendingReceivedFollowRequestsCount }}

    @@ -46,14 +53,14 @@
    • - + {{ $t('@.settings') }}
    • - + {{ $t('admin') }} @@ -76,7 +83,7 @@
      • - + {{ $t('@.signout') }}

      • @@ -95,14 +102,14 @@ import MkFollowRequestsWindow from './received-follow-requests-window.vue'; import MkDriveWindow from './drive-window.vue'; import contains from '../../../common/scripts/contains'; import { faHome, faColumns } from '@fortawesome/free-solid-svg-icons'; -import { faMoon, faSun } from '@fortawesome/free-regular-svg-icons'; +import { faMoon, faSun, faStickyNote } from '@fortawesome/free-regular-svg-icons'; export default Vue.extend({ i18n: i18n('desktop/views/components/ui.header.account.vue'), data() { return { isOpen: false, - faHome, faColumns, faMoon, faSun + faHome, faColumns, faMoon, faSun, faStickyNote }; }, computed: { diff --git a/src/client/app/desktop/views/home/pages.vue b/src/client/app/desktop/views/home/pages.vue new file mode 100644 index 0000000000..9f7fb65159 --- /dev/null +++ b/src/client/app/desktop/views/home/pages.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/src/client/app/desktop/views/pages/page-editor.vue b/src/client/app/desktop/views/pages/page-editor.vue new file mode 100644 index 0000000000..50d1e7db61 --- /dev/null +++ b/src/client/app/desktop/views/pages/page-editor.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/src/client/app/desktop/views/pages/page.vue b/src/client/app/desktop/views/pages/page.vue new file mode 100644 index 0000000000..1ddff08c76 --- /dev/null +++ b/src/client/app/desktop/views/pages/page.vue @@ -0,0 +1,36 @@ + + + + + diff --git a/src/client/app/mobile/script.ts b/src/client/app/mobile/script.ts index 510141f94b..136bbc31c4 100644 --- a/src/client/app/mobile/script.ts +++ b/src/client/app/mobile/script.ts @@ -135,6 +135,7 @@ init((launch, os) => { { path: '/signup', name: 'signup', component: MkSignup }, { path: '/i/settings', name: 'settings', component: () => import('./views/pages/settings.vue').then(m => m.default) }, { path: '/i/favorites', name: 'favorites', component: MkFavorites }, + { path: '/i/pages', name: 'pages', component: () => import('./views/pages/pages.vue').then(m => m.default) }, { path: '/i/lists', name: 'user-lists', component: MkUserLists }, { path: '/i/lists/:list', name: 'user-list', component: MkUserList }, { path: '/i/received-follow-requests', name: 'received-follow-requests', component: MkReceivedFollowRequests }, @@ -144,6 +145,8 @@ init((launch, os) => { { path: '/i/drive', name: 'drive', component: MkDrive }, { path: '/i/drive/folder/:folder', component: MkDrive }, { path: '/i/drive/file/:file', component: MkDrive }, + { path: '/i/pages/new', component: () => import('./views/pages/page-editor.vue').then(m => m.default) }, + { path: '/i/pages/edit/:page', props: true, component: () => import('./views/pages/page-editor.vue').then(m => m.default) }, { path: '/selectdrive', component: MkSelectDrive }, { path: '/search', component: MkSearch }, { path: '/tags/:tag', component: MkTag }, @@ -156,6 +159,7 @@ init((launch, os) => { { path: 'following', component: () => import('../common/views/pages/following.vue').then(m => m.default) }, { path: 'followers', component: () => import('../common/views/pages/followers.vue').then(m => m.default) }, ]}, + { path: '/@:user/pages/:page', props: true, component: () => import('./views/pages/page.vue').then(m => m.default) }, { path: '/notes/:note', component: MkNote }, { path: '/authorize-follow', component: MkFollow }, { path: '*', component: MkNotFound } diff --git a/src/client/app/mobile/views/components/ui.nav.vue b/src/client/app/mobile/views/components/ui.nav.vue index 9a3ade4c63..da9bb518ef 100644 --- a/src/client/app/mobile/views/components/ui.nav.vue +++ b/src/client/app/mobile/views/components/ui.nav.vue @@ -29,6 +29,7 @@
      • {{ $t('@.favorites') }}
      • {{ $t('user-lists') }}
      • {{ $t('@.drive') }}
      • +
      • {{ $t('@.pages') }}
      • {{ $t('search') }}
      • @@ -66,7 +67,7 @@ import Vue from 'vue'; import i18n from '../../../i18n'; import { lang } from '../../../config'; import { faNewspaper, faHashtag, faHome, faColumns } from '@fortawesome/free-solid-svg-icons'; -import { faMoon, faSun } from '@fortawesome/free-regular-svg-icons'; +import { faMoon, faSun, faStickyNote } from '@fortawesome/free-regular-svg-icons'; import { search } from '../../../common/scripts/search'; export default Vue.extend({ @@ -86,7 +87,7 @@ export default Vue.extend({ announcements: [], searching: false, showNotifications: false, - faNewspaper, faHashtag, faMoon, faSun, faHome, faColumns + faNewspaper, faHashtag, faMoon, faSun, faHome, faColumns, faStickyNote }; }, diff --git a/src/client/app/mobile/views/pages/page-editor.vue b/src/client/app/mobile/views/pages/page-editor.vue new file mode 100644 index 0000000000..9d549c784f --- /dev/null +++ b/src/client/app/mobile/views/pages/page-editor.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/src/client/app/mobile/views/pages/page.vue b/src/client/app/mobile/views/pages/page.vue new file mode 100644 index 0000000000..27ade4a398 --- /dev/null +++ b/src/client/app/mobile/views/pages/page.vue @@ -0,0 +1,36 @@ + + + + + diff --git a/src/client/app/mobile/views/pages/pages.vue b/src/client/app/mobile/views/pages/pages.vue new file mode 100644 index 0000000000..100c814ad9 --- /dev/null +++ b/src/client/app/mobile/views/pages/pages.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/src/client/themes/dark.json5 b/src/client/themes/dark.json5 index 5f44f8570e..8e0c726b4c 100644 --- a/src/client/themes/dark.json5 +++ b/src/client/themes/dark.json5 @@ -232,5 +232,8 @@ adminDashboardCardBg: '$secondary', adminDashboardCardFg: '$text', adminDashboardCardDivider: 'rgba(0, 0, 0, 0.3)', + + pageBlockBorder: 'rgba(255, 255, 255, 0.1)', + pageBlockBorderHover: 'rgba(255, 255, 255, 0.15)', }, } diff --git a/src/client/themes/light.json5 b/src/client/themes/light.json5 index d5680f8f82..1fff18176a 100644 --- a/src/client/themes/light.json5 +++ b/src/client/themes/light.json5 @@ -232,5 +232,8 @@ adminDashboardCardBg: '$secondary', adminDashboardCardFg: '$text', adminDashboardCardDivider: 'rgba(0, 0, 0, 0.082)', + + pageBlockBorder: 'rgba(0, 0, 0, 0.1)', + pageBlockBorderHover: 'rgba(0, 0, 0, 0.15)', }, } -- cgit v1.2.3-freya