├── .eslintrc.js
├── .github
└── FUNDING.yml
├── .gitignore
├── .prettierrc.js
├── LICENSE
├── README.md
├── docs
├── .vuepress
│ ├── config.js
│ └── public
│ │ ├── favicon.ico
│ │ ├── logo.svg
│ │ └── preview1.png
├── README.md
├── demo
│ └── simple
│ │ └── README.md
├── vueber-fire
│ ├── demo
│ │ └── README.md
│ ├── getting-started
│ │ └── README.md
│ ├── introduction
│ │ └── README.md
│ ├── props
│ │ └── README.md
│ └── usage
│ │ └── README.md
└── vueber
│ ├── demo
│ └── README.md
│ ├── getting-started
│ └── README.md
│ ├── introduction
│ └── README.md
│ ├── props
│ └── README.md
│ └── usage
│ └── README.md
├── package.json
├── packages
├── README.md
├── vueber-demo
│ ├── README.md
│ ├── assets
│ │ └── user2.jpeg
│ ├── babel.config.js
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ └── index.html
│ └── src
│ │ ├── App.vue
│ │ ├── initialData.js
│ │ └── main.js
├── vueber-fire-nuxt-demo
│ ├── .nuxt
│ │ ├── App.js
│ │ ├── client.js
│ │ ├── components
│ │ │ ├── nuxt-build-indicator.vue
│ │ │ ├── nuxt-child.js
│ │ │ ├── nuxt-error.vue
│ │ │ ├── nuxt-link.client.js
│ │ │ ├── nuxt-link.server.js
│ │ │ ├── nuxt-loading.vue
│ │ │ └── nuxt.js
│ │ ├── empty.js
│ │ ├── firebase-module
│ │ │ └── main.js
│ │ ├── index.js
│ │ ├── loading.html
│ │ ├── middleware.js
│ │ ├── router.js
│ │ ├── router.scrollBehavior.js
│ │ ├── server.js
│ │ ├── utils.js
│ │ └── views
│ │ │ ├── app.template.html
│ │ │ └── error.html
│ ├── README.md
│ ├── layouts
│ │ └── default.vue
│ ├── nuxt.config.js
│ ├── package.json
│ ├── pages
│ │ └── index.vue
│ └── plugins
│ │ └── vuefire.js
├── vueber-fire
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── components
│ │ ├── main.vue
│ │ └── unreadConversations.vue
│ │ ├── helpers
│ │ └── index.js
│ │ └── index.js
└── vueber
│ ├── README.md
│ ├── package.json
│ ├── postcss.config.js
│ └── src
│ ├── assets
│ ├── demoData
│ │ ├── getDemoData.js
│ │ ├── user1.jpeg
│ │ ├── user2.jpeg
│ │ ├── user3.jpeg
│ │ ├── user4.jpeg
│ │ └── user5.jpeg
│ └── noAvatar_xs.png
│ ├── components
│ ├── _elements
│ │ ├── avatar.vue
│ │ ├── btn.vue
│ │ ├── icon.vue
│ │ ├── textField.vue
│ │ └── toolbarWrapper.vue
│ ├── headerBar
│ │ └── index.vue
│ ├── index.vue
│ ├── inputFooter
│ │ ├── emojiPicker.vue
│ │ └── index.vue
│ ├── leftSidebar
│ │ ├── conversationTile.vue
│ │ └── index.vue
│ ├── loginView
│ │ └── index.vue
│ ├── messagesView
│ │ ├── index.vue
│ │ └── message
│ │ │ ├── index.vue
│ │ │ └── isReadFlag.vue
│ ├── noSelectionView
│ │ └── index.vue
│ └── rightSidebar
│ │ ├── index.vue
│ │ ├── mobileHeader.vue
│ │ └── userActions.vue
│ └── index.js
└── yarn.lock
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | node: true
5 | },
6 | parserOptions: {
7 | parser: 'babel-eslint'
8 | },
9 | extends: [
10 | 'plugin:vue/recommended',
11 | 'prettier',
12 | 'prettier/vue',
13 | 'plugin:prettier/recommended',
14 | 'eslint:recommended'
15 | ],
16 | plugins: ['prettier'],
17 | rules: {
18 | 'no-console': 'off',
19 | 'no-unused-vars': 'warn',
20 | 'object-shorthand': 'warn',
21 | 'vue/max-attributes-per-line': [
22 | 2,
23 | {
24 | singleline: 20,
25 | multiline: {
26 | max: 1,
27 | allowFirstLine: false
28 | }
29 | }
30 | ]
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # Enables "Sponsor" button on project
2 |
3 | github: lupas
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | dist
4 | build
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
24 | .nuxt
25 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true,
3 | arrowParens: 'always',
4 | semi: false
5 | }
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-present Pascal Luther
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 | # 💬 Vueber
2 |
3 |

4 |
5 | > Messenger & Chat framework for Vue & Nuxt apps. 💬
6 |
7 | 📖 [**Read Full Documentation**]() (coming soon)
8 |
9 | # Information
10 |
11 | This package is being developed and in alpha phase.
12 |
13 | Therefore, there is no documentation available yet.
14 |
15 | Do not use it in any app, it will change A LOT in the next few days, weeks and months.
16 |
17 | # Preview
18 |
19 | Currently looks something like this:
20 |
21 | 
22 |
--------------------------------------------------------------------------------
/docs/.vuepress/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | title: 'vueber',
3 | description: 'Messenger & Chat framework for Vue.js.',
4 | themeConfig: {
5 | heroImage: '/logo.svg',
6 | logo: '/logo.svg',
7 | repo: 'lupas/vueber',
8 | sidebar: [
9 | {
10 | title: '💬 vueber',
11 | sidebarDepth: 3,
12 | children: [
13 | '/vueber/introduction/',
14 | '/vueber/getting-started/',
15 | '/vueber/props/',
16 | '/vueber/usage/',
17 | '/vueber/demo/'
18 | ]
19 | },
20 | {
21 | title: '🔥 vueber-fire',
22 | sidebarDepth: 3,
23 | children: [
24 | '/vueber-fire/introduction/',
25 | '/vueber-fire/getting-started/',
26 | '/vueber-fire/props/',
27 | '/vueber-fire/usage/',
28 | '/vueber-fire/demo/'
29 | ]
30 | },
31 | {
32 | title: 'Demo',
33 | sidebarDepth: 1,
34 | children: [
35 | '/demo/simple/'
36 | ]
37 | }
38 | ],
39 | sidebarDepth: 2,
40 | nav: [
41 | { text: 'Home', link: '/' },
42 | { text: 'Guide', link: '/vueber/introduction/' },
43 | { text: 'Sponsor', link: 'https://github.com/sponsors/lupas' }
44 | ]
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lupas/vueber/fa1f193b87eaeadddf29fcc3619c88f1f6a4887f/docs/.vuepress/public/favicon.ico
--------------------------------------------------------------------------------
/docs/.vuepress/public/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/preview1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lupas/vueber/fa1f193b87eaeadddf29fcc3619c88f1f6a4887f/docs/.vuepress/public/preview1.png
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | home: true
3 | heroImage: /logo.svg
4 | heroText: Vueber Chat
5 | tagline: Messenger & Chat framework for Vue.js.
6 | actionText: Get Started →
7 | actionLink: /vueber/introduction/
8 | features:
9 | - title: Easy Setup
10 | details: Set up a 1-on-1 chat feature in your application without much effort.
11 | - title: Reactive & Real Time
12 | details: Our chat framework is fully reactive and will give your users a smooth chat experience.
13 | - title: Addons
14 | details: Various addons that make your life easier, like popup message windows (coming soon) or Firebase integration.
15 | footer: MIT Licensed | Copyright © 2019-present Pascal Luther
16 | ---
17 |
--------------------------------------------------------------------------------
/docs/demo/simple/README.md:
--------------------------------------------------------------------------------
1 | # Simple Demo
2 |
3 |
--------------------------------------------------------------------------------
/docs/vueber-fire/demo/README.md:
--------------------------------------------------------------------------------
1 | # Demo
2 |
3 |
4 |
--------------------------------------------------------------------------------
/docs/vueber-fire/getting-started/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ## Requirements
4 |
5 | Make sure you are using the newest Version of Vue.js installed in your project.
6 |
7 | If using Nuxt, add vueber to build.transpile in nuxt.config.js:
8 |
9 | ```js
10 | build: {
11 | transpile: [/^vueber/],
12 | }
13 | ```
14 |
15 | ## Install
16 |
17 | Install Vueber and Vueber-Fire via NPM or Yarn.
18 |
19 | ```js
20 | yarn add vueber # OR npm i vueber
21 | yarn add vueber-fire # OR npm i vueber-fire
22 |
23 | // for now (will be fixed)
24 | yarn add @nuxtjs/firebase
25 | yarn add anchorme
26 | yarn add emoji-mart-vue
27 | yarn add firewings
28 |
29 | yarn add firebase
30 | yarn add @nuxtjs/firebase
31 |
32 | // If you run into core-js issues:
33 | yarn add core-js@2
34 | ```
35 |
36 | ## Install More
37 |
38 | ### Install and configure Vuefire
39 |
40 | ```js
41 | // For Nuxt
42 | plugins: ['@/plugins/vuefire'],
43 |
44 | // Plugin
45 | import Vue from 'vue'
46 | import { firestorePlugin } from 'vuefire'
47 |
48 | Vue.use(firestorePlugin)
49 | ```
50 |
51 | ### After first run - create indexes
52 |
53 | ```
54 | The query requires an index. You can create it here: URL
55 | ```
56 |
--------------------------------------------------------------------------------
/docs/vueber-fire/introduction/README.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | Vueber-fire combines Firebases (Firestore) with the vueber chat framework.
4 |
5 |
--------------------------------------------------------------------------------
/docs/vueber-fire/props/README.md:
--------------------------------------------------------------------------------
1 | # Props
2 |
3 |
--------------------------------------------------------------------------------
/docs/vueber-fire/usage/README.md:
--------------------------------------------------------------------------------
1 | # Usage
2 |
3 | Import and add the VueberFire component wherever you want:
4 |
5 | ```html
6 |
7 |
8 |
9 |
10 |
11 | ```
12 |
13 | ```js
14 |
27 | ```
28 |
29 | The minimum props required are:
30 |
31 | - currentUser
32 | - ...
33 |
--------------------------------------------------------------------------------
/docs/vueber/demo/README.md:
--------------------------------------------------------------------------------
1 | # Demo
2 |
3 |
4 |
--------------------------------------------------------------------------------
/docs/vueber/getting-started/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ## Requirements
4 |
5 | Make sure you have the newest Version of Vue.js installed.
6 |
7 | ## Install
8 |
9 | Install Nuxt-Fire via NPM or Yarn.
10 |
11 | ```bash
12 | yarn add vueber # OR npm i vueber
13 | ```
--------------------------------------------------------------------------------
/docs/vueber/introduction/README.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | Vueber is a Chat & Messenger framework for Vue.js that let's you easily implement a __1-on-1 chat__ feature (soon also group chats) to your application or website.
4 |
5 | While the main module does not contain any database logic, additional modules like `vueber-fire` help you easily implement a chat feature with your database without much effort.
6 |
7 | ::: warning
8 | We just started and are currently in alpha phase - so be patient. The framework is already working but has various bugs and might change a lot in future updates. Keep that in mind when using this framework at this point.
--------------------------------------------------------------------------------
/docs/vueber/props/README.md:
--------------------------------------------------------------------------------
1 | # Props
2 |
3 | ## current-user
4 |
5 | Sets the avatar path,
6 |
7 | ```js
8 | currentUser: {
9 | id: '123',
10 |
11 | }
12 | ```
13 |
14 |
--------------------------------------------------------------------------------
/docs/vueber/usage/README.md:
--------------------------------------------------------------------------------
1 | # Usage
2 |
3 | Add the vueber component anywhere in your application like so:
4 |
5 | ```vue
6 |
7 |
20 |
21 |
22 |
29 | ```
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "workspaces": [
4 | "packages/*"
5 | ],
6 | "author": "Pascal Luther",
7 | "repository": "https://github.com/lupas/vueber",
8 | "homepage": "https://github.com/lupas",
9 | "keywords": [
10 | "vue, vuejs, chat, vueber, messaging, inbox"
11 | ],
12 | "scripts": {
13 | "demo": "cd packages/vueber-fire-nuxt-demo && npm run dev",
14 | "demo-basic": "cd packages/vueber-demo && yarn dev",
15 | "docs:dev": "vuepress dev docs",
16 | "docs:build": "vuepress build docs",
17 | "npm:publish": "cd packages/vueber && npm publish",
18 | "npm:publishAlpha": "cd packages/vueber && npm publish --tag alpha"
19 | },
20 | "devDependencies": {
21 | "@vue/eslint-config-prettier": "^6.0.0",
22 | "babel-eslint": "^10.0.1",
23 | "eslint": "^6.7.2",
24 | "eslint-config-prettier": "^6.5.0",
25 | "eslint-plugin-prettier": "^3.0.1",
26 | "eslint-plugin-vue": "^6.0.1",
27 | "prettier": "^2.0.4",
28 | "vuepress": "^1.2.0"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/README.md:
--------------------------------------------------------------------------------
1 | These are the packages for the vueber monorepo.
2 |
3 | ## Packages
4 |
5 | - **vueber** - main source code for vueber
6 | - **vueber-demo** - demo project source code for vueber
7 | - **vueber-firemo** - main source code for vueber-fire
8 |
--------------------------------------------------------------------------------
/packages/vueber-demo/README.md:
--------------------------------------------------------------------------------
1 | # simple-demo
2 |
3 | tbd
--------------------------------------------------------------------------------
/packages/vueber-demo/assets/user2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lupas/vueber/fa1f193b87eaeadddf29fcc3619c88f1f6a4887f/packages/vueber-demo/assets/user2.jpeg
--------------------------------------------------------------------------------
/packages/vueber-demo/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['@vue/cli-plugin-babel/preset'],
3 | }
4 |
--------------------------------------------------------------------------------
/packages/vueber-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vueber-demo",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "dev": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "vue": "^2.6.10",
12 | "vueber": "0.0.1-alpha.0"
13 | },
14 | "devDependencies": {
15 | "@vue/cli-plugin-babel": "^4.0.0",
16 | "@vue/cli-plugin-eslint": "^4.0.0",
17 | "@vue/cli-service": "^4.0.0",
18 | "babel-eslint": "^10.0.3",
19 | "eslint": "^6.8.0",
20 | "eslint-plugin-vue": "^6.2.1",
21 | "vue-template-compiler": "^2.6.10"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/vueber-demo/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lupas/vueber/fa1f193b87eaeadddf29fcc3619c88f1f6a4887f/packages/vueber-demo/public/favicon.ico
--------------------------------------------------------------------------------
/packages/vueber-demo/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | vueber-simple-demo
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/packages/vueber-demo/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
18 |
19 |
83 |
84 |
105 |
--------------------------------------------------------------------------------
/packages/vueber-demo/src/initialData.js:
--------------------------------------------------------------------------------
1 | export function getInitialConversations() {
2 | return [
3 | {
4 | lastMessage: {
5 | hasPendingNotification: true,
6 | isRead: false,
7 | message: "Hello, I'm User 1",
8 | senderId: 'user1',
9 | senderName: 'User1',
10 | sentDate: '2019-11-21T08:50:57.796Z',
11 | _ownMessage: false,
12 | },
13 | noOfMessages: 13,
14 | participants: {
15 | user1: {
16 | avatarPath: require('./../assets/user2.jpeg'),
17 | chatDisabled: false,
18 | id: 'user1',
19 | username: 'User1',
20 | },
21 | },
22 | participantsArray: ['user0', 'user1'],
23 | id: 'conversation1',
24 | path: 'conversations/conversation1',
25 | _chatpartner: {
26 | avatarPath: null,
27 | chatDisabled: false,
28 | id: 'user1',
29 | username: 'User1',
30 | },
31 | },
32 | {
33 | lastMessage: {
34 | hasPendingNotification: false,
35 | isRead: true,
36 | message: "Hey there's user 2.",
37 | senderId: 'user2',
38 | senderName: 'User2',
39 | sentDate: '2019-11-20T08:50:57.796Z',
40 | _ownMessage: false,
41 | },
42 | noOfMessages: 3,
43 | participants: {
44 | user2: {
45 | avatarPath: require('./../assets/user2.jpeg'),
46 | chatDisabled: false,
47 | id: 'user2',
48 | username: 'User2',
49 | },
50 | },
51 | participantsArray: ['user2', 'user0'],
52 | id: 'conversation2',
53 | path: 'conversations/conversation2',
54 | _chatpartner: {
55 | avatarPath: null,
56 | chatDisabled: false,
57 | id: 'user2',
58 | username: 'User2',
59 | },
60 | },
61 | ]
62 | }
63 |
64 | export function getInitialUsers() {
65 | return [
66 | {
67 | id: 'user1',
68 | avatar: require('./../assets/user2.jpeg'),
69 | username: 'User1',
70 | },
71 | {
72 | id: 'user2',
73 | avatar: require('./../assets/user2.jpeg'),
74 | username: 'User2',
75 | },
76 | {
77 | id: 'user3',
78 | avatar: require('./../assets/user2.jpeg'),
79 | username: 'User3',
80 | },
81 | ]
82 | }
83 |
84 | export function getInitialMessages() {
85 | return [
86 | {
87 | hasPendingNotification: true,
88 | isRead: true,
89 | message: 'Hallo, dies ist ein Test',
90 | senderId: 'user2',
91 | senderName: 'User2',
92 | sentDate: '2019-01-07T10:36:38.296Z',
93 | id: 'message1',
94 | conversationId: 'conversation1',
95 | },
96 | {
97 | hasPendingNotification: true,
98 | isRead: true,
99 | message: 'Testchat',
100 | senderId: 'user2',
101 | senderName: 'User2',
102 | sentDate: '2019-01-07T10:36:38.296Z',
103 | id: 'message2',
104 | conversationId: 'conversation2',
105 | },
106 | ]
107 | }
108 |
--------------------------------------------------------------------------------
/packages/vueber-demo/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 |
4 | Vue.config.productionTip = false
5 |
6 | new Vue({
7 | render: (h) => h(App),
8 | }).$mount('#app')
9 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/App.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | import {
4 | getMatchedComponentsInstances,
5 | promisify,
6 | globalHandleError
7 | } from './utils'
8 |
9 | import NuxtLoading from './components/nuxt-loading.vue'
10 | import NuxtBuildIndicator from './components/nuxt-build-indicator'
11 |
12 | import _6f6c098b from '../layouts/default.vue'
13 |
14 | const layouts = { "_default": _6f6c098b }
15 |
16 | export default {
17 | head: {"title":"vueber-nuxt-fire-demo","meta":[{"charset":"utf-8"},{"name":"viewport","content":"width=device-width, initial-scale=1"},{"hid":"description","name":"description","content":"My astounding Nuxt.js project"}],"link":[{"rel":"icon","type":"image\u002Fx-icon","href":"\u002Ffavicon.ico"}],"style":[],"script":[]},
18 |
19 | render (h, props) {
20 | const loadingEl = h('NuxtLoading', { ref: 'loading' })
21 |
22 | const layoutEl = h(this.layout || 'nuxt')
23 | const templateEl = h('div', {
24 | domProps: {
25 | id: '__layout'
26 | },
27 | key: this.layoutName
28 | }, [layoutEl])
29 |
30 | const transitionEl = h('transition', {
31 | props: {
32 | name: 'layout',
33 | mode: 'out-in'
34 | },
35 | on: {
36 | beforeEnter (el) {
37 | // Ensure to trigger scroll event after calling scrollBehavior
38 | window.$nuxt.$nextTick(() => {
39 | window.$nuxt.$emit('triggerScroll')
40 | })
41 | }
42 | }
43 | }, [templateEl])
44 |
45 | return h('div', {
46 | domProps: {
47 | id: '__nuxt'
48 | }
49 | }, [
50 | loadingEl,
51 | h(NuxtBuildIndicator),
52 | transitionEl
53 | ])
54 | },
55 |
56 | data: () => ({
57 | isOnline: true,
58 |
59 | layout: null,
60 | layoutName: ''
61 | }),
62 |
63 | beforeCreate () {
64 | Vue.util.defineReactive(this, 'nuxt', this.$options.nuxt)
65 | },
66 | created () {
67 | // Add this.$nuxt in child instances
68 | Vue.prototype.$nuxt = this
69 | // add to window so we can listen when ready
70 | if (process.client) {
71 | window.$nuxt = this
72 |
73 | this.refreshOnlineStatus()
74 | // Setup the listeners
75 | window.addEventListener('online', this.refreshOnlineStatus)
76 | window.addEventListener('offline', this.refreshOnlineStatus)
77 | }
78 | // Add $nuxt.error()
79 | this.error = this.nuxt.error
80 | // Add $nuxt.context
81 | this.context = this.$options.context
82 | },
83 |
84 | mounted () {
85 | this.$loading = this.$refs.loading
86 | },
87 | watch: {
88 | 'nuxt.err': 'errorChanged'
89 | },
90 |
91 | computed: {
92 | isOffline () {
93 | return !this.isOnline
94 | }
95 | },
96 |
97 | methods: {
98 | refreshOnlineStatus () {
99 | if (process.client) {
100 | if (typeof window.navigator.onLine === 'undefined') {
101 | // If the browser doesn't support connection status reports
102 | // assume that we are online because most apps' only react
103 | // when they now that the connection has been interrupted
104 | this.isOnline = true
105 | } else {
106 | this.isOnline = window.navigator.onLine
107 | }
108 | }
109 | },
110 |
111 | async refresh () {
112 | const pages = getMatchedComponentsInstances(this.$route)
113 |
114 | if (!pages.length) {
115 | return
116 | }
117 | this.$loading.start()
118 |
119 | const promises = pages.map((page) => {
120 | const p = []
121 |
122 | if (page.$options.fetch) {
123 | p.push(promisify(page.$options.fetch, this.context))
124 | }
125 |
126 | if (page.$options.asyncData) {
127 | p.push(
128 | promisify(page.$options.asyncData, this.context)
129 | .then((newData) => {
130 | for (const key in newData) {
131 | Vue.set(page.$data, key, newData[key])
132 | }
133 | })
134 | )
135 | }
136 |
137 | return Promise.all(p)
138 | })
139 | try {
140 | await Promise.all(promises)
141 | } catch (error) {
142 | this.$loading.fail()
143 | globalHandleError(error)
144 | this.error(error)
145 | }
146 | this.$loading.finish()
147 | },
148 |
149 | errorChanged () {
150 | if (this.nuxt.err && this.$loading) {
151 | if (this.$loading.fail) {
152 | this.$loading.fail()
153 | }
154 | if (this.$loading.finish) {
155 | this.$loading.finish()
156 | }
157 | }
158 | },
159 |
160 | setLayout (layout) {
161 | if(layout && typeof layout !== 'string') {
162 | throw new Error('[nuxt] Avoid using non-string value as layout property.')
163 | }
164 |
165 | if (!layout || !layouts['_' + layout]) {
166 | layout = 'default'
167 | }
168 | this.layoutName = layout
169 | this.layout = layouts['_' + layout]
170 | return this.layout
171 | },
172 | loadLayout (layout) {
173 | if (!layout || !layouts['_' + layout]) {
174 | layout = 'default'
175 | }
176 | return Promise.resolve(layouts['_' + layout])
177 | }
178 | },
179 |
180 | components: {
181 | NuxtLoading
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/client.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import fetch from 'unfetch'
3 | import middleware from './middleware.js'
4 | import {
5 | applyAsyncData,
6 | promisify,
7 | middlewareSeries,
8 | sanitizeComponent,
9 | resolveRouteComponents,
10 | getMatchedComponents,
11 | getMatchedComponentsInstances,
12 | flatMapComponents,
13 | setContext,
14 | getLocation,
15 | compile,
16 | getQueryDiff,
17 | globalHandleError
18 | } from './utils.js'
19 | import { createApp, NuxtError } from './index.js'
20 | import NuxtLink from './components/nuxt-link.client.js' // should be included after ./index.js
21 |
22 | // Component:
23 | Vue.component(NuxtLink.name, NuxtLink)
24 | Vue.component('NLink', NuxtLink)
25 |
26 | if (!global.fetch) { global.fetch = fetch }
27 |
28 | // Global shared references
29 | let _lastPaths = []
30 | let app
31 | let router
32 |
33 | // Try to rehydrate SSR data from window
34 | const NUXT = window.__NUXT__ || {}
35 |
36 | Object.assign(Vue.config, {"silent":false,"performance":true})
37 |
38 | const logs = NUXT.logs || []
39 | if (logs.length > 0) {
40 | const ssrLogSyle = 'background: #2E495E;border-radius: 0.5em;color: white;font-weight: bold;padding: 2px 0.5em;'
41 | console.group && console.group ('%cNuxt SSR', ssrLogSyle)
42 | logs.forEach(logObj => (console[logObj.type] || console.log)(...logObj.args))
43 | delete NUXT.logs
44 | console.groupEnd && console.groupEnd()
45 | }
46 |
47 | // Setup global Vue error handler
48 | if (!Vue.config.$nuxt) {
49 | const defaultErrorHandler = Vue.config.errorHandler
50 | Vue.config.errorHandler = (err, vm, info, ...rest) => {
51 | // Call other handler if exist
52 | let handled = null
53 | if (typeof defaultErrorHandler === 'function') {
54 | handled = defaultErrorHandler(err, vm, info, ...rest)
55 | }
56 | if (handled === true) {
57 | return handled
58 | }
59 |
60 | if (vm && vm.$root) {
61 | const nuxtApp = Object.keys(Vue.config.$nuxt)
62 | .find(nuxtInstance => vm.$root[nuxtInstance])
63 |
64 | // Show Nuxt Error Page
65 | if (nuxtApp && vm.$root[nuxtApp].error && info !== 'render function') {
66 | vm.$root[nuxtApp].error(err)
67 | }
68 | }
69 |
70 | if (typeof defaultErrorHandler === 'function') {
71 | return handled
72 | }
73 |
74 | // Log to console
75 | if (process.env.NODE_ENV !== 'production') {
76 | console.error(err)
77 | } else {
78 | console.error(err.message || err)
79 | }
80 | }
81 | Vue.config.$nuxt = {}
82 | }
83 | Vue.config.$nuxt.$nuxt = true
84 |
85 | const errorHandler = Vue.config.errorHandler || console.error
86 |
87 | // Create and mount App
88 | createApp().then(mountApp).catch(errorHandler)
89 |
90 | function componentOption (component, key, ...args) {
91 | if (!component || !component.options || !component.options[key]) {
92 | return {}
93 | }
94 | const option = component.options[key]
95 | if (typeof option === 'function') {
96 | return option(...args)
97 | }
98 | return option
99 | }
100 |
101 | function mapTransitions (Components, to, from) {
102 | const componentTransitions = (component) => {
103 | const transition = componentOption(component, 'transition', to, from) || {}
104 | return (typeof transition === 'string' ? { name: transition } : transition)
105 | }
106 |
107 | return Components.map((Component) => {
108 | // Clone original object to prevent overrides
109 | const transitions = Object.assign({}, componentTransitions(Component))
110 |
111 | // Combine transitions & prefer `leave` transitions of 'from' route
112 | if (from && from.matched.length && from.matched[0].components.default) {
113 | const fromTransitions = componentTransitions(from.matched[0].components.default)
114 | Object.keys(fromTransitions)
115 | .filter(key => fromTransitions[key] && key.toLowerCase().includes('leave'))
116 | .forEach((key) => { transitions[key] = fromTransitions[key] })
117 | }
118 |
119 | return transitions
120 | })
121 | }
122 |
123 | async function loadAsyncComponents (to, from, next) {
124 | // Check if route path changed (this._pathChanged), only if the page is not an error (for validate())
125 | this._pathChanged = Boolean(app.nuxt.err) || from.path !== to.path
126 | this._queryChanged = JSON.stringify(to.query) !== JSON.stringify(from.query)
127 | this._diffQuery = (this._queryChanged ? getQueryDiff(to.query, from.query) : [])
128 |
129 | if (this._pathChanged && this.$loading.start && !this.$loading.manual) {
130 | this.$loading.start()
131 | }
132 |
133 | try {
134 | if (!this._pathChanged && this._queryChanged) {
135 | const Components = await resolveRouteComponents(
136 | to,
137 | (Component, instance) => ({ Component, instance })
138 | )
139 | // Add a marker on each component that it needs to refresh or not
140 | const startLoader = Components.some(({ Component, instance }) => {
141 | const watchQuery = Component.options.watchQuery
142 | if (watchQuery === true) {
143 | return true
144 | }
145 | if (Array.isArray(watchQuery)) {
146 | return watchQuery.some(key => this._diffQuery[key])
147 | }
148 | if (typeof watchQuery === 'function') {
149 | return watchQuery.apply(instance, [to.query, from.query])
150 | }
151 | return false
152 | })
153 | if (startLoader && this.$loading.start && !this.$loading.manual) {
154 | this.$loading.start()
155 | }
156 | }
157 |
158 | // Call next()
159 | next()
160 | } catch (error) {
161 | const err = error || {}
162 | const statusCode = err.statusCode || err.status || (err.response && err.response.status) || 500
163 | const message = err.message || ''
164 |
165 | // Handle chunk loading errors
166 | // This may be due to a new deployment or a network problem
167 | if (/^Loading( CSS)? chunk (\d)+ failed\./.test(message)) {
168 | window.location.reload(true /* skip cache */)
169 | return // prevent error page blinking for user
170 | }
171 |
172 | this.error({ statusCode, message })
173 | this.$nuxt.$emit('routeChanged', to, from, err)
174 | next()
175 | }
176 | }
177 |
178 | function applySSRData (Component, ssrData) {
179 | if (NUXT.serverRendered && ssrData) {
180 | applyAsyncData(Component, ssrData)
181 | }
182 |
183 | Component._Ctor = Component
184 | return Component
185 | }
186 |
187 | // Get matched components
188 | function resolveComponents (router) {
189 | const path = getLocation(router.options.base, router.options.mode)
190 |
191 | return flatMapComponents(router.match(path), async (Component, _, match, key, index) => {
192 | // If component is not resolved yet, resolve it
193 | if (typeof Component === 'function' && !Component.options) {
194 | Component = await Component()
195 | }
196 | // Sanitize it and save it
197 | const _Component = applySSRData(sanitizeComponent(Component), NUXT.data ? NUXT.data[index] : null)
198 | match.components[key] = _Component
199 | return _Component
200 | })
201 | }
202 |
203 | function callMiddleware (Components, context, layout) {
204 | let midd = []
205 | let unknownMiddleware = false
206 |
207 | // If layout is undefined, only call global middleware
208 | if (typeof layout !== 'undefined') {
209 | midd = [] // Exclude global middleware if layout defined (already called before)
210 | layout = sanitizeComponent(layout)
211 | if (layout.options.middleware) {
212 | midd = midd.concat(layout.options.middleware)
213 | }
214 | Components.forEach((Component) => {
215 | if (Component.options.middleware) {
216 | midd = midd.concat(Component.options.middleware)
217 | }
218 | })
219 | }
220 |
221 | midd = midd.map((name) => {
222 | if (typeof name === 'function') {
223 | return name
224 | }
225 | if (typeof middleware[name] !== 'function') {
226 | unknownMiddleware = true
227 | this.error({ statusCode: 500, message: 'Unknown middleware ' + name })
228 | }
229 | return middleware[name]
230 | })
231 |
232 | if (unknownMiddleware) {
233 | return
234 | }
235 | return middlewareSeries(midd, context)
236 | }
237 |
238 | async function render (to, from, next) {
239 | if (this._pathChanged === false && this._queryChanged === false) {
240 | return next()
241 | }
242 | // Handle first render on SPA mode
243 | if (to === from) {
244 | _lastPaths = []
245 | } else {
246 | const fromMatches = []
247 | _lastPaths = getMatchedComponents(from, fromMatches).map((Component, i) => {
248 | return compile(from.matched[fromMatches[i]].path)(from.params)
249 | })
250 | }
251 |
252 | // nextCalled is true when redirected
253 | let nextCalled = false
254 | const _next = (path) => {
255 | if (from.path === path.path && this.$loading.finish) {
256 | this.$loading.finish()
257 | }
258 |
259 | if (from.path !== path.path && this.$loading.pause) {
260 | this.$loading.pause()
261 | }
262 |
263 | if (nextCalled) {
264 | return
265 | }
266 |
267 | nextCalled = true
268 | next(path)
269 | }
270 |
271 | // Update context
272 | await setContext(app, {
273 | route: to,
274 | from,
275 | next: _next.bind(this)
276 | })
277 | this._dateLastError = app.nuxt.dateErr
278 | this._hadError = Boolean(app.nuxt.err)
279 |
280 | // Get route's matched components
281 | const matches = []
282 | const Components = getMatchedComponents(to, matches)
283 |
284 | // If no Components matched, generate 404
285 | if (!Components.length) {
286 | // Default layout
287 | await callMiddleware.call(this, Components, app.context)
288 | if (nextCalled) {
289 | return
290 | }
291 |
292 | // Load layout for error page
293 | const errorLayout = (NuxtError.options || NuxtError).layout
294 | const layout = await this.loadLayout(
295 | typeof errorLayout === 'function'
296 | ? errorLayout.call(NuxtError, app.context)
297 | : errorLayout
298 | )
299 |
300 | await callMiddleware.call(this, Components, app.context, layout)
301 | if (nextCalled) {
302 | return
303 | }
304 |
305 | // Show error page
306 | app.context.error({ statusCode: 404, message: 'This page could not be found' })
307 | return next()
308 | }
309 |
310 | // Update ._data and other properties if hot reloaded
311 | Components.forEach((Component) => {
312 | if (Component._Ctor && Component._Ctor.options) {
313 | Component.options.asyncData = Component._Ctor.options.asyncData
314 | Component.options.fetch = Component._Ctor.options.fetch
315 | }
316 | })
317 |
318 | // Apply transitions
319 | this.setTransitions(mapTransitions(Components, to, from))
320 |
321 | try {
322 | // Call middleware
323 | await callMiddleware.call(this, Components, app.context)
324 | if (nextCalled) {
325 | return
326 | }
327 | if (app.context._errored) {
328 | return next()
329 | }
330 |
331 | // Set layout
332 | let layout = Components[0].options.layout
333 | if (typeof layout === 'function') {
334 | layout = layout(app.context)
335 | }
336 | layout = await this.loadLayout(layout)
337 |
338 | // Call middleware for layout
339 | await callMiddleware.call(this, Components, app.context, layout)
340 | if (nextCalled) {
341 | return
342 | }
343 | if (app.context._errored) {
344 | return next()
345 | }
346 |
347 | // Call .validate()
348 | let isValid = true
349 | try {
350 | for (const Component of Components) {
351 | if (typeof Component.options.validate !== 'function') {
352 | continue
353 | }
354 |
355 | isValid = await Component.options.validate(app.context)
356 |
357 | if (!isValid) {
358 | break
359 | }
360 | }
361 | } catch (validationError) {
362 | // ...If .validate() threw an error
363 | this.error({
364 | statusCode: validationError.statusCode || '500',
365 | message: validationError.message
366 | })
367 | return next()
368 | }
369 |
370 | // ...If .validate() returned false
371 | if (!isValid) {
372 | this.error({ statusCode: 404, message: 'This page could not be found' })
373 | return next()
374 | }
375 |
376 | let instances
377 | // Call asyncData & fetch hooks on components matched by the route.
378 | await Promise.all(Components.map((Component, i) => {
379 | // Check if only children route changed
380 | Component._path = compile(to.matched[matches[i]].path)(to.params)
381 | Component._dataRefresh = false
382 | // Check if Component need to be refreshed (call asyncData & fetch)
383 | // Only if its slug has changed or is watch query changes
384 | if ((this._pathChanged && this._queryChanged) || Component._path !== _lastPaths[i]) {
385 | Component._dataRefresh = true
386 | } else if (!this._pathChanged && this._queryChanged) {
387 | const watchQuery = Component.options.watchQuery
388 | if (watchQuery === true) {
389 | Component._dataRefresh = true
390 | } else if (Array.isArray(watchQuery)) {
391 | Component._dataRefresh = watchQuery.some(key => this._diffQuery[key])
392 | } else if (typeof watchQuery === 'function') {
393 | if (!instances) {
394 | instances = getMatchedComponentsInstances(to)
395 | }
396 | Component._dataRefresh = watchQuery.apply(instances[i], [to.query, from.query])
397 | }
398 | }
399 | if (!this._hadError && this._isMounted && !Component._dataRefresh) {
400 | return
401 | }
402 |
403 | const promises = []
404 |
405 | const hasAsyncData = (
406 | Component.options.asyncData &&
407 | typeof Component.options.asyncData === 'function'
408 | )
409 |
410 | const hasFetch = Boolean(Component.options.fetch)
411 |
412 | const loadingIncrease = (hasAsyncData && hasFetch) ? 30 : 45
413 |
414 | // Call asyncData(context)
415 | if (hasAsyncData) {
416 | const promise = promisify(Component.options.asyncData, app.context)
417 | .then((asyncDataResult) => {
418 | applyAsyncData(Component, asyncDataResult)
419 |
420 | if (this.$loading.increase) {
421 | this.$loading.increase(loadingIncrease)
422 | }
423 | })
424 | promises.push(promise)
425 | }
426 |
427 | // Check disabled page loading
428 | this.$loading.manual = Component.options.loading === false
429 |
430 | // Call fetch(context)
431 | if (hasFetch) {
432 | let p = Component.options.fetch(app.context)
433 | if (!p || (!(p instanceof Promise) && (typeof p.then !== 'function'))) {
434 | p = Promise.resolve(p)
435 | }
436 | p.then((fetchResult) => {
437 | if (this.$loading.increase) {
438 | this.$loading.increase(loadingIncrease)
439 | }
440 | })
441 | promises.push(p)
442 | }
443 |
444 | return Promise.all(promises)
445 | }))
446 |
447 | // If not redirected
448 | if (!nextCalled) {
449 | if (this.$loading.finish && !this.$loading.manual) {
450 | this.$loading.finish()
451 | }
452 |
453 | next()
454 | }
455 | } catch (err) {
456 | const error = err || {}
457 | if (error.message === 'ERR_REDIRECT') {
458 | return this.$nuxt.$emit('routeChanged', to, from, error)
459 | }
460 | _lastPaths = []
461 |
462 | globalHandleError(error)
463 |
464 | // Load error layout
465 | let layout = (NuxtError.options || NuxtError).layout
466 | if (typeof layout === 'function') {
467 | layout = layout(app.context)
468 | }
469 | await this.loadLayout(layout)
470 |
471 | this.error(error)
472 | this.$nuxt.$emit('routeChanged', to, from, error)
473 | next()
474 | }
475 | }
476 |
477 | // Fix components format in matched, it's due to code-splitting of vue-router
478 | function normalizeComponents (to, ___) {
479 | flatMapComponents(to, (Component, _, match, key) => {
480 | if (typeof Component === 'object' && !Component.options) {
481 | // Updated via vue-router resolveAsyncComponents()
482 | Component = Vue.extend(Component)
483 | Component._Ctor = Component
484 | match.components[key] = Component
485 | }
486 | return Component
487 | })
488 | }
489 |
490 | function showNextPage (to) {
491 | // Hide error component if no error
492 | if (this._hadError && this._dateLastError === this.$options.nuxt.dateErr) {
493 | this.error()
494 | }
495 |
496 | // Set layout
497 | let layout = this.$options.nuxt.err
498 | ? (NuxtError.options || NuxtError).layout
499 | : to.matched[0].components.default.options.layout
500 |
501 | if (typeof layout === 'function') {
502 | layout = layout(app.context)
503 | }
504 | this.setLayout(layout)
505 | }
506 |
507 | // When navigating on a different route but the same component is used, Vue.js
508 | // Will not update the instance data, so we have to update $data ourselves
509 | function fixPrepatch (to, ___) {
510 | if (this._pathChanged === false && this._queryChanged === false) {
511 | return
512 | }
513 |
514 | const instances = getMatchedComponentsInstances(to)
515 | const Components = getMatchedComponents(to)
516 |
517 | Vue.nextTick(() => {
518 | instances.forEach((instance, i) => {
519 | if (!instance || instance._isDestroyed) {
520 | return
521 | }
522 |
523 | if (
524 | instance.constructor._dataRefresh &&
525 | Components[i] === instance.constructor &&
526 | instance.$vnode.data.keepAlive !== true &&
527 | typeof instance.constructor.options.data === 'function'
528 | ) {
529 | const newData = instance.constructor.options.data.call(instance)
530 | for (const key in newData) {
531 | Vue.set(instance.$data, key, newData[key])
532 | }
533 |
534 | // Ensure to trigger scroll event after calling scrollBehavior
535 | window.$nuxt.$nextTick(() => {
536 | window.$nuxt.$emit('triggerScroll')
537 | })
538 | }
539 | })
540 | showNextPage.call(this, to)
541 |
542 | // Hot reloading
543 | setTimeout(() => hotReloadAPI(this), 100)
544 | })
545 | }
546 |
547 | function nuxtReady (_app) {
548 | window.onNuxtReadyCbs.forEach((cb) => {
549 | if (typeof cb === 'function') {
550 | cb(_app)
551 | }
552 | })
553 | // Special JSDOM
554 | if (typeof window._onNuxtLoaded === 'function') {
555 | window._onNuxtLoaded(_app)
556 | }
557 | // Add router hooks
558 | router.afterEach((to, from) => {
559 | // Wait for fixPrepatch + $data updates
560 | Vue.nextTick(() => _app.$nuxt.$emit('routeChanged', to, from))
561 | })
562 | }
563 |
564 | const noopData = () => { return {} }
565 | const noopFetch = () => {}
566 |
567 | // Special hot reload with asyncData(context)
568 | function getNuxtChildComponents ($parent, $components = []) {
569 | $parent.$children.forEach(($child) => {
570 | if ($child.$vnode && $child.$vnode.data.nuxtChild && !$components.find(c =>(c.$options.__file === $child.$options.__file))) {
571 | $components.push($child)
572 | }
573 | if ($child.$children && $child.$children.length) {
574 | getNuxtChildComponents($child, $components)
575 | }
576 | })
577 |
578 | return $components
579 | }
580 |
581 | function hotReloadAPI(_app) {
582 | if (!module.hot) return
583 |
584 | let $components = getNuxtChildComponents(_app.$nuxt, [])
585 |
586 | $components.forEach(addHotReload.bind(_app))
587 | }
588 |
589 | function addHotReload ($component, depth) {
590 | if ($component.$vnode.data._hasHotReload) return
591 | $component.$vnode.data._hasHotReload = true
592 |
593 | var _forceUpdate = $component.$forceUpdate.bind($component.$parent)
594 |
595 | $component.$vnode.context.$forceUpdate = async () => {
596 | let Components = getMatchedComponents(router.currentRoute)
597 | let Component = Components[depth]
598 | if (!Component) {
599 | return _forceUpdate()
600 | }
601 | if (typeof Component === 'object' && !Component.options) {
602 | // Updated via vue-router resolveAsyncComponents()
603 | Component = Vue.extend(Component)
604 | Component._Ctor = Component
605 | }
606 | this.error()
607 | let promises = []
608 | const next = function (path) {
609 | this.$loading.finish && this.$loading.finish()
610 | router.push(path)
611 | }
612 | await setContext(app, {
613 | route: router.currentRoute,
614 | isHMR: true,
615 | next: next.bind(this)
616 | })
617 | const context = app.context
618 |
619 | if (this.$loading.start && !this.$loading.manual) {
620 | this.$loading.start()
621 | }
622 |
623 | callMiddleware.call(this, Components, context)
624 | .then(() => {
625 | // If layout changed
626 | if (depth !== 0) {
627 | return
628 | }
629 |
630 | let layout = Component.options.layout || 'default'
631 | if (typeof layout === 'function') {
632 | layout = layout(context)
633 | }
634 | if (this.layoutName === layout) {
635 | return
636 | }
637 | let promise = this.loadLayout(layout)
638 | promise.then(() => {
639 | this.setLayout(layout)
640 | Vue.nextTick(() => hotReloadAPI(this))
641 | })
642 | return promise
643 | })
644 |
645 | .then(() => {
646 | return callMiddleware.call(this, Components, context, this.layout)
647 | })
648 |
649 | .then(() => {
650 | // Call asyncData(context)
651 | let pAsyncData = promisify(Component.options.asyncData || noopData, context)
652 | pAsyncData.then((asyncDataResult) => {
653 | applyAsyncData(Component, asyncDataResult)
654 | this.$loading.increase && this.$loading.increase(30)
655 | })
656 | promises.push(pAsyncData)
657 |
658 | // Call fetch()
659 | Component.options.fetch = Component.options.fetch || noopFetch
660 | let pFetch = Component.options.fetch(context)
661 | if (!pFetch || (!(pFetch instanceof Promise) && (typeof pFetch.then !== 'function'))) { pFetch = Promise.resolve(pFetch) }
662 | pFetch.then(() => this.$loading.increase && this.$loading.increase(30))
663 | promises.push(pFetch)
664 |
665 | return Promise.all(promises)
666 | })
667 | .then(() => {
668 | this.$loading.finish && this.$loading.finish()
669 | _forceUpdate()
670 | setTimeout(() => hotReloadAPI(this), 100)
671 | })
672 | }
673 | }
674 |
675 | async function mountApp (__app) {
676 | // Set global variables
677 | app = __app.app
678 | router = __app.router
679 |
680 | // Create Vue instance
681 | const _app = new Vue(app)
682 |
683 | // Load layout
684 | const layout = NUXT.layout || 'default'
685 | await _app.loadLayout(layout)
686 | _app.setLayout(layout)
687 |
688 | // Mounts Vue app to DOM element
689 | const mount = () => {
690 | _app.$mount('#__nuxt')
691 |
692 | // Add afterEach router hooks
693 | router.afterEach(normalizeComponents)
694 | router.afterEach(fixPrepatch.bind(_app))
695 |
696 | // Listen for first Vue update
697 | Vue.nextTick(() => {
698 | // Call window.{{globals.readyCallback}} callbacks
699 | nuxtReady(_app)
700 |
701 | // Enable hot reloading
702 | hotReloadAPI(_app)
703 | })
704 | }
705 |
706 | // Resolve route components
707 | const Components = await Promise.all(resolveComponents(router))
708 |
709 | // Enable transitions
710 | _app.setTransitions = _app.$options.nuxt.setTransitions.bind(_app)
711 | if (Components.length) {
712 | _app.setTransitions(mapTransitions(Components, router.currentRoute))
713 | _lastPaths = router.currentRoute.matched.map(route => compile(route.path)(router.currentRoute.params))
714 | }
715 |
716 | // Initialize error handler
717 | _app.$loading = {} // To avoid error while _app.$nuxt does not exist
718 | if (NUXT.error) {
719 | _app.error(NUXT.error)
720 | }
721 |
722 | // Add beforeEach router hooks
723 | router.beforeEach(loadAsyncComponents.bind(_app))
724 | router.beforeEach(render.bind(_app))
725 |
726 | // If page already is server rendered
727 | if (NUXT.serverRendered) {
728 | mount()
729 | return
730 | }
731 |
732 | // First render on client-side
733 | const clientFirstMount = () => {
734 | normalizeComponents(router.currentRoute, router.currentRoute)
735 | showNextPage.call(_app, router.currentRoute)
736 | // Don't call fixPrepatch.call(_app, router.currentRoute, router.currentRoute) since it's first render
737 | mount()
738 | }
739 |
740 | render.call(_app, router.currentRoute, router.currentRoute, (path) => {
741 | // If not redirected
742 | if (!path) {
743 | clientFirstMount()
744 | return
745 | }
746 |
747 | // Add a one-time afterEach hook to
748 | // mount the app wait for redirect and route gets resolved
749 | const unregisterHook = router.afterEach((to, from) => {
750 | unregisterHook()
751 | clientFirstMount()
752 | })
753 |
754 | // Push the path and let route to be resolved
755 | router.push(path, undefined, (err) => {
756 | if (err) {
757 | errorHandler(err)
758 | }
759 | })
760 | })
761 | }
762 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/components/nuxt-build-indicator.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 | {{ animatedProgress }}%
13 |
14 |
15 |
16 |
17 |
112 |
113 |
143 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/components/nuxt-child.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 | name: 'NuxtChild',
4 | functional: true,
5 | props: {
6 | nuxtChildKey: {
7 | type: String,
8 | default: ''
9 | },
10 | keepAlive: Boolean,
11 | keepAliveProps: {
12 | type: Object,
13 | default: undefined
14 | }
15 | },
16 | render (h, { parent, data, props }) {
17 | data.nuxtChild = true
18 | const _parent = parent
19 | const transitions = parent.$nuxt.nuxt.transitions
20 | const defaultTransition = parent.$nuxt.nuxt.defaultTransition
21 |
22 | let depth = 0
23 | while (parent) {
24 | if (parent.$vnode && parent.$vnode.data.nuxtChild) {
25 | depth++
26 | }
27 | parent = parent.$parent
28 | }
29 | data.nuxtChildDepth = depth
30 | const transition = transitions[depth] || defaultTransition
31 | const transitionProps = {}
32 | transitionsKeys.forEach((key) => {
33 | if (typeof transition[key] !== 'undefined') {
34 | transitionProps[key] = transition[key]
35 | }
36 | })
37 |
38 | const listeners = {}
39 | listenersKeys.forEach((key) => {
40 | if (typeof transition[key] === 'function') {
41 | listeners[key] = transition[key].bind(_parent)
42 | }
43 | })
44 | // Add triggerScroll event on beforeEnter (fix #1376)
45 | const beforeEnter = listeners.beforeEnter
46 | listeners.beforeEnter = (el) => {
47 | // Ensure to trigger scroll event after calling scrollBehavior
48 | window.$nuxt.$nextTick(() => {
49 | window.$nuxt.$emit('triggerScroll')
50 | })
51 | if (beforeEnter) {
52 | return beforeEnter.call(_parent, el)
53 | }
54 | }
55 |
56 | // make sure that leave is called asynchronous (fix #5703)
57 | if (transition.css === false) {
58 | const leave = listeners.leave
59 |
60 | // only add leave listener when user didnt provide one
61 | // or when it misses the done argument
62 | if (!leave || leave.length < 2) {
63 | listeners.leave = (el, done) => {
64 | if (leave) {
65 | leave.call(_parent, el)
66 | }
67 |
68 | _parent.$nextTick(done)
69 | }
70 | }
71 | }
72 |
73 | let routerView = h('routerView', data)
74 |
75 | if (props.keepAlive) {
76 | routerView = h('keep-alive', { props: props.keepAliveProps }, [routerView])
77 | }
78 |
79 | return h('transition', {
80 | props: transitionProps,
81 | on: listeners
82 | }, [routerView])
83 | }
84 | }
85 |
86 | const transitionsKeys = [
87 | 'name',
88 | 'mode',
89 | 'appear',
90 | 'css',
91 | 'type',
92 | 'duration',
93 | 'enterClass',
94 | 'leaveClass',
95 | 'appearClass',
96 | 'enterActiveClass',
97 | 'enterActiveClass',
98 | 'leaveActiveClass',
99 | 'appearActiveClass',
100 | 'enterToClass',
101 | 'leaveToClass',
102 | 'appearToClass'
103 | ]
104 |
105 | const listenersKeys = [
106 | 'beforeEnter',
107 | 'enter',
108 | 'afterEnter',
109 | 'enterCancelled',
110 | 'beforeLeave',
111 | 'leave',
112 | 'afterLeave',
113 | 'leaveCancelled',
114 | 'beforeAppear',
115 | 'appear',
116 | 'afterAppear',
117 | 'appearCancelled'
118 | ]
119 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/components/nuxt-error.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
{{ message }}
9 |
10 | Back to the home page
11 |
12 |
13 |
An error occurred while rendering the page. Check developer tools console for details.
14 |
15 |
18 |
19 |
20 |
21 |
22 |
52 |
53 |
98 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/components/nuxt-link.client.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | const requestIdleCallback = window.requestIdleCallback ||
4 | function (cb) {
5 | const start = Date.now()
6 | return setTimeout(function () {
7 | cb({
8 | didTimeout: false,
9 | timeRemaining: () => Math.max(0, 50 - (Date.now() - start))
10 | })
11 | }, 1)
12 | }
13 |
14 | const cancelIdleCallback = window.cancelIdleCallback || function (id) {
15 | clearTimeout(id)
16 | }
17 |
18 | const observer = window.IntersectionObserver && new window.IntersectionObserver((entries) => {
19 | entries.forEach(({ intersectionRatio, target: link }) => {
20 | if (intersectionRatio <= 0) {
21 | return
22 | }
23 | link.__prefetch()
24 | })
25 | })
26 |
27 | export default {
28 | name: 'NuxtLink',
29 | extends: Vue.component('RouterLink'),
30 | props: {
31 | prefetch: {
32 | type: Boolean,
33 | default: true
34 | },
35 | noPrefetch: {
36 | type: Boolean,
37 | default: false
38 | }
39 | },
40 | mounted () {
41 | if (this.prefetch && !this.noPrefetch) {
42 | this.handleId = requestIdleCallback(this.observe, { timeout: 2e3 })
43 | }
44 | },
45 | beforeDestroy () {
46 | cancelIdleCallback(this.handleId)
47 |
48 | if (this.__observed) {
49 | observer.unobserve(this.$el)
50 | delete this.$el.__prefetch
51 | }
52 | },
53 | methods: {
54 | observe () {
55 | // If no IntersectionObserver, avoid prefetching
56 | if (!observer) {
57 | return
58 | }
59 | // Add to observer
60 | if (this.shouldPrefetch()) {
61 | this.$el.__prefetch = this.prefetchLink.bind(this)
62 | observer.observe(this.$el)
63 | this.__observed = true
64 | }
65 | },
66 | shouldPrefetch () {
67 | return this.getPrefetchComponents().length > 0
68 | },
69 | canPrefetch () {
70 | const conn = navigator.connection
71 | const hasBadConnection = this.$nuxt.isOffline || (conn && ((conn.effectiveType || '').includes('2g') || conn.saveData))
72 |
73 | return !hasBadConnection
74 | },
75 | getPrefetchComponents () {
76 | const ref = this.$router.resolve(this.to, this.$route, this.append)
77 | const Components = ref.resolved.matched.map(r => r.components.default)
78 |
79 | return Components.filter(Component => typeof Component === 'function' && !Component.options && !Component.__prefetched)
80 | },
81 | prefetchLink () {
82 | if (!this.canPrefetch()) {
83 | return
84 | }
85 | // Stop observing this link (in case of internet connection changes)
86 | observer.unobserve(this.$el)
87 | const Components = this.getPrefetchComponents()
88 |
89 | for (const Component of Components) {
90 | const componentOrPromise = Component()
91 | if (componentOrPromise instanceof Promise) {
92 | componentOrPromise.catch(() => {})
93 | }
94 | Component.__prefetched = true
95 | }
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/components/nuxt-link.server.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | export default {
4 | name: 'NuxtLink',
5 | extends: Vue.component('RouterLink'),
6 | props: {
7 | prefetch: {
8 | type: Boolean,
9 | default: true
10 | },
11 | noPrefetch: {
12 | type: Boolean,
13 | default: false
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/components/nuxt-loading.vue:
--------------------------------------------------------------------------------
1 |
155 |
156 |
178 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/components/nuxt.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { compile } from '../utils'
3 |
4 | import NuxtError from './nuxt-error.vue'
5 |
6 | import NuxtChild from './nuxt-child'
7 |
8 | export default {
9 | name: 'Nuxt',
10 | components: {
11 | NuxtChild,
12 | NuxtError
13 | },
14 | props: {
15 | nuxtChildKey: {
16 | type: String,
17 | default: undefined
18 | },
19 | keepAlive: Boolean,
20 | keepAliveProps: {
21 | type: Object,
22 | default: undefined
23 | },
24 | name: {
25 | type: String,
26 | default: 'default'
27 | }
28 | },
29 | errorCaptured (error) {
30 | // if we receive and error while showing the NuxtError component
31 | // capture the error and force an immediate update so we re-render
32 | // without the NuxtError component
33 | if (this.displayingNuxtError) {
34 | this.errorFromNuxtError = error
35 | this.$forceUpdate()
36 | }
37 | },
38 | computed: {
39 | routerViewKey () {
40 | // If nuxtChildKey prop is given or current route has children
41 | if (typeof this.nuxtChildKey !== 'undefined' || this.$route.matched.length > 1) {
42 | return this.nuxtChildKey || compile(this.$route.matched[0].path)(this.$route.params)
43 | }
44 |
45 | const [matchedRoute] = this.$route.matched
46 |
47 | if (!matchedRoute) {
48 | return this.$route.path
49 | }
50 |
51 | const Component = matchedRoute.components.default
52 |
53 | if (Component && Component.options) {
54 | const { options } = Component
55 |
56 | if (options.key) {
57 | return (typeof options.key === 'function' ? options.key(this.$route) : options.key)
58 | }
59 | }
60 |
61 | const strict = /\/$/.test(matchedRoute.path)
62 | return strict ? this.$route.path : this.$route.path.replace(/\/$/, '')
63 | }
64 | },
65 | beforeCreate () {
66 | Vue.util.defineReactive(this, 'nuxt', this.$root.$options.nuxt)
67 | },
68 | render (h) {
69 | // if there is no error
70 | if (!this.nuxt.err) {
71 | // Directly return nuxt child
72 | return h('NuxtChild', {
73 | key: this.routerViewKey,
74 | props: this.$props
75 | })
76 | }
77 |
78 | // if an error occured within NuxtError show a simple
79 | // error message instead to prevent looping
80 | if (this.errorFromNuxtError) {
81 | this.$nextTick(() => (this.errorFromNuxtError = false))
82 |
83 | return h('div', {}, [
84 | h('h2', 'An error occured while showing the error page'),
85 | h('p', 'Unfortunately an error occured and while showing the error page another error occured'),
86 | h('p', `Error details: ${this.errorFromNuxtError.toString()}`),
87 | h('nuxt-link', { props: { to: '/' } }, 'Go back to home')
88 | ])
89 | }
90 |
91 | // track if we are showing the NuxtError component
92 | this.displayingNuxtError = true
93 | this.$nextTick(() => (this.displayingNuxtError = false))
94 |
95 | return h(NuxtError, {
96 | props: {
97 | error: this.nuxt.err
98 | }
99 | })
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/empty.js:
--------------------------------------------------------------------------------
1 | // This file is intentionally left empty for noop aliases
2 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/firebase-module/main.js:
--------------------------------------------------------------------------------
1 | import firebase from 'firebase/app'
2 |
3 | /** --------------------------------------------------------------------------------------------- **/
4 | /** -------------------------------------- END: Import Scripts ---------------------------------- **/
5 | /** --------------------------------------------------------------------------------------------- **/
6 |
7 | export default async (ctx, inject) => {
8 | const options = {"config":{"apiKey":"AIzaSyBPbKMVLeY1x_1sgikvuP5cyfUd6iFs0l0","authDomain":"vueber-fire-nuxt-demo.firebaseapp.com","databaseURL":"https:\u002F\u002Fvueber-fire-nuxt-demo.firebaseio.com","projectId":"vueber-fire-nuxt-demo","storageBucket":"vueber-fire-nuxt-demo.appspot.com","messagingSenderId":"499279429791","appId":"1:499279429791:web:e0ef9deb856d8ed2c1a2aa"},"services":{"firestore":true}}
9 | const firebaseConfig = options.config
10 |
11 | // Don't include when Firebase is already initialized
12 | if (!firebase.apps.length) {
13 | firebase.initializeApp(firebaseConfig)
14 | }
15 |
16 | /** --------------------------------------------------------------------------------------------- **/
17 | /** -------------------------------------- FIREBASE AUTH ---------------------------------------- **/
18 | /** --------------------------------------------------------------------------------------------- **/
19 |
20 | /** --------------------------------------------------------------------------------------------- **/
21 | /** -------------------------------------- FIREBASE REALTIME DB --------------------------------- **/
22 | /** --------------------------------------------------------------------------------------------- **/
23 |
24 | /** --------------------------------------------------------------------------------------------- **/
25 | /** ---------------------------------------- FIREBASE FIRESTORE --------------------------------- **/
26 | /** --------------------------------------------------------------------------------------------- **/
27 |
28 | await import(/* webpackChunkName: 'firebase-firestore' */'firebase/firestore')
29 |
30 | const fireStore = firebase.firestore()
31 | const fireStoreObj = firebase.firestore
32 | inject('fireStore', fireStore)
33 | inject('fireStoreObj', fireStoreObj)
34 |
35 | const enablePersistence = options.services.firestore.enablePersistence
36 | if (enablePersistence && process.client) {
37 | try {
38 | await fireStore.enablePersistence((
39 | typeof enablePersistence === 'object'
40 | ? enablePersistence
41 | : {}
42 | ))
43 | } catch (err) {
44 | if (err.code == 'failed-precondition') {
45 | console.info("Firestore Persistence not enabled. Multiple tabs open, persistence can only be enabled in one tab at a a time.")
46 | } else if (err.code == 'unimplemented') {
47 | console.info("Firestore Persistence not enabled. The current browser does not support all of the features required to enable persistence.")
48 | }
49 | }
50 | }
51 |
52 | /** --------------------------------------------------------------------------------------------- **/
53 | /** ------------------------------------------ FIREBASE STORAGE --------------------------------- **/
54 | /** --------------------------------------------------------------------------------------------- **/
55 |
56 | /** --------------------------------------------------------------------------------------------- **/
57 | /** ---------------------------------------- FIREBASE FUNCTIONS --------------------------------- **/
58 | /** --------------------------------------------------------------------------------------------- **/
59 |
60 | /** --------------------------------------------------------------------------------------------- **/
61 | /** ---------------------------------------- FIREBASE MESSAGING --------------------------------- **/
62 | /** --------------------------------------------------------------------------------------------- **/
63 |
64 | /** --------------------------------------------------------------------------------------------- **/
65 | /** -------------------------------------- FIREBASE REALTIME DB --------------------------------- **/
66 | /** --------------------------------------------------------------------------------------------- **/
67 |
68 | // Firebase Performance can only be initiated on client side
69 |
70 | /** --------------------------------------------------------------------------------------------- **/
71 | /** ---------------------------------------- FIREBASE ANALYTICS --------------------------------- **/
72 | /** --------------------------------------------------------------------------------------------- **/
73 |
74 | // Firebase Analytics can only be initiated on the client side
75 |
76 | /** --------------------------------------------------------------------------------------------- **/
77 | /** --------------------------------- FIREBASE REMOTE CONFIG DB --------------------------------- **/
78 | /** --------------------------------------------------------------------------------------------- **/
79 | }
80 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Meta from 'vue-meta'
3 | import ClientOnly from 'vue-client-only'
4 | import NoSsr from 'vue-no-ssr'
5 | import { createRouter } from './router.js'
6 | import NuxtChild from './components/nuxt-child.js'
7 | import NuxtError from './components/nuxt-error.vue'
8 | import Nuxt from './components/nuxt.js'
9 | import App from './App.js'
10 | import { setContext, getLocation, getRouteData, normalizeError } from './utils'
11 |
12 | /* Plugins */
13 |
14 | import nuxt_plugin_main_fd5aff36 from 'nuxt_plugin_main_fd5aff36' // Source: ./firebase-module/main.js (mode: 'all')
15 | import nuxt_plugin_vuefire_e59e0770 from 'nuxt_plugin_vuefire_e59e0770' // Source: ../plugins/vuefire (mode: 'all')
16 |
17 | // Component:
18 | Vue.component(ClientOnly.name, ClientOnly)
19 |
20 | // TODO: Remove in Nuxt 3:
21 | Vue.component(NoSsr.name, {
22 | ...NoSsr,
23 | render (h, ctx) {
24 | if (process.client && !NoSsr._warned) {
25 | NoSsr._warned = true
26 |
27 | console.warn(' has been deprecated and will be removed in Nuxt 3, please use instead')
28 | }
29 | return NoSsr.render(h, ctx)
30 | }
31 | })
32 |
33 | // Component:
34 | Vue.component(NuxtChild.name, NuxtChild)
35 | Vue.component('NChild', NuxtChild)
36 |
37 | // Component NuxtLink is imported in server.js or client.js
38 |
39 | // Component:
40 | Vue.component(Nuxt.name, Nuxt)
41 |
42 | Vue.use(Meta, {"keyName":"head","attribute":"data-n-head","ssrAttribute":"data-n-head-ssr","tagIDKeyName":"hid"})
43 |
44 | const defaultTransition = {"name":"page","mode":"out-in","appear":false,"appearClass":"appear","appearActiveClass":"appear-active","appearToClass":"appear-to"}
45 |
46 | async function createApp (ssrContext) {
47 | const router = await createRouter(ssrContext)
48 |
49 | // Create Root instance
50 |
51 | // here we inject the router and store to all child components,
52 | // making them available everywhere as `this.$router` and `this.$store`.
53 | const app = {
54 | router,
55 | nuxt: {
56 | defaultTransition,
57 | transitions: [defaultTransition],
58 | setTransitions (transitions) {
59 | if (!Array.isArray(transitions)) {
60 | transitions = [transitions]
61 | }
62 | transitions = transitions.map((transition) => {
63 | if (!transition) {
64 | transition = defaultTransition
65 | } else if (typeof transition === 'string') {
66 | transition = Object.assign({}, defaultTransition, { name: transition })
67 | } else {
68 | transition = Object.assign({}, defaultTransition, transition)
69 | }
70 | return transition
71 | })
72 | this.$options.nuxt.transitions = transitions
73 | return transitions
74 | },
75 |
76 | err: null,
77 | dateErr: null,
78 | error (err) {
79 | err = err || null
80 | app.context._errored = Boolean(err)
81 | err = err ? normalizeError(err) : null
82 | const nuxt = this.nuxt || this.$options.nuxt
83 | nuxt.dateErr = Date.now()
84 | nuxt.err = err
85 | // Used in src/server.js
86 | if (ssrContext) {
87 | ssrContext.nuxt.error = err
88 | }
89 | return err
90 | }
91 | },
92 | ...App
93 | }
94 |
95 | const next = ssrContext ? ssrContext.next : location => app.router.push(location)
96 | // Resolve route
97 | let route
98 | if (ssrContext) {
99 | route = router.resolve(ssrContext.url).route
100 | } else {
101 | const path = getLocation(router.options.base, router.options.mode)
102 | route = router.resolve(path).route
103 | }
104 |
105 | // Set context to app.context
106 | await setContext(app, {
107 | route,
108 | next,
109 | error: app.nuxt.error.bind(app),
110 | payload: ssrContext ? ssrContext.payload : undefined,
111 | req: ssrContext ? ssrContext.req : undefined,
112 | res: ssrContext ? ssrContext.res : undefined,
113 | beforeRenderFns: ssrContext ? ssrContext.beforeRenderFns : undefined,
114 | ssrContext
115 | })
116 |
117 | const inject = function (key, value) {
118 | if (!key) {
119 | throw new Error('inject(key, value) has no key provided')
120 | }
121 | if (value === undefined) {
122 | throw new Error('inject(key, value) has no value provided')
123 | }
124 |
125 | key = '$' + key
126 | // Add into app
127 | app[key] = value
128 |
129 | // Check if plugin not already installed
130 | const installKey = '__nuxt_' + key + '_installed__'
131 | if (Vue[installKey]) {
132 | return
133 | }
134 | Vue[installKey] = true
135 | // Call Vue.use() to install the plugin into vm
136 | Vue.use(() => {
137 | if (!Object.prototype.hasOwnProperty.call(Vue, key)) {
138 | Object.defineProperty(Vue.prototype, key, {
139 | get () {
140 | return this.$root.$options[key]
141 | }
142 | })
143 | }
144 | })
145 | }
146 |
147 | // Plugin execution
148 |
149 | if (typeof nuxt_plugin_main_fd5aff36 === 'function') {
150 | await nuxt_plugin_main_fd5aff36(app.context, inject)
151 | }
152 |
153 | if (typeof nuxt_plugin_vuefire_e59e0770 === 'function') {
154 | await nuxt_plugin_vuefire_e59e0770(app.context, inject)
155 | }
156 |
157 | // If server-side, wait for async component to be resolved first
158 | if (process.server && ssrContext && ssrContext.url) {
159 | await new Promise((resolve, reject) => {
160 | router.push(ssrContext.url, resolve, () => {
161 | // navigated to a different route in router guard
162 | const unregister = router.afterEach(async (to, from, next) => {
163 | ssrContext.url = to.fullPath
164 | app.context.route = await getRouteData(to)
165 | app.context.params = to.params || {}
166 | app.context.query = to.query || {}
167 | unregister()
168 | resolve()
169 | })
170 | })
171 | })
172 | }
173 |
174 | return {
175 | app,
176 | router
177 | }
178 | }
179 |
180 | export { createApp, NuxtError }
181 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/loading.html:
--------------------------------------------------------------------------------
1 |
97 |
98 |
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/middleware.js:
--------------------------------------------------------------------------------
1 | const middleware = {}
2 |
3 | export default middleware
4 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import { interopDefault } from './utils'
4 | import scrollBehavior from './router.scrollBehavior.js'
5 |
6 | const _32f3a421 = () => interopDefault(import('../pages/index.vue' /* webpackChunkName: "pages/index" */))
7 |
8 | // TODO: remove in Nuxt 3
9 | const emptyFn = () => {}
10 | const originalPush = Router.prototype.push
11 | Router.prototype.push = function push (location, onComplete = emptyFn, onAbort) {
12 | return originalPush.call(this, location, onComplete, onAbort)
13 | }
14 |
15 | Vue.use(Router)
16 |
17 | export const routerOptions = {
18 | mode: 'history',
19 | base: decodeURI('/'),
20 | linkActiveClass: 'nuxt-link-active',
21 | linkExactActiveClass: 'nuxt-link-exact-active',
22 | scrollBehavior,
23 |
24 | routes: [{
25 | path: "/",
26 | component: _32f3a421,
27 | name: "index"
28 | }],
29 |
30 | fallback: false
31 | }
32 |
33 | export function createRouter () {
34 | return new Router(routerOptions)
35 | }
36 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/router.scrollBehavior.js:
--------------------------------------------------------------------------------
1 | import { getMatchedComponents } from './utils'
2 |
3 | if (process.client) {
4 | if ('scrollRestoration' in window.history) {
5 | window.history.scrollRestoration = 'manual'
6 |
7 | // reset scrollRestoration to auto when leaving page, allowing page reload
8 | // and back-navigation from other pages to use the browser to restore the
9 | // scrolling position.
10 | window.addEventListener('beforeunload', () => {
11 | window.history.scrollRestoration = 'auto'
12 | })
13 |
14 | // Setting scrollRestoration to manual again when returning to this page.
15 | window.addEventListener('load', () => {
16 | window.history.scrollRestoration = 'manual'
17 | })
18 | }
19 | }
20 |
21 | export default function (to, from, savedPosition) {
22 | // if the returned position is falsy or an empty object,
23 | // will retain current scroll position.
24 | let position = false
25 |
26 | // if no children detected and scrollToTop is not explicitly disabled
27 | const Pages = getMatchedComponents(to)
28 | if (
29 | Pages.length < 2 &&
30 | Pages.every(Page => Page.options.scrollToTop !== false)
31 | ) {
32 | // scroll to the top of the page
33 | position = { x: 0, y: 0 }
34 | } else if (Pages.some(Page => Page.options.scrollToTop)) {
35 | // if one of the children has scrollToTop option set to true
36 | position = { x: 0, y: 0 }
37 | }
38 |
39 | // savedPosition is only available for popstate navigations (back button)
40 | if (savedPosition) {
41 | position = savedPosition
42 | }
43 |
44 | const nuxt = window.$nuxt
45 |
46 | // triggerScroll is only fired when a new component is loaded
47 | if (to.path === from.path && to.hash !== from.hash) {
48 | nuxt.$nextTick(() => nuxt.$emit('triggerScroll'))
49 | }
50 |
51 | return new Promise((resolve) => {
52 | // wait for the out transition to complete (if necessary)
53 | nuxt.$once('triggerScroll', () => {
54 | // coords will be used if no selector is provided,
55 | // or if the selector didn't match any element.
56 | if (to.hash) {
57 | let hash = to.hash
58 | // CSS.escape() is not supported with IE and Edge.
59 | if (typeof window.CSS !== 'undefined' && typeof window.CSS.escape !== 'undefined') {
60 | hash = '#' + window.CSS.escape(hash.substr(1))
61 | }
62 | try {
63 | if (document.querySelector(hash)) {
64 | // scroll to anchor by returning the selector
65 | position = { selector: hash }
66 | }
67 | } catch (e) {
68 | console.warn('Failed to save scroll position. Please add CSS.escape() polyfill (https://github.com/mathiasbynens/CSS.escape).')
69 | }
70 | }
71 | resolve(position)
72 | })
73 | })
74 | }
75 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/server.js:
--------------------------------------------------------------------------------
1 | import { stringify } from 'querystring'
2 | import Vue from 'vue'
3 | import fetch from 'node-fetch'
4 | import middleware from './middleware.js'
5 | import {
6 | applyAsyncData,
7 | middlewareSeries,
8 | sanitizeComponent,
9 | getMatchedComponents,
10 | promisify
11 | } from './utils.js'
12 | import { createApp, NuxtError } from './index.js'
13 | import NuxtLink from './components/nuxt-link.server.js' // should be included after ./index.js
14 |
15 | // Component:
16 | Vue.component(NuxtLink.name, NuxtLink)
17 | Vue.component('NLink', NuxtLink)
18 |
19 | if (!global.fetch) { global.fetch = fetch }
20 |
21 | const noopApp = () => new Vue({ render: h => h('div') })
22 |
23 | function urlJoin () {
24 | return Array.prototype.slice.call(arguments).join('/').replace(/\/+/g, '/')
25 | }
26 |
27 | const createNext = ssrContext => (opts) => {
28 | ssrContext.redirected = opts
29 | // If nuxt generate
30 | if (!ssrContext.res) {
31 | ssrContext.nuxt.serverRendered = false
32 | return
33 | }
34 | opts.query = stringify(opts.query)
35 | opts.path = opts.path + (opts.query ? '?' + opts.query : '')
36 | const routerBase = '/'
37 | if (!opts.path.startsWith('http') && (routerBase !== '/' && !opts.path.startsWith(routerBase))) {
38 | opts.path = urlJoin(routerBase, opts.path)
39 | }
40 | // Avoid loop redirect
41 | if (opts.path === ssrContext.url) {
42 | ssrContext.redirected = false
43 | return
44 | }
45 | ssrContext.res.writeHead(opts.status, {
46 | Location: opts.path
47 | })
48 | ssrContext.res.end()
49 | }
50 |
51 | // This exported function will be called by `bundleRenderer`.
52 | // This is where we perform data-prefetching to determine the
53 | // state of our application before actually rendering it.
54 | // Since data fetching is async, this function is expected to
55 | // return a Promise that resolves to the app instance.
56 | export default async (ssrContext) => {
57 | // Create ssrContext.next for simulate next() of beforeEach() when wanted to redirect
58 | ssrContext.redirected = false
59 | ssrContext.next = createNext(ssrContext)
60 | // Used for beforeNuxtRender({ Components, nuxtState })
61 | ssrContext.beforeRenderFns = []
62 | // Nuxt object (window{{globals.context}}, defaults to window.__NUXT__)
63 | ssrContext.nuxt = { layout: 'default', data: [], error: null, serverRendered: true }
64 | // Create the app definition and the instance (created for each request)
65 | const { app, router } = await createApp(ssrContext)
66 | const _app = new Vue(app)
67 |
68 | // Add meta infos (used in renderer.js)
69 | ssrContext.meta = _app.$meta()
70 |
71 | // Keep asyncData for each matched component in ssrContext (used in app/utils.js via this.$ssrContext)
72 | ssrContext.asyncData = {}
73 |
74 | const beforeRender = async () => {
75 | // Call beforeNuxtRender() methods
76 | await Promise.all(ssrContext.beforeRenderFns.map(fn => promisify(fn, { Components, nuxtState: ssrContext.nuxt })))
77 | }
78 |
79 | const renderErrorPage = async () => {
80 | // Load layout for error page
81 | const layout = (NuxtError.options || NuxtError).layout
82 | const errLayout = typeof layout === 'function' ? layout.call(NuxtError, app.context) : layout
83 | ssrContext.nuxt.layout = errLayout || 'default'
84 | await _app.loadLayout(errLayout)
85 | _app.setLayout(errLayout)
86 |
87 | await beforeRender()
88 | return _app
89 | }
90 | const render404Page = () => {
91 | app.context.error({ statusCode: 404, path: ssrContext.url, message: 'This page could not be found' })
92 | return renderErrorPage()
93 | }
94 |
95 | const s = Date.now()
96 |
97 | // Components are already resolved by setContext -> getRouteData (app/utils.js)
98 | const Components = getMatchedComponents(router.match(ssrContext.url))
99 |
100 | /*
101 | ** Call global middleware (nuxt.config.js)
102 | */
103 | let midd = []
104 | midd = midd.map((name) => {
105 | if (typeof name === 'function') {
106 | return name
107 | }
108 | if (typeof middleware[name] !== 'function') {
109 | app.context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
110 | }
111 | return middleware[name]
112 | })
113 | await middlewareSeries(midd, app.context)
114 | // ...If there is a redirect or an error, stop the process
115 | if (ssrContext.redirected) {
116 | return noopApp()
117 | }
118 | if (ssrContext.nuxt.error) {
119 | return renderErrorPage()
120 | }
121 |
122 | /*
123 | ** Set layout
124 | */
125 | let layout = Components.length ? Components[0].options.layout : NuxtError.layout
126 | if (typeof layout === 'function') {
127 | layout = layout(app.context)
128 | }
129 | await _app.loadLayout(layout)
130 | if (ssrContext.nuxt.error) {
131 | return renderErrorPage()
132 | }
133 | layout = _app.setLayout(layout)
134 | ssrContext.nuxt.layout = _app.layoutName
135 |
136 | /*
137 | ** Call middleware (layout + pages)
138 | */
139 | midd = []
140 |
141 | layout = sanitizeComponent(layout)
142 | if (layout.options.middleware) {
143 | midd = midd.concat(layout.options.middleware)
144 | }
145 |
146 | Components.forEach((Component) => {
147 | if (Component.options.middleware) {
148 | midd = midd.concat(Component.options.middleware)
149 | }
150 | })
151 | midd = midd.map((name) => {
152 | if (typeof name === 'function') {
153 | return name
154 | }
155 | if (typeof middleware[name] !== 'function') {
156 | app.context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
157 | }
158 | return middleware[name]
159 | })
160 | await middlewareSeries(midd, app.context)
161 | // ...If there is a redirect or an error, stop the process
162 | if (ssrContext.redirected) {
163 | return noopApp()
164 | }
165 | if (ssrContext.nuxt.error) {
166 | return renderErrorPage()
167 | }
168 |
169 | /*
170 | ** Call .validate()
171 | */
172 | let isValid = true
173 | try {
174 | for (const Component of Components) {
175 | if (typeof Component.options.validate !== 'function') {
176 | continue
177 | }
178 |
179 | isValid = await Component.options.validate(app.context)
180 |
181 | if (!isValid) {
182 | break
183 | }
184 | }
185 | } catch (validationError) {
186 | // ...If .validate() threw an error
187 | app.context.error({
188 | statusCode: validationError.statusCode || '500',
189 | message: validationError.message
190 | })
191 | return renderErrorPage()
192 | }
193 |
194 | // ...If .validate() returned false
195 | if (!isValid) {
196 | // Don't server-render the page in generate mode
197 | if (ssrContext._generate) {
198 | ssrContext.nuxt.serverRendered = false
199 | }
200 | // Render a 404 error page
201 | return render404Page()
202 | }
203 |
204 | // If no Components found, returns 404
205 | if (!Components.length) {
206 | return render404Page()
207 | }
208 |
209 | // Call asyncData & fetch hooks on components matched by the route.
210 | const asyncDatas = await Promise.all(Components.map((Component) => {
211 | const promises = []
212 |
213 | // Call asyncData(context)
214 | if (Component.options.asyncData && typeof Component.options.asyncData === 'function') {
215 | const promise = promisify(Component.options.asyncData, app.context)
216 | promise.then((asyncDataResult) => {
217 | ssrContext.asyncData[Component.cid] = asyncDataResult
218 | applyAsyncData(Component)
219 | return asyncDataResult
220 | })
221 | promises.push(promise)
222 | } else {
223 | promises.push(null)
224 | }
225 |
226 | // Call fetch(context)
227 | if (Component.options.fetch) {
228 | promises.push(Component.options.fetch(app.context))
229 | } else {
230 | promises.push(null)
231 | }
232 |
233 | return Promise.all(promises)
234 | }))
235 |
236 | if (process.env.DEBUG && asyncDatas.length) console.debug('Data fetching ' + ssrContext.url + ': ' + (Date.now() - s) + 'ms')
237 |
238 | // datas are the first row of each
239 | ssrContext.nuxt.data = asyncDatas.map(r => r[0] || {})
240 |
241 | // ...If there is a redirect or an error, stop the process
242 | if (ssrContext.redirected) {
243 | return noopApp()
244 | }
245 | if (ssrContext.nuxt.error) {
246 | return renderErrorPage()
247 | }
248 |
249 | // Call beforeNuxtRender methods & add store state
250 | await beforeRender()
251 |
252 | return _app
253 | }
254 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/utils.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | // window.{{globals.loadedCallback}} hook
4 | // Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
5 | if (process.client) {
6 | window.onNuxtReadyCbs = []
7 | window.onNuxtReady = (cb) => {
8 | window.onNuxtReadyCbs.push(cb)
9 | }
10 | }
11 |
12 | export function empty () {}
13 |
14 | export function globalHandleError (error) {
15 | if (Vue.config.errorHandler) {
16 | Vue.config.errorHandler(error)
17 | }
18 | }
19 |
20 | export function interopDefault (promise) {
21 | return promise.then(m => m.default || m)
22 | }
23 |
24 | export function applyAsyncData (Component, asyncData) {
25 | if (
26 | // For SSR, we once all this function without second param to just apply asyncData
27 | // Prevent doing this for each SSR request
28 | !asyncData && Component.options.__hasNuxtData
29 | ) {
30 | return
31 | }
32 |
33 | const ComponentData = Component.options._originDataFn || Component.options.data || function () { return {} }
34 | Component.options._originDataFn = ComponentData
35 |
36 | Component.options.data = function () {
37 | const data = ComponentData.call(this, this)
38 | if (this.$ssrContext) {
39 | asyncData = this.$ssrContext.asyncData[Component.cid]
40 | }
41 | return { ...data, ...asyncData }
42 | }
43 |
44 | Component.options.__hasNuxtData = true
45 |
46 | if (Component._Ctor && Component._Ctor.options) {
47 | Component._Ctor.options.data = Component.options.data
48 | }
49 | }
50 |
51 | export function sanitizeComponent (Component) {
52 | // If Component already sanitized
53 | if (Component.options && Component._Ctor === Component) {
54 | return Component
55 | }
56 | if (!Component.options) {
57 | Component = Vue.extend(Component) // fix issue #6
58 | Component._Ctor = Component
59 | } else {
60 | Component._Ctor = Component
61 | Component.extendOptions = Component.options
62 | }
63 | // For debugging purpose
64 | if (!Component.options.name && Component.options.__file) {
65 | Component.options.name = Component.options.__file
66 | }
67 | return Component
68 | }
69 |
70 | export function getMatchedComponents (route, matches = false, prop = 'components') {
71 | return Array.prototype.concat.apply([], route.matched.map((m, index) => {
72 | return Object.keys(m[prop]).map((key) => {
73 | matches && matches.push(index)
74 | return m[prop][key]
75 | })
76 | }))
77 | }
78 |
79 | export function getMatchedComponentsInstances (route, matches = false) {
80 | return getMatchedComponents(route, matches, 'instances')
81 | }
82 |
83 | export function flatMapComponents (route, fn) {
84 | return Array.prototype.concat.apply([], route.matched.map((m, index) => {
85 | return Object.keys(m.components).reduce((promises, key) => {
86 | if (m.components[key]) {
87 | promises.push(fn(m.components[key], m.instances[key], m, key, index))
88 | } else {
89 | delete m.components[key]
90 | }
91 | return promises
92 | }, [])
93 | }))
94 | }
95 |
96 | export function resolveRouteComponents (route, fn) {
97 | return Promise.all(
98 | flatMapComponents(route, async (Component, instance, match, key) => {
99 | // If component is a function, resolve it
100 | if (typeof Component === 'function' && !Component.options) {
101 | Component = await Component()
102 | }
103 | match.components[key] = Component = sanitizeComponent(Component)
104 | return typeof fn === 'function' ? fn(Component, instance, match, key) : Component
105 | })
106 | )
107 | }
108 |
109 | export async function getRouteData (route) {
110 | if (!route) {
111 | return
112 | }
113 | // Make sure the components are resolved (code-splitting)
114 | await resolveRouteComponents(route)
115 | // Send back a copy of route with meta based on Component definition
116 | return {
117 | ...route,
118 | meta: getMatchedComponents(route).map((Component, index) => {
119 | return { ...Component.options.meta, ...(route.matched[index] || {}).meta }
120 | })
121 | }
122 | }
123 |
124 | export async function setContext (app, context) {
125 | // If context not defined, create it
126 | if (!app.context) {
127 | app.context = {
128 | isStatic: process.static,
129 | isDev: true,
130 | isHMR: false,
131 | app,
132 |
133 | payload: context.payload,
134 | error: context.error,
135 | base: '/',
136 | env: {}
137 | }
138 | // Only set once
139 | if (context.req) {
140 | app.context.req = context.req
141 | }
142 | if (context.res) {
143 | app.context.res = context.res
144 | }
145 | if (context.ssrContext) {
146 | app.context.ssrContext = context.ssrContext
147 | }
148 | app.context.redirect = (status, path, query) => {
149 | if (!status) {
150 | return
151 | }
152 | app.context._redirected = true
153 | // if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
154 | let pathType = typeof path
155 | if (typeof status !== 'number' && (pathType === 'undefined' || pathType === 'object')) {
156 | query = path || {}
157 | path = status
158 | pathType = typeof path
159 | status = 302
160 | }
161 | if (pathType === 'object') {
162 | path = app.router.resolve(path).route.fullPath
163 | }
164 | // "/absolute/route", "./relative/route" or "../relative/route"
165 | if (/(^[.]{1,2}\/)|(^\/(?!\/))/.test(path)) {
166 | app.context.next({
167 | path,
168 | query,
169 | status
170 | })
171 | } else {
172 | path = formatUrl(path, query)
173 | if (process.server) {
174 | app.context.next({
175 | path,
176 | status
177 | })
178 | }
179 | if (process.client) {
180 | // https://developer.mozilla.org/en-US/docs/Web/API/Location/replace
181 | window.location.replace(path)
182 |
183 | // Throw a redirect error
184 | throw new Error('ERR_REDIRECT')
185 | }
186 | }
187 | }
188 | if (process.server) {
189 | app.context.beforeNuxtRender = fn => context.beforeRenderFns.push(fn)
190 | }
191 | if (process.client) {
192 | app.context.nuxtState = window.__NUXT__
193 | }
194 | }
195 |
196 | // Dynamic keys
197 | const [currentRouteData, fromRouteData] = await Promise.all([
198 | getRouteData(context.route),
199 | getRouteData(context.from)
200 | ])
201 |
202 | if (context.route) {
203 | app.context.route = currentRouteData
204 | }
205 |
206 | if (context.from) {
207 | app.context.from = fromRouteData
208 | }
209 |
210 | app.context.next = context.next
211 | app.context._redirected = false
212 | app.context._errored = false
213 | app.context.isHMR = Boolean(context.isHMR)
214 | app.context.params = app.context.route.params || {}
215 | app.context.query = app.context.route.query || {}
216 | }
217 |
218 | export function middlewareSeries (promises, appContext) {
219 | if (!promises.length || appContext._redirected || appContext._errored) {
220 | return Promise.resolve()
221 | }
222 | return promisify(promises[0], appContext)
223 | .then(() => {
224 | return middlewareSeries(promises.slice(1), appContext)
225 | })
226 | }
227 |
228 | export function promisify (fn, context) {
229 | let promise
230 | if (fn.length === 2) {
231 | console.warn('Callback-based asyncData, fetch or middleware calls are deprecated. ' +
232 | 'Please switch to promises or async/await syntax')
233 |
234 | // fn(context, callback)
235 | promise = new Promise((resolve) => {
236 | fn(context, function (err, data) {
237 | if (err) {
238 | context.error(err)
239 | }
240 | data = data || {}
241 | resolve(data)
242 | })
243 | })
244 | } else {
245 | promise = fn(context)
246 | }
247 |
248 | if (promise && promise instanceof Promise && typeof promise.then === 'function') {
249 | return promise
250 | }
251 | return Promise.resolve(promise)
252 | }
253 |
254 | // Imported from vue-router
255 | export function getLocation (base, mode) {
256 | let path = decodeURI(window.location.pathname)
257 | if (mode === 'hash') {
258 | return window.location.hash.replace(/^#\//, '')
259 | }
260 | if (base && path.indexOf(base) === 0) {
261 | path = path.slice(base.length)
262 | }
263 | return (path || '/') + window.location.search + window.location.hash
264 | }
265 |
266 | // Imported from path-to-regexp
267 |
268 | /**
269 | * Compile a string to a template function for the path.
270 | *
271 | * @param {string} str
272 | * @param {Object=} options
273 | * @return {!function(Object=, Object=)}
274 | */
275 | export function compile (str, options) {
276 | return tokensToFunction(parse(str, options), options)
277 | }
278 |
279 | export function getQueryDiff (toQuery, fromQuery) {
280 | const diff = {}
281 | const queries = { ...toQuery, ...fromQuery }
282 | for (const k in queries) {
283 | if (String(toQuery[k]) !== String(fromQuery[k])) {
284 | diff[k] = true
285 | }
286 | }
287 | return diff
288 | }
289 |
290 | export function normalizeError (err) {
291 | let message
292 | if (!(err.message || typeof err === 'string')) {
293 | try {
294 | message = JSON.stringify(err, null, 2)
295 | } catch (e) {
296 | message = `[${err.constructor.name}]`
297 | }
298 | } else {
299 | message = err.message || err
300 | }
301 | return {
302 | ...err,
303 | message,
304 | statusCode: (err.statusCode || err.status || (err.response && err.response.status) || 500)
305 | }
306 | }
307 |
308 | /**
309 | * The main path matching regexp utility.
310 | *
311 | * @type {RegExp}
312 | */
313 | const PATH_REGEXP = new RegExp([
314 | // Match escaped characters that would otherwise appear in future matches.
315 | // This allows the user to escape special characters that won't transform.
316 | '(\\\\.)',
317 | // Match Express-style parameters and un-named parameters with a prefix
318 | // and optional suffixes. Matches appear as:
319 | //
320 | // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
321 | // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined]
322 | // "/*" => ["/", undefined, undefined, undefined, undefined, "*"]
323 | '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))'
324 | ].join('|'), 'g')
325 |
326 | /**
327 | * Parse a string for the raw tokens.
328 | *
329 | * @param {string} str
330 | * @param {Object=} options
331 | * @return {!Array}
332 | */
333 | function parse (str, options) {
334 | const tokens = []
335 | let key = 0
336 | let index = 0
337 | let path = ''
338 | const defaultDelimiter = (options && options.delimiter) || '/'
339 | let res
340 |
341 | while ((res = PATH_REGEXP.exec(str)) != null) {
342 | const m = res[0]
343 | const escaped = res[1]
344 | const offset = res.index
345 | path += str.slice(index, offset)
346 | index = offset + m.length
347 |
348 | // Ignore already escaped sequences.
349 | if (escaped) {
350 | path += escaped[1]
351 | continue
352 | }
353 |
354 | const next = str[index]
355 | const prefix = res[2]
356 | const name = res[3]
357 | const capture = res[4]
358 | const group = res[5]
359 | const modifier = res[6]
360 | const asterisk = res[7]
361 |
362 | // Push the current path onto the tokens.
363 | if (path) {
364 | tokens.push(path)
365 | path = ''
366 | }
367 |
368 | const partial = prefix != null && next != null && next !== prefix
369 | const repeat = modifier === '+' || modifier === '*'
370 | const optional = modifier === '?' || modifier === '*'
371 | const delimiter = res[2] || defaultDelimiter
372 | const pattern = capture || group
373 |
374 | tokens.push({
375 | name: name || key++,
376 | prefix: prefix || '',
377 | delimiter,
378 | optional,
379 | repeat,
380 | partial,
381 | asterisk: Boolean(asterisk),
382 | pattern: pattern ? escapeGroup(pattern) : (asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?')
383 | })
384 | }
385 |
386 | // Match any characters still remaining.
387 | if (index < str.length) {
388 | path += str.substr(index)
389 | }
390 |
391 | // If the path exists, push it onto the end.
392 | if (path) {
393 | tokens.push(path)
394 | }
395 |
396 | return tokens
397 | }
398 |
399 | /**
400 | * Prettier encoding of URI path segments.
401 | *
402 | * @param {string}
403 | * @return {string}
404 | */
405 | function encodeURIComponentPretty (str, slashAllowed) {
406 | const re = slashAllowed ? /[?#]/g : /[/?#]/g
407 | return encodeURI(str).replace(re, (c) => {
408 | return '%' + c.charCodeAt(0).toString(16).toUpperCase()
409 | })
410 | }
411 |
412 | /**
413 | * Encode the asterisk parameter. Similar to `pretty`, but allows slashes.
414 | *
415 | * @param {string}
416 | * @return {string}
417 | */
418 | function encodeAsterisk (str) {
419 | return encodeURIComponentPretty(str, true)
420 | }
421 |
422 | /**
423 | * Escape a regular expression string.
424 | *
425 | * @param {string} str
426 | * @return {string}
427 | */
428 | function escapeString (str) {
429 | return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1')
430 | }
431 |
432 | /**
433 | * Escape the capturing group by escaping special characters and meaning.
434 | *
435 | * @param {string} group
436 | * @return {string}
437 | */
438 | function escapeGroup (group) {
439 | return group.replace(/([=!:$/()])/g, '\\$1')
440 | }
441 |
442 | /**
443 | * Expose a method for transforming tokens into the path function.
444 | */
445 | function tokensToFunction (tokens, options) {
446 | // Compile all the tokens into regexps.
447 | const matches = new Array(tokens.length)
448 |
449 | // Compile all the patterns before compilation.
450 | for (let i = 0; i < tokens.length; i++) {
451 | if (typeof tokens[i] === 'object') {
452 | matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$', flags(options))
453 | }
454 | }
455 |
456 | return function (obj, opts) {
457 | let path = ''
458 | const data = obj || {}
459 | const options = opts || {}
460 | const encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent
461 |
462 | for (let i = 0; i < tokens.length; i++) {
463 | const token = tokens[i]
464 |
465 | if (typeof token === 'string') {
466 | path += token
467 |
468 | continue
469 | }
470 |
471 | const value = data[token.name || 'pathMatch']
472 | let segment
473 |
474 | if (value == null) {
475 | if (token.optional) {
476 | // Prepend partial segment prefixes.
477 | if (token.partial) {
478 | path += token.prefix
479 | }
480 |
481 | continue
482 | } else {
483 | throw new TypeError('Expected "' + token.name + '" to be defined')
484 | }
485 | }
486 |
487 | if (Array.isArray(value)) {
488 | if (!token.repeat) {
489 | throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`')
490 | }
491 |
492 | if (value.length === 0) {
493 | if (token.optional) {
494 | continue
495 | } else {
496 | throw new TypeError('Expected "' + token.name + '" to not be empty')
497 | }
498 | }
499 |
500 | for (let j = 0; j < value.length; j++) {
501 | segment = encode(value[j])
502 |
503 | if (!matches[i].test(segment)) {
504 | throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`')
505 | }
506 |
507 | path += (j === 0 ? token.prefix : token.delimiter) + segment
508 | }
509 |
510 | continue
511 | }
512 |
513 | segment = token.asterisk ? encodeAsterisk(value) : encode(value)
514 |
515 | if (!matches[i].test(segment)) {
516 | throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')
517 | }
518 |
519 | path += token.prefix + segment
520 | }
521 |
522 | return path
523 | }
524 | }
525 |
526 | /**
527 | * Get the flags for a regexp from the options.
528 | *
529 | * @param {Object} options
530 | * @return {string}
531 | */
532 | function flags (options) {
533 | return options && options.sensitive ? '' : 'i'
534 | }
535 |
536 | /**
537 | * Format given url, append query to url query string
538 | *
539 | * @param {string} url
540 | * @param {string} query
541 | * @return {string}
542 | */
543 | function formatUrl (url, query) {
544 | let protocol
545 | const index = url.indexOf('://')
546 | if (index !== -1) {
547 | protocol = url.substring(0, index)
548 | url = url.substring(index + 3)
549 | } else if (url.startsWith('//')) {
550 | url = url.substring(2)
551 | }
552 |
553 | let parts = url.split('/')
554 | let result = (protocol ? protocol + '://' : '//') + parts.shift()
555 |
556 | let path = parts.filter(Boolean).join('/')
557 | let hash
558 | parts = path.split('#')
559 | if (parts.length === 2) {
560 | [path, hash] = parts
561 | }
562 |
563 | result += path ? '/' + path : ''
564 |
565 | if (query && JSON.stringify(query) !== '{}') {
566 | result += (url.split('?').length === 2 ? '&' : '?') + formatQuery(query)
567 | }
568 | result += hash ? '#' + hash : ''
569 |
570 | return result
571 | }
572 |
573 | /**
574 | * Transform data object to query string
575 | *
576 | * @param {object} query
577 | * @return {string}
578 | */
579 | function formatQuery (query) {
580 | return Object.keys(query).sort().map((key) => {
581 | const val = query[key]
582 | if (val == null) {
583 | return ''
584 | }
585 | if (Array.isArray(val)) {
586 | return val.slice().map(val2 => [key, '=', val2].join('')).join('&')
587 | }
588 | return key + '=' + val
589 | }).filter(Boolean).join('&')
590 | }
591 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/views/app.template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ HEAD }}
5 |
6 |
7 | {{ APP }}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/.nuxt/views/error.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Server error
5 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
Server error
16 |
{{ message }}
17 |
18 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/README.md:
--------------------------------------------------------------------------------
1 | # vueber-nuxt-fire-demo
2 |
3 | tbd
4 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/layouts/default.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/nuxt.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | mode: 'universal',
3 | /*
4 | ** Headers of the page
5 | */
6 | head: {
7 | title: process.env.npm_package_name || '',
8 | meta: [
9 | { charset: 'utf-8' },
10 | { name: 'viewport', content: 'width=device-width, initial-scale=1' },
11 | {
12 | hid: 'description',
13 | name: 'description',
14 | content: process.env.npm_package_description || '',
15 | },
16 | ],
17 | link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
18 | },
19 | /*
20 | ** Customize the progress-bar color
21 | */
22 | loading: { color: '#fff' },
23 | /*
24 | ** Global CSS
25 | */
26 | css: [],
27 | /*
28 | ** Plugins to load before mounting the App
29 | */
30 | plugins: ['@/plugins/vuefire'],
31 | /*
32 | ** Nuxt.js dev-modules
33 | */
34 | buildModules: [],
35 | /*
36 | ** Nuxt.js modules
37 | */
38 | modules: [
39 | [
40 | '@nuxtjs/firebase',
41 | {
42 | config: {
43 | apiKey: 'AIzaSyBPbKMVLeY1x_1sgikvuP5cyfUd6iFs0l0',
44 | authDomain: 'vueber-fire-nuxt-demo.firebaseapp.com',
45 | databaseURL: 'https://vueber-fire-nuxt-demo.firebaseio.com',
46 | projectId: 'vueber-fire-nuxt-demo',
47 | storageBucket: 'vueber-fire-nuxt-demo.appspot.com',
48 | messagingSenderId: '499279429791',
49 | appId: '1:499279429791:web:e0ef9deb856d8ed2c1a2aa',
50 | },
51 | services: {
52 | firestore: true,
53 | },
54 | },
55 | ],
56 | ],
57 | /*
58 | ** Build configuration
59 | */
60 | build: {
61 | transpile: [/^vueber/],
62 | /*
63 | ** You can extend webpack config here
64 | */
65 | extend(config, ctx) {},
66 | },
67 | }
68 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vueber-nuxt-fire-demo",
3 | "version": "1.0.0",
4 | "description": "My astounding Nuxt.js project",
5 | "author": "Pascal Luther",
6 | "private": true,
7 | "scripts": {
8 | "dev": "nuxt",
9 | "build": "nuxt build",
10 | "start": "nuxt start",
11 | "generate": "nuxt generate"
12 | },
13 | "dependencies": {
14 | "@nuxtjs/firebase": "^5.0.7",
15 | "anchorme": "^2.0.0",
16 | "core-js": "^2",
17 | "emoji-mart-vue": "^2.6.6",
18 | "firebase": "^7.9.1",
19 | "firewings": "^1.0.3",
20 | "nuxt": "^2.0.0",
21 | "vueber": "0.0.1-alpha.0",
22 | "vueber-fire": "0.0.1-alpha.0",
23 | "vuefire": "^2.2.1"
24 | },
25 | "devDependencies": {}
26 | }
27 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
31 |
32 |
37 |
--------------------------------------------------------------------------------
/packages/vueber-fire-nuxt-demo/plugins/vuefire.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { firestorePlugin } from 'vuefire'
3 |
4 | Vue.use(firestorePlugin)
5 |
--------------------------------------------------------------------------------
/packages/vueber-fire/README.md:
--------------------------------------------------------------------------------
1 | # vueber-fire
2 |
3 | This package is being developed and in alpha phase.
4 |
5 | Do not use it in any app, it will change a lot in the next few days! ;-)
6 |
--------------------------------------------------------------------------------
/packages/vueber-fire/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vueber-fire",
3 | "version": "0.0.1-alpha.0",
4 | "lockfileVersion": 1
5 | }
6 |
--------------------------------------------------------------------------------
/packages/vueber-fire/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vueber-fire",
3 | "version": "0.0.1-alpha.0",
4 | "license": "MIT",
5 | "description": "Linking Firebase Firestore to Vueber.",
6 | "main": "src/index.js",
7 | "author": "Pascal Luther",
8 | "repository": "https://github.com/lupas/vueber",
9 | "homepage": "https://github.com/lupas",
10 | "keywords": [
11 | "vue, vuejs, chat, vueber, messaging, inbox, firebase, firestore"
12 | ],
13 | "publishConfig": {
14 | "access": "public"
15 | },
16 | "peerDependencies": {
17 | "firewings": "^1.0.3",
18 | "vueber": "0.0.1-alpha.0",
19 | "vueber-fire": "0.0.1-alpha.0",
20 | "vuefire": "^2.2.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/packages/vueber-fire/src/components/main.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
280 |
--------------------------------------------------------------------------------
/packages/vueber-fire/src/components/unreadConversations.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
89 |
--------------------------------------------------------------------------------
/packages/vueber-fire/src/helpers/index.js:
--------------------------------------------------------------------------------
1 | export const serializeConversation = (currentUserId) => {
2 | return (doc) => {
3 | const conversation = doc.data()
4 | conversation.id = doc.id
5 |
6 | // Create Date Object
7 | if (conversation.lastMessage.sentDate) {
8 | conversation.lastMessage.sentDate = conversation.lastMessage.sentDate.toDate()
9 | }
10 |
11 | // Delete Own Participant
12 | delete conversation.participants[currentUserId]
13 |
14 | // Add _chatpartner
15 | let chatpartner
16 | for (const i in conversation.participants) {
17 | chatpartner = conversation.participants[i]
18 | break
19 | }
20 | conversation._chatpartner = chatpartner
21 |
22 | // Set _ownMessage Flag
23 | const isOwnMessage = conversation.lastMessage.senderId === currentUserId
24 | conversation.lastMessage._ownMessage = isOwnMessage
25 |
26 | return conversation
27 | }
28 | }
29 |
30 | export const serializeMessage = (doc) => {
31 | const message = doc.data()
32 | message.id = doc.id
33 | if (message.sentDate) {
34 | message.sentDate = message.sentDate.toDate()
35 | }
36 | return message
37 | }
38 |
--------------------------------------------------------------------------------
/packages/vueber-fire/src/index.js:
--------------------------------------------------------------------------------
1 | import VueberFire from './components/main.vue'
2 | import UnreadConversations from './components/unreadConversations.vue'
3 |
4 | // Register Components
5 |
6 | export { VueberFire, UnreadConversations }
7 |
8 | export const meta = require('../package.json')
9 |
--------------------------------------------------------------------------------
/packages/vueber/README.md:
--------------------------------------------------------------------------------
1 | # vueber
2 |
3 | This package is being developed and in alpha phase.
4 |
5 | Do not use it in any app, it will change a lot in the next few days! ;-)
6 |
--------------------------------------------------------------------------------
/packages/vueber/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vueber",
3 | "version": "0.0.1-alpha.0",
4 | "license": "MIT",
5 | "description": "Messenger & Chat framework for Vue.js applications.",
6 | "main": "src/index.js",
7 | "author": "Pascal Luther",
8 | "repository": "https://github.com/lupas/vueber",
9 | "homepage": "https://github.com/lupas",
10 | "keywords": [
11 | "vue, vuejs, chat, vueber, messaging, inbox"
12 | ],
13 | "publishConfig": {
14 | "access": "public"
15 | },
16 | "peerDependencies": {},
17 | "devDependencies": {
18 | "@mdi/js": "^5.1.45",
19 | "anchorme": "^2.0.0",
20 | "emoji-mart-vue": "^2.6.6",
21 | "moment": "^2.24.0",
22 | "v-tooltip": "^2.0.2"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/vueber/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // TODO: Can be deleted at some point?
3 | }
4 |
--------------------------------------------------------------------------------
/packages/vueber/src/assets/demoData/getDemoData.js:
--------------------------------------------------------------------------------
1 | export default function getDemoData() {
2 | const avatar1 = require('./user1.jpeg')
3 | const avatar2 = require('./user2.jpeg')
4 | const avatar3 = require('./user3.jpeg')
5 | const avatar4 = require('./user4.jpeg')
6 | const avatar5 = require('./user5.jpeg')
7 | return {
8 | conversations: [
9 | {
10 | lastMessage: {
11 | hasPendingNotification: true,
12 | isRead: false,
13 | message: "Hello, I'm User 1",
14 | senderId: 'user1',
15 | senderName: 'User 1',
16 | sentDate: new Date('2019-11-21T08:50:57.796Z'),
17 | _ownMessage: false
18 | },
19 | noOfMessages: 13,
20 | participants: {
21 | user1: {
22 | avatarPath: avatar1,
23 | chatDisabled: false,
24 | id: 'user1',
25 | username: 'User 1'
26 | },
27 | user2: {
28 | avatarPath: avatar2,
29 | chatDisabled: false,
30 | id: 'user2',
31 | username: 'User 2'
32 | }
33 | },
34 | participantsArray: ['user1', 'user2'],
35 | id: 'conversation1'
36 | // path: 'conversations/conversation1',
37 | // _chatpartner: {
38 | // avatarPath: null,
39 | // chatDisabled: false,
40 | // id: 'user1',
41 | // username: 'User1'
42 | // }
43 | },
44 | {
45 | lastMessage: {
46 | hasPendingNotification: false,
47 | isRead: true,
48 | message: "Hey it's User 3.",
49 | senderId: 'user3',
50 | senderName: 'User 3',
51 | sentDate: new Date('2019-11-20T08:50:57.796Z'),
52 | _ownMessage: false
53 | },
54 | noOfMessages: 3,
55 | participants: {
56 | user1: {
57 | avatarPath: avatar1,
58 | chatDisabled: false,
59 | id: 'user1',
60 | username: 'User 1'
61 | },
62 | user3: {
63 | avatarPath: avatar3,
64 | chatDisabled: false,
65 | id: 'user3',
66 | username: 'User 3'
67 | }
68 | },
69 | participantsArray: ['user1', 'user3'],
70 | id: 'conversation2'
71 | // path: 'conversations/conversation2',
72 | // _chatpartner: {
73 | // avatarPath: null,
74 | // chatDisabled: false,
75 | // id: 'user1',
76 | // username: 'User1'
77 | // }
78 | },
79 | {
80 | lastMessage: {
81 | hasPendingNotification: false,
82 | isRead: true,
83 | message: "Hey it's User 4.",
84 | senderId: 'user4',
85 | senderName: 'User 4',
86 | sentDate: new Date('2019-11-20T08:50:57.796Z'),
87 | _ownMessage: false
88 | },
89 | noOfMessages: 3,
90 | participants: {
91 | user1: {
92 | avatarPath: avatar1,
93 | chatDisabled: false,
94 | id: 'user1',
95 | username: 'User 1'
96 | },
97 | user4: {
98 | avatarPath: avatar4,
99 | chatDisabled: false,
100 | id: 'user4',
101 | username: 'User 4'
102 | }
103 | },
104 | participantsArray: ['user1', 'user4'],
105 | id: 'conversation3'
106 | // path: 'conversations/conversation3',
107 | // _chatpartner: {
108 | // avatarPath: null,
109 | // chatDisabled: false,
110 | // id: 'user1',
111 | // username: 'User1'
112 | // }
113 | },
114 | {
115 | lastMessage: {
116 | hasPendingNotification: false,
117 | isRead: true,
118 | message: "Hey it's User 5.",
119 | senderId: 'user5',
120 | senderName: 'User 5',
121 | sentDate: new Date('2019-11-20T08:50:57.796Z'),
122 | _ownMessage: false
123 | },
124 | noOfMessages: 3,
125 | participants: {
126 | user1: {
127 | avatarPath: avatar1,
128 | chatDisabled: false,
129 | id: 'user1',
130 | username: 'User 1'
131 | },
132 | user5: {
133 | avatarPath: avatar5,
134 | chatDisabled: false,
135 | id: 'user5',
136 | username: 'User 5'
137 | }
138 | },
139 | participantsArray: ['user1', 'user5'],
140 | id: 'conversation4'
141 | // path: 'conversations/conversation4',
142 | // _chatpartner: {
143 | // avatarPath: null,
144 | // chatDisabled: false,
145 | // id: 'user5',
146 | // username: 'User 5'
147 | // }
148 | }
149 | ],
150 | users: [
151 | {
152 | id: 'user1',
153 | avatar: avatar1,
154 | username: 'User1'
155 | },
156 | {
157 | id: 'user2',
158 | avatar: avatar2,
159 | username: 'User2'
160 | },
161 | {
162 | id: 'user3',
163 | avatar: avatar3,
164 | username: 'User3'
165 | }
166 | ],
167 | messages: [
168 | {
169 | hasPendingNotification: true,
170 | isRead: true,
171 | message: 'Hallo, dies ist ein Test',
172 | senderId: 'user2',
173 | senderName: 'User2',
174 | sentDate: new Date('2019-01-07T10:36:38.296Z'),
175 | id: 'message1',
176 | conversationId: 'conversation1'
177 | },
178 | {
179 | hasPendingNotification: true,
180 | isRead: true,
181 | message: 'Testchat',
182 | senderId: 'user2',
183 | senderName: 'User2',
184 | sentDate: new Date('2019-01-07T10:36:38.296Z'),
185 | id: 'message2',
186 | conversationId: 'conversation2'
187 | }
188 | ]
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/packages/vueber/src/assets/demoData/user1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lupas/vueber/fa1f193b87eaeadddf29fcc3619c88f1f6a4887f/packages/vueber/src/assets/demoData/user1.jpeg
--------------------------------------------------------------------------------
/packages/vueber/src/assets/demoData/user2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lupas/vueber/fa1f193b87eaeadddf29fcc3619c88f1f6a4887f/packages/vueber/src/assets/demoData/user2.jpeg
--------------------------------------------------------------------------------
/packages/vueber/src/assets/demoData/user3.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lupas/vueber/fa1f193b87eaeadddf29fcc3619c88f1f6a4887f/packages/vueber/src/assets/demoData/user3.jpeg
--------------------------------------------------------------------------------
/packages/vueber/src/assets/demoData/user4.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lupas/vueber/fa1f193b87eaeadddf29fcc3619c88f1f6a4887f/packages/vueber/src/assets/demoData/user4.jpeg
--------------------------------------------------------------------------------
/packages/vueber/src/assets/demoData/user5.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lupas/vueber/fa1f193b87eaeadddf29fcc3619c88f1f6a4887f/packages/vueber/src/assets/demoData/user5.jpeg
--------------------------------------------------------------------------------
/packages/vueber/src/assets/noAvatar_xs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lupas/vueber/fa1f193b87eaeadddf29fcc3619c88f1f6a4887f/packages/vueber/src/assets/noAvatar_xs.png
--------------------------------------------------------------------------------
/packages/vueber/src/components/_elements/avatar.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
40 |
41 |
46 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/_elements/btn.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
38 |
39 |
76 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/_elements/icon.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
53 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/_elements/textField.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
48 |
49 |
75 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/_elements/toolbarWrapper.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
51 |
52 |
81 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/headerBar/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 | {{ chatpartner.username }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
64 |
65 |
72 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
23 |
24 |
25 |
26 |
36 |
37 |
44 |
45 |
46 |
54 |
55 |
56 |
57 |
58 |
63 |
64 |
65 |
66 |
67 |
68 |
210 |
211 |
321 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/inputFooter/emojiPicker.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
34 |
35 |
40 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/inputFooter/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
26 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
115 |
116 |
122 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/leftSidebar/conversationTile.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
13 |
14 |
15 |
16 | {{ conversation._chatpartner.username }} -
17 | {{ conversation.lastMessage.sentDate | daymonthyear_1 }}
18 |
19 |
20 | You:
21 | {{ conversation.lastMessage.message }}
22 |
23 |
24 |
25 |
26 |
35 |
36 |
45 |
46 |
54 |
55 |
56 |
57 |
58 |
114 |
115 |
162 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/leftSidebar/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
13 | Load more...
14 |
15 |
16 |
17 |
18 |
19 |
49 |
50 |
63 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/loginView/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Not logged in.
5 |
6 |
7 | Login
8 |
9 |
10 | Initialize
11 |
12 |
13 |
14 |
15 |
24 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/messagesView/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Load older messages
7 |
8 |
9 |
10 |
19 |
20 |
23 |
24 |
25 |
26 |
127 |
128 |
149 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/messagesView/message/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ message.sentDate | daymonthyeartime_1 }}
6 |
7 |
8 |
9 |
13 |
14 |
15 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
110 |
111 |
157 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/messagesView/message/isReadFlag.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
29 |
30 |
35 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/noSelectionView/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Select a chatpartner.
4 |
5 |
6 |
7 |
10 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/rightSidebar/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
14 |
15 | {{ chatpartner.username }}
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
52 |
53 |
80 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/rightSidebar/mobileHeader.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/packages/vueber/src/components/rightSidebar/userActions.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Go to User Profile
6 |
7 |
8 |
9 |
10 | Report this User
11 |
12 |
13 |
14 |
15 |
16 |
30 |
31 |
36 |
--------------------------------------------------------------------------------
/packages/vueber/src/index.js:
--------------------------------------------------------------------------------
1 | import Vueber from './components/index.vue'
2 |
3 | // Register Components
4 |
5 | export default Vueber
6 |
7 | export const meta = require('../package.json')
8 |
--------------------------------------------------------------------------------