Feature updates

-- added option to save an update from intern
-- added UI to see assigned tasks in intern profile
This commit is contained in:
sairaj mote 2022-11-25 01:07:40 +05:30
parent 4ddeb8ce73
commit c196d31d1b
11 changed files with 871 additions and 821 deletions

View File

@ -1152,6 +1152,7 @@ ul {
user-select: none;
padding: 0.8rem 1rem;
gap: 0.8rem;
color: inherit;
}
.intern-card input {
height: 1.3em;
@ -1284,7 +1285,7 @@ ul {
flex: 1;
}
#intern_info__initials {
#intern_profile__initials {
position: relative;
height: 4rem;
width: 4rem;
@ -1292,12 +1293,12 @@ ul {
color: var(--color);
}
#intern_info__name {
#intern_profile__name {
font-size: 1.5rem;
padding: 0.4rem 0.5rem;
border-radius: 0.5rem;
}
#intern_info__name[contenteditable=true] {
#intern_profile__name[contenteditable=true] {
background-color: rgba(var(--text-color), 0.1);
}
@ -1379,33 +1380,39 @@ ul {
color: var(--nice-blue);
}
#intern_info__wrapper {
#intern_profile {
display: flex;
flex-wrap: wrap;
gap: 1rem;
gap: 1.5rem;
}
#intern_info__wrapper > * {
#intern_profile__left {
width: min(26rem, 100%);
border-radius: 0.5rem;
background-color: rgba(var(--foreground-color), 1);
padding: 1rem;
}
#intern_profile__right {
flex: 1;
}
.intern_info__task {
.intern_profile__task {
display: grid;
align-items: center;
width: 100%;
gap: 0.3rem;
margin-top: 1rem;
padding: 0.5rem;
padding: 1rem;
border-radius: 0.5rem;
background-color: rgba(var(--text-color), 0.06);
background-color: rgba(var(--foreground-color), 1);
grid-template-columns: minmax(0, 1fr) auto;
}
.intern_info__task h4 {
.intern_profile__task h4 {
grid-column: 1/-1;
}
.intern_info__task p {
.intern_profile__task p {
font-weight: 500;
}
.intern_info__task time {
.intern_profile__task time {
font-size: 0.9rem;
color: rgba(var(--text-color), 0.8);
}
@ -1853,16 +1860,6 @@ ul {
margin-top: 0.2rem;
}
.send-update-button,
.init-update-replay {
color: var(--accent-color);
background-color: rgba(var(--text-color), 0.04);
}
.send-update-button .icon,
.init-update-replay .icon {
fill: var(--accent-color);
}
.temp-task {
padding: 1rem;
background-color: rgba(var(--foreground-color), 1);
@ -2223,6 +2220,13 @@ input[type=date]:focus {
width: min(48rem, 100%);
margin: 0 auto;
}
#intern_profile__left {
position: -webkit-sticky;
position: sticky;
top: 1rem;
align-self: flex-start;
padding: 1.5rem;
}
}
@media only screen and (min-width: 1280px) {
#main_page {

2
css/main.min.css vendored

File diff suppressed because one or more lines are too long

View File

@ -1129,6 +1129,7 @@ ul {
user-select: none;
padding: 0.8rem 1rem;
gap: 0.8rem;
color: inherit;
input {
height: 1.3em;
width: 1.3em;
@ -1256,14 +1257,14 @@ ul {
.container-header h4 {
flex: 1;
}
#intern_info__initials {
#intern_profile__initials {
position: relative;
height: 4rem;
width: 4rem;
font-size: 1.3rem;
color: var(--color);
}
#intern_info__name {
#intern_profile__name {
font-size: 1.5rem;
padding: 0.4rem 0.5rem;
border-radius: 0.5rem;
@ -1348,23 +1349,29 @@ ul {
color: var(--nice-blue);
}
}
#intern_info__wrapper {
#intern_profile {
display: flex;
flex-wrap: wrap;
gap: 1rem;
& > * {
gap: 1.5rem;
&__left {
width: min(26rem, 100%);
border-radius: 0.5rem;
background-color: rgba(var(--foreground-color), 1);
padding: 1rem;
}
&__right {
flex: 1;
}
}
.intern_info__task {
.intern_profile__task {
display: grid;
align-items: center;
width: 100%;
gap: 0.3rem;
margin-top: 1rem;
padding: 0.5rem;
padding: 1rem;
border-radius: 0.5rem;
background-color: rgba(var(--text-color), 0.06);
background-color: rgba(var(--foreground-color), 1);
grid-template-columns: minmax(0, 1fr) auto;
h4 {
grid-column: 1/-1;
@ -1794,15 +1801,6 @@ ul {
}
}
.send-update-button,
.init-update-replay {
color: var(--accent-color);
background-color: rgba(var(--text-color), 0.04);
.icon {
fill: var(--accent-color);
}
}
.temp-task {
padding: 1rem;
background-color: rgba(var(--foreground-color), 1);
@ -2174,6 +2172,14 @@ input[type="date"] {
margin: 0 auto;
}
}
#intern_profile {
&__left {
position: sticky;
top: 1rem;
align-self: flex-start;
padding: 1.5rem;
}
}
}
@media only screen and (min-width: 1280px) {

View File

@ -517,6 +517,7 @@
<ul id="task_requests_list" class="grid gap-0-5 observe-empty-state margin-top-1"></ul>
<h4 class="empty-state">No task requests</h4>
</section>
<section id="intern_profile" class="inner-page hidden flex align-start"></section>
<section id="all_interns_page" class="inner-page hidden flex flex-direction-column align-start">
<div id="all_interns_page__header" class="grid gap-0-5">
<h2>Interns</h2>
@ -563,19 +564,6 @@
</main>
</main>
<sm-popup id="intern_info_popup">
<header slot="header" class="popup__header">
<button class="popup__header__close" onclick="closePopup()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
</svg>
</button>
</header>
<section id="intern_info__wrapper" class="grid gap-1"></section>
</sm-popup>
<sm-popup id="intern_list_popup">
<header slot="header" class="popup__header">
<button class="popup__header__close" onclick="closePopup()">
@ -1224,6 +1212,9 @@
case 'all_interns_page':
renderAllInterns()
break;
case 'intern_profile':
render.internProfile(params?.id)
break;
case 'project_explorer':
let breadcrumbs = []
if (subPageId1) {
@ -1917,7 +1908,8 @@
</div>
`;
},
internCard(internFloId, { selectable = false } = {}) {
internCard(internFloId, options = {}) {
const { selectable } = options
const internName = RIBC.getInternList()[internFloId]
const internPoints = RIBC.getInternRating(internFloId)
const splitName = internName.split(' ')
@ -1925,9 +1917,10 @@
if (splitName.length > 1) {
initials += splitName[splitName.length - 1][0]
}
return html`
<label class="intern-card align-center interact" .dataset=${{ internFloId }} onclick=${selectable ? false : showInternInfo} title="Intern Information">
${selectable ? html`<input type="checkbox" class="intern-card__checkbox" value=${internFloId}>` : ''}
if (selectable) {
return html`
<label class="intern-card align-center interact" .dataset=${{ internFloId }} title="Intern Information">
<input type="checkbox" class="intern-card__checkbox" value=${internFloId}>
<div class="intern-card__initials" style=${`--color: var(${getInternColor(internFloId)})`}>${initials}</div>
<div class="intern-card__name">${internName}</div>
<div class="intern-card__score-wrapper flex align-center">
@ -1935,6 +1928,17 @@
<svg class="icon icon--star" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> <path fill="none" d="M0 0h24v24H0z" /> <path d="M12 18.26l-7.053 3.948 1.575-7.928L.587 8.792l8.027-.952L12 .5l3.386 7.34 8.027.952-5.935 5.488 1.575 7.928z" /> </svg>
</div>
</label>`;
} else {
return html`
<a class="intern-card align-center interact" href=${`#/intern_profile?id=${internFloId}`} title="Intern Information">
<div class="intern-card__initials" style=${`--color: var(${getInternColor(internFloId)})`}>${initials}</div>
<div class="intern-card__name">${internName}</div>
<div class="intern-card__score-wrapper flex align-center">
<b class="intern-card__score">${internPoints}</b>
<svg class="icon icon--star" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> <path fill="none" d="M0 0h24v24H0z" /> <path d="M12 18.26l-7.053 3.948 1.575-7.928L.587 8.792l8.027-.952L12 .5l3.386 7.34 8.027.952-5.935 5.488 1.575 7.928z" /> </svg>
</div>
</a>`;
}
},
adminInterns() {
const addInternButton = html`<button class="button button--colored" onclick="openPopup('add_intern_popup')">
@ -1944,12 +1948,16 @@
renderElem(getRef('admin_page__intern_list'), html`${[addInternButton, filterInterns('')]}`)
},
internUpdateCard(update) {
const { floID, time, note, update: { projectCode, branch, task, description, link } } = update
const { floID, time, note, update: { projectCode, branch, task, description, link }, tag } = update
let topic = `${RIBC.getProjectDetails(projectCode).projectName} / ${RIBC.getTaskDetails(projectCode, branch, task).title}`
const internName = RIBC.getInternList()[floID]
let replyButton
if (userType === "admin" && !note) {
replyButton = html`<button class="button button--small init-update-replay margin-left-auto">Reply</button>`
let saveButton
if (userType === "admin") {
if (!note)
replyButton = html`<button class="button button--small button--colored init-update-replay">Reply</button>`
if (!tag)
saveButton = html`<button class="button button--small button--colored save-update margin-left-auto">Save</button>`
}
let providedLink
if (link) {
@ -1971,8 +1979,8 @@
<h4 class="update__topic">${topic}</h4>
<p class="update__message ws-pre-line wrap-around">${description}</p>
${providedLink}
${replyButton}
${adminReply}
${saveButton || replyButton ? html`<div class="flex align-center gap-0-3">${saveButton}${replyButton}</div>` : ''}
</li>`;
},
branchButton(obj = {}) {
@ -1997,14 +2005,33 @@
</span>
`
},
internSpecificTasks(internId) {
assignedInternTasks(internId) {
const { assignedTasks, completedTasks } = RIBC.getInternRecord(internId)
if (getObjLength(assignedTasks) === 0) return false
const assignedTasksList = [];
for (const task in assignedTasks) {
if (completedTasks[task]) continue;
const { points, assignedOn } = assignedTasks[task];
const { title } = RIBC.getAllTasks()[task];
assignedTasksList.push(html`
<div class="intern_profile__task">
<h4>${title}</h4>
<time>${getFormattedTime(assignedOn, 'date-only')}</time>
<p class="flex align-center gap-0-3">
${points}
<svg class="icon icon--star" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> <path fill="none" d="M0 0h24v24H0z"></path> <path d="M12 18.26l-7.053 3.948 1.575-7.928L.587 8.792l8.027-.952L12 .5l3.386 7.34 8.027.952-5.935 5.488 1.575 7.928z"></path> </svg>
</p>
</div>`)
}
},
completedInternTasks(internId) {
const { completedTasks } = RIBC.getInternRecord(internId)
if (getObjLength(completedTasks) === 0) return false
return Object.keys(completedTasks).map(task => {
const { points, completionDate } = completedTasks[task];
const { title } = RIBC.getAllTasks()[task];
return html`
<div class="intern_info__task">
<div class="intern_profile__task">
<h4>${title}</h4>
<time>${getFormattedTime(completionDate, 'date-only')}</time>
<p class="flex align-center gap-0-3">
@ -2197,13 +2224,94 @@
<h4 class="task__title">${title}</h4>
${linkifyDescription}
</div>
<button class="send-update-button button--small margin-left-auto" onclick=${initTaskUpdate}>
<button class="send-update-button button--small button--colored margin-left-auto" onclick=${initTaskUpdate}>
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M1.946 9.315c-.522-.174-.527-.455.01-.634l19.087-6.362c.529-.176.832.12.684.638l-5.454 19.086c-.15.529-.455.547-.679.045L12 14l6-8-8 6-8.054-2.685z"/></svg>
Post an update
</button>
</li>
`;
},
internProfile(internFloId) {
const { joined, completedTasks = {}, active = true } = RIBC.getInternRecord(internFloId) || {}
const internName = RIBC.getInternList()[internFloId]
const rating = RIBC.getInternRating(internFloId)
let completedTasksCount = 0;
let totalPoints = 0
for (const task in completedTasks) {
completedTasksCount++
totalPoints += completedTasks[task].points
}
const splitName = internName.split(' ')
let initials = splitName[0][0]
if (splitName.length > 1) {
initials += splitName[splitName.length - 1][0]
}
renderElem(getRef('intern_profile'), html`
<div id="intern_profile__left">
<div class="flex flex-direction-column align-items-center gap-1-5">
<div id="intern_profile__initials" class="intern-card__initials" style=${`--color: var(${getInternColor(internFloId)})`}>${initials}</div>
<div class="flex flex-direction-column align-items-center gap-0-5">
<div class="flex align-center gap-0-5">
<h3 id="intern_profile__name" class="text-center">${internName}</h3>
${userType === "admin" ? html`<button id="edit_intern_name" class="button button--small button--colored" onclick=${toggleInternNameEditing}>Edit</button> ` : ''}
</div>
<sm-copy id="intern_profile__flo_id" value=${internFloId}></sm-copy>
</div>
${joined ? html`<p>Joined on ${getFormattedTime(joined, 'date-only')}</p>` : ''}
</div>
<div id="stats_wrapper">
<div id="intern_rating" class="stat">
<div class="stat__display">
<svg class="stat__circle" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"> <circle cx="64" cy="64" r="63.5"/> </svg>
<span class="stat__count">${rating}%</h4>
</div>
<p>Rating</p>
</div>
<div id="intern_complete_tasks" class="stat">
<div class="stat__display">
<svg class="stat__circle" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"> <circle cx="64" cy="64" r="63.5"/> </svg>
<span class="stat__count">${completedTasksCount}</h4>
</div>
<p>Task completed</p>
</div>
<div id="intern_points" class="stat">
<div class="stat__display">
<svg class="stat__circle" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"> <circle cx="64" cy="64" r="63.5"/> </svg>
<span class="stat__count">${totalPoints}</h4>
</div>
<p>Points earned</p>
</div>
</div>
</div>
<div id="intern_profile__right" class="flex flex-direction-column gap-1-5">
<div class="flex align-center space-between gap-1">
<h3>Tasks</h3>
<a href=${`#/updates_page?projectCode=all&internId=${internFloId}`} class="button button--small button--colored">See updates</a>
</div>
<div>
<h4>Assigned</h4>
<div>
${render.assignedInternTasks(internFloId) || html`<p>No currently assigned tasks</p>`}
</div>
</div>
<div>
<h4>Completed</h4>
<div>
${render.completedInternTasks(internFloId) || html`<p>No tasks completed yet</p>`}
</div>
</div>
</div>
`)
let color = '--green';
if (rating < 50) {
color = '--danger-color'
} else if (rating < 80) {
color = '--orange'
}
setTimeout(() => {
getRef('intern_rating').style = `--progress: ${400 - (rating * 4)}; --rating-color:var(${color})`;
}, 100);
},
dashProject(projectCode) {
const { projectName } = RIBC.getProjectDetails(projectCode)
const projectMap = RIBC.getProjectMap(projectCode)
@ -2620,85 +2728,6 @@
return days;
}
// opens a popup containing various intern information
function showInternInfo(e) {
const internFloId = e.target.closest('.intern-card').dataset.internFloId;
const { joined, completedTasks = {}, active = true } = RIBC.getInternRecord(internFloId) || {}
const internName = RIBC.getInternList()[internFloId]
const rating = RIBC.getInternRating(internFloId)
let completedTasksCount = 0;
let totalPoints = 0
for (const task in completedTasks) {
completedTasksCount++
totalPoints += completedTasks[task].points
}
const splitName = internName.split(' ')
let initials = splitName[0][0]
if (splitName.length > 1) {
initials += splitName[splitName.length - 1][0]
}
renderElem(getRef('intern_info__wrapper'), html`
<div id="intern_info__left">
<div class="flex flex-direction-column align-items-center gap-1-5">
<div id="intern_info__initials" class="intern-card__initials" style=${`--color: var(${getInternColor(internFloId)})`}>${initials}</div>
<div class="flex flex-direction-column align-items-center gap-0-5">
<div class="flex align-center gap-0-5">
<h3 id="intern_info__name" class="text-center">${internName}</h3>
${userType === "admin" ? html`<button id="edit_intern_name" class="button button--small button--colored" onclick=${toggleInternNameEditing}>Edit</button> ` : ''}
</div>
<sm-copy id="intern_info__flo_id" value=${internFloId}></sm-copy>
</div>
${joined ? html`<p>Joined on ${getFormattedTime(joined, 'date-only')}</p>` : ''}
</div>
<div id="stats_wrapper">
<div id="intern_rating" class="stat">
<div class="stat__display">
<svg class="stat__circle" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"> <circle cx="64" cy="64" r="63.5"/> </svg>
<span class="stat__count">${rating}%</h4>
</div>
<p>Rating</p>
</div>
<div id="intern_complete_tasks" class="stat">
<div class="stat__display">
<svg class="stat__circle" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"> <circle cx="64" cy="64" r="63.5"/> </svg>
<span class="stat__count">${completedTasksCount}</h4>
</div>
<p>Task completed</p>
</div>
<div id="intern_points" class="stat">
<div class="stat__display">
<svg class="stat__circle" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"> <circle cx="64" cy="64" r="63.5"/> </svg>
<span class="stat__count">${totalPoints}</h4>
</div>
<p>Points earned</p>
</div>
</div>
</div>
<div id="intern_info__right">
<div id="intern_info__tasks">
<div class="flex align-center space-between gap-1">
<h3>Tasks completed</h3>
<a href=${`#/updates_page?projectCode=all&internId=${internFloId}`} class="button button--small button--colored">See updates</a>
</div>
<div id="intern_info__tasks_list">
${render.internSpecificTasks(internFloId) || html`<p>No tasks completed yet</p>`}
</div>
</div>
</div>
`)
openPopup('intern_info_popup');
let color = '--green';
if (rating < 50) {
color = '--danger-color'
} else if (rating < 80) {
color = '--orange'
}
setTimeout(() => {
getRef('intern_rating').style = `--progress: ${400 - (rating * 4)}; --rating-color:var(${color})`;
}, 100);
}
// opens a popup containing various project information
function showProjectInfo(projectCode) {
const { projectName, projectDescription } = RIBC.getProjectDetails(projectCode);
@ -3156,22 +3185,22 @@
function toggleInternNameEditing(e) {
const button = e.target.closest('button');
if (getRef('intern_info__name').isContentEditable) {
const floId = getRef('intern_info__flo_id').value;
const newName = getRef('intern_info__name').textContent.trim();
if (getRef('intern_profile__name').isContentEditable) {
const floId = getRef('intern_profile__flo_id').value;
const newName = getRef('intern_profile__name').textContent.trim();
if (newName !== '' && floGlobals.tempEditableContent !== newName) {
RIBC.admin.renameIntern(floId, newName)
const highPerformingInterns = Object.keys(RIBC.getInternList()).sort((a, b) => RIBC.getInternRating(b) - RIBC.getInternRating(a));
renderElem(getRef('top_interns'), html`${highPerformingInterns.slice(0, 8).map(floId => render.internCard(floId))}`)
notify('Intern name updated locally, please commit changes to make them permanent.', 'success')
}
getRef('intern_info__name').contentEditable = false;
getRef('intern_profile__name').contentEditable = false;
button.textContent = 'Edit';
document.getSelection().collapseToEnd()
floGlobals.tempEditableContent = '';
adminDataChanged();
} else {
makeEditable(getRef('intern_info__name'))
makeEditable(getRef('intern_profile__name'))
button.textContent = 'Done'
}
}
@ -3233,7 +3262,7 @@
}
delegate(getRef('all_updates_list'), 'click', '.init-update-replay', (e) => {
const vectorClock = e.delegateTarget.closest('.intern-update').dataset.vectorClock;
e.delegateTarget.after(html.node`
e.delegateTarget.parentNode.after(html.node`
<sm-form class="update-replay grid gap-0-5">
<sm-textarea placeholder="Enter your reply here" class="update-reply-textarea" rows="4" required></sm-textarea>
<div class="flex align-center gap-0-3 margin-left-auto">
@ -3244,9 +3273,22 @@
</div>
</sm-form>
`)
e.delegateTarget.classList.add('hidden')
e.delegateTarget.parentNode.classList.add('hidden')
e.target.closest('.intern-update').querySelector('.update-reply-textarea').focusIn()
})
delegate(getRef('all_updates_list'), 'click', '.save-update', (e) => {
const vectorClock = e.delegateTarget.closest('.intern-update').dataset.vectorClock;
getConfirmation('Are you sure you want to save this update?', { confirmText: 'Save' }).then((res) => {
if (res) {
floCloudAPI.tagApplicationData(vectorClock, 'saved').then(() => {
notify('Update saved', 'success')
e.delegateTarget.remove()
}).catch(() => {
notify('Failed to save update', 'error')
})
}
})
})
function cancelUpdateReply(replayBox) {
replayBox.previousElementSibling.classList.remove('hidden')
@ -3385,9 +3427,6 @@
case 'rate_participants_popup':
renderElem(getRef('rating_wrapper'), html``)
break;
case 'intern_info_popup':
renderElem(getRef('intern_info__wrapper'), html``)
break;
}
if (popupStack.items.length === 0) {
getRef('main_page').removeAttribute('inert')

View File

@ -1,4 +1,4 @@
(function(EXPORTS) { //compactIDB v2.1.0
(function (EXPORTS) { //compactIDB v2.1.0
/* Compact IndexedDB operations */
'use strict';
const compactIDB = EXPORTS;
@ -59,7 +59,7 @@
})
}
compactIDB.initDB = function(dbName, objectStores = {}) {
compactIDB.initDB = function (dbName, objectStores = {}) {
return new Promise((resolve, reject) => {
if (!(objectStores instanceof Object))
return reject('ObjectStores must be an object or array')
@ -87,14 +87,14 @@
resolve("Initiated IndexedDB");
else
upgradeDB(dbName, a_obs, d_obs)
.then(result => resolve(result))
.catch(error => reject(error))
.then(result => resolve(result))
.catch(error => reject(error))
db.close();
}
});
}
const openDB = compactIDB.openDB = function(dbName = defaultDB) {
const openDB = compactIDB.openDB = function (dbName = defaultDB) {
return new Promise((resolve, reject) => {
var idb = indexedDB.open(dbName);
idb.onerror = (event) => reject("Error in opening IndexedDB");
@ -106,7 +106,7 @@
});
}
const deleteDB = compactIDB.deleteDB = function(dbName = defaultDB) {
const deleteDB = compactIDB.deleteDB = function (dbName = defaultDB) {
return new Promise((resolve, reject) => {
var deleteReq = indexedDB.deleteDatabase(dbName);;
deleteReq.onerror = (event) => reject("Error deleting database!");
@ -114,7 +114,7 @@
});
}
compactIDB.writeData = function(obsName, data, key = false, dbName = defaultDB) {
compactIDB.writeData = function (obsName, data, key = false, dbName = defaultDB) {
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
@ -128,7 +128,7 @@
});
}
compactIDB.addData = function(obsName, data, key = false, dbName = defaultDB) {
compactIDB.addData = function (obsName, data, key = false, dbName = defaultDB) {
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
@ -142,7 +142,7 @@
});
}
compactIDB.removeData = function(obsName, key, dbName = defaultDB) {
compactIDB.removeData = function (obsName, key, dbName = defaultDB) {
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
@ -156,7 +156,7 @@
});
}
compactIDB.clearData = function(obsName, dbName = defaultDB) {
compactIDB.clearData = function (obsName, dbName = defaultDB) {
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
@ -168,7 +168,7 @@
});
}
compactIDB.readData = function(obsName, key, dbName = defaultDB) {
compactIDB.readData = function (obsName, key, dbName = defaultDB) {
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
@ -182,7 +182,7 @@
});
}
compactIDB.readAllData = function(obsName, dbName = defaultDB) {
compactIDB.readAllData = function (obsName, dbName = defaultDB) {
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
@ -223,7 +223,7 @@
})
}*/
compactIDB.searchData = function(obsName, options = {}, dbName = defaultDB) {
compactIDB.searchData = function (obsName, options = {}, dbName = defaultDB) {
options.lowerKey = options.atKey || options.lowerKey || 0
options.upperKey = options.atKey || options.upperKey || false
options.patternEval = options.patternEval || ((k, v) => {

View File

@ -1,4 +1,4 @@
(function(EXPORTS) { //floBlockchainAPI v2.3.3b
(function (EXPORTS) { //floBlockchainAPI v2.3.3b
/* FLO Blockchain Operator to send/receive data from blockchain using API calls*/
'use strict';
const floBlockchainAPI = EXPORTS;
@ -102,7 +102,7 @@
});
//Promised function to get data from API
const promisedAPI = floBlockchainAPI.promisedAPI = floBlockchainAPI.fetch = function(apicall) {
const promisedAPI = floBlockchainAPI.promisedAPI = floBlockchainAPI.fetch = function (apicall) {
return new Promise((resolve, reject) => {
//console.log(apicall);
fetch_api(apicall)
@ -112,7 +112,7 @@
}
//Get balance for the given Address
const getBalance = floBlockchainAPI.getBalance = function(addr) {
const getBalance = floBlockchainAPI.getBalance = function (addr) {
return new Promise((resolve, reject) => {
promisedAPI(`api/addr/${addr}/balance`)
.then(balance => resolve(parseFloat(balance)))
@ -121,7 +121,7 @@
}
//Send Tx to blockchain
const sendTx = floBlockchainAPI.sendTx = function(senderAddr, receiverAddr, sendAmt, privKey, floData = '', strict_utxo = true) {
const sendTx = floBlockchainAPI.sendTx = function (senderAddr, receiverAddr, sendAmt, privKey, floData = '', strict_utxo = true) {
return new Promise((resolve, reject) => {
if (!floCrypto.validateASCII(floData))
return reject("Invalid FLO_Data: only printable ASCII characters are allowed");
@ -187,7 +187,7 @@
}
//Write Data into blockchain
floBlockchainAPI.writeData = function(senderAddr, data, privKey, receiverAddr = DEFAULT.receiverID, options = {}) {
floBlockchainAPI.writeData = function (senderAddr, data, privKey, receiverAddr = DEFAULT.receiverID, options = {}) {
let strict_utxo = options.strict_utxo === false ? false : true,
sendAmt = isNaN(options.sendAmt) ? DEFAULT.sendAmt : options.sendAmt;
return new Promise((resolve, reject) => {
@ -200,7 +200,7 @@
}
//merge all UTXOs of a given floID into a single UTXO
floBlockchainAPI.mergeUTXOs = function(floID, privKey, floData = '') {
floBlockchainAPI.mergeUTXOs = function (floID, privKey, floData = '') {
return new Promise((resolve, reject) => {
if (!floCrypto.validateFloID(floID))
return reject(`Invalid floID`);
@ -234,7 +234,7 @@
* @param {boolean} preserveRatio (optional) preserve ratio or equal contribution
* @return {Promise}
*/
floBlockchainAPI.writeDataMultiple = function(senderPrivKeys, data, receivers = [DEFAULT.receiverID], preserveRatio = true) {
floBlockchainAPI.writeDataMultiple = function (senderPrivKeys, data, receivers = [DEFAULT.receiverID], preserveRatio = true) {
return new Promise((resolve, reject) => {
if (!Array.isArray(senderPrivKeys))
return reject("Invalid senderPrivKeys: SenderPrivKeys must be Array");
@ -266,7 +266,7 @@
* @param {string} floData FLO data of the txn
* @return {Promise}
*/
const sendTxMultiple = floBlockchainAPI.sendTxMultiple = function(senderPrivKeys, receivers, floData = '') {
const sendTxMultiple = floBlockchainAPI.sendTxMultiple = function (senderPrivKeys, receivers, floData = '') {
return new Promise((resolve, reject) => {
if (!floCrypto.validateASCII(floData))
return reject("Invalid FLO_Data: only printable ASCII characters are allowed");
@ -421,7 +421,7 @@
}
//Broadcast signed Tx in blockchain using API
const broadcastTx = floBlockchainAPI.broadcastTx = function(signedTxHash) {
const broadcastTx = floBlockchainAPI.broadcastTx = function (signedTxHash) {
return new Promise((resolve, reject) => {
if (signedTxHash.length < 1)
return reject("Empty Signature");
@ -441,7 +441,7 @@
})
}
floBlockchainAPI.getTx = function(txid) {
floBlockchainAPI.getTx = function (txid) {
return new Promise((resolve, reject) => {
promisedAPI(`api/tx/${txid}`)
.then(response => resolve(response))
@ -450,7 +450,7 @@
}
//Read Txs of Address between from and to
const readTxs = floBlockchainAPI.readTxs = function(addr, from, to) {
const readTxs = floBlockchainAPI.readTxs = function (addr, from, to) {
return new Promise((resolve, reject) => {
promisedAPI(`api/addrs/${addr}/txs?from=${from}&to=${to}`)
.then(response => resolve(response))
@ -459,7 +459,7 @@
}
//Read All Txs of Address (newest first)
floBlockchainAPI.readAllTxs = function(addr) {
floBlockchainAPI.readAllTxs = function (addr) {
return new Promise((resolve, reject) => {
promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => {
promisedAPI(`api/addrs/${addr}/txs?from=0&to=${response.totalItems}0`)
@ -481,7 +481,7 @@
sender : flo-id(s) of sender
receiver : flo-id(s) of receiver
*/
floBlockchainAPI.readData = function(addr, options = {}) {
floBlockchainAPI.readData = function (addr, options = {}) {
options.limit = options.limit || 0;
options.ignoreOld = options.ignoreOld || 0;
if (typeof options.sender === "string") options.sender = [options.sender];
@ -489,7 +489,7 @@
return new Promise((resolve, reject) => {
promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => {
var newItems = response.totalItems - options.ignoreOld;
promisedAPI(`api/addrs/${addr}/txs?from=0&to=${newItems*2}`).then(response => {
promisedAPI(`api/addrs/${addr}/txs?from=0&to=${newItems * 2}`).then(response => {
if (options.limit <= 0)
options.limit = response.items.length;
var filteredData = [];

View File

@ -1,4 +1,4 @@
(function(EXPORTS) { //floCrypto v2.3.3d
(function (EXPORTS) { //floCrypto v2.3.3d
/* FLO Crypto Operators */
'use strict';
const floCrypto = EXPORTS;
@ -78,14 +78,14 @@
}
//generate a random Interger within range
floCrypto.randInt = function(min, max) {
floCrypto.randInt = function (min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(securedMathRandom() * (max - min + 1)) + min;
}
//generate a random String within length (options : alphaNumeric chars only)
floCrypto.randString = function(length, alphaNumeric = true) {
floCrypto.randString = function (length, alphaNumeric = true) {
var result = '';
var characters = alphaNumeric ? 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' :
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+-./*?@#&$<>=[]{}():';
@ -95,7 +95,7 @@
}
//Encrypt Data using public-key
floCrypto.encryptData = function(data, receiverPublicKeyHex) {
floCrypto.encryptData = function (data, receiverPublicKeyHex) {
var senderECKeyData = getSenderPublicKeyString();
var senderDerivedKey = deriveSharedKeySender(receiverPublicKeyHex, senderECKeyData.privateKey);
let senderKey = senderDerivedKey.XValue + senderDerivedKey.YValue;
@ -107,7 +107,7 @@
}
//Decrypt Data using private-key
floCrypto.decryptData = function(data, privateKeyHex) {
floCrypto.decryptData = function (data, privateKeyHex) {
var receiverECKeyData = {};
if (typeof privateKeyHex !== "string") throw new Error("No private key found.");
let privateKey = wifToDecimal(privateKeyHex, true);
@ -120,7 +120,7 @@
}
//Sign data using private-key
floCrypto.signData = function(data, privateKeyHex) {
floCrypto.signData = function (data, privateKeyHex) {
var key = new Bitcoin.ECKey(privateKeyHex);
var messageHash = Crypto.SHA256(data);
var messageSign = Bitcoin.ECDSA.sign(messageHash, key.priv);
@ -129,7 +129,7 @@
}
//Verify signatue of the data using public-key
floCrypto.verifySign = function(data, signatureHex, publicKeyHex) {
floCrypto.verifySign = function (data, signatureHex, publicKeyHex) {
var msgHash = Crypto.SHA256(data);
var sigBytes = Crypto.util.hexToBytes(signatureHex);
var publicKeyPoint = ecparams.getCurve().decodePointHex(publicKeyHex);
@ -138,7 +138,7 @@
}
//Generates a new flo ID and returns private-key, public-key and floID
const generateNewID = floCrypto.generateNewID = function() {
const generateNewID = floCrypto.generateNewID = function () {
var key = new Bitcoin.ECKey(false);
key.setCompressed(true);
return {
@ -168,7 +168,7 @@
});
//Returns public-key from private-key
floCrypto.getPubKeyHex = function(privateKeyHex) {
floCrypto.getPubKeyHex = function (privateKeyHex) {
if (!privateKeyHex)
return null;
var key = new Bitcoin.ECKey(privateKeyHex);
@ -179,7 +179,7 @@
}
//Returns flo-ID from public-key or private-key
floCrypto.getFloID = function(keyHex) {
floCrypto.getFloID = function (keyHex) {
if (!keyHex)
return null;
try {
@ -192,7 +192,7 @@
}
}
floCrypto.getAddress = function(privateKeyHex, strict = false) {
floCrypto.getAddress = function (privateKeyHex, strict = false) {
if (!privateKeyHex)
return;
var key = new Bitcoin.ECKey(privateKeyHex);
@ -212,7 +212,7 @@
}
//Verify the private-key for the given public-key or flo-ID
floCrypto.verifyPrivKey = function(privateKeyHex, pubKey_floID, isfloID = true) {
floCrypto.verifyPrivKey = function (privateKeyHex, pubKey_floID, isfloID = true) {
if (!privateKeyHex || !pubKey_floID)
return false;
try {
@ -232,7 +232,7 @@
}
//Check if the given flo-id is valid or not
floCrypto.validateFloID = function(floID) {
floCrypto.validateFloID = function (floID) {
if (!floID)
return false;
try {
@ -244,7 +244,7 @@
}
//Check if the given address (any blockchain) is valid or not
floCrypto.validateAddr = function(address, std = true, bech = true) {
floCrypto.validateAddr = function (address, std = true, bech = true) {
let raw = decodeAddress(address);
if (!raw)
return false;
@ -267,7 +267,7 @@
}
//Check the public-key for the address (any blockchain)
floCrypto.verifyPubKey = function(pubKeyHex, address) {
floCrypto.verifyPubKey = function (pubKeyHex, address) {
let raw = decodeAddress(address),
pub_hash = Crypto.util.bytesToHex(ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubKeyHex), {
asBytes: true
@ -276,7 +276,7 @@
}
//Convert the given address (any blockchain) to equivalent floID
floCrypto.toFloID = function(address) {
floCrypto.toFloID = function (address) {
if (!address)
return;
let raw = decodeAddress(address);
@ -292,7 +292,7 @@
}
//Checks if the given addresses (any blockchain) are same (w.r.t keys)
floCrypto.isSameAddr = function(addr1, addr2) {
floCrypto.isSameAddr = function (addr1, addr2) {
if (!addr1 || !addr2)
return;
let raw1 = decodeAddress(addr1),
@ -303,7 +303,7 @@
return raw1.hex === raw2.hex;
}
const decodeAddress = floCrypto.decodeAddr = function(address) {
const decodeAddress = floCrypto.decodeAddr = function (address) {
if (!address)
return;
else if (address.length == 33 || address.length == 34) { //legacy encoding
@ -338,7 +338,7 @@
}
//Split the str using shamir's Secret and Returns the shares
floCrypto.createShamirsSecretShares = function(str, total_shares, threshold_limit) {
floCrypto.createShamirsSecretShares = function (str, total_shares, threshold_limit) {
try {
if (str.length > 0) {
var strHex = shamirSecretShare.str2hex(str);
@ -352,7 +352,7 @@
}
//Returns the retrived secret by combining the shamirs shares
const retrieveShamirSecret = floCrypto.retrieveShamirSecret = function(sharesArray) {
const retrieveShamirSecret = floCrypto.retrieveShamirSecret = function (sharesArray) {
try {
if (sharesArray.length > 0) {
var comb = shamirSecretShare.combine(sharesArray.slice(0, sharesArray.length));
@ -366,7 +366,7 @@
}
//Verifies the shares and str
floCrypto.verifyShamirsSecret = function(sharesArray, str) {
floCrypto.verifyShamirsSecret = function (sharesArray, str) {
if (!str)
return null;
else if (retrieveShamirSecret(sharesArray) === str)
@ -375,7 +375,7 @@
return false;
}
const validateASCII = floCrypto.validateASCII = function(string, bool = true) {
const validateASCII = floCrypto.validateASCII = function (string, bool = true) {
if (typeof string !== "string")
return null;
if (bool) {
@ -393,8 +393,8 @@
if (x < 32 || x > 127)
if (x in invalids)
invalids[string[i]].push(i)
else
invalids[string[i]] = [i];
else
invalids[string[i]] = [i];
}
if (Object.keys(invalids).length)
return invalids;
@ -403,7 +403,7 @@
}
}
floCrypto.convertToASCII = function(string, mode = 'soft-remove') {
floCrypto.convertToASCII = function (string, mode = 'soft-remove') {
let chars = validateASCII(string, false);
if (chars === true)
return string;
@ -414,9 +414,9 @@
ascii_alternatives.split('\n').forEach(a => refAlt[a[0]] = a.slice(2));
mode = mode.toLowerCase();
if (mode === "hard-unicode")
convertor = (c) => `\\u${('000'+c.charCodeAt().toString(16)).slice(-4)}`;
convertor = (c) => `\\u${('000' + c.charCodeAt().toString(16)).slice(-4)}`;
else if (mode === "soft-unicode")
convertor = (c) => refAlt[c] || `\\u${('000'+c.charCodeAt().toString(16)).slice(-4)}`;
convertor = (c) => refAlt[c] || `\\u${('000' + c.charCodeAt().toString(16)).slice(-4)}`;
else if (mode === "hard-remove")
convertor = c => "";
else if (mode === "soft-remove")
@ -428,7 +428,7 @@
return result;
}
floCrypto.revertUnicode = function(string) {
floCrypto.revertUnicode = function (string) {
return string.replace(/\\u[\dA-F]{4}/gi,
m => String.fromCharCode(parseInt(m.replace(/\\u/g, ''), 16)));
}

View File

@ -1,4 +1,4 @@
(function(EXPORTS) { //floDapps v2.3.2d
(function (EXPORTS) { //floDapps v2.3.2d
/* General functions for FLO Dapps*/
'use strict';
const floDapps = EXPORTS;
@ -454,7 +454,7 @@
})
});
floDapps.launchStartUp = function() {
floDapps.launchStartUp = function () {
return new Promise((resolve, reject) => {
initIndexedDB().then(log => {
console.log(log)
@ -497,7 +497,7 @@
floDapps.setAppObjectStores = appObs => initIndexedDB.appObs = appObs;
floDapps.storeContact = function(floID, name) {
floDapps.storeContact = function (floID, name) {
return new Promise((resolve, reject) => {
if (!floCrypto.validateAddr(floID))
return reject("Invalid floID!")
@ -508,7 +508,7 @@
});
}
floDapps.storePubKey = function(floID, pubKey) {
floDapps.storePubKey = function (floID, pubKey) {
return new Promise((resolve, reject) => {
if (floID in user.pubKeys)
return resolve("pubKey already stored")
@ -523,7 +523,7 @@
});
}
floDapps.sendMessage = function(floID, message) {
floDapps.sendMessage = function (floID, message) {
return new Promise((resolve, reject) => {
let options = {
receiverID: floID,
@ -538,7 +538,7 @@
})
}
floDapps.requestInbox = function(callback) {
floDapps.requestInbox = function (callback) {
return new Promise((resolve, reject) => {
let lastVC = Object.keys(user.messages).sort().pop()
let options = {
@ -552,7 +552,7 @@
try {
if (d[v].message instanceof Object && "secret" in d[v].message)
d[v].message = floCrypto.decryptData(d[v].message, privKey)
} catch (error) {}
} catch (error) { }
compactIDB.writeData("messages", d[v], v, user.db_name)
user.messages[v] = d[v]
}
@ -565,7 +565,7 @@
})
}
floDapps.manageAppConfig = function(adminPrivKey, addList, rmList, settings) {
floDapps.manageAppConfig = function (adminPrivKey, addList, rmList, settings) {
return new Promise((resolve, reject) => {
if (!Array.isArray(addList) || !addList.length) addList = undefined;
if (!Array.isArray(rmList) || !rmList.length) rmList = undefined;
@ -584,12 +584,12 @@
reject('Access Denied for Admin privilege')
else
floBlockchainAPI.writeData(floID, JSON.stringify(floData), adminPrivKey)
.then(result => resolve(['Updated App Configuration', result]))
.catch(error => reject(error))
.then(result => resolve(['Updated App Configuration', result]))
.catch(error => reject(error))
})
}
const clearCredentials = floDapps.clearCredentials = function() {
const clearCredentials = floDapps.clearCredentials = function () {
return new Promise((resolve, reject) => {
compactIDB.clearData('credentials', DEFAULT.application).then(result => {
localStorage.removeItem(`${DEFAULT.application}#privKey`);
@ -599,7 +599,7 @@
})
}
floDapps.deleteUserData = function(credentials = false) {
floDapps.deleteUserData = function (credentials = false) {
return new Promise((resolve, reject) => {
let p = []
p.push(compactIDB.deleteDB(user.db_name))
@ -611,7 +611,7 @@
})
}
floDapps.deleteAppData = function() {
floDapps.deleteAppData = function () {
return new Promise((resolve, reject) => {
compactIDB.deleteDB(DEFAULT.application).then(result => {
localStorage.removeItem(`${DEFAULT.application}#privKey`)
@ -623,7 +623,7 @@
})
}
floDapps.securePrivKey = function(pwd) {
floDapps.securePrivKey = function (pwd) {
return new Promise(async (resolve, reject) => {
let indexArr = localStorage.getItem(`${DEFAULT.application}#privKey`)
if (!indexArr)
@ -643,8 +643,8 @@
})
}
floDapps.verifyPin = function(pin = null) {
const readSharesFromIDB = function(indexArr) {
floDapps.verifyPin = function (pin = null) {
const readSharesFromIDB = function (indexArr) {
return new Promise((resolve, reject) => {
var promises = []
for (var i = 0; i < indexArr.length; i++)
@ -687,7 +687,7 @@
})
}
const getNextGeneralData = floDapps.getNextGeneralData = function(type, vectorClock = null, options = {}) {
const getNextGeneralData = floDapps.getNextGeneralData = function (type, vectorClock = null, options = {}) {
var fk = floCloudAPI.util.filterKey(type, options)
vectorClock = vectorClock || getNextGeneralData[fk] || '0';
var filteredResult = {}
@ -716,10 +716,10 @@
let tmp = floCrypto.decryptData(data.message, key)
data.message = JSON.parse(tmp)
break;
} catch (error) {}
} catch (error) { }
}
}
} catch (error) {}
} catch (error) { }
}
}
getNextGeneralData[fk] = Object.keys(filteredResult).sort().pop();

File diff suppressed because it is too large Load Diff

View File

@ -71,7 +71,8 @@
floID: data.senderID,
update: data.message,
time: data.vectorClock.split('_')[0],
note: data.note
note: data.note,
tag: data.tag
}
})
internUpdates = internUpdates.filter(data => data.floID in _.internList)

2
scripts/ribc.min.js vendored

File diff suppressed because one or more lines are too long