├── .codeclimate.yml ├── .dockerignore ├── .editorconfig ├── .eslintrc.js ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── nodejs.yml │ └── reg.yml ├── .gitignore ├── .prettierrc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── __mocks__ ├── bootstrap.native.ts └── styleMock.ts ├── __tests__ ├── assets │ └── ts │ │ ├── oembed.spec.ts │ │ └── util.spec.ts ├── axios-mock.ts ├── components │ ├── ActionButton.spec.ts │ ├── AppDropdown.ts │ ├── Avatar.spec.ts │ ├── BlockButton.spec.ts │ ├── ChatPanel.spec.ts │ ├── Compose.spec.ts │ ├── CustomCheckbox.spec.ts │ ├── EntityText.spec.ts │ ├── FollowButton.spec.ts │ ├── Header.spec.ts │ ├── InputLongost.spec.ts │ ├── InputLongpost.spec.ts │ ├── Interaction.spec.ts │ ├── List.spec.ts │ ├── MessageCompose.spec.ts │ ├── Nsfw.spec.ts │ ├── NuxtLinkMod.spec.ts │ ├── Poll.spec.ts │ ├── Post.spec.ts │ ├── PostList.spec.ts │ ├── PostModal.spec.ts │ ├── Profile.spec.ts │ ├── RemoveModal.spec.ts │ ├── SearchForm.spec.ts │ ├── Splash.spec.ts │ ├── ToggleLongpost.spec.ts │ ├── ToggleNsfw.spec.ts │ ├── ToggleSpoiler.spec.ts │ ├── User.spec.ts │ ├── atoms │ │ ├── MuteButton.spec.ts │ │ ├── RelationBadge.spec.ts │ │ └── TogglePoll.spec.ts │ ├── molecules │ │ ├── EmojiPicker.ts │ │ ├── FileRow.spec.ts │ │ └── Sound.spec.ts │ ├── organisms │ │ └── LongPost.ts │ ├── settings │ │ └── Account │ │ │ ├── Avatar.spec.ts │ │ │ ├── Cover.spec.ts │ │ │ └── index.spec.ts │ └── sidebar │ │ └── App.spec.ts ├── fixtures │ ├── channel.ts │ ├── client.ts │ ├── file.ts │ ├── index.ts │ ├── interaction.ts │ ├── poll.ts │ ├── post.ts │ └── user.ts ├── helper.ts ├── setup.ts └── setupAfter.ts ├── _redirects ├── example.env ├── jest.config.js ├── nuxt.config.ts ├── package.json ├── regconfig.json ├── renovate.json ├── src ├── assets │ ├── css │ │ ├── _mixin.scss │ │ ├── _override.scss │ │ ├── adn_base_variables.scss │ │ └── main.scss │ ├── img │ │ └── beta.svg │ ├── json │ │ ├── locales.json │ │ └── timezones.json │ └── ts │ │ ├── actionable.ts │ │ ├── bus.ts │ │ ├── key-binding │ │ ├── default-mixin.ts │ │ ├── for-list.ts │ │ └── index.ts │ │ ├── list-item.ts │ │ ├── notification-wrapper.ts │ │ ├── oembed.ts │ │ ├── refresh-after-added.ts │ │ ├── resettable.ts │ │ ├── search.ts │ │ ├── text-count.ts │ │ └── util.ts ├── components │ ├── atoms │ │ ├── AclSelect.stories.ts │ │ ├── AclSelect.vue │ │ ├── ActionButton.vue │ │ ├── AddFile.vue │ │ ├── Avatar.stories.ts │ │ ├── Avatar.vue │ │ ├── BlockButton.vue │ │ ├── CustomCheckbox.stories.ts │ │ ├── CustomCheckbox.vue │ │ ├── EmojiButton.vue │ │ ├── EntityText.stories.ts │ │ ├── EntityText.vue │ │ ├── FollowButton.stories.ts │ │ ├── FollowButton.vue │ │ ├── MuteButton.stories.ts │ │ ├── MuteButton.vue │ │ ├── NuxtLinkMod.vue │ │ ├── PageTitle.vue │ │ ├── RelationBadge.stories.ts │ │ ├── RelationBadge.vue │ │ ├── SourceLink.vue │ │ ├── ThumbnailImage.vue │ │ ├── ToggleButton.stories.ts │ │ ├── ToggleButton.vue │ │ ├── ToggleLongpost.vue │ │ ├── ToggleNsfw.vue │ │ ├── TogglePoll.vue │ │ └── ToggleSpoiler.vue │ ├── layouts │ │ └── channel.vue │ ├── molecules │ │ ├── AppDropdown.vue │ │ ├── BaseChannelPanel.ts │ │ ├── BaseList.vue │ │ ├── BaseModal.vue │ │ ├── Channel.vue │ │ ├── EmojiPicker.vue │ │ ├── FilePreview │ │ │ ├── FilePreview.vue │ │ │ ├── FilePreviewAbstract.ts │ │ │ ├── FilePreviewAudio.vue │ │ │ ├── FilePreviewImage.vue │ │ │ └── FilePreviewVideo.vue │ │ ├── FileRow.stories.ts │ │ ├── FileRow.vue │ │ ├── KeySets.vue │ │ ├── SearchForm.vue │ │ ├── Sound.vue │ │ ├── Splash.vue │ │ ├── Thumb.vue │ │ ├── User.vue │ │ ├── UserPopper.vue │ │ ├── UserPopperInner.stories.ts │ │ ├── UserPopperInner.vue │ │ └── settings │ │ │ ├── Account │ │ │ ├── Avatar.vue │ │ │ ├── Cover.vue │ │ │ └── index.vue │ │ │ ├── Display.vue │ │ │ ├── Notifications.vue │ │ │ └── Stream.vue │ └── organisms │ │ ├── ChannelCompose.vue │ │ ├── ChannelEditModal.vue │ │ ├── ChannelList.vue │ │ ├── ChannelMemberEditModal.vue │ │ ├── ChannelPanel.vue │ │ ├── ChannelUserList.vue │ │ ├── ChatPanel.vue │ │ ├── Compose.stories.ts │ │ ├── Compose.vue │ │ ├── ComposeAbstract.ts │ │ ├── CreatePmModal.vue │ │ ├── FilePreviewList.vue │ │ ├── FileView.vue │ │ ├── Header.stories.ts │ │ ├── Header.vue │ │ ├── HelpModal.stories.ts │ │ ├── HelpModal.vue │ │ ├── InputLongpost.vue │ │ ├── InputPoll.vue │ │ ├── InputSpoiler.vue │ │ ├── Interaction.vue │ │ ├── InteractionList.vue │ │ ├── LongPost.vue │ │ ├── Message.vue │ │ ├── MessageCompose.vue │ │ ├── MessageIcon.vue │ │ ├── MessageList.vue │ │ ├── MessageModal.vue │ │ ├── MessageRemoveModal.vue │ │ ├── Nsfw.vue │ │ ├── PmPanel.vue │ │ ├── Poll.vue │ │ ├── PollList.vue │ │ ├── PollNotice.vue │ │ ├── Post.vue │ │ ├── PostList.vue │ │ ├── PostModal.vue │ │ ├── Profile.stories.ts │ │ ├── Profile.vue │ │ ├── RemoveModal.vue │ │ ├── SuggestUsers.vue │ │ ├── UserList.vue │ │ ├── file-list.vue │ │ └── sidebar │ │ ├── About.ts │ │ ├── App.ts │ │ ├── Files.ts │ │ ├── MenuItem.ts │ │ ├── Search.ts │ │ ├── Settings.ts │ │ └── Sidebar.vue ├── entity │ ├── Unread.ts │ ├── channel.ts │ ├── client.ts │ ├── config.ts │ ├── connection.ts │ ├── entity.ts │ ├── file.ts │ ├── interaction.ts │ ├── marker.ts │ ├── message.ts │ ├── pageable.ts │ ├── pnut-response.ts │ ├── poll.ts │ ├── post.ts │ ├── raw.ts │ ├── raw │ │ ├── raw │ │ │ ├── broadcast-notice.ts │ │ │ ├── channel-avatar-image.ts │ │ │ ├── channel-cover-image.ts │ │ │ ├── channel-invite.ts │ │ │ ├── chat-room-settings.ts │ │ │ ├── crosspost.ts │ │ │ ├── embedded-media.ts │ │ │ ├── external-user-profiles.ts │ │ │ ├── fallback-url.ts │ │ │ ├── language.ts │ │ │ ├── live-photo.ts │ │ │ ├── long-post.ts │ │ │ ├── oembed.ts │ │ │ ├── poll-notice.ts │ │ │ ├── quote.ts │ │ │ └── spoiler.ts │ │ └── replacement-values │ │ │ ├── file.ts │ │ │ └── poll.ts │ ├── source.ts │ ├── stats.ts │ ├── token.ts │ └── user.ts ├── fixtures │ ├── accessor.ts │ ├── client.ts │ ├── index.ts │ ├── poll.ts │ ├── post.ts │ └── user.ts ├── layouts │ ├── default.vue │ ├── error.vue │ ├── loading.vue │ └── no-sidebar.vue ├── pages │ ├── about │ │ ├── bookmarklet.vue │ │ ├── index.vue │ │ └── stats.vue │ ├── at_name.vue │ ├── at_name │ │ ├── followers.vue │ │ ├── follows.vue │ │ ├── index.vue │ │ ├── posts │ │ │ └── _id │ │ │ │ ├── index.vue │ │ │ │ └── revisions.vue │ │ └── starred.vue │ ├── callback.vue │ ├── channels │ │ ├── _channelId.vue │ │ └── index.vue │ ├── conversations.vue │ ├── files │ │ ├── _id.vue │ │ └── index.vue │ ├── global.vue │ ├── index.vue │ ├── intent │ │ └── post.vue │ ├── interactions.vue │ ├── logout.vue │ ├── mentions.vue │ ├── missed-conversations.vue │ ├── newcomers.vue │ ├── photos.vue │ ├── polls │ │ ├── _id.vue │ │ └── index.vue │ ├── posts │ │ └── _id │ │ │ ├── index.vue │ │ │ └── revisions.vue │ ├── search │ │ ├── posts.vue │ │ └── users.vue │ ├── settings.vue │ ├── settings │ │ ├── blocked-accounts.vue │ │ ├── display.vue │ │ ├── index.vue │ │ ├── muted-accounts.vue │ │ ├── notifications.vue │ │ └── stream.vue │ ├── stars.vue │ ├── tags │ │ └── _name.vue │ └── trending.vue ├── plugins │ ├── axios │ │ └── index.ts │ ├── composition-api.ts │ ├── created.ts │ ├── dayjs.ts │ ├── di │ │ ├── bind.ts │ │ ├── index.ts │ │ └── interactors.ts │ ├── domain │ │ ├── dto │ │ │ ├── channel.ts │ │ │ ├── common.ts │ │ │ ├── file.ts │ │ │ ├── marker.ts │ │ │ ├── message.ts │ │ │ ├── poll.ts │ │ │ ├── post.ts │ │ │ ├── streamType.ts │ │ │ └── user.ts │ │ ├── entity │ │ │ └── ModifiedFile.ts │ │ ├── repository │ │ │ ├── configStorage.ts │ │ │ └── pnutRepository.ts │ │ ├── usecases │ │ │ ├── abstractCreatePost.ts │ │ │ ├── createChannel.ts │ │ │ ├── createConnection.ts │ │ │ ├── createFile.ts │ │ │ ├── createMessage.ts │ │ │ ├── createPoll.ts │ │ │ ├── createPost.ts │ │ │ ├── createPrivateChannel.ts │ │ │ ├── existingPm.ts │ │ │ ├── getChannels.ts │ │ │ ├── getFile.ts │ │ │ ├── getFiles.ts │ │ │ ├── getInteractions.ts │ │ │ ├── getMessages.ts │ │ │ ├── getPoll.ts │ │ │ ├── getPolls.ts │ │ │ ├── getPosts.ts │ │ │ ├── getProfile.ts │ │ │ ├── getRevision.ts │ │ │ ├── getStats.ts │ │ │ ├── getThread.ts │ │ │ ├── getUnreadCount.ts │ │ │ ├── getUsers.ts │ │ │ ├── markAsRead.ts │ │ │ ├── search.ts │ │ │ ├── suggestUsers.ts │ │ │ ├── updateAvatar.ts │ │ │ ├── updateCover.ts │ │ │ ├── updatePost.ts │ │ │ ├── updateProfile.ts │ │ │ ├── updateRelation.ts │ │ │ └── usecase.ts │ │ └── util │ │ │ ├── postUtil.ts │ │ │ └── util.ts │ ├── emoji.ts │ ├── emojify │ │ ├── Emojify.vue │ │ └── index.ts │ ├── font-awesome.ts │ ├── infrastructure │ │ └── repository │ │ │ ├── configStorageImpl.ts │ │ │ └── pnutRepositoryImpl.ts │ ├── intersection-observer.client.ts │ ├── modal │ │ ├── PromiseModal.vue │ │ ├── index.ts │ │ ├── mediator.ts │ │ └── modal.ts │ ├── mousetrap.ts │ ├── vue-outside.ts │ └── vue-scrollto.ts ├── static │ ├── favicon.ico │ └── img │ │ └── beta.png ├── store │ └── index.ts └── util │ ├── channel.ts │ ├── createRawList.ts │ ├── minimum-entities.ts │ └── vue.ts ├── tsconfig.json ├── types ├── emoji-mart-vue-fast.d.ts ├── index.d.ts ├── mousetrap.d.ts ├── svg.d.ts ├── unicode-substring.d.ts ├── vue-on-click-outside.d.ts ├── vue-shim.d.ts └── vuedraggable.d.ts └── yarn.lock /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | eslint: 2 | enabled: true 3 | config: 4 | extensions: 5 | - .js 6 | - .ts 7 | - .vue 8 | ratings: 9 | paths: 10 | - '**.js' 11 | - '**.ts' 12 | - '**.vue' 13 | 14 | exclude_patterns: 15 | - '__tests__/' 16 | - '__mocks__/' 17 | - '**/*.stories.ts' 18 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = 2 7 | indent_size = 2 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | --- 5 | 6 | **Describe the bug** 7 | A clear and concise description of what the bug is. 8 | 9 | **To Reproduce** 10 | Steps to reproduce the behavior: 11 | 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | 25 | - OS: [e.g. iOS] 26 | - Browser [e.g. chrome, safari] 27 | - Version [e.g. 22] 28 | 29 | **Smartphone (please complete the following information):** 30 | 31 | - Device: [e.g. iPhone6] 32 | - OS: [e.g. iOS8.1] 33 | - Browser [e.g. stock browser, safari] 34 | - Version [e.g. 22] 35 | 36 | **Additional context** 37 | Add any other context about the problem here. 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | --- 5 | 6 | **Is your feature request related to a problem? Please describe.** 7 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 8 | 9 | **Describe the solution you'd like** 10 | A clear and concise description of what you want to happen. 11 | 12 | **Describe alternatives you've considered** 13 | A clear and concise description of any alternative solutions or features you've considered. 14 | 15 | **Additional context** 16 | Add any other context or screenshots about the feature request here. 17 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [14.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v2 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - name: Get yarn cache directory path 24 | id: yarn-cache-dir-path 25 | run: echo "::set-output name=dir::$(yarn cache dir)" 26 | - uses: actions/cache@v2 27 | id: yarn-cache 28 | with: 29 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 30 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 31 | restore-keys: | 32 | ${{ runner.os }}-yarn- 33 | - name: Install modules 34 | run: yarn 35 | - run: yarn ci 36 | env: 37 | COVERAGE_FILE: coverage/lcov.info 38 | - run: yarn build 39 | - name: Send coverage report 40 | uses: paambaati/codeclimate-action@v2.7.5 41 | env: 42 | CC_TEST_REPORTER_ID: 06f5b199c1d9bb6f814adf03807ad1df9b6184d4a268daba4672f4f95418ac7e 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # nyc test coverage 25 | .nyc_output 26 | 27 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # Bower dependency directory (https://bower.io/) 31 | bower_components 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (http://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # Typescript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | 64 | 65 | 66 | # End of https://www.gitignore.io/api/node 67 | 68 | # Nuxt build 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | sw.* 75 | 76 | .vscode 77 | __screenshots__ 78 | .reg 79 | .nuxt-storybook 80 | storybook-static 81 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": false, 4 | "htmlWhitespaceSensitivity": "ignore" 5 | } 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | 1. Fork it! 4 | 2. Create a branch (git checkout -b new-feature) 5 | 3. Commit your changes (git commit -am "Add a feature") 6 | 4. Push to the branch (git push origin new-feature) 7 | 5. Create a new Pull Request 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:latest 2 | 3 | # Create app directory 4 | RUN mkdir -p /usr/src/app 5 | WORKDIR /usr/src/app 6 | 7 | COPY package.json /usr/src/app/ 8 | 9 | RUN yarn 10 | 11 | COPY . /usr/src/app 12 | 13 | ENV CLIENT_ID pJ2VRJzLBwBitL6ZJoiXOLeamCxRs8Bw 14 | RUN yarn run build 15 | EXPOSE 3000 16 | 17 | CMD [ "yarn", "start" ] 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 sunya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Beta 2 | 3 |  4 | [](https://david-dm.org/sunya9/beta) 5 | [](https://david-dm.org/sunya9/beta?type=dev) 6 | [](https://codeclimate.com/github/sunya9/beta/maintainability) 7 | [](https://codeclimate.com/github/sunya9/beta/test_coverage) 8 | 9 | pnut.io client. 10 | 11 | ## Build Setup 12 | 13 | ```bash 14 | # install dependencies 15 | $ npm install # Or yarn install*[see note below] 16 | 17 | # serve with hot reload at localhost:3000 18 | $ npm run dev 19 | 20 | # build for production and launch server 21 | $ npm run build 22 | $ npm start 23 | 24 | # generate static project 25 | $ npm run generate 26 | ``` 27 | 28 | \*Note: Due to a bug in yarn's engine version detection code if you are 29 | using a prerelease version of Node (i.e. v7.6.0-rc.1) you will need to either: 30 | 31 | 1. Use `npm install` 32 | 2. Run `yarn` with a standard release of Node and then switch back 33 | 34 | For detailed explanation on how things work, checkout the [Nuxt.js docs](https://github.com/nuxt/nuxt.js). 35 | 36 | ## Client Setup 37 | 38 | Set up a client in the pnut.io developer area. 39 | 40 | Set environment variables referenced in [example.env](example.env). 41 | -------------------------------------------------------------------------------- /__mocks__/bootstrap.native.ts: -------------------------------------------------------------------------------- 1 | function noop() {} 2 | const Collapse = noop 3 | const Popover = noop 4 | 5 | const toggleBoolStr = (b: string | null) => !(b === 'true') 6 | 7 | class Dropdown { 8 | el: HTMLElement 9 | btn: HTMLElement | null = null 10 | constructor(dropdownEl: HTMLElement) { 11 | this.el = dropdownEl 12 | if (!this.el) return 13 | this.btn = this.el.querySelector('[data-toggle="dropdown"]') 14 | if (!this.btn) return 15 | this.btn.addEventListener('click', this.toggle.bind(this)) 16 | } 17 | 18 | toggle() { 19 | const b = this.el.getAttribute('aria-expanded') 20 | this.el.setAttribute('aria-expanded', `${toggleBoolStr(b)}`) 21 | } 22 | 23 | show() {} 24 | } 25 | 26 | class Modal { 27 | el: HTMLElement 28 | constructor(el: HTMLElement) { 29 | this.el = el 30 | } 31 | 32 | show() {} 33 | hide() {} 34 | } 35 | const Tab = noop 36 | 37 | export default { 38 | Collapse, 39 | Popover, 40 | Dropdown, 41 | Modal, 42 | Tab, 43 | } 44 | -------------------------------------------------------------------------------- /__mocks__/styleMock.ts: -------------------------------------------------------------------------------- 1 | export default {} 2 | -------------------------------------------------------------------------------- /__tests__/assets/ts/oembed.spec.ts: -------------------------------------------------------------------------------- 1 | import { createVideoEmbedRaw } from '~/assets/ts/oembed' 2 | import { OEmbed } from '~/entity/raw/raw/oembed' 3 | 4 | describe('oembed', () => { 5 | test('createVideoEmbedRaw', () => { 6 | const res = createVideoEmbedRaw( 7 | 'lorem https://www.youtube.com/watch?v=exam-ple_ http://youtu.be/123exXmp-le_ ipsum' 8 | ) 9 | const el0: OEmbed.Video = { 10 | type: 'io.pnut.core.oembed', 11 | value: { 12 | version: '1.0', 13 | type: 'video', 14 | width: 480, 15 | height: 270, 16 | html: '', 17 | embeddable_url: 'https://www.youtube.com/watch?v=exam-ple_', 18 | }, 19 | } 20 | expect(res[0]).toStrictEqual(el0) 21 | 22 | const el1: OEmbed.Video = { 23 | type: 'io.pnut.core.oembed', 24 | value: { 25 | version: '1.0', 26 | type: 'video', 27 | width: 480, 28 | height: 270, 29 | html: '', 30 | embeddable_url: 'http://youtu.be/123exXmp-le_', 31 | }, 32 | } 33 | expect(res[1]).toStrictEqual(el1) 34 | 35 | expect(createVideoEmbedRaw('embeddable contents not found')).toStrictEqual( 36 | [] 37 | ) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /__tests__/components/ActionButton.spec.ts: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import ActionButton from '~/components/atoms/ActionButton.vue' 3 | 4 | describe('ActionButton', () => { 5 | describe('icon props', () => { 6 | test('pass string', () => { 7 | const wrapper = mount(ActionButton, { 8 | propsData: { 9 | icon: 'user', 10 | checked: false, 11 | }, 12 | }) 13 | expect(wrapper.find('.fa-user').exists()).toBe(true) 14 | }) 15 | test('pass array', () => { 16 | const wrapper = mount(ActionButton, { 17 | propsData: { 18 | icon: ['user', 'users'], 19 | checked: false, 20 | }, 21 | }) 22 | expect(wrapper.find('.fa-user').exists()).toBe(true) 23 | expect(wrapper.find('.fa-users').exists()).toBe(false) 24 | const wrapper2 = mount(ActionButton, { 25 | propsData: { 26 | icon: ['user', 'users'], 27 | checked: true, 28 | }, 29 | }) 30 | expect(wrapper2.find('.fa-user').exists()).toBe(false) 31 | expect(wrapper2.find('.fa-users').exists()).toBe(true) 32 | }) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /__tests__/components/AppDropdown.ts: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import AppDropdown from '~/components/molecules/AppDropdown.vue' 3 | 4 | describe('AppDropdown', () => { 5 | test('render', () => { 6 | const wrapper = mount(AppDropdown, { 7 | propsData: { 8 | value: false, 9 | }, 10 | scopedSlots: { 11 | button: '', 12 | default: '
Do you want to remove this message?
12 |This post includes NSFW.
9 |Do you want to remove this post?
11 |5 | {{ error.message }} 6 |
7 |6 | Beta is a pnut.io app built with respect to Alpha, the official app.net 7 | client. 8 |
9 |
10 | It is made by
11 |
17 | Most of the code is original, but part of the CSS mimics Alpha. 18 |
19 |