0.2.3 #99
158 changed files with 549 additions and 757 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -197,5 +197,7 @@ src/newsreader/fixtures/local
|
|||
# Javascript
|
||||
node_modules/
|
||||
|
||||
static/
|
||||
|
||||
# Css
|
||||
*.css
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
import path from 'path';
|
||||
|
||||
import { dest, series } from 'gulp';
|
||||
import babelify from 'babelify';
|
||||
import browserify from 'browserify';
|
||||
import source from 'vinyl-source-stream';
|
||||
import buffer from 'vinyl-buffer';
|
||||
import concat from 'gulp-concat';
|
||||
|
||||
const PROJECT_DIR = path.join('src', 'newsreader');
|
||||
const SRC_DIR = path.join(PROJECT_DIR, 'js');
|
||||
const STATIC_SUFFIX = 'dist/js/';
|
||||
|
||||
const CORE_DIR = path.join(PROJECT_DIR, 'news', 'core', 'static', 'core');
|
||||
const COLLECTION_DIR = path.join(
|
||||
PROJECT_DIR,
|
||||
'news',
|
||||
'collection',
|
||||
'static',
|
||||
'collection'
|
||||
);
|
||||
|
||||
const taskMappings = [
|
||||
{ name: 'homepage', destDir: `${CORE_DIR}/${STATIC_SUFFIX}` },
|
||||
{ name: 'categories', destDir: `${CORE_DIR}/${STATIC_SUFFIX}` },
|
||||
{ name: 'rules', destDir: `${COLLECTION_DIR}/${STATIC_SUFFIX}` },
|
||||
];
|
||||
|
||||
const babelTask = done => {
|
||||
const tasks = taskMappings.map(taskMapping => {
|
||||
const { name, destDir } = taskMapping;
|
||||
|
||||
const bundle = browserify({
|
||||
entries: `${SRC_DIR}/pages/${name}/index.js`,
|
||||
debug: true,
|
||||
});
|
||||
|
||||
const transpiledBundle = bundle.transform(babelify);
|
||||
|
||||
return () =>
|
||||
transpiledBundle
|
||||
.bundle()
|
||||
.pipe(source('index.js'))
|
||||
.pipe(buffer())
|
||||
.pipe(concat(`${name}.js`))
|
||||
.pipe(dest(destDir));
|
||||
});
|
||||
|
||||
return series(...tasks)(done);
|
||||
};
|
||||
|
||||
export default babelTask;
|
||||
46
gulp/sass.js
46
gulp/sass.js
|
|
@ -1,46 +0,0 @@
|
|||
import { dest, series, src } from 'gulp';
|
||||
|
||||
import concat from 'gulp-concat';
|
||||
import path from 'path';
|
||||
import sass from 'gulp-sass';
|
||||
|
||||
const PROJECT_DIR = path.join('src', 'newsreader');
|
||||
const SRC_DIR = path.join(PROJECT_DIR, 'scss');
|
||||
const STATIC_SUFFIX = 'dist/css/';
|
||||
|
||||
export const ACCOUNTS_DIR = path.join(PROJECT_DIR, 'accounts', 'static', 'accounts');
|
||||
export const CORE_DIR = path.join(PROJECT_DIR, 'news', 'core', 'static', 'core');
|
||||
export const COLLECTION_DIR = path.join(
|
||||
PROJECT_DIR,
|
||||
'news',
|
||||
'collection',
|
||||
'static',
|
||||
'collection'
|
||||
);
|
||||
|
||||
const taskMappings = [
|
||||
{ name: 'login', destDir: `${ACCOUNTS_DIR}/${STATIC_SUFFIX}` },
|
||||
{ name: 'register', destDir: `${ACCOUNTS_DIR}/${STATIC_SUFFIX}` },
|
||||
{ name: 'activate', destDir: `${ACCOUNTS_DIR}/${STATIC_SUFFIX}` },
|
||||
{ name: 'password-reset', destDir: `${ACCOUNTS_DIR}/${STATIC_SUFFIX}` },
|
||||
{ name: 'homepage', destDir: `${CORE_DIR}/${STATIC_SUFFIX}` },
|
||||
{ name: 'categories', destDir: `${CORE_DIR}/${STATIC_SUFFIX}` },
|
||||
{ name: 'category', destDir: `${CORE_DIR}/${STATIC_SUFFIX}` },
|
||||
{ name: 'rules', destDir: `${COLLECTION_DIR}/${STATIC_SUFFIX}` },
|
||||
{ name: 'rule', destDir: `${COLLECTION_DIR}/${STATIC_SUFFIX}` },
|
||||
{ name: 'import', destDir: `${COLLECTION_DIR}/${STATIC_SUFFIX}` },
|
||||
];
|
||||
|
||||
export const sassTask = done => {
|
||||
const tasks = taskMappings.map(taskMapping => {
|
||||
const { name, destDir } = taskMapping;
|
||||
|
||||
return () =>
|
||||
src(`${SRC_DIR}/pages/${name}/index.scss`)
|
||||
.pipe(sass().on('error', sass.logError))
|
||||
.pipe(concat(`${name}.css`))
|
||||
.pipe(dest(destDir));
|
||||
});
|
||||
|
||||
series(...tasks)(done);
|
||||
};
|
||||
|
|
@ -1,26 +1,70 @@
|
|||
import { parallel, series, watch as _watch } from 'gulp';
|
||||
|
||||
import path from 'path';
|
||||
import del from 'del';
|
||||
|
||||
import { ACCOUNTS_DIR, CORE_DIR, sassTask } from './gulp/sass';
|
||||
import babelTask from './gulp/babel';
|
||||
import { dest, parallel, series, src, watch as _watch } from 'gulp';
|
||||
import concat from 'gulp-concat';
|
||||
import globby from 'globby';
|
||||
import sass from 'gulp-sass';
|
||||
import babelify from 'babelify';
|
||||
import browserify from 'browserify';
|
||||
import source from 'vinyl-source-stream';
|
||||
import buffer from 'vinyl-buffer';
|
||||
|
||||
const PROJECT_DIR = path.join('src', 'newsreader');
|
||||
const STATIC_DIR = path.join(PROJECT_DIR, 'static');
|
||||
const CSS_SUFFIX = 'css/';
|
||||
const JS_SUFFIX = 'js/';
|
||||
|
||||
const SASS_DEST_DIR = path.join(STATIC_DIR, CSS_SUFFIX);
|
||||
const JS_DEST_DIR = path.join(STATIC_DIR, JS_SUFFIX);
|
||||
|
||||
const SASS_DIR = path.join(PROJECT_DIR, 'scss');
|
||||
const JS_DIR = path.join(PROJECT_DIR, 'js');
|
||||
|
||||
const clean = () => {
|
||||
return del([
|
||||
`${ACCOUNTS_DIR}/accounts/dist/css/*`,
|
||||
return del([`${STATIC_DIR}/${CSS_SUFFIX}/*.css`, `${STATIC_DIR}/${JS_SUFFIX}/*.js`]);
|
||||
};
|
||||
|
||||
`${CORE_DIR}/core/dist/css/*`,
|
||||
`${CORE_DIR}/core/dist/js/*`,
|
||||
]);
|
||||
const sassTask = () => {
|
||||
return src(`${SASS_DIR}/index.scss`)
|
||||
.pipe(sass().on('error', sass.logError))
|
||||
.pipe(concat(`main.css`))
|
||||
.pipe(dest(SASS_DEST_DIR));
|
||||
};
|
||||
|
||||
const babelTask = () => {
|
||||
const getDirName = filename => {
|
||||
const fragments = filename.split('/');
|
||||
|
||||
return fragments[fragments.length - 2];
|
||||
};
|
||||
|
||||
const promise = globby([`${JS_DIR}/pages/**/index.js`]).then(entries => {
|
||||
entries.forEach(entry => {
|
||||
const bundle = browserify({ entries: entry, debug: true });
|
||||
const transpiledBundle = bundle.transform(babelify);
|
||||
const bundleName = getDirName(entry);
|
||||
|
||||
return transpiledBundle
|
||||
.bundle()
|
||||
.pipe(source('index.js'))
|
||||
.pipe(buffer())
|
||||
.pipe(concat(`${bundleName}.js`))
|
||||
.pipe(dest(JS_DEST_DIR));
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.resolve(promise);
|
||||
};
|
||||
|
||||
export const watch = () => {
|
||||
return _watch([`${PROJECT_DIR}/scss/**/*.scss`, `${PROJECT_DIR}/js/**/*.js`], done => {
|
||||
series(clean, sassTask, babelTask)(done);
|
||||
});
|
||||
return _watch(
|
||||
[`${PROJECT_DIR}/scss/**/*.scss`, `${PROJECT_DIR}/js/**/*.js`],
|
||||
{ ignoreInitial: false },
|
||||
done => {
|
||||
series(clean, parallel(sassTask, babelTask))(done);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default series(clean, sassTask, babelTask);
|
||||
export default series(clean, parallel(babelTask, sassTask));
|
||||
|
|
|
|||
85
package-lock.json
generated
85
package-lock.json
generated
|
|
@ -1203,7 +1203,6 @@
|
|||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
|
||||
"integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@nodelib/fs.stat": "2.0.3",
|
||||
"run-parallel": "^1.1.9"
|
||||
|
|
@ -1212,14 +1211,12 @@
|
|||
"@nodelib/fs.stat": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz",
|
||||
"integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA=="
|
||||
},
|
||||
"@nodelib/fs.walk": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz",
|
||||
"integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@nodelib/fs.scandir": "2.1.3",
|
||||
"fastq": "^1.6.0"
|
||||
|
|
@ -1315,9 +1312,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.1.2.tgz",
|
||||
"integrity": "sha512-B8emQA1qeKerqd1dmIsQYnXi+mmAzTB7flExjmy5X1aVAKFNNNDubkavwR13kR6JnpeLp3aLoJhwn9trWPAyFQ==",
|
||||
"version": "13.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.0.tgz",
|
||||
"integrity": "sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/stack-utils": {
|
||||
|
|
@ -1641,8 +1638,7 @@
|
|||
"array-union": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
|
||||
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="
|
||||
},
|
||||
"array-unique": {
|
||||
"version": "0.3.2",
|
||||
|
|
@ -2932,6 +2928,22 @@
|
|||
"slash": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"globby": {
|
||||
"version": "10.0.2",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz",
|
||||
"integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/glob": "^7.1.1",
|
||||
"array-union": "^2.1.0",
|
||||
"dir-glob": "^3.0.1",
|
||||
"fast-glob": "^3.0.3",
|
||||
"glob": "^7.1.3",
|
||||
"ignore": "^5.1.1",
|
||||
"merge2": "^1.2.3",
|
||||
"slash": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
|
||||
|
|
@ -3018,7 +3030,6 @@
|
|||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||
"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-type": "^4.0.0"
|
||||
},
|
||||
|
|
@ -3026,8 +3037,7 @@
|
|||
"path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -3496,7 +3506,6 @@
|
|||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.1.tgz",
|
||||
"integrity": "sha512-nTCREpBY8w8r+boyFYAx21iL6faSsQynliPHM4Uf56SbkyohCNxpVPEH9xrF5TXKy+IsjkPUHDKiUkzBVRXn9g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
"@nodelib/fs.walk": "^1.2.3",
|
||||
|
|
@ -3509,7 +3518,6 @@
|
|||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fill-range": "^7.0.1"
|
||||
}
|
||||
|
|
@ -3518,7 +3526,6 @@
|
|||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
}
|
||||
|
|
@ -3527,7 +3534,6 @@
|
|||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
|
||||
"integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-glob": "^4.0.1"
|
||||
}
|
||||
|
|
@ -3535,14 +3541,12 @@
|
|||
"is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
|
||||
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"braces": "^3.0.1",
|
||||
"picomatch": "^2.0.5"
|
||||
|
|
@ -3552,7 +3556,6 @@
|
|||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
|
|
@ -3581,7 +3584,6 @@
|
|||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz",
|
||||
"integrity": "sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"reusify": "^1.0.0"
|
||||
}
|
||||
|
|
@ -4511,18 +4513,15 @@
|
|||
"dev": true
|
||||
},
|
||||
"globby": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz",
|
||||
"integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==",
|
||||
"dev": true,
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-11.0.0.tgz",
|
||||
"integrity": "sha512-iuehFnR3xu5wBBtm4xi0dMe92Ob87ufyu/dHwpDYfbcpYpIbrO5OnS8M1vWvrBhSGEJ3/Ecj7gnX76P8YxpPEg==",
|
||||
"requires": {
|
||||
"@types/glob": "^7.1.1",
|
||||
"array-union": "^2.1.0",
|
||||
"dir-glob": "^3.0.1",
|
||||
"fast-glob": "^3.0.3",
|
||||
"glob": "^7.1.3",
|
||||
"ignore": "^5.1.1",
|
||||
"merge2": "^1.2.3",
|
||||
"fast-glob": "^3.1.1",
|
||||
"ignore": "^5.1.4",
|
||||
"merge2": "^1.3.0",
|
||||
"slash": "^3.0.0"
|
||||
}
|
||||
},
|
||||
|
|
@ -4869,8 +4868,7 @@
|
|||
"ignore": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz",
|
||||
"integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A=="
|
||||
},
|
||||
"import-local": {
|
||||
"version": "2.0.0",
|
||||
|
|
@ -5089,8 +5087,7 @@
|
|||
"is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
|
||||
"dev": true
|
||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
|
||||
},
|
||||
"is-finite": {
|
||||
"version": "1.0.2",
|
||||
|
|
@ -5117,7 +5114,6 @@
|
|||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
|
||||
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-extglob": "^2.1.1"
|
||||
}
|
||||
|
|
@ -6527,8 +6523,7 @@
|
|||
"merge2": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz",
|
||||
"integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw=="
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "3.1.10",
|
||||
|
|
@ -7376,10 +7371,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz",
|
||||
"integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==",
|
||||
"dev": true
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz",
|
||||
"integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA=="
|
||||
},
|
||||
"pify": {
|
||||
"version": "4.0.1",
|
||||
|
|
@ -8057,8 +8051,7 @@
|
|||
"reusify": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "3.0.0",
|
||||
|
|
@ -8088,8 +8081,7 @@
|
|||
"run-parallel": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz",
|
||||
"integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q=="
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
|
|
@ -8299,8 +8291,7 @@
|
|||
"slash": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
|
||||
},
|
||||
"snapdragon": {
|
||||
"version": "0.8.2",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
"author": "Sonny",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"globby": "^11.0.0",
|
||||
"js-cookie": "^2.2.1",
|
||||
"lodash": "^4.17.15",
|
||||
"object-assign": "^4.1.1",
|
||||
|
|
|
|||
|
|
@ -2,12 +2,8 @@
|
|||
|
||||
{% load static %}
|
||||
|
||||
{% block head %}
|
||||
<link href="{% static 'accounts/dist/css/login.css' %}" rel="stylesheet" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="main">
|
||||
<main id="login--page" class="main">
|
||||
<form class="form login-form" method="POST" action="{% url 'accounts:login' %}">
|
||||
{% csrf_token %}
|
||||
<div class="form__header">
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ USE_TZ = True
|
|||
# https://docs.djangoproject.com/en/2.2/howto/static-files/
|
||||
STATIC_URL = "/static/"
|
||||
|
||||
STATICFILES_DIRS = ["src/newsreader/static/icons"]
|
||||
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
|
||||
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-STATICFILES_FINDERS
|
||||
STATICFILES_FINDERS = [
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import React from 'react';
|
||||
|
||||
const Modal = props => {
|
||||
return <div className="modal">{props.content}</div>;
|
||||
return (
|
||||
<div className="modal">
|
||||
<div className="modal__item">{props.content}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Modal;
|
||||
|
|
|
|||
3
src/newsreader/js/index.js
Normal file
3
src/newsreader/js/index.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import './pages/homepage/index.js';
|
||||
import './pages/rules/index.js';
|
||||
import './pages/categories/index.js';
|
||||
|
|
@ -6,7 +6,7 @@ const CategoryCard = props => {
|
|||
const { category } = props;
|
||||
|
||||
const categoryRules = category.rules.map(rule => {
|
||||
const faviconUrl = rule.favicon ? rule.favicon : '/static/picture.svg';
|
||||
const faviconUrl = rule.favicon ? rule.favicon : '/static/icons/picture.svg';
|
||||
|
||||
return (
|
||||
<li key={rule.pk} className="list__item">
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import Modal from '../../../components/Modal.js';
|
|||
|
||||
const CategoryModal = props => {
|
||||
const content = (
|
||||
<div className="category-modal">
|
||||
<div className="category-modal__header">
|
||||
<h1 className="h1 category-modal__title">Delete category</h1>
|
||||
<>
|
||||
<div className="modal__header">
|
||||
<h1 className="h1 modal__title">Delete category</h1>
|
||||
</div>
|
||||
|
||||
<div className="category-modal__content">
|
||||
<div className="modal__content">
|
||||
<p className="p">Are you sure you want to delete {props.category.name}?</p>
|
||||
<small className="small">
|
||||
Collection rules coupled to this category will not be deleted but will have no
|
||||
|
|
@ -17,7 +17,7 @@ const CategoryModal = props => {
|
|||
</small>
|
||||
</div>
|
||||
|
||||
<div className="category-modal__footer">
|
||||
<div className="modal__footer">
|
||||
<button className="button button--confirm" onClick={props.handleCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
|
|
@ -28,7 +28,7 @@ const CategoryModal = props => {
|
|||
Delete category
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return <Modal content={content} />;
|
||||
|
|
|
|||
|
|
@ -3,10 +3,8 @@ import ReactDOM from 'react-dom';
|
|||
|
||||
import App from './App.js';
|
||||
|
||||
const content = document.getElementById('categories--page');
|
||||
const dataScript = document.getElementById('categories-data');
|
||||
const categories = JSON.parse(dataScript.textContent);
|
||||
|
||||
ReactDOM.render(
|
||||
<App categories={categories} />,
|
||||
document.getElementsByClassName('content')[0]
|
||||
);
|
||||
ReactDOM.render(<App categories={categories} />, content);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ class App extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<>
|
||||
<main className="main">
|
||||
<Sidebar />
|
||||
<FeedList />
|
||||
|
||||
|
|
@ -28,7 +27,6 @@ class App extends React.Component {
|
|||
category={this.props.category}
|
||||
/>
|
||||
)}
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { unSelectPost, markPostRead } from '../actions/posts.js';
|
|||
import { formatDatetime } from '../../../utils.js';
|
||||
|
||||
class PostModal extends React.Component {
|
||||
modalListener = ::this.modalListener;
|
||||
readTimer = null;
|
||||
|
||||
componentDidMount() {
|
||||
|
|
@ -16,6 +17,8 @@ class PostModal extends React.Component {
|
|||
if (!post.read) {
|
||||
this.readTimer = setTimeout(markPostRead, 3000, post, token);
|
||||
}
|
||||
|
||||
window.addEventListener('click', this.modalListener);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
|
@ -24,6 +27,16 @@ class PostModal extends React.Component {
|
|||
}
|
||||
|
||||
this.readTimer = null;
|
||||
|
||||
window.removeEventListener('click', this.modalListener);
|
||||
}
|
||||
|
||||
modalListener(e) {
|
||||
const targetClassName = e.target.className;
|
||||
|
||||
if (this.props.post && targetClassName == 'modal post-modal') {
|
||||
this.props.unSelectPost();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
@ -32,7 +45,7 @@ class PostModal extends React.Component {
|
|||
const titleClassName = post.read ? 'post__title post__title--read' : 'post__title';
|
||||
|
||||
return (
|
||||
<div className="modal">
|
||||
<div className="modal post-modal">
|
||||
<div className="post">
|
||||
<button
|
||||
className="button post__close-button"
|
||||
|
|
@ -40,7 +53,7 @@ class PostModal extends React.Component {
|
|||
>
|
||||
Close{' '}
|
||||
<span>
|
||||
<img src="/static/cross.svg" width="20" />
|
||||
<img src="/static/icons/cross.svg" width="20" />
|
||||
</span>
|
||||
</button>
|
||||
<div className="post__header">
|
||||
|
|
@ -53,7 +66,7 @@ class PostModal extends React.Component {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<img src="/static/link.svg" />
|
||||
<img src="/static/icons/link.svg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class FeedList extends React.Component {
|
|||
return (
|
||||
<div className="post-message">
|
||||
<div className="post-message__block">
|
||||
<img src="/static/arrow-left.svg" height="28" width="28" />
|
||||
<img src="/static/icons/arrow-left.svg" height="28" width="28" />
|
||||
<p className="post-message__text">
|
||||
Select a category or rule to show its unread posts
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class PostItem extends React.Component {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<img src="/static/link.svg" />
|
||||
<img src="/static/icons/link.svg" />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ class CategoryItem extends React.Component {
|
|||
|
||||
render() {
|
||||
const imageSrc = this.state.open
|
||||
? '/static/chevron-down.svg'
|
||||
: '/static/chevron-right.svg';
|
||||
? '/static/icons/chevron-down.svg'
|
||||
: '/static/icons/chevron-right.svg';
|
||||
const selected = isSelected(this.props.category, this.props.selected, CATEGORY_TYPE);
|
||||
const className = selected ? 'category category--selected' : 'category';
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ class CategoryItem extends React.Component {
|
|||
});
|
||||
|
||||
return (
|
||||
<li className="categories__item">
|
||||
<li className="sidebar__item">
|
||||
<div className={className}>
|
||||
<div className="category__menu" onClick={() => this.toggleRules()}>
|
||||
<img src={imageSrc} width="20" heigth="20" />
|
||||
|
|
|
|||
|
|
@ -21,15 +21,15 @@ class RuleItem extends React.Component {
|
|||
const className = `rules__item ${selected ? 'rules__item--selected' : ''}`;
|
||||
const favicon = this.props.rule.favicon
|
||||
? this.props.rule.favicon
|
||||
: '/static/picture.svg';
|
||||
: '/static/icons/picture.svg';
|
||||
|
||||
return (
|
||||
<li className={className} onClick={() => this.handleSelect()}>
|
||||
<div className="rule">
|
||||
<div className="rules__info">
|
||||
<span>
|
||||
<img className="icon" width="20" src={favicon} />
|
||||
</span>
|
||||
<h5 className="rule__title" title={this.props.rule.name}>
|
||||
<h5 className="rules__title" title={this.props.rule.name}>
|
||||
{this.props.rule.name}
|
||||
</h5>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -28,13 +28,11 @@ class Sidebar extends React.Component {
|
|||
|
||||
return (
|
||||
<div className="sidebar">
|
||||
<nav className="categories">
|
||||
{(this.props.categories.isFetching || this.props.rules.isFetching) && (
|
||||
<LoadingIndicator />
|
||||
)}
|
||||
|
||||
<ul>{items}</ul>
|
||||
</nav>
|
||||
<ul className="sidebar__nav">{items}</ul>
|
||||
|
||||
{!isEqual(this.props.selected.item, {}) && <ReadButton />}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,11 +6,12 @@ import configureStore from './configureStore.js';
|
|||
|
||||
import App from './App.js';
|
||||
|
||||
const content = document.getElementById('homepage--page');
|
||||
const store = configureStore();
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>,
|
||||
document.getElementsByClassName('content')[0]
|
||||
content
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import Card from '../../../components/Card.js';
|
|||
const RuleCard = props => {
|
||||
const { rule } = props;
|
||||
|
||||
const faviconUrl = rule.favicon ? rule.favicon : '/static/picture.svg';
|
||||
const stateIcon = rule.succeeded
|
||||
? '/static/checkmark-circle.svg'
|
||||
: '/static/warning.svg';
|
||||
const faviconUrl = rule.favicon ? rule.favicon : '/static/icons/picture.svg';
|
||||
const stateIcon = !rule.error
|
||||
? '/static/icons/checkmark-circle.svg'
|
||||
: '/static/icons/warning.svg';
|
||||
|
||||
const cardHeader = (
|
||||
<>
|
||||
|
|
@ -23,20 +23,21 @@ const RuleCard = props => {
|
|||
const cardContent = (
|
||||
<>
|
||||
<ul className="list rules">
|
||||
<li className="list__item rules__item">{rule.category}</li>
|
||||
<li className="list__item rules__item">
|
||||
<a className="link" target="_blank" rel="noopener noreferrer" href={rule.url}>
|
||||
{rule.url}
|
||||
</a>
|
||||
</li>
|
||||
<li className="list__item rules__item">{rule.created}</li>
|
||||
<li className="list__item rules__item">{rule.timezone}</li>
|
||||
</ul>
|
||||
{!rule.succeeded && (
|
||||
{rule.error && (
|
||||
<ul className="list errorlist">
|
||||
<li className="list__item errorlist__item">{rule.error}</li>
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{rule.category && <li className="list__item">{rule.category}</li>}
|
||||
<li className="list__item">
|
||||
<a className="link" target="_blank" rel="noopener noreferrer" href={rule.url}>
|
||||
{rule.url}
|
||||
</a>
|
||||
</li>
|
||||
<li className="list__item">{rule.created}</li>
|
||||
<li className="list__item">{rule.timezone}</li>
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,16 +4,17 @@ import Modal from '../../../components/Modal.js';
|
|||
|
||||
const RuleModal = props => {
|
||||
const content = (
|
||||
<div className="rule-modal">
|
||||
<div className="rule-modal__header">
|
||||
<h1 className="h1 rule-modal__title">Delete rule</h1>
|
||||
<>
|
||||
<div>
|
||||
<div className="modal__header">
|
||||
<h1 className="h1 modal__title">Delete rule</h1>
|
||||
</div>
|
||||
|
||||
<div className="rule-modal__content">
|
||||
<div className="modal__content">
|
||||
<p className="p">Are you sure you want to delete {props.rule.name}?</p>
|
||||
</div>
|
||||
|
||||
<div className="rule-modal__footer">
|
||||
<div className="modal__footer">
|
||||
<button className="button button--confirm" onClick={props.handleCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
|
|
@ -25,6 +26,7 @@ const RuleModal = props => {
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return <Modal content={content} />;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import ReactDOM from 'react-dom';
|
|||
|
||||
import App from './App.js';
|
||||
|
||||
const content = document.getElementById('rules--page');
|
||||
const dataScript = document.getElementById('rules-data');
|
||||
const rules = JSON.parse(dataScript.textContent);
|
||||
|
||||
ReactDOM.render(<App rules={rules} />, document.getElementsByClassName('content')[0]);
|
||||
ReactDOM.render(<App rules={rules} />, content);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
from django import forms
|
||||
|
||||
import pytz
|
||||
|
||||
from newsreader.news.collection.models import CollectionRule
|
||||
from newsreader.news.core.models import Category
|
||||
|
||||
|
||||
class CollectionRuleForm(forms.ModelForm):
|
||||
category = forms.ModelChoiceField(required=False, queryset=Category.objects.all())
|
||||
timezone = forms.ChoiceField(
|
||||
widget=forms.Select(attrs={"size": len(pytz.all_timezones)}),
|
||||
choices=((timezone, timezone) for timezone in pytz.all_timezones),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
self.user = kwargs.pop("user")
|
||||
|
|
|
|||
|
|
@ -2,12 +2,8 @@
|
|||
|
||||
{% load static i18n %}
|
||||
|
||||
{% block head %}
|
||||
<link href="{% static 'collection/dist/css/import.css' %}" rel="stylesheet" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content">
|
||||
<main id="import--page" class="main">
|
||||
<form class="form import-form" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
|
|
@ -37,5 +33,5 @@
|
|||
</fieldset>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -4,28 +4,6 @@
|
|||
<h1 class="h1 form__title">Create a rule</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block name-input %}
|
||||
<input class="input rule-form__input" type="text" name="name" required />
|
||||
{% endblock %}
|
||||
|
||||
{% block category-input %}
|
||||
<option class="option rule-form__option" value="{{ category.pk }}">
|
||||
{{ category.name }}
|
||||
</option>
|
||||
{% endblock %}
|
||||
|
||||
{% block url-input %}
|
||||
<input class="input rule-form__input" type="url" name="url" required />
|
||||
{% endblock %}
|
||||
|
||||
{% block favicon-input %}
|
||||
<input class="input rule-form__input" type="url" name="favicon" />
|
||||
{% endblock %}
|
||||
|
||||
{% block timezone-input %}
|
||||
<option class="option rule-form__option">{{ timezone }}</option>
|
||||
{% endblock %}
|
||||
|
||||
{% block confirm-button %}
|
||||
<button class="button button--confirm">Create rule</button>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -4,31 +4,6 @@
|
|||
<h1 class="h1 form__title">Update rule</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block name-input %}
|
||||
<input class="input rule-form__input" type="text" name="name"
|
||||
value="{{ rule.name }}" required />
|
||||
{% endblock %}
|
||||
|
||||
{% block category-input %}
|
||||
<option class="option rule-form__option" value="{{ category.pk }}" {% if rule.category and rule.category.pk == category.pk %}selected{% endif %}>
|
||||
{{ category.name }}
|
||||
</option>
|
||||
{% endblock %}
|
||||
|
||||
{% block url-input %}
|
||||
<input class="input rule-form__input" type="url" name="url" value="{{ rule.url }}" required />
|
||||
{% endblock %}
|
||||
|
||||
{% block favicon-input %}
|
||||
<input class="input rule-form__input" type="url" value="{{ rule.favicon|default:"" }}" name="favicon" />
|
||||
{% endblock %}
|
||||
|
||||
{% block timezone-input %}
|
||||
<option class="option rule-form__option" {% if rule.timezone == timezone %}selected{% endif %}>
|
||||
{{ timezone }}
|
||||
</option>
|
||||
{% endblock %}
|
||||
|
||||
{% block confirm-button %}
|
||||
<button class="button button--confirm">Save rule</button>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,8 @@
|
|||
|
||||
{% load static %}
|
||||
|
||||
{% block head %}
|
||||
<link href="{% static 'collection/dist/css/rule.css' %}" rel="stylesheet" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content">
|
||||
<main id="rule--page" class="main">
|
||||
<form class="form rule-form" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
|
|
@ -17,45 +13,34 @@
|
|||
</div>
|
||||
<section class="section form__section rule-form__section">
|
||||
<fieldset class="form__fieldset rule-form__fieldset">
|
||||
<label class="label rule-form__label" for="name">Name</label>
|
||||
{% block name-input %}{% endblock %}
|
||||
<label class="label" for="name">Name</label>
|
||||
{{ form.name.errors }}
|
||||
{{ form.name }}
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="form__fieldset rule-form__fieldset">
|
||||
<label class="label rule-form__label" for="name">Category</label>
|
||||
<select class="select rule-form__select" name="category">
|
||||
{% for category in categories %}
|
||||
{% block category-input %}{% endblock %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
|
||||
<label class="label" for="name">Category</label>
|
||||
{{ form.category.errors }}
|
||||
{{ form.category }}
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="form__fieldset rule-form__fieldset">
|
||||
<label class="label rule-form__label" for="name">Feed url</label>
|
||||
{% block url-input %}{% endblock %}
|
||||
<label class="label" for="name">Feed url</label>
|
||||
{{ form.url.errors }}
|
||||
{{ form.url }}
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="form__fieldset rule-form__fieldset">
|
||||
<label class="label rule-form__label" for="name">Favicon url</label>
|
||||
{% block favicon-input %}{% endblock %}
|
||||
<label class="label" for="name">Favicon url</label>
|
||||
{{ form.favicon.errors }}
|
||||
{{ form.favicon }}
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="form__fieldset rule-form__fieldset">
|
||||
<label class="label rule-form__label" for="name">Timezone</label>
|
||||
<label class="label" for="name">Timezone</label>
|
||||
<small class="small helptext">The timezone which the feed uses</small>
|
||||
<select class="select rule-form__select" size="{{ timezones|length }}" name="timezone">
|
||||
{% for timezone in timezones %}
|
||||
{% block timezone-input %}{% endblock %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
{{ form.timezone.errors }}
|
||||
{{ form.timezone }}
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
|
|
@ -66,5 +51,5 @@
|
|||
</fieldset>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,8 @@
|
|||
|
||||
{% load static %}
|
||||
|
||||
{% block head %}
|
||||
<link href="{% static 'collection/dist/css/rules.css' %}" rel="stylesheet" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content"></div>
|
||||
<main id="rules--page" class="main"></main>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
|
|
@ -19,15 +15,15 @@
|
|||
"pk": {{ rule.pk }},
|
||||
"name": "{{ rule.name }}",
|
||||
"url": "{{ rule.url }}",
|
||||
"favicon": {% if rule.favicon %}"{{ rule.favicon }}"{% else %}""{% endif %},
|
||||
"category": "{{ rule.category }}",
|
||||
"favicon": "{{ rule.favicon|default:'' }}",
|
||||
"category": "{{ rule.category|default:'' }}",
|
||||
"timezone": "{{ rule.timezone }}",
|
||||
"created": "{{ rule.created }}",
|
||||
"succeeded": {% if rule.succeeded %}true{% else %}false{% endif %},
|
||||
"error": {% if rule.error %}"{{ rule.error }}"{% else %}""{% endif %}
|
||||
"error": "{{ rule.error|default:'' }}"
|
||||
}
|
||||
{% endfor %}
|
||||
]
|
||||
</script>
|
||||
<script src="{% static 'collection/dist/js/rules.js' %}"></script>
|
||||
<script src="{% static 'js/rules.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ class NestedPostCategoryView(ListAPIView):
|
|||
|
||||
queryset = Post.objects.filter(
|
||||
rule__in=category.rules.values_list("id", flat=True)
|
||||
).order_by("rule__name", "-publication_date")
|
||||
).order_by("rule", "-publication_date")
|
||||
|
||||
return queryset
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from django import forms
|
||||
|
||||
from newsreader.accounts.models import User
|
||||
from newsreader.news.collection.models import CollectionRule
|
||||
from newsreader.news.core.models import Category
|
||||
|
||||
|
|
@ -11,17 +12,23 @@ class CategoryForm(forms.ModelForm):
|
|||
widget=forms.widgets.CheckboxSelectMultiple,
|
||||
)
|
||||
|
||||
user = forms.ModelChoiceField(
|
||||
queryset=User.objects.none(),
|
||||
widget=forms.widgets.HiddenInput(attrs={"readonly": True}),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user = kwargs.pop("user")
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
if self.user:
|
||||
self.fields["rules"].queryset = CollectionRule.objects.filter(user=self.user)
|
||||
self.fields["user"].queryset = User.objects.filter(pk=self.user.pk)
|
||||
|
||||
self.initial["user"] = self.user
|
||||
|
||||
def save(self, commit=True) -> Category:
|
||||
instance = super().save(commit=False)
|
||||
instance.user = self.user
|
||||
|
||||
if commit:
|
||||
instance.save()
|
||||
|
|
@ -34,4 +41,4 @@ class CategoryForm(forms.ModelForm):
|
|||
|
||||
class Meta:
|
||||
model = Category
|
||||
fields = ("name", "rules")
|
||||
fields = ("name", "rules", "user")
|
||||
|
|
|
|||
|
|
@ -2,12 +2,8 @@
|
|||
|
||||
{% load static %}
|
||||
|
||||
{% block head %}
|
||||
<link href="{% static 'core/dist/css/categories.css' %}" rel="stylesheet" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content"></div>
|
||||
<main id="categories--page" class="main"></main>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
|
|
@ -25,7 +21,7 @@
|
|||
{
|
||||
"pk": {{ rule.pk }},
|
||||
"name": "{{ rule.name }}",
|
||||
"favicon": {% if rule.favicon %}"{{ rule.favicon }}"{% else %}null{% endif %},
|
||||
"favicon": "{{ rule.favicon|default:'' }}",
|
||||
"created": "{{ rule.created }}"
|
||||
}
|
||||
{% endfor %}
|
||||
|
|
@ -34,5 +30,5 @@
|
|||
{% endfor %}
|
||||
]
|
||||
</script>
|
||||
<script src="{% static 'core/dist/js/categories.js' %}"></script>
|
||||
<script src="{% static 'js/categories.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,6 @@
|
|||
<h1 class="h1 form__title">Create a category</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block name-input %}
|
||||
<input class="input category-form__input" type="text" name="name" required />
|
||||
{% endblock %}
|
||||
|
||||
{% block rule-input %}
|
||||
<input class="input category-form__input" type="checkbox" name="rules"
|
||||
value="{{ rule.pk }}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block confirm-button %}
|
||||
<button class="button button--confirm">Create category</button>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -4,17 +4,6 @@
|
|||
<h1 class="h1 form__title">Update category</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block name-input %}
|
||||
<input class="input category-form__input" type="text" name="name"
|
||||
value="{{ category.name }}" required />
|
||||
{% endblock %}
|
||||
|
||||
{% block rule-input %}
|
||||
<input class="input category-form__input" type="checkbox" name="rules"
|
||||
{% if rule.pk in category.rule_ids %}checked{% endif %}
|
||||
value="{{ rule.pk }}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block confirm-button %}
|
||||
<button class="button button--confirm">Save category</button>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -2,24 +2,24 @@
|
|||
|
||||
{% load static %}
|
||||
|
||||
{% block head %}
|
||||
<link href="{% static 'core/dist/css/category.css' %}" rel="stylesheet" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content">
|
||||
<main id="category--page" class="main">
|
||||
<form class="form category-form" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
|
||||
<div class="form__header">
|
||||
{% block form-header %}{% endblock %}
|
||||
</div>
|
||||
|
||||
{{ form.non_field_errors }}
|
||||
{{ form.user.errors }}
|
||||
{{ form.user }}
|
||||
|
||||
<section class="section form__section category-form__section">
|
||||
<fieldset class="form__fieldset category-form__fieldset">
|
||||
<label class="label category-form__label" for="name">Name</label>
|
||||
{% block name-input %}{% endblock %}
|
||||
{{ form.name.errors }}
|
||||
{{ form.name }}
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
|
|
@ -29,17 +29,21 @@
|
|||
<small class="small help-text">
|
||||
Note that existing assigned rules will be reassigned to this category
|
||||
</small>
|
||||
{{ form.rules.errors }}
|
||||
|
||||
<ul class="list checkbox-list">
|
||||
{% for rule in rules %}
|
||||
<li class="list__item checkbox-list__item">
|
||||
{% block rule-input %}{% endblock %}
|
||||
<input class="input category-form__input" type="checkbox" name="rules"
|
||||
{% if category and rule.pk in category.rule_ids %}checked{% endif %}
|
||||
value="{{ rule.pk }}" />
|
||||
|
||||
<img class="favicon"
|
||||
src="{% if rule.favicon %}{{ rule.favicon }}{% else %}/static/picture.svg{% endif %}" />
|
||||
src="{% if rule.favicon %}{{ rule.favicon }}{% else %}/static/icons/picture.svg{% endif %}" />
|
||||
<span>{{ rule.name }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{{ form.rules.errors }}
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
|
|
@ -50,5 +54,5 @@
|
|||
</fieldset>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,10 @@
|
|||
|
||||
{% load static %}
|
||||
|
||||
{% block head %}
|
||||
<link href="{% static 'core/dist/css/homepage.css' %}" rel="stylesheet" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content"></div>
|
||||
<main id="homepage--page" class="main"></main>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{% static 'core/dist/js/homepage.js' %}"></script>
|
||||
<script src="{% static 'js/homepage.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -488,11 +488,11 @@ class NestedCategoryPostView(TestCase):
|
|||
self.assertEquals(posts[0]["title"], "Second BBC post")
|
||||
self.assertEquals(posts[1]["title"], "First BBC post")
|
||||
|
||||
self.assertEquals(posts[2]["title"], "Second Reuters post")
|
||||
self.assertEquals(posts[3]["title"], "First Reuters post")
|
||||
self.assertEquals(posts[2]["title"], "Second Guardian post")
|
||||
self.assertEquals(posts[3]["title"], "First Guardian post")
|
||||
|
||||
self.assertEquals(posts[4]["title"], "Second Guardian post")
|
||||
self.assertEquals(posts[5]["title"], "First Guardian post")
|
||||
self.assertEquals(posts[4]["title"], "Second Reuters post")
|
||||
self.assertEquals(posts[5]["title"], "First Reuters post")
|
||||
|
||||
def test_only_posts_from_category_are_returned(self):
|
||||
category = CategoryFactory.create(user=self.user)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,11 @@ class CategoryCreateViewTestCase(CategoryViewTestCase, TestCase):
|
|||
def test_creation(self):
|
||||
rules = CollectionRuleFactory.create_batch(size=4, user=self.user)
|
||||
|
||||
data = {"name": "new-category", "rules": [rule.pk for rule in rules]}
|
||||
data = {
|
||||
"name": "new-category",
|
||||
"rules": [rule.pk for rule in rules],
|
||||
"user": self.user.pk,
|
||||
}
|
||||
response = self.client.post(self.url, data)
|
||||
|
||||
self.assertEquals(response.status_code, 302)
|
||||
|
|
@ -55,13 +59,29 @@ class CategoryCreateViewTestCase(CategoryViewTestCase, TestCase):
|
|||
size=3, user=self.user, category=None
|
||||
)
|
||||
|
||||
data = {"name": "new-category", "rules": [rule.pk for rule in other_rules]}
|
||||
data = {
|
||||
"name": "new-category",
|
||||
"rules": [rule.pk for rule in other_rules],
|
||||
"user": self.user.pk,
|
||||
}
|
||||
|
||||
response = self.client.post(self.url, data)
|
||||
|
||||
self.assertContains(response, "not one of the available choices")
|
||||
self.assertEquals(Category.objects.count(), 0)
|
||||
|
||||
def test_unique_together(self):
|
||||
category = CategoryFactory(name="category", user=self.user)
|
||||
|
||||
data = {"name": "category", "user": self.user.pk, "rules": []}
|
||||
response = self.client.post(self.url, data)
|
||||
|
||||
categories = Category.objects.all()
|
||||
|
||||
self.assertContains(response, "already exists")
|
||||
|
||||
self.assertCountEqual(categories, [category])
|
||||
|
||||
|
||||
class CategoryUpdateViewTestCase(CategoryViewTestCase, TestCase):
|
||||
def setUp(self):
|
||||
|
|
@ -71,7 +91,7 @@ class CategoryUpdateViewTestCase(CategoryViewTestCase, TestCase):
|
|||
self.url = reverse("category-update", args=[self.category.pk])
|
||||
|
||||
def test_name_change(self):
|
||||
data = {"name": "durp"}
|
||||
data = {"name": "durp", "user": self.user.pk}
|
||||
self.client.post(self.url, data)
|
||||
|
||||
self.category.refresh_from_db()
|
||||
|
|
@ -80,7 +100,11 @@ class CategoryUpdateViewTestCase(CategoryViewTestCase, TestCase):
|
|||
|
||||
def test_add_collection_rules(self):
|
||||
rules = CollectionRuleFactory.create_batch(size=4, user=self.user)
|
||||
data = {"name": self.category.name, "rules": [rule.pk for rule in rules]}
|
||||
data = {
|
||||
"name": self.category.name,
|
||||
"rules": [rule.pk for rule in rules],
|
||||
"user": self.user.pk,
|
||||
}
|
||||
|
||||
self.client.post(self.url, data)
|
||||
|
||||
|
|
@ -100,7 +124,11 @@ class CategoryUpdateViewTestCase(CategoryViewTestCase, TestCase):
|
|||
|
||||
self.assertCountEqual(self.category.rule_ids, [rule.pk for rule in current_rules])
|
||||
|
||||
data = {"name": self.category.name, "rules": [rule.pk for rule in other_rules]}
|
||||
data = {
|
||||
"name": self.category.name,
|
||||
"rules": [rule.pk for rule in other_rules],
|
||||
"user": self.user.pk,
|
||||
}
|
||||
|
||||
self.client.post(self.url, data)
|
||||
|
||||
|
|
@ -116,7 +144,7 @@ class CategoryUpdateViewTestCase(CategoryViewTestCase, TestCase):
|
|||
|
||||
self.assertCountEqual(self.category.rule_ids, [rule.pk for rule in current_rules])
|
||||
|
||||
data = {"name": "durp"}
|
||||
data = {"name": "durp", "user": self.user.pk}
|
||||
self.client.post(self.url, data)
|
||||
|
||||
self.category.refresh_from_db()
|
||||
|
|
@ -139,7 +167,7 @@ class CategoryUpdateViewTestCase(CategoryViewTestCase, TestCase):
|
|||
other_category = CategoryFactory(name="other category", user=other_user)
|
||||
other_category.rules.set([*other_rules])
|
||||
|
||||
data = {"name": "durp"}
|
||||
data = {"name": "durp", "user": other_user.pk}
|
||||
other_url = reverse("category-update", args=[other_category.pk])
|
||||
response = self.client.post(other_url, data)
|
||||
|
||||
|
|
@ -161,7 +189,11 @@ class CategoryUpdateViewTestCase(CategoryViewTestCase, TestCase):
|
|||
|
||||
self.assertCountEqual(self.category.rule_ids, [rule.pk for rule in current_rules])
|
||||
|
||||
data = {"name": self.category.name, "rules": [rule.pk for rule in other_rules]}
|
||||
data = {
|
||||
"name": self.category.name,
|
||||
"rules": [rule.pk for rule in other_rules],
|
||||
"user": self.user.pk,
|
||||
}
|
||||
|
||||
response = self.client.post(self.url, data)
|
||||
|
||||
|
|
@ -172,3 +204,16 @@ class CategoryUpdateViewTestCase(CategoryViewTestCase, TestCase):
|
|||
|
||||
other_category.refresh_from_db()
|
||||
self.assertCountEqual(other_category.rule_ids, [rule.pk for rule in other_rules])
|
||||
|
||||
def test_unique_together(self):
|
||||
other_category = CategoryFactory(name="other category", user=self.user)
|
||||
|
||||
url = reverse("category-update", args=[other_category.pk])
|
||||
data = {"name": "category", "user": self.user.pk, "rules": []}
|
||||
response = self.client.post(url, data)
|
||||
|
||||
categories = Category.objects.all()
|
||||
|
||||
self.assertContains(response, "already exists")
|
||||
|
||||
self.assertCountEqual(categories, [self.category, other_category])
|
||||
|
|
|
|||
|
|
@ -53,9 +53,7 @@ class CategoryDetailMixin:
|
|||
return context_data
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs["user"] = self.request.user
|
||||
return kwargs
|
||||
return {**super().get_form_kwargs(), "user": self.request.user}
|
||||
|
||||
|
||||
class CategoryListView(CategoryViewMixin, ListView):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
.card {
|
||||
@extend .card;
|
||||
|
||||
&__header {
|
||||
& div {
|
||||
display: flex;
|
||||
|
|
@ -1 +1,2 @@
|
|||
@import "card";
|
||||
@import "rule-card";
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
@import "content";
|
||||
|
|
@ -1,20 +1,23 @@
|
|||
.errorlist {
|
||||
@extend .list;
|
||||
|
||||
padding: 10px;
|
||||
margin: 5px 0;
|
||||
padding: 0;
|
||||
|
||||
background-color: $error-red;
|
||||
color: $white;
|
||||
|
||||
list-style: disc;
|
||||
list-style-position: inside;
|
||||
|
||||
& li {
|
||||
margin: 15px 0 0 0;
|
||||
&__item {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
|
||||
&:first-child {
|
||||
margin: 0;
|
||||
background-color: $error-red;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
& li {
|
||||
@extend .errorlist__item;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
.category-form {
|
||||
@extend .form;
|
||||
|
||||
margin: 20px 0;
|
||||
|
||||
&__section:last-child {
|
||||
|
||||
& .category-form__fieldset {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
margin: 20px 0;
|
||||
|
||||
&__section:last-child {
|
||||
|
||||
& .rule-form__fieldset {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
|
@ -10,18 +9,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__select[name=category] {
|
||||
#id_category {
|
||||
width: 50%;
|
||||
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
&__select[name=timezone] {
|
||||
#id_timezone {
|
||||
max-height: 200px;
|
||||
width: 50%;
|
||||
|
||||
margin: 0 15px;
|
||||
padding: 0 10px;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1,12 @@
|
|||
@import "form";
|
||||
|
||||
@import "category-form";
|
||||
@import "rule-form";
|
||||
@import "import-form";
|
||||
|
||||
@import "login-form";
|
||||
@import "activation-form";
|
||||
@import "register-form";
|
||||
|
||||
@import "password-reset-form";
|
||||
@import "password-reset-confirm-form";
|
||||
|
|
|
|||
|
|
@ -1,13 +1,26 @@
|
|||
@import "./body/index";
|
||||
@import "./form/index";
|
||||
@import "./main/index";
|
||||
@import "./navbar/index";
|
||||
@import "./loading-indicator/index";
|
||||
@import "./modal/index";
|
||||
@import "./card/index";
|
||||
@import "./list/index";
|
||||
@import "./content/index";
|
||||
@import "./messages/index";
|
||||
@import "./section/index";
|
||||
@import "./errorlist/index";
|
||||
@import "./fieldset/index";
|
||||
@import "body/index";
|
||||
@import "form/index";
|
||||
@import "main/index";
|
||||
@import "navbar/index";
|
||||
@import "loading-indicator/index";
|
||||
|
||||
@import "modal/index";
|
||||
|
||||
@import "card/index";
|
||||
@import "list/index";
|
||||
@import "messages/index";
|
||||
@import "section/index";
|
||||
@import "errorlist/index";
|
||||
@import "fieldset/index";
|
||||
@import "sidebar/index";
|
||||
|
||||
@import "rules/index";
|
||||
@import "category/index";
|
||||
|
||||
@import "post/index";
|
||||
@import "post-block/index";
|
||||
@import "post-message/index";
|
||||
@import "posts/index";
|
||||
@import "posts-header/index";
|
||||
@import "posts-info/index";
|
||||
@import "posts-section/index";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
.main {
|
||||
margin: 1% 10% 5% 10%;
|
||||
background-color: $white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,32 @@
|
|||
.messages {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
|
||||
margin: 5px 0 20px 0;
|
||||
padding: 15px 0;
|
||||
|
||||
color: $white;
|
||||
background-color: $error-red;
|
||||
|
||||
&__item {
|
||||
padding: 0 30px;
|
||||
width: 80%;
|
||||
|
||||
padding: 20px 15px;
|
||||
margin: 5px 0;
|
||||
|
||||
border-radius: 5px;
|
||||
|
||||
background-color: $focus-blue;
|
||||
|
||||
&--error {
|
||||
background-color: $error-red;
|
||||
}
|
||||
|
||||
&--warning {
|
||||
background-color: $light-orange;
|
||||
}
|
||||
|
||||
&--success {
|
||||
background-color: $success-green;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,42 @@
|
|||
.modal {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
position: fixed;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
|
||||
background-color: $dark;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: center;
|
||||
|
||||
margin: 20px 0;
|
||||
padding: 20px;
|
||||
|
||||
width: 60%;
|
||||
|
||||
border-radius: 5px;
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
&__header {
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding: 10px 30px;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
9
src/newsreader/scss/components/modal/_post-modal.scss
Normal file
9
src/newsreader/scss/components/modal/_post-modal.scss
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
.post-modal {
|
||||
@extend .modal;
|
||||
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
border-radius: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
@ -1 +1,3 @@
|
|||
@import "modal";
|
||||
|
||||
@import "post-modal";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
.post-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
width: 70%;
|
||||
margin: 0 0 2% 0;
|
||||
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
background-color: $white;
|
||||
|
||||
cursor: initial;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -58,7 +60,11 @@
|
|||
font-size: 18px;
|
||||
|
||||
& p {
|
||||
padding: 20px 0 0 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
& h1, h2, h3 {
|
||||
margin: 20px 0 5px 0;
|
||||
}
|
||||
|
||||
& img {
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
.posts-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 95%;
|
||||
|
||||
margin: 20px;
|
||||
padding: 10px;
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
.rules {
|
||||
padding: 0;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
|
@ -25,4 +27,20 @@
|
|||
background-color: darken($azureish-white, +10%);
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
&__title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
& .badge {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
26
src/newsreader/scss/components/sidebar/_sidebar.scss
Normal file
26
src/newsreader/scss/components/sidebar/_sidebar.scss
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
.sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
align-self: start;
|
||||
|
||||
position: sticky;
|
||||
top: 5%;
|
||||
|
||||
width: 20%;
|
||||
|
||||
&__nav {
|
||||
width: 100%;
|
||||
max-height: 80vh;
|
||||
overflow: auto;
|
||||
|
||||
list-style: none;
|
||||
border-radius: 5px;
|
||||
|
||||
font-family: $sidebar-font;
|
||||
|
||||
&__item {
|
||||
padding: 2px 10px 5px 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
.read-button {
|
||||
@extend .button;
|
||||
|
||||
margin: 20px 0 0 0;
|
||||
|
||||
color: $white;
|
||||
|
|
@ -1 +1,2 @@
|
|||
@import "button";
|
||||
@import "_read-button";
|
||||
|
|
|
|||
|
|
@ -7,3 +7,4 @@
|
|||
@import "input/index";
|
||||
@import "label/index";
|
||||
@import "help-text/index";
|
||||
@import "badge/index";
|
||||
|
|
|
|||
5
src/newsreader/scss/index.scss
Normal file
5
src/newsreader/scss/index.scss
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
@import "partials/index";
|
||||
@import "components/index";
|
||||
@import "elements/index";
|
||||
|
||||
@import "pages/index";
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
// General imports
|
||||
@import "../../partials/variables";
|
||||
@import "../../components/index";
|
||||
@import "../../elements/index";
|
||||
|
||||
// Page specific
|
||||
@import "./components/index";
|
||||
@import "./elements/index";
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
.card {
|
||||
&__footer > *:last-child {
|
||||
margin: 0 0 0 10px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
@import "card";
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
.category-modal {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: center;
|
||||
|
||||
margin: 20px 0;
|
||||
width: 50%;
|
||||
|
||||
border-radius: 2px;
|
||||
background-color: $white;
|
||||
|
||||
&__header {
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding: 10px 30px;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
@import "category-modal";
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
@import "card/index";
|
||||
@import "category-modal/index";
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
// General imports
|
||||
@import "../../partials/variables";
|
||||
@import "../../components/index";
|
||||
@import "../../elements/index";
|
||||
|
||||
// Page specific
|
||||
@import "./components/index";
|
||||
@import "./elements/index";
|
||||
#categories--page {
|
||||
& .card {
|
||||
&__footer > *:last-child {
|
||||
margin: 0 0 0 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
@import "category-form";
|
||||
|
|
@ -1 +0,0 @@
|
|||
@import "category-form/index";
|
||||
|
|
@ -1,8 +1,2 @@
|
|||
// General imports
|
||||
@import "../../partials/variables";
|
||||
@import "../../components/index";
|
||||
@import "../../elements/index";
|
||||
|
||||
// Page specific
|
||||
@import "./components/index";
|
||||
@import "./elements/index";
|
||||
#category--page {
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue