DEV: Update CI workflows, fix linting issues (#32)
Included: * DEV: Update CI workflows * DEV: Update the plugin URL * DEV: Update Gemfile.lock * DEV: Remove an old rubocop artifact * DEV: Add .prettierrc * DEV: Clean up .gitignore * DEV: Add license * DEV: Update package.json * DEV: Fix template issues * DEV: Re-format code * DEV: Use `@action` * DEV: `inject as controller` * DEV: Use `@computed` * DEV: Avoid naming collision and extra indirection * DEV: Use `discourse-common/utils/decorators` * DEV: Use `role="button"` Co-authored-by: Jarek Radosz <jradosz@gmail.com> Co-authored-by: jjaffeux <j.jaffeux@gmail.com>
This commit is contained in:
parent
50f7c25de2
commit
f75c9d5e32
|
@ -20,18 +20,13 @@ jobs:
|
||||||
node-version: 12
|
node-version: 12
|
||||||
|
|
||||||
- name: Set up ruby
|
- name: Set up ruby
|
||||||
uses: actions/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 2.7
|
ruby-version: 2.7
|
||||||
|
bundler-cache: true
|
||||||
- name: Setup bundler
|
|
||||||
run: gem install bundler -v 2.1.4 --no-doc
|
|
||||||
|
|
||||||
- name: Setup gems
|
|
||||||
run: bundle install --jobs 4
|
|
||||||
|
|
||||||
- name: Yarn install
|
- name: Yarn install
|
||||||
run: yarn install --dev
|
run: yarn install
|
||||||
|
|
||||||
- name: ESLint
|
- name: ESLint
|
||||||
run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern {test,assets}/javascripts
|
run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern {test,assets}/javascripts
|
||||||
|
@ -46,5 +41,8 @@ jobs:
|
||||||
yarn prettier --list-different "test/**/*.{js,es6}" ; \
|
yarn prettier --list-different "test/**/*.{js,es6}" ; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Ember template lint
|
||||||
|
run: yarn ember-template-lint assets/javascripts
|
||||||
|
|
||||||
- name: Rubocop
|
- name: Rubocop
|
||||||
run: bundle exec rubocop .
|
run: bundle exec rubocop .
|
||||||
|
|
|
@ -10,14 +10,15 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: ${{ matrix.build_type }}
|
name: ${{ matrix.build_type }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ubuntu-latest
|
||||||
|
container: discourse/discourse_test:release
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DISCOURSE_HOSTNAME: www.example.com
|
DISCOURSE_HOSTNAME: www.example.com
|
||||||
RUBY_GLOBAL_METHOD_CACHE_SIZE: 131072
|
RUBY_GLOBAL_METHOD_CACHE_SIZE: 131072
|
||||||
RAILS_ENV: test
|
RAILS_ENV: test
|
||||||
PGHOST: localhost
|
PGHOST: postgres
|
||||||
PGUSER: discourse
|
PGUSER: discourse
|
||||||
PGPASSWORD: discourse
|
PGPASSWORD: discourse
|
||||||
|
|
||||||
|
@ -26,8 +27,7 @@ jobs:
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
build_type: ["backend", "frontend"]
|
build_type: ["backend", "frontend"]
|
||||||
os: [ubuntu-latest]
|
ruby: ["2.7"]
|
||||||
ruby: ["2.6"]
|
|
||||||
postgres: ["12"]
|
postgres: ["12"]
|
||||||
redis: ["4.x"]
|
redis: ["4.x"]
|
||||||
|
|
||||||
|
@ -47,13 +47,13 @@ jobs:
|
||||||
--health-retries 5
|
--health-retries 5
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
repository: discourse/discourse
|
repository: discourse/discourse
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
- name: Install plugin
|
- name: Install plugin
|
||||||
uses: actions/checkout@master
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: plugins/${{ github.event.repository.name }}
|
path: plugins/${{ github.event.repository.name }}
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
@ -75,46 +75,30 @@ jobs:
|
||||||
git config --global user.email "ci@ci.invalid"
|
git config --global user.email "ci@ci.invalid"
|
||||||
git config --global user.name "Discourse CI"
|
git config --global user.name "Discourse CI"
|
||||||
|
|
||||||
- name: Setup packages
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get -yqq install postgresql-client libpq-dev gifsicle jpegoptim optipng jhead
|
|
||||||
wget -qO- https://raw.githubusercontent.com/discourse/discourse_docker/master/image/base/install-pngquant | sudo sh
|
|
||||||
|
|
||||||
- name: Update imagemagick
|
|
||||||
if: matrix.build_type == 'backend'
|
|
||||||
run: |
|
|
||||||
wget https://raw.githubusercontent.com/discourse/discourse_docker/master/image/base/install-imagemagick
|
|
||||||
chmod +x install-imagemagick
|
|
||||||
sudo ./install-imagemagick
|
|
||||||
|
|
||||||
- name: Setup redis
|
- name: Setup redis
|
||||||
uses: shogo82148/actions-setup-redis@v1
|
uses: shogo82148/actions-setup-redis@v1
|
||||||
with:
|
with:
|
||||||
redis-version: ${{ matrix.redis }}
|
redis-version: ${{ matrix.redis }}
|
||||||
|
|
||||||
- name: Setup ruby
|
|
||||||
uses: actions/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
ruby-version: ${{ matrix.ruby }}
|
|
||||||
|
|
||||||
- name: Setup bundler
|
|
||||||
run: |
|
|
||||||
gem install bundler -v 2.1.4 --no-doc
|
|
||||||
bundle config deployment 'true'
|
|
||||||
bundle config without 'development'
|
|
||||||
|
|
||||||
- name: Bundler cache
|
- name: Bundler cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: bundler-cache
|
|
||||||
with:
|
with:
|
||||||
path: vendor/bundle
|
path: vendor/bundle
|
||||||
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
key: ${{ runner.os }}-${{ matrix.ruby }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-gem-
|
${{ runner.os }}-${{ matrix.ruby }}-gem-
|
||||||
|
|
||||||
- name: Setup gems
|
- name: Setup gems
|
||||||
run: bundle install --jobs 4
|
run: |
|
||||||
|
bundle config --local path vendor/bundle
|
||||||
|
bundle config --local deployment true
|
||||||
|
bundle config --local without development
|
||||||
|
bundle install --jobs 4
|
||||||
|
bundle clean
|
||||||
|
|
||||||
|
- name: Lint English locale
|
||||||
|
if: matrix.build_type == 'backend'
|
||||||
|
run: bundle exec ruby script/i18n_lint.rb "plugins/${{ github.event.repository.name }}/locales/{client,server}.en.yml"
|
||||||
|
|
||||||
- name: Get yarn cache directory
|
- name: Get yarn cache directory
|
||||||
id: yarn-cache-dir
|
id: yarn-cache-dir
|
||||||
|
@ -130,7 +114,7 @@ jobs:
|
||||||
${{ runner.os }}-${{ matrix.os }}-yarn-
|
${{ runner.os }}-${{ matrix.os }}-yarn-
|
||||||
|
|
||||||
- name: Yarn install
|
- name: Yarn install
|
||||||
run: yarn install --dev
|
run: yarn install
|
||||||
|
|
||||||
- name: Migrate database
|
- name: Migrate database
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -1,4 +1 @@
|
||||||
.rubocop-https---raw-githubusercontent-com-discourse-discourse-master--rubocop-yml
|
|
||||||
node_modules
|
node_modules
|
||||||
yarn-error.log
|
|
||||||
.rubocop-https---raw-githubusercontent-com-discourse-*
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -1,2 +0,0 @@
|
||||||
inherit_gem:
|
|
||||||
rubocop-discourse: default.yml
|
|
39
Gemfile.lock
39
Gemfile.lock
|
@ -1,31 +1,32 @@
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
ast (2.4.1)
|
ast (2.4.2)
|
||||||
parallel (1.19.2)
|
parallel (1.20.1)
|
||||||
parser (2.7.1.5)
|
parser (3.0.1.0)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
rainbow (3.0.0)
|
rainbow (3.0.0)
|
||||||
regexp_parser (1.8.1)
|
regexp_parser (2.1.1)
|
||||||
rexml (3.2.4)
|
rexml (3.2.5)
|
||||||
rubocop (0.92.0)
|
rubocop (1.13.0)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 2.7.1.5)
|
parser (>= 3.0.0.0)
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
regexp_parser (>= 1.7)
|
regexp_parser (>= 1.8, < 3.0)
|
||||||
rexml
|
rexml
|
||||||
rubocop-ast (>= 0.5.0)
|
rubocop-ast (>= 1.2.0, < 2.0)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 1.4.0, < 2.0)
|
unicode-display_width (>= 1.4.0, < 3.0)
|
||||||
rubocop-ast (0.7.1)
|
rubocop-ast (1.4.1)
|
||||||
parser (>= 2.7.1.5)
|
parser (>= 2.7.1.5)
|
||||||
rubocop-discourse (2.3.2)
|
rubocop-discourse (2.4.1)
|
||||||
rubocop (>= 0.69.0)
|
rubocop (>= 1.1.0)
|
||||||
rubocop-rspec (>= 1.39.0)
|
rubocop-rspec (>= 2.0.0)
|
||||||
rubocop-rspec (1.43.2)
|
rubocop-rspec (2.2.0)
|
||||||
rubocop (~> 0.87)
|
rubocop (~> 1.0)
|
||||||
ruby-progressbar (1.10.1)
|
rubocop-ast (>= 1.1.0)
|
||||||
unicode-display_width (1.7.0)
|
ruby-progressbar (1.11.0)
|
||||||
|
unicode-display_width (2.0.0)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
@ -34,4 +35,4 @@ DEPENDENCIES
|
||||||
rubocop-discourse
|
rubocop-discourse
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.1.4
|
2.2.16
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2021 Civilized Discourse Construction Kit
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
|
@ -3,14 +3,9 @@ import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
tagName: "",
|
tagName: "",
|
||||||
|
|
||||||
@discourseComputed("category")
|
@discourseComputed("category")
|
||||||
categoryName(category) {
|
categoryName(category) {
|
||||||
return this.site.categories.findBy("id", category.id).name;
|
return this.site.categories.findBy("id", category.id).name;
|
||||||
},
|
},
|
||||||
actions: {
|
|
||||||
selectCategory() {
|
|
||||||
this.selectCategory(this.category);
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { debounce } from "@ember/runloop";
|
import { debounce } from "@ember/runloop";
|
||||||
|
import { action } from "@ember/object";
|
||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
|
@ -12,14 +13,14 @@ export default Component.extend({
|
||||||
debounceFunc(this, "onSearch", term, 500);
|
debounceFunc(this, "onSearch", term, 500);
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
@action
|
||||||
onSearchTermChange(term) {
|
onSearchTermChange(term) {
|
||||||
this.debouncedSearch(term);
|
this.debouncedSearch(term);
|
||||||
},
|
},
|
||||||
|
|
||||||
clearSearch() {
|
@action
|
||||||
this.set("searchTerm", "");
|
clearSearch() {
|
||||||
this.onSearch("");
|
this.set("searchTerm", "");
|
||||||
},
|
this.onSearch("");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
tagName: "",
|
tagName: "",
|
||||||
actions: {
|
|
||||||
selectTag() {
|
|
||||||
this.selectTag(this.tag);
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
import { action } from "@ember/object";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
classNames: "docs-topic-list",
|
classNames: "docs-topic-list",
|
||||||
|
|
||||||
@discourseComputed("order")
|
@discourseComputed("order")
|
||||||
sortTitle(order) {
|
sortTitle(order) {
|
||||||
return order === "title";
|
return order === "title";
|
||||||
|
@ -22,14 +24,15 @@ export default Component.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
@action
|
||||||
sortListActivity() {
|
sortListActivity() {
|
||||||
this.sortBy("activity");
|
this.sortBy("activity");
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
sortListTitle() {
|
|
||||||
this.sortBy("title");
|
@action
|
||||||
return false;
|
sortListTitle() {
|
||||||
},
|
this.sortBy("title");
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { reads } from "@ember/object/computed";
|
import { reads } from "@ember/object/computed";
|
||||||
import { computed } from "@ember/object";
|
import computed from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
classNames: "docs-topic",
|
classNames: "docs-topic",
|
||||||
|
|
||||||
originalPostContent: reads("post.cooked"),
|
originalPostContent: reads("post.cooked"),
|
||||||
|
|
||||||
post: computed("topic", function () {
|
@computed("topic")
|
||||||
|
post() {
|
||||||
return this.store.createRecord(
|
return this.store.createRecord(
|
||||||
"post",
|
"post",
|
||||||
this.topic.post_stream.posts.firstObject
|
this.topic.post_stream.posts.firstObject
|
||||||
);
|
);
|
||||||
}),
|
},
|
||||||
|
|
||||||
model: computed("post", "topic", function () {
|
@computed("post", "topic")
|
||||||
|
model() {
|
||||||
const post = this.post;
|
const post = this.post;
|
||||||
|
|
||||||
if (!post.topic) {
|
if (!post.topic) {
|
||||||
|
@ -22,7 +24,7 @@ export default Component.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
return post;
|
return post;
|
||||||
}),
|
},
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import Controller, { inject } from "@ember/controller";
|
import Controller, { inject as controller } from "@ember/controller";
|
||||||
import discourseComputed, { on } from "discourse-common/utils/decorators";
|
import discourseComputed, { on } from "discourse-common/utils/decorators";
|
||||||
|
import { action } from "@ember/object";
|
||||||
import { alias, equal, readOnly } from "@ember/object/computed";
|
import { alias, equal, readOnly } from "@ember/object/computed";
|
||||||
import Docs from "discourse/plugins/discourse-docs/discourse/models/docs";
|
import Docs from "discourse/plugins/discourse-docs/discourse/models/docs";
|
||||||
import { getOwner } from "@ember/application";
|
import { getOwner } from "@ember/application";
|
||||||
|
@ -14,14 +15,12 @@ export default Controller.extend({
|
||||||
searchTerm: "search",
|
searchTerm: "search",
|
||||||
selectedTopic: "topic",
|
selectedTopic: "topic",
|
||||||
},
|
},
|
||||||
application: inject(),
|
|
||||||
|
application: controller(),
|
||||||
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isLoadingMore: false,
|
isLoadingMore: false,
|
||||||
loadMoreUrl: alias("model.topics.load_more_url"),
|
|
||||||
isTopicLoading: false,
|
isTopicLoading: false,
|
||||||
categories: readOnly("model.categories"),
|
|
||||||
topics: alias("model.topics.topic_list.topics"),
|
|
||||||
tags: readOnly("model.tags"),
|
|
||||||
filterTags: null,
|
filterTags: null,
|
||||||
filterCategories: null,
|
filterCategories: null,
|
||||||
filterSolved: false,
|
filterSolved: false,
|
||||||
|
@ -31,7 +30,13 @@ export default Controller.extend({
|
||||||
expandedFilters: false,
|
expandedFilters: false,
|
||||||
ascending: null,
|
ascending: null,
|
||||||
orderColumn: null,
|
orderColumn: null,
|
||||||
|
|
||||||
|
loadMoreUrl: alias("model.topics.load_more_url"),
|
||||||
|
categories: readOnly("model.categories"),
|
||||||
|
topics: alias("model.topics.topic_list.topics"),
|
||||||
|
tags: readOnly("model.tags"),
|
||||||
topicCount: alias("model.topic_count"),
|
topicCount: alias("model.topic_count"),
|
||||||
|
emptyResults: equal("topicCount", 0),
|
||||||
|
|
||||||
@on("init")
|
@on("init")
|
||||||
_setupFilters() {
|
_setupFilters() {
|
||||||
|
@ -61,8 +66,6 @@ export default Controller.extend({
|
||||||
return isSearching || filterSolved;
|
return isSearching || filterSolved;
|
||||||
},
|
},
|
||||||
|
|
||||||
emptyResults: equal("topicCount", 0),
|
|
||||||
|
|
||||||
@discourseComputed
|
@discourseComputed
|
||||||
canFilterSolved() {
|
canFilterSolved() {
|
||||||
return (
|
return (
|
||||||
|
@ -76,108 +79,117 @@ export default Controller.extend({
|
||||||
return !!filterTags;
|
return !!filterTags;
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
@action
|
||||||
setSelectedTopic(topicId) {
|
setSelectedTopic(topicId) {
|
||||||
this.set("selectedTopic", topicId);
|
this.set("selectedTopic", topicId);
|
||||||
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
onChangeFilterSolved(solvedFilter) {
|
@action
|
||||||
this.set("filterSolved", solvedFilter);
|
onChangeFilterSolved(solvedFilter) {
|
||||||
},
|
this.set("filterSolved", solvedFilter);
|
||||||
|
},
|
||||||
|
|
||||||
updateSelectedTags(tag) {
|
@action
|
||||||
let filter = this.filterTags;
|
updateSelectedTags(tag) {
|
||||||
if (filter && filter.includes(tag.id)) {
|
let filter = this.filterTags;
|
||||||
filter = filter.replace(tag.id, "").replace(/^\|+|\|+$/g, "");
|
if (filter && filter.includes(tag.id)) {
|
||||||
} else if (filter) {
|
filter = filter.replace(tag.id, "").replace(/^\|+|\|+$/g, "");
|
||||||
filter = `${filter}|${tag.id}`;
|
} else if (filter) {
|
||||||
} else {
|
filter = `${filter}|${tag.id}`;
|
||||||
filter = tag.id;
|
} else {
|
||||||
}
|
filter = tag.id;
|
||||||
|
}
|
||||||
|
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
filterTags: filter,
|
filterTags: filter,
|
||||||
selectedTopic: null,
|
selectedTopic: null,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
updateSelectedCategories(category) {
|
@action
|
||||||
let filter = this.filterCategories;
|
updateSelectedCategories(category) {
|
||||||
if (filter && filter.includes(category.id)) {
|
let filter = this.filterCategories;
|
||||||
filter = filter.replace(category.id, "").replace(/^\|+|\|+$/g, "");
|
if (filter && filter.includes(category.id)) {
|
||||||
} else if (filter) {
|
filter = filter.replace(category.id, "").replace(/^\|+|\|+$/g, "");
|
||||||
filter = `${filter}|${category.id}`;
|
} else if (filter) {
|
||||||
} else {
|
filter = `${filter}|${category.id}`;
|
||||||
filter = category.id;
|
} else {
|
||||||
}
|
filter = category.id;
|
||||||
|
}
|
||||||
|
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
filterCategories: filter,
|
filterCategories: filter,
|
||||||
selectedTopic: null,
|
selectedTopic: null,
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
performSearch(term) {
|
return false;
|
||||||
if (term === "") {
|
},
|
||||||
this.set("searchTerm", null);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (term.length < this.siteSettings.min_search_term_length) {
|
@action
|
||||||
return false;
|
performSearch(term) {
|
||||||
}
|
if (term === "") {
|
||||||
|
this.set("searchTerm", null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
this.setProperties({
|
if (term.length < this.siteSettings.min_search_term_length) {
|
||||||
searchTerm: term,
|
return false;
|
||||||
selectedTopic: null,
|
}
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
sortBy(column) {
|
this.setProperties({
|
||||||
const order = this.orderColumn;
|
searchTerm: term,
|
||||||
const ascending = this.ascending;
|
selectedTopic: null,
|
||||||
if (column === "title") {
|
});
|
||||||
this.set("orderColumn", "title");
|
},
|
||||||
} else if (column === "activity") {
|
|
||||||
this.set("orderColumn", "activity");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ascending && order) {
|
@action
|
||||||
this.set("ascending", true);
|
sortBy(column) {
|
||||||
} else {
|
const order = this.orderColumn;
|
||||||
this.set("ascending", "");
|
const ascending = this.ascending;
|
||||||
}
|
if (column === "title") {
|
||||||
},
|
this.set("orderColumn", "title");
|
||||||
|
} else if (column === "activity") {
|
||||||
|
this.set("orderColumn", "activity");
|
||||||
|
}
|
||||||
|
|
||||||
loadMore() {
|
if (!ascending && order) {
|
||||||
if (this.canLoadMore && !this.isLoadingMore) {
|
this.set("ascending", true);
|
||||||
this.set("isLoadingMore", true);
|
} else {
|
||||||
|
this.set("ascending", "");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
Docs.loadMore(this.loadMoreUrl).then((result) => {
|
@action
|
||||||
const topics = this.topics.concat(result.topics.topic_list.topics);
|
loadMore() {
|
||||||
|
if (this.canLoadMore && !this.isLoadingMore) {
|
||||||
|
this.set("isLoadingMore", true);
|
||||||
|
|
||||||
this.setProperties({
|
Docs.loadMore(this.loadMoreUrl).then((result) => {
|
||||||
topics,
|
const topics = this.topics.concat(result.topics.topic_list.topics);
|
||||||
loadMoreUrl: result.topics.load_more_url || null,
|
|
||||||
isLoadingMore: false,
|
this.setProperties({
|
||||||
});
|
topics,
|
||||||
|
loadMoreUrl: result.topics.load_more_url || null,
|
||||||
|
isLoadingMore: false,
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
},
|
}
|
||||||
|
},
|
||||||
|
|
||||||
toggleFilters() {
|
@action
|
||||||
if (!this.expandedFilters) {
|
toggleFilters() {
|
||||||
this.set("expandedFilters", true);
|
if (!this.expandedFilters) {
|
||||||
} else {
|
this.set("expandedFilters", true);
|
||||||
this.set("expandedFilters", false);
|
} else {
|
||||||
}
|
this.set("expandedFilters", false);
|
||||||
},
|
}
|
||||||
|
},
|
||||||
|
|
||||||
returnToList() {
|
@action
|
||||||
this.set("selectedTopic", null);
|
returnToList() {
|
||||||
getOwner(this).lookup("router:main").transitionTo("docs");
|
this.set("selectedTopic", null);
|
||||||
},
|
getOwner(this).lookup("router:main").transitionTo("docs");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,19 +1,24 @@
|
||||||
import Controller, { inject } from "@ember/controller";
|
import Controller, { inject as controller } from "@ember/controller";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
indexController: inject("docs.index"),
|
indexController: controller("docs.index"),
|
||||||
actions: {
|
|
||||||
updateSelectedCategories(category) {
|
@action
|
||||||
this.indexController.send("updateSelectedCategories", category);
|
updateSelectedCategories(category) {
|
||||||
return false;
|
this.indexController.send("updateSelectedCategories", category);
|
||||||
},
|
return false;
|
||||||
updateSelectedTags(tag) {
|
},
|
||||||
this.indexController.send("updateSelectedTags", tag);
|
|
||||||
return false;
|
@action
|
||||||
},
|
updateSelectedTags(tag) {
|
||||||
performSearch(term) {
|
this.indexController.send("updateSelectedTags", tag);
|
||||||
this.indexController.send("performSearch", term);
|
return false;
|
||||||
return false;
|
},
|
||||||
},
|
|
||||||
|
@action
|
||||||
|
performSearch(term) {
|
||||||
|
this.indexController.send("performSearch", term);
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,6 +7,7 @@ const Docs = EmberObject.extend({});
|
||||||
Docs.reopenClass({
|
Docs.reopenClass({
|
||||||
list(params) {
|
list(params) {
|
||||||
let filters = [];
|
let filters = [];
|
||||||
|
|
||||||
if (params.filterCategories) {
|
if (params.filterCategories) {
|
||||||
filters.push(`category=${params.filterCategories}`);
|
filters.push(`category=${params.filterCategories}`);
|
||||||
}
|
}
|
||||||
|
@ -53,10 +54,7 @@ Docs.reopenClass({
|
||||||
|
|
||||||
promise = promise.then((data) => {
|
promise = promise.then((data) => {
|
||||||
data.topics.topic_list.topics = data.topics.topic_list.topics.map(
|
data.topics.topic_list.topics = data.topics.topic_list.topics.map(
|
||||||
(topic) => {
|
(topic) => Topic.create(topic)
|
||||||
topic = Topic.create(topic);
|
|
||||||
return topic;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,6 +15,7 @@ export default DiscourseRoute.extend({
|
||||||
refreshModel: true,
|
refreshModel: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
model(params) {
|
model(params) {
|
||||||
this.controllerFor("docs.index").set("isLoading", true);
|
this.controllerFor("docs.index").set("isLoading", true);
|
||||||
return Docs.list(params).then((result) => {
|
return Docs.list(params).then((result) => {
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
<a {{action "selectCategory"}} class="docs-item docs-category {{if category.active 'selected'}}">
|
<a href {{action "selectCategory"}} class="docs-item docs-category {{if category.active "selected"}}">
|
||||||
{{#unless category.active}}
|
{{#unless category.active}}
|
||||||
{{d-icon "plus"}}
|
{{d-icon "plus"}}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
{{#if category.active}}
|
{{#if category.active}}
|
||||||
{{d-icon "times-circle"}}
|
{{d-icon "times-circle"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<span class="docs-item-id category-id">{{categoryName}}</span>
|
<span class="docs-item-id category-id">{{categoryName}}</span>
|
||||||
<span class="docs-item-count category-count">{{category.count}}</span>
|
<span class="docs-item-count category-count">{{category.count}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
<a {{action "selectTag"}} class="docs-item docs-tag {{if tag.active 'selected'}} {{if subtag 'subtag'}}">
|
<a href {{action "selectTag"}} class="docs-item docs-tag {{if tag.active "selected"}} {{if subtag "subtag"}}">
|
||||||
{{#unless tag.active}}
|
{{#unless tag.active}}
|
||||||
{{d-icon "plus"}}
|
{{d-icon "plus"}}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
{{#if tag.active}}
|
{{#if tag.active}}
|
||||||
{{d-icon "times-circle"}}
|
{{d-icon "times-circle"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<span class="docs-item-id tag-id">{{tag.id}}</span>
|
<span class="docs-item-id tag-id">{{tag.id}}</span>
|
||||||
<span class="docs-item-count tag-count">{{tag.count}}</span>
|
<span class="docs-item-count tag-count">{{tag.count}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,27 +1,31 @@
|
||||||
{{#load-more selector=".topic-list tr" action=loadMore}}
|
{{#load-more selector=".topic-list tr" action=loadMore}}
|
||||||
<table class="topic-list">
|
<table class="topic-list">
|
||||||
<thead>
|
<thead>
|
||||||
<th {{action "sortListTitle"}}>
|
<th role="button" {{action "sortListTitle"}}>
|
||||||
{{i18n 'docs.column_titles.topic'}}
|
{{i18n "docs.column_titles.topic"}}
|
||||||
|
|
||||||
{{#if sortTitle}}
|
{{#if sortTitle}}
|
||||||
{{#if ascending}}
|
{{#if ascending}}
|
||||||
{{d-icon 'angle-up'}}
|
{{d-icon "angle-up"}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{d-icon 'angle-down'}}
|
{{d-icon "angle-down"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</th>
|
</th>
|
||||||
<th {{action "sortListActivity"}}>
|
|
||||||
{{i18n 'docs.column_titles.activity'}}
|
<th role="button" {{action "sortListActivity"}}>
|
||||||
|
{{i18n "docs.column_titles.activity"}}
|
||||||
|
|
||||||
{{#if sortActivity}}
|
{{#if sortActivity}}
|
||||||
{{#if ascending}}
|
{{#if ascending}}
|
||||||
{{d-icon 'angle-up'}}
|
{{d-icon "angle-up"}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{d-icon 'angle-down'}}
|
{{d-icon "angle-down"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</th>
|
</th>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
{{#each topics as |topic|}}
|
{{#each topics as |topic|}}
|
||||||
{{raw "docs-topic-list-item" topic=topic}}
|
{{raw "docs-topic-list-item" topic=topic}}
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
}}
|
}}
|
||||||
|
|
||||||
<div class="topic-content">
|
<div class="topic-content">
|
||||||
<h1>{{{topic.fancyTitle}}}</h1>
|
<h1>{{html-safe topic.fancyTitle}}</h1>
|
||||||
|
|
||||||
{{mount-widget
|
{{mount-widget
|
||||||
widget="post"
|
widget="post"
|
||||||
model=model
|
model=model
|
||||||
|
@ -16,7 +17,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a class="docs-nav-link more" href="/t/{{topic.id}}">
|
<a class="docs-nav-link more" href="/t/{{topic.id}}">
|
||||||
{{d-icon "far-comment"}} {{i18n 'docs.topic.navigate_to_topic'}}
|
{{d-icon "far-comment"}} {{i18n "docs.topic.navigate_to_topic"}}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{{plugin-outlet name="after-docs-topic"}}
|
{{plugin-outlet name="after-docs-topic"}}
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
{{#conditional-loading-spinner condition=isLoading}}
|
{{#conditional-loading-spinner condition=isLoading}}
|
||||||
{{#if emptyTopics}}
|
{{#if emptyTopics}}
|
||||||
<span class="no-topics-found">{{html-safe (i18n 'docs.no_topics')}}</span>
|
<span class="no-topics-found">{{html-safe (i18n "docs.no_topics")}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="docs-browse">
|
<div class="docs-browse">
|
||||||
{{#if site.mobileView}}
|
{{#if site.mobileView}}
|
||||||
{{#unless selectedTopic}}
|
{{#unless selectedTopic}}
|
||||||
{{d-button class="docs-expander" icon=(if expandedFilters "angle-up" "angle-down") action=(action "toggleFilters") label="docs.filter_button"}}
|
{{d-button class="docs-expander" icon=(if expandedFilters "angle-up" "angle-down") action=(action "toggleFilters") label="docs.filter_button"}}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="docs-filters">
|
|
||||||
{{#if expandedFilters}}
|
<div class="docs-filters">
|
||||||
|
{{#if expandedFilters}}
|
||||||
{{#if canFilterSolved}}
|
{{#if canFilterSolved}}
|
||||||
<div class="docs-items docs-solved">
|
<div class="docs-items docs-solved">
|
||||||
<label class="checkbox-label docs-item">
|
<label class="checkbox-label docs-item">
|
||||||
|
@ -21,11 +22,11 @@
|
||||||
{{i18n "docs.filter_solved"}}
|
{{i18n "docs.filter_solved"}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if categories}}
|
{{#if categories}}
|
||||||
<div class="docs-items docs-categories">
|
<div class="docs-items docs-categories">
|
||||||
<h3>{{i18n 'docs.categories'}}</h3>
|
<h3>{{i18n "docs.categories"}}</h3>
|
||||||
{{#each categories as |category|}}
|
{{#each categories as |category|}}
|
||||||
{{docs-category
|
{{docs-category
|
||||||
category=category
|
category=category
|
||||||
|
@ -35,9 +36,10 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if tags}}
|
{{#if tags}}
|
||||||
<div class="docs-items docs-tags">
|
<div class="docs-items docs-tags">
|
||||||
<h3>{{i18n 'docs.tags'}}</h3>
|
<h3>{{i18n "docs.tags"}}</h3>
|
||||||
{{#each tags as |tag|}}
|
{{#each tags as |tag|}}
|
||||||
{{docs-tag
|
{{docs-tag
|
||||||
tag=tag
|
tag=tag
|
||||||
|
@ -46,41 +48,41 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if selectedTopic}}
|
||||||
|
{{#conditional-loading-spinner condition=isTopicLoading}}
|
||||||
|
{{docs-topic topic=topic return=(action "returnToList")}}
|
||||||
|
{{/conditional-loading-spinner}}
|
||||||
|
{{else}}
|
||||||
|
<div class="docs-results">
|
||||||
|
{{#if isSearchingOrFiltered}}
|
||||||
|
{{#if emptyResults}}
|
||||||
|
<div class="result-count no-result">
|
||||||
|
{{i18n "search.no_results"}}
|
||||||
|
</div>
|
||||||
|
{{plugin-outlet name="after-docs-empty-results"}}
|
||||||
|
{{else}}
|
||||||
|
<div class="result-count">
|
||||||
|
{{i18n "docs.search.results" count=topicCount}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#unless emptyResults}}
|
||||||
|
{{docs-topic-list
|
||||||
|
topics=topics
|
||||||
|
ascending=ascending
|
||||||
|
order=orderColumn
|
||||||
|
sortBy=(action "sortBy")
|
||||||
|
selectTopic=(action "setSelectedTopic")
|
||||||
|
loadMore=(action "loadMore")
|
||||||
|
loading=isLoadingMore
|
||||||
|
}}
|
||||||
|
{{/unless}}
|
||||||
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{#if selectedTopic}}
|
|
||||||
{{#conditional-loading-spinner condition=isTopicLoading}}
|
|
||||||
{{docs-topic topic=topic return=(action "returnToList")}}
|
|
||||||
{{/conditional-loading-spinner}}
|
|
||||||
{{else}}
|
|
||||||
<div class="docs-results">
|
|
||||||
{{#if isSearchingOrFiltered}}
|
|
||||||
{{#if emptyResults}}
|
|
||||||
<div class="result-count no-result">
|
|
||||||
{{i18n 'search.no_results'}}
|
|
||||||
</div>
|
|
||||||
{{plugin-outlet name="after-docs-empty-results"}}
|
|
||||||
{{else}}
|
|
||||||
<div class="result-count">
|
|
||||||
{{i18n 'docs.search.results'
|
|
||||||
count=topicCount
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
{{#unless emptyResults}}
|
|
||||||
{{docs-topic-list
|
|
||||||
topics=topics
|
|
||||||
ascending=ascending
|
|
||||||
order=orderColumn
|
|
||||||
sortBy=(action "sortBy")
|
|
||||||
selectTopic=(action "setSelectedTopic")
|
|
||||||
loadMore=(action "loadMore")
|
|
||||||
loading=isLoadingMore
|
|
||||||
}}
|
|
||||||
{{/unless}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/conditional-loading-spinner}}
|
{{/conditional-loading-spinner}}
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
<div class="docs">
|
<div class="docs">
|
||||||
{{plugin-outlet name="before-docs-search" args=(hash selectCategory=(action "updateSelectedCategories") selectTag=(action "updateSelectedTags") tags=indexController.tags categories=indexController.categories )}}
|
{{plugin-outlet
|
||||||
|
name="before-docs-search"
|
||||||
|
args=(hash
|
||||||
|
selectCategory=(action "updateSelectedCategories")
|
||||||
|
selectTag=(action "updateSelectedTags")
|
||||||
|
tags=indexController.tags
|
||||||
|
categories=indexController.categories
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
|
||||||
{{docs-search
|
{{docs-search
|
||||||
searchTerm=(readonly indexController.searchTerm)
|
searchTerm=(readonly indexController.searchTerm)
|
||||||
onSearch=(action "performSearch")
|
onSearch=(action "performSearch")
|
||||||
|
|
|
@ -127,6 +127,10 @@
|
||||||
th {
|
th {
|
||||||
min-width: 5em;
|
min-width: 5em;
|
||||||
|
|
||||||
|
&[role="button"] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--primary-low, $primary-low);
|
background-color: var(--primary-low, $primary-low);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
{
|
{
|
||||||
|
"name": "discourse-docs",
|
||||||
|
"version": "0.1",
|
||||||
|
"repository": "https://github.com/discourse/discourse-docs",
|
||||||
"author": "Discourse",
|
"author": "Discourse",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
# about: A plugin to make it easy to explore and find knowledge base documents in Discourse
|
# about: A plugin to make it easy to explore and find knowledge base documents in Discourse
|
||||||
# version: 0.1
|
# version: 0.1
|
||||||
# author: Justin DiRose
|
# author: Justin DiRose
|
||||||
# url: https://github.com/discourse/discourse-knowledge-explorer
|
# url: https://github.com/discourse/discourse-docs
|
||||||
|
|
||||||
enabled_site_setting :docs_enabled
|
enabled_site_setting :docs_enabled
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ after_initialize do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
add_to_class(:topic_query, :list_docs_topics) do
|
add_to_class(:topic_query, :list_docs_topics) do
|
||||||
default_results(@options)
|
default_results(@options)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue