Skip to content

Commit

Permalink
LDBR-4.3: ServiceWorker & Offline (#55)
Browse files Browse the repository at this point in the history
* LDBR-4.3: Внести правки для запуска на localhost

* LDBR-4.3: Кэширование в SW

* LDBR-4.3: Частично реализовать offline

* LDBR-4.3: Исправить проблемы с оффлан работой

* LDBR-4.3: Добавить сообщение об оффлайн работе и порезать функционал в офлайне

* LDBR-4.3: Исправить замечания линтера

* LDBR-4.3: Внести правки в код связанный с SW

* LDBR-4.3: Добавить расчет хэша для файла SW

* LDBR-4.3: Устранить круговую зависимость SettingStore->Router->NotFoundView->SettingStore

* LDBR-4.3: Подключить SettingStore

* LDBR-4.3: Изменить replase spate при переходе на /offline
  • Loading branch information
GeorgiyX authored Dec 22, 2021
1 parent 10031a7 commit e533171
Show file tree
Hide file tree
Showing 33 changed files with 2,328 additions and 211 deletions.
1,539 changes: 1,496 additions & 43 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"lint:fix": "npx eslint . --ext .js --fix",
"test": "echo \"Error: no test specified\" && exit 1",
"build-dev": "npx webpack",
"server-dev-express": "PORT=3000 node server/server.js",
"server-dev-express": "PORT=3001 node server/server.js",
"server-dev": "npx webpack serve",
"start-dev": "npm run server-dev",
"build": "NODE_ENV='production' npx webpack",
Expand Down Expand Up @@ -46,6 +46,7 @@
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^9.0.1",
"css-loader": "^6.5.1",
"css-minimizer-webpack-plugin": "^3.3.0",
"html-webpack-plugin": "^5.5.0",
"mini-css-extract-plugin": "^2.4.4",
"node-sass": "^6.0.1",
Expand Down
Binary file added public/assets/no-internet-icon.webp
Binary file not shown.
6 changes: 3 additions & 3 deletions server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ const port = process.env.PORT || 80;
app.use(morgan('dev'));

/* Время кэширования ответов, в сек */
const cacheTime = 256 * 24 * 60 * 60;
// const cacheTime = 256 * 24 * 60 * 60;
/* middleware для кэширования бандлов */
const cacheMW = (req, res, next) => {
/* const cacheMW = (req, res, next) => {
res.set('Cache-control', `public, max-age=${cacheTime}`);
next();
};
app.use([/\/bundle\.[A-Za-z0-9]*\.js/, /\/style\.[A-Za-z0-9]*\.css/], cacheMW);
app.use([/\/bundle\.[A-Za-z0-9]*\.js/, /\/style\.[A-Za-z0-9]*\.css/], cacheMW); */

/* Директория со статикой */
const distFolder = path.resolve(__dirname, '..', 'dist');
Expand Down
66 changes: 66 additions & 0 deletions src/actions/serviceworker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
'use strict';

// Modules
import Dispatcher from '../modules/Dispatcher/Dispatcher.js';

/**
* Константа, содержащая в себе типы действий для собыйтий от sw.
*/
export const ServiceWorkerTypes = {
HALF_OFFLINE: 'half-offline',
FULL_OFFLINE: 'full-offline',
CLOSE_OFFLINE_MSG: 'close-offline',
SHOW_OFFLINE_MSG: 'show-offline',
HIDE_OFFLINE_MSG: 'hide-offline',
};

/**
* Класс, содержащий в себе действия для работы с тегами.
*/
export const serviceWorkerActions = {

/**
* Отображает предупреждение об offline работе
*/
responseFromCache() {
Dispatcher.dispatch({
actionName: ServiceWorkerTypes.HALF_OFFLINE,
});
},

/**
* Отображает offline страницу
*/
fullOffline() {
Dispatcher.dispatch({
actionName: ServiceWorkerTypes.FULL_OFFLINE,
});
},

/**
* Сворачивает предупреждение об offline работе
*/
onCloseOfflineMessage() {
Dispatcher.dispatch({
actionName: ServiceWorkerTypes.CLOSE_OFFLINE_MSG,
});
},

/**
* Разворачивает offline сообшение
*/
onOpenOfflineMessage() {
Dispatcher.dispatch({
actionName: ServiceWorkerTypes.SHOW_OFFLINE_MSG,
});
},

/**
* Скрывает предупреждение об offline работе
*/
hideOfflineMessage() {
Dispatcher.dispatch({
actionName: ServiceWorkerTypes.HIDE_OFFLINE_MSG,
});
},
};
18 changes: 18 additions & 0 deletions src/components/OfflineMessage/OfflineMessage.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{{#with offline}}
{{# if visible }}
<div class="offline__wrapper"
{{# if open }}
class = "offline__wrapper offline__wrapper_non-clickable"
{{ else}}
class = "offline__wrapper offline__wrapper-clickable"
id="offlineMessageShowId"
{{/if}}
>
<div class="material-icon-warning"></div>
{{# if open }}
<div class="offline__message">Похоже у вас отсутствует соединиение с Интернетом<br>Часть функций недоступна.</div>
<div class="close-button" id="offlineMessageCloseId"></div>
{{/if}}
</div>
{{/if}}
{{/with}}
97 changes: 97 additions & 0 deletions src/components/OfflineMessage/OfflineMessage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Базовый компонент
import BaseComponent from '../BaseComponent.js';
// Стили

import './OfflineMessage.scss';
import UserStore from '../../stores/UserStore/UserStore.js';

// Шаблон
import template from './OfflineMessage.hbs';

// Action
import {serviceWorkerActions} from '../../actions/serviceworker.js';


/**
* Класс, реализующий компонент OfflineMessage.
*/
export default class OfflineMessage extends BaseComponent {
/**
* Конструктор, создающий класс компонента OfflineMessage.
* @param {Object} context контекст отрисовки шаблона
*/
constructor(context) {
super(context, template);

this._onRefresh = this._onRefresh.bind(this);
UserStore.addListener(this._onRefresh);

this._elements = {};
this._bindCallBacks();
}

/**
* Метод, вызывающийся по умолчанию при обновлении компонента.
*/
_onRefresh() {
this.context = UserStore.getContext();
}

/**
* Метод биндит callback'и к this
* @private
*/
_bindCallBacks() {
this._onClose = this._onClose.bind(this);
this._onOpen = this._onOpen.bind(this);
}

/**
* Метод сохраняет элементы DOM связанные с navbar
* @private
*/
_registerElements() {
this._elements = {
wrapper: document.getElementById('offlineMessageShowId'),
close: document.getElementById('offlineMessageCloseId'),
};
}

/**
* Метод, добавляющий обработчики событий для компонента.
*/
addEventListeners() {
this._registerElements();
this._elements.wrapper?.addEventListener('click', this._onOpen);
this._elements.close?.addEventListener('click', this._onClose);
}

/**
* Метод, удаляющий обработчики событий для компонента.
*/
removeEventListeners() {
this._elements.wrapper?.removeEventListener('click', this._onOpen);
this._elements.close?.removeEventListener('click', this._onClose);
}

/**
* CallBack на закрытие сообщения
* @param {Event} event объект события
* @private
*/
_onClose(event) {
event.preventDefault();
serviceWorkerActions.onCloseOfflineMessage();
}


/**
* CallBack на открытие сообщения
* @param {Event} event объект события
* @private
*/
_onOpen(event) {
event.preventDefault();
serviceWorkerActions.onOpenOfflineMessage();
}
}
32 changes: 32 additions & 0 deletions src/components/OfflineMessage/OfflineMessage.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@import '../../styles/scss/Constants';

.offline {
&__wrapper {
position: fixed;
z-index: 2;
left: 20px;
bottom: 20px;
background-color: $offline-background;
border: 2px solid $offline-border;
border-radius: 20px;

display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
column-gap: 5px;
padding: 5px;

&_clickable {
cursor: pointer;
}

&_non-clickable {
cursor: pointer;
}
}


&__content {
}
}
40 changes: 28 additions & 12 deletions src/constants/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const Urls = {
CardPath: '/invite/card/',
Card: '/invite/card/<accessPathCard>',
},
Offline: '/offline',
};

/**
Expand All @@ -29,19 +30,18 @@ export const Html = {
};

/**
* Константа, содержащая в себе параметры самого себя.
* Константа, содержащая в себе параметры URL
*/
export const SelfAddress = {
Url: FRONTEND_ADDRESS,
Port: FRONTEND_PORT,
};

/**
* Константа, содержащая в себе параметры бэкенда.
*/
export const BackendAddress = {
Url: BACKEND_ADDRESS,
Port: BACKEND_PORT,
export const HTTP = {
SelfAddress: {
Url: FRONTEND_ADDRESS,
Port: FRONTEND_PORT,
},
BackendAddress: {
Url: BACKEND_ADDRESS,
Port: BACKEND_PORT,
},
Scheme: SCHEME,
};

/**
Expand Down Expand Up @@ -119,3 +119,19 @@ export const CheckLists = {
export const SettingStoreConstants = {
MobileNavWidth: 500,
};

export const ServiceWorker = {
CacheUrls: {
HTML_URL: '/index.html',
NO_INTERNET_IMG_URL: '/assets/no-internet-icon.webp',
},
API_PREFIX: '/api',
STATIC_CACHE_NAME: `static-cache-${APP_VERSION}`,
API_CACHE_NAME: `api-cache-${APP_VERSION}`,
SW_HEADER: 'X-Is-From-Service-Worker',
Messages: {
OFFLINE_FROM_CACHE: 'offline-cache', // Приложение работает в offline
OFFLINE_NO_CACHE: 'offline-no-cache', // Приложение offline и дальше не может работать
ONLINE: 'online', // Приложение online
},
};
29 changes: 18 additions & 11 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
'use strict';

import {userActions} from './actions/user.js';

// Stores
Expand All @@ -22,16 +21,9 @@ import BoardsView from './views/BoardsView/BoardsView.js';
import BoardView from './views/BoardView/BoardView.js';
import ProfileView from './views/ProfileView/ProfileView.js';
import {settingsActions} from './actions/settings';

if ('serviceWorker' in navigator && !DEBUG) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js').then((registration) => {
console.log('SW registered with scope ', registration.scope);
}).catch((registrationError) => {
console.log('SW registration failed: ', registrationError);
});
});
}
import ServiceWorkerClient from './modules/ServiceWorkerClient/ServiceWorkerClient';
import OfflineView from './views/OfflineView/OfflineView.js';
import NotFoundView from './views/NotFoundView/NotFoundView';

if (UserStore.getContext('isAuthorized') === undefined) {
userActions.fetchUser();
Expand All @@ -44,6 +36,7 @@ window.addEventListener('DOMContentLoaded', async () => {

const boardsView = new BoardsView(root);
const boardView = new BoardView(root);
Router.registerNotFound(new NotFoundView(root));
Router.register(Urls.Root, boardsView);
Router.register(Urls.Boards, boardsView);
Router.register(Urls.Invite.Board, boardView);
Expand All @@ -52,6 +45,7 @@ window.addEventListener('DOMContentLoaded', async () => {
Router.register(Urls.Login, new LoginView(root));
Router.register(Urls.Board, boardView);
Router.register(Urls.Profile, new ProfileView(root));
Router.register(Urls.Offline, new OfflineView(root));

UserStore.addListener(() => {
if (UserStore.getContext('isAuthorized') === undefined) {
Expand Down Expand Up @@ -81,3 +75,16 @@ window.addEventListener('resize', (() => {
}
};
})(), false);


if ('serviceWorker' in navigator) {
// eslint-disable-next-line no-unused-vars
const serviceWorkerClient = new ServiceWorkerClient(navigator.serviceWorker);
window.addEventListener('load', async () => {
try {
await navigator.serviceWorker.register(`/${SW_FILE_NAME}`);
} catch (error) {
console.log(`Ошибка при регистрации SW: ${error}`);
}
});
}
3 changes: 2 additions & 1 deletion src/index_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ <h3 class="refuse-box__title">Ошибка :с</h3>

<noscript>
<div>
<img class="refuse-box__image" src="../public/assets/noscript.webp" alt="Sad Shiben">
<img class="refuse-box__image" src="/assets/noscript.webp" alt="Sad Shiben">
</div>
<div class="refuse-box">
<h3 class="refuse-box__title">Ошибка :с</h3>
<p class="refuse-box__text">К сожалению, ваш браузер не поддерживает JavaScript или его использование запрещено настройками</p>
<img alt="You are offline" src="/assets/no-internet.webp" draggable="false"/>
</div>
</noscript>
<div id="no-connection">
Expand Down
Loading

0 comments on commit e533171

Please sign in to comment.