diff --git a/.gitignore b/.gitignore index 0a5c71c..8759428 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ node_modules/ .sass-cache/ .cache/ .nova/ -/* + !public/ public/* !public/favicon.ico diff --git a/src/com/events/AuthEvent.js b/src/com/events/AuthEvent.js new file mode 100644 index 0000000..a32338c --- /dev/null +++ b/src/com/events/AuthEvent.js @@ -0,0 +1,28 @@ +export const MEMBER_STATUS = "memberStatus"; +export const LOGIN_STATUS = 'loginStatus'; +export const SUPPORTER_FOUND = 'SUPPORTER FOUND'; +export const SUPPORTER_LISTED = 'SUPPORTER LISTED'; +export const SUPPORTER_NOT_FOUND = 'SUPPORTER NOT FOUND'; +export const MEMBER_ADDED = 'MEMBER ADDED'; +export const MEMBER_NOT_ADDED = 'MEMBER NOT ADDED'; +export const MEMBER_LOGIN_GOOD = 'MEMBER LOGIN GOOD'; +export const MEMBER_LOGIN_LAME = 'MEMBER LOGIN LAME'; +export const MEMBER_EXISTS = 'USER ALREADY EXISTS'; +export const MEMBER_LOGIN_MISSING = 'Missing credentials'; + + + +class AuthEvent +{ + + //-------------------------- + // methods + //-------------------------- + + + + //-------------------------- + // event handlers + //-------------------------- +} +export default new AuthEvent diff --git a/src/com/events/DataEvent.js b/src/com/events/DataEvent.js new file mode 100644 index 0000000..77b1fc0 --- /dev/null +++ b/src/com/events/DataEvent.js @@ -0,0 +1,44 @@ +export const REQUEST_GOOD = 'requestGood'; +export const REQUEST_LAME = 'requestLame'; +export const API_REQUEST_GOOD = 'apiUseAuthorized'; +export const API_REQUEST_LAME = 'apiUseNotAuthorized'; +export const IMG_REQUEST_GOOD = 'imgRequestGood'; +export const IMG_REQUEST_LAME = 'imgRequestLame'; +export const SETTINGS_LOADED = 'settingsLoaded'; +export const POST_IMAGE_ADDED = 'postImageAdded'; +export const FEATURE_IMAGE_ADDED = 'featureImageAdded'; +export const PAGE_ERROR = 'postError'; +export const PAGE_ADDED = 'postAdded'; +export const PAGE_UPDATED = 'postUpdated'; +export const PAGE_DELETED = 'postImageAdded'; +export const PAGES_RENDERED = 'pagesRendered'; +export const PAGES_NOT_RENDERED = 'pagesNotRendered'; +export const TAG_PAGES_RENDERED = 'tagPagesRendered'; +export const TAG_PAGES_NOT_RENDERED = 'tagPagesNotRendered'; +export const SETTINGS_UPDATED = 'settingsUpdated'; +export const SETTINGS_NOT_UPDATED = 'settingsNotUpdated'; +export const MENU_ADD_ITEM = 'menuAddItem'; +export const MENU_DELETE_ITEM = 'menuDeleteItem'; +export const MENU_UPDATED = 'menuUpdated'; +export const AVATAR_UPLOADED = 'avatarUploaded'; +export const SITE_BACKGROUND_UPLOADED = 'siteBackgroundUploaded'; +export const UPLOAD_PROGRESS = 'uploadProgress'; +export const API_PAGE_WRITE = 'writingItDown'; +export const API_PAGE_CREATE = 'writingNewEntry'; +export const API_PAGE_DELETE = 'erasingPage'; +export const API_SETTINGS_WRITE = 'savingSettings'; +export const API_IMAGES_UPLOAD = 'uploadProfileImages'; +export const API_RENDER_PAGES = 'renderPages'; +export const API_INIT = 'blogInit'; +export const API_INIT_GOOD = 'blogInitGood'; +export const API_INIT_LAME = 'blogInitLame'; +export const SEND_MAIL = 'sendMail'; +class DataEvent { + //-------------------------- + // methods + //-------------------------- + //-------------------------- + // event handlers + //-------------------------- +} +export default new DataEvent(); diff --git a/src/com/events/EditorEvent.js b/src/com/events/EditorEvent.js new file mode 100644 index 0000000..2cd24c6 --- /dev/null +++ b/src/com/events/EditorEvent.js @@ -0,0 +1,14 @@ +export const EDITOR_DELETE = 'editorDelete'; +export const EDITOR_UPLOAD_POST_IMAGE = 'editorUploadImage'; +export const EDITOR_SAVE = 'editorSave'; +export const EDITOR_UPDATE = 'editorUpdate'; + +class EditorEvent { + //-------------------------- + // methods + //-------------------------- + //-------------------------- + // event handlers + //-------------------------- +} +export default new EditorEvent(); diff --git a/src/com/events/EventEmitter.js b/src/com/events/EventEmitter.js new file mode 100644 index 0000000..56f5dfe --- /dev/null +++ b/src/com/events/EventEmitter.js @@ -0,0 +1,52 @@ +class EventEmitter { + //-------------------------- + // constructor + //-------------------------- + constructor() { + this.listeners = new Map(); + } + //-------------------------- + // methods + //-------------------------- + addListener(label, callback) { + this.listeners.has(label) || this.listeners.set(label, []); + this.listeners.get(label).push(callback); + } + + removeListener(label, callback) { + var isFunction = function(obj) { + return typeof obj == 'function' || false; + }; + + var listeners = this.listeners.get(label), + index; + + if (listeners && listeners.length) { + index = listeners.reduce((i, listener, index) => { + return isFunction(listener) && listener === callback ? (i = index) : i; + }, -1); + + if (index > -1) { + listeners.splice(index, 1); + this.listeners.set(label, listeners); + return true; + } + } + return false; + } + + emitEvent(label, ...args) { + var listeners = this.listeners.get(label); + if (listeners && listeners.length) { + listeners.forEach(listener => { + listener(...args); + }); + return true; + } + return false; + } + //-------------------------- + // event handlers + //-------------------------- +} +export default EventEmitter; diff --git a/src/libraries/FipamoAPI.js b/src/libraries/FipamoAPI.js new file mode 100644 index 0000000..48adf2a --- /dev/null +++ b/src/libraries/FipamoAPI.js @@ -0,0 +1,121 @@ +export const REQUEST_TYPE_POST = 'POST'; +export const REQUEST_TYPE_GET = 'GET'; +export const REQUEST_TYPE_PUT = 'PUT'; +export const REQUEST_TYPE_DELETE = 'DELETE'; +export const CONTENT_TYPE_JSON = 'json'; +export const CONTENT_TYPE_FORM = 'x-www-form-urlencoded'; +export const API_STATUS = '/api/v1/auth/status'; +export const API_INIT = '/api/v1/auth/init'; +export const API_LOGIN = '/api/v1/auth/login'; +export const API_GET_PAGES = '/api/v1/page/published'; +export const API_GET_PAGE = '/api/v1/page/single'; +import * as DataEvent from '../com/events/DataEvent'; +export default class APIUtils { + //-------------------------- + // constructor + //-------------------------- + constructor() {} + //-------------------------- + // public + //-------------------------- + login(data) { + return new Promise((resolve, reject) => { + this._request( + API_LOGIN, + DataEvent.AUTH_STATUS, + REQUEST_TYPE_POST, + CONTENT_TYPE_JSON, + data + ) + .then(result => { + resolve(result); + }) + .catch(err => { + reject(err); + }); + }); + } + init(data) { + return new Promise((resolve, reject) => { + this._request(API_INIT, DataEvent.API_INIT, REQUEST_TYPE_POST, CONTENT_TYPE_JSON, data) + .then(result => { + resolve(result); + }) + .catch(err => { + reject(err); + }); + }); + } + getPages(num) { + let pageNum = num; + if (pageNum === null || pageNum === '' || !pageNum) pageNum = 1; + return new Promise((resolve, reject) => { + this._request(API_GET_PAGES + '/' + pageNum, DataEvent.API_GET_PAGES, REQUEST_TYPE_GET) + .then(result => { + resolve(result); + }) + .catch(err => { + reject(err); + }); + }); + } + + getPage(id) { + return new Promise((resolve, reject) => { + this._request(API_GET_PAGE + '/' + id, DataEvent.API_GET_PAGES, REQUEST_TYPE_GET) + .then(result => { + resolve(result); + }) + .catch(err => { + reject(err); + }); + }); + } + //-------------------------- + // private + //-------------------------- + _request( + requestURL, + eventType, + requestType = REQUEST_TYPE_GET, + contentType = CONTENT_TYPE_JSON, + requestData = null + ) { + var self = this; + return new Promise(function (resolve, reject) { + var request = new XMLHttpRequest(); + request.upload.onprogress = self.handleLoadProgress; + request.open(requestType, requestURL, true); + request.onload = () => { + if (request.status == 200) { + let response = JSON.parse(request['response']); + resolve(response); + } else { + let error = JSON.parse(request['response']); + reject(error); + } + }; + if (requestType == REQUEST_TYPE_PUT || requestType == REQUEST_TYPE_POST) { + switch (contentType) { + case CONTENT_TYPE_JSON: + request.setRequestHeader('Content-type', 'application/' + contentType); + request.send(JSON.stringify(requestData)); + break; + case CONTENT_TYPE_FORM: + request.send(requestData); + break; + } + } else { + request.send(); + } + }); + } + + //-------------------------- + // event handlers + //-------------------------- + handleLoadProgress(e) { + this.percentComplete = Math.ceil((e.loaded / e.total) * 100); + //pass element to display request progress + } +} diff --git a/src/libraries/FipamoAdminAPI.js b/src/libraries/FipamoAdminAPI.js new file mode 100644 index 0000000..fedcfe4 --- /dev/null +++ b/src/libraries/FipamoAdminAPI.js @@ -0,0 +1,247 @@ +export const REQUEST_TYPE_POST = 'POST'; +export const REQUEST_TYPE_GET = 'GET'; +export const REQUEST_TYPE_PUT = 'PUT'; +export const REQUEST_TYPE_DELETE = 'DELETE'; +export const TASK_PAGE_CREATE = 'createNewPage'; +export const TASK_PAGE_EDIT = 'editPage'; +export const TASK_PAGE_DELETE = 'deletePage'; +export const CONTENT_TYPE_JSON = 'json'; +export const CONTENT_TYPE_FORM = 'x-www-form-urlencoded'; +export const API_STATUS = '/api/v1/auth/status'; +export const API_GET_NAV = '/api/settings/nav'; +export const API_NEW_PAGE = '/api/v1/page/write/new'; +export const API_EDIT_PAGE = '/api/v1/page/write'; +export const API_DELETE_PAGE = '/api/v1/page/delete'; +export const API_IMAGE_UPLOAD = '/api/v1/page/add-post-image'; +export const API_SETTINGS_SYNC = '/api/v1/settings/sync'; +export const API_UPLOAD_AVATAR = '/api/v1/settings/add-avatar'; +export const API_UPLOAD_BACKGROUND = '/api/v1/settings/add-feature-background'; +export const API_PUBLISH_PAGES = '/api/v1/settings/publish-pages'; +export const API_NAV_SYNC = '/api/v1/settings/nav-sync'; +export const API_SEND_MAIL = '/api/v1/mailer'; +import * as DataEvent from '../com/events/DataEvent'; +export default class APIUtils { + //-------------------------- + // constructor + //-------------------------- + constructor() { + this.percentComplete = 0; + this.token = null; + //checks backend to see if user is logged in + //and requests encrypted token for api calls + this._request(API_STATUS).then(response => { + if (response.type === DataEvent.API_REQUEST_GOOD) { + this.token = response.token; + } else { + //don't set token + } + }); + } + //-------------------------- + // public + //-------------------------- + syncSettings(data) { + return new Promise((resolve, reject) => { + this._request( + API_SETTINGS_SYNC, + DataEvent.API_SETTINGS_WRITE, + REQUEST_TYPE_POST, + CONTENT_TYPE_JSON, + data + ) + .then(result => { + resolve(result); + }) + .catch(err => { + reject(err); + }); + }); + } + imageUpload(type, files) { + return new Promise((resolve, reject) => { + let url = ''; + switch (type) { + case 'avatar-upload': + url = API_UPLOAD_AVATAR; + break; + case 'background-upload': + url = API_UPLOAD_BACKGROUND; + break; + default: + url = API_IMAGE_UPLOAD; + break; + } + var imageData = new FormData(); + for (var i = 0; i < files.length; i++) { + var file = files[i]; + // Check the file type. + if (!file.type.match('image.*')) { + continue; + } + if (type === 'avatar-upload') { + imageData.append('avatar_upload', file, file.name); + } else if (type === 'background-upload') { + imageData.append('background_upload', file, file.name); + } else { + imageData.append('post_image', file, file.name); + } + } + this._request( + url, + DataEvent.API_IMAGES_UPLOAD, + REQUEST_TYPE_POST, + CONTENT_TYPE_FORM, + imageData + ) + .then(r => { + resolve(r); + }) + .catch(err => { + reject(err); + }); + }); + } + publishSite(data) { + return new Promise((resolve, reject) => { + this._request( + API_PUBLISH_PAGES, + DataEvent.API_RENDER_PAGES, + REQUEST_TYPE_POST, + CONTENT_TYPE_JSON, + data + ) + .then(result => { + resolve(result); + }) + .catch(err => { + reject(err); + }); + }); + } + + pageActions(task, data) { + let url, event, content; + switch (task) { + case TASK_PAGE_CREATE: + url = API_NEW_PAGE; + event = DataEvent.API_PAGE_WRITE; + content = CONTENT_TYPE_FORM; + break; + case TASK_PAGE_EDIT: + url = API_EDIT_PAGE; + event = DataEvent.API_PAGE_WRITE; + content = CONTENT_TYPE_FORM; + break; + + case TASK_PAGE_DELETE: + url = API_DELETE_PAGE; + event = DataEvent.API_PAGE_DELETE; + content = CONTENT_TYPE_JSON; + break; + + default: + break; + } + + return new Promise((resolve, reject) => { + this._request(url, event, REQUEST_TYPE_POST, content, data) + .then(result => { + resolve(result); + }) + .catch(err => { + reject(err); + }); + }); + } + + syncNav(data) { + return new Promise((resolve, reject) => { + this._request( + API_NAV_SYNC, + DataEvent.API_SETTINGS_WRITE, + REQUEST_TYPE_POST, + CONTENT_TYPE_JSON, + data + ) + .then(result => { + resolve(result); + }) + .catch(err => { + reject(err); + }); + }); + } + sendMail(message) { + return new Promise((resolve, reject) => { + this._request( + API_SEND_MAIL, + DataEvent.SEND_MAIL, + REQUEST_TYPE_POST, + CONTENT_TYPE_JSON, + message + ) + .then(result => { + resolve(result); + }) + .catch(err => { + reject(err); + }); + }); + } + //-------------------------- + // private + //-------------------------- + _request( + requestURL, + eventType, + requestType = REQUEST_TYPE_GET, + contentType = CONTENT_TYPE_JSON, + requestData = null + ) { + var self = this; + return new Promise(function (resolve, reject) { + var request = new XMLHttpRequest(); + request.upload.onprogress = self.handleLoadProgress; + request.open(requestType, requestURL, true); + request.onload = () => { + if (request.status == 200) { + let response = JSON.parse(request['response']); + resolve(response); + } else { + let error = JSON.parse(request['response']); + reject(error); + } + }; + if (requestType == REQUEST_TYPE_PUT || requestType == REQUEST_TYPE_POST) { + if ( + eventType === DataEvent.API_PAGE_WRITE || + eventType === DataEvent.API_IMAGES_UPLOAD || + eventType === DataEvent.API_SETTINGS_WRITE || + eventType === DataEvent.API_PAGE_DELETE || + eventType === DataEvent.API_RENDER_PAGES + ) + request.setRequestHeader('x-access-token', self.token); + + switch (contentType) { + case CONTENT_TYPE_JSON: + request.setRequestHeader('Content-type', 'application/' + contentType); + request.send(JSON.stringify(requestData)); + break; + case CONTENT_TYPE_FORM: + request.send(requestData); + break; + } + } else { + request.send(); + } + }); + } + + //-------------------------- + // event handlers + //-------------------------- + handleLoadProgress(e) { + this.percentComplete = Math.ceil((e.loaded / e.total) * 100); + //pass element to display request progress + } +}