diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json
index 602c94624..666145e84 100644
--- a/i18n/en.i18n.json
+++ b/i18n/en.i18n.json
@@ -880,5 +880,6 @@
"search-contract": "๐ {{searchTerm}}",
"search-summon": "๐ฅ Summon of {{dao}}",
"search-ragequit": "๐ธ Ragequit of {{shares}} shares from {{dao}} by {{address}}",
- "search-default": "๐ {{searchTerm}}"
+ "search-default": "๐ {{searchTerm}}",
+ "search-dates": "๐
{{searchTerm}}"
}
diff --git a/imports/api/server/methods.js b/imports/api/server/methods.js
index 31e905160..d6722d20c 100644
--- a/imports/api/server/methods.js
+++ b/imports/api/server/methods.js
@@ -246,9 +246,23 @@ Meteor.methods({
* @summary given a keyword returns contract id
* @param {keyword} keyword identify contract by given keyword
*/
+ getProposalContract(contractId) {
+ check(contractId, String);
+ log(`{ method: 'getProposalContract', user: ${logUser()}, _id: '${contractId}' }`);
+ const contract = Contracts.findOne({ _id: contractId });
+ if (contract.pollId) {
+ return Contracts.findOne({ _id: contract.pollId });
+ }
+ return contract;
+ },
+
+ /**
+ * @summary given a keyword returns contract id
+ * @param {keyword} keyword identify contract by given keyword
+ */
getContractById(contractId) {
check(contractId, String);
- log(`{ method: 'getContractById', user: ${logUser()}, _id: '${contractId}' }`);
+ log(`{ method: 'getProposalContract', user: ${logUser()}, _id: '${contractId}' }`);
return Contracts.findOne({ _id: contractId });
},
diff --git a/imports/api/server/publications.js b/imports/api/server/publications.js
index 67dd6801e..fe94e3feb 100644
--- a/imports/api/server/publications.js
+++ b/imports/api/server/publications.js
@@ -50,6 +50,20 @@ Meteor.publish('singleDao', function (daoQuery) {
return this.ready();
});
+/**
+* @summary gets information of proposals from a given date
+* @return {Object} user data
+*/
+Meteor.publish('dateContracts', function (terms) {
+ check(terms, Object);
+ const parameters = query(terms);
+ const contracts = Contracts.find(parameters.find, parameters.options);
+ if (contracts.count() > 0) {
+ return contracts;
+ }
+ return this.ready();
+});
+
/**
* @summary transactions between a user and a contract
* @return {Object} querying terms
diff --git a/imports/startup/both/routes.js b/imports/startup/both/routes.js
index 3db91f9e6..22e22f372 100644
--- a/imports/startup/both/routes.js
+++ b/imports/startup/both/routes.js
@@ -260,6 +260,35 @@ Router.route('/dao/:dao', {
});
+/**
+* @summary loads a post using date in url
+**/
+Router.route('/date', {
+ name: 'date',
+ template: 'home',
+ loadingTemplate: 'load',
+ onBeforeAction() {
+ Session.set('sidebarMenuSelectedId', 999);
+ _reset();
+ this.next();
+ },
+ waitOn() {
+ return Meteor.subscribe('dateContracts', { view: 'dateRange', from: this.params.query.from, until: this.params.query.until });
+ },
+ data() {
+ if (this.ready()) {
+ return {
+ options: { view: 'dateRange', from: this.params.query.from, until: this.params.query.until },
+ };
+ }
+ return {};
+ },
+ onAfterAction() {
+ _boilerPlate();
+ },
+});
+
+
/**
* @summary loads a post using date in url
**/
diff --git a/imports/ui/modules/chronos.js b/imports/ui/modules/chronos.js
index b5a34895d..56942d160 100644
--- a/imports/ui/modules/chronos.js
+++ b/imports/ui/modules/chronos.js
@@ -37,11 +37,20 @@ const _timeCompressed = (date, micro) => {
return buildSentence(seconds, 'compressed', micro);
};
-const _timeComplete = (date) => {
+const _timeDateOnly = (date) => {
const options = { year: 'numeric', month: 'long', day: 'numeric' };
- return `${_timeAgo(date)} · ${date.toLocaleDateString('en', options)} · ${(date.getHours() < 10) ? `0${(date.getHours())}` : date.getHours()}:${(date.getMinutes() < 10) ? `0${(date.getMinutes())}` : date.getMinutes()}`;
+ return `${date.toLocaleDateString('en', options)}`;
+};
+
+const _hourOnly = (date) => {
+ return `${(date.getHours() < 10) ? `0${(date.getHours())}` : date.getHours()}:${(date.getMinutes() < 10) ? `0${(date.getMinutes())}` : date.getMinutes()}`;
};
+const _timeComplete = (date) => {
+ return `${_timeAgo(date)} · ${_timeDateOnly(date)} · ${_hourOnly(date)}`;
+};
+
+
const timeLeft = (date) => {
const seconds = Math.floor((date - new Date()) / 1000);
if (seconds > 0) {
@@ -50,6 +59,8 @@ const timeLeft = (date) => {
return false;
};
+export const hourOnly = _hourOnly;
+export const timeDateOnly = _timeDateOnly;
export const timeCompressed = _timeCompressed;
export const timeSince = _timeAgo;
export const timeComplete = _timeComplete;
diff --git a/imports/ui/templates/components/decision/ledger/ledger.js b/imports/ui/templates/components/decision/ledger/ledger.js
index 9f283f7f2..1f1c22e2c 100644
--- a/imports/ui/templates/components/decision/ledger/ledger.js
+++ b/imports/ui/templates/components/decision/ledger/ledger.js
@@ -38,6 +38,9 @@ const _convertQuery = (instance) => {
case 'search':
tally.options.view = 'transactionsSearch';
break;
+ case 'dateRange':
+ tally.options.view = 'transactionsDate';
+ break;
default:
}
return tally;
diff --git a/imports/ui/templates/widgets/feed/feed.js b/imports/ui/templates/widgets/feed/feed.js
index 4f59787fa..641704572 100644
--- a/imports/ui/templates/widgets/feed/feed.js
+++ b/imports/ui/templates/widgets/feed/feed.js
@@ -100,6 +100,7 @@ const _isIndexFeed = (instance) => {
|| instance.options.view === 'token'
|| instance.options.view === 'transactionsToken'
|| instance.options.view === 'transactionsPeer'
+ || instance.options.view === 'transactionsDate'
|| instance.options.view === 'transactionsGeo'
|| instance.options.view === 'peer'
|| instance.options.view === 'threadVotes'
diff --git a/imports/ui/templates/widgets/feed/feedItem.html b/imports/ui/templates/widgets/feed/feedItem.html
index cbea2e6f4..d68a5d675 100644
--- a/imports/ui/templates/widgets/feed/feedItem.html
+++ b/imports/ui/templates/widgets/feed/feedItem.html
@@ -86,7 +86,7 @@
{{#if summon}}
{{> warning label="moloch-summon-dao" style='summon'}}
@@ -116,7 +116,7 @@
{{#if ragequit}}
{{> warning label="moloch-contract-ragequit" style='summon ragequit'}}
@@ -135,7 +135,7 @@
{{else}}
{{{description}}}
diff --git a/imports/ui/templates/widgets/feed/feedItem.js b/imports/ui/templates/widgets/feed/feedItem.js
index c9dd86068..248b07d96 100644
--- a/imports/ui/templates/widgets/feed/feedItem.js
+++ b/imports/ui/templates/widgets/feed/feedItem.js
@@ -6,9 +6,9 @@ import { $ } from 'meteor/jquery';
import { Router } from 'meteor/iron:router';
import { ReactiveVar } from 'meteor/reactive-var';
-import { getProfileFromUsername, getAnonymous } from '/imports/startup/both/modules/User';
+import { getAnonymous } from '/imports/startup/both/modules/User';
import { removeContract } from '/imports/startup/both/modules/Contract';
-import { getProfileName, stripHTMLfromText } from '/imports/ui/modules/utils';
+import { stripHTMLfromText } from '/imports/ui/modules/utils';
import { timeComplete } from '/imports/ui/modules/chronos';
import { displayModal } from '/imports/ui/modules/modal';
import { animationSettings } from '/imports/ui/modules/animation';
@@ -19,9 +19,9 @@ import { templetize, getImage } from '/imports/ui/templates/layout/templater';
import { tokenWeb } from '/lib/token';
import { wrapURLs } from '/lib/utils';
import { Collectives } from '/imports/api/collectives/Collectives';
+import { createDateQuery } from '/imports/ui/templates/widgets/transaction/transaction';
import '/imports/ui/templates/widgets/feed/feedItem.html';
-import '/imports/ui/templates/widgets/transaction/transaction.js';
import '/imports/ui/templates/widgets/spinner/spinner.js';
import '/imports/ui/templates/components/identity/avatar/avatar.js';
import '/imports/ui/templates/components/decision/countdown/countdown.js';
@@ -695,6 +695,13 @@ Template.feedItem.helpers({
style: this.period ? `period period-${this.period.toLowerCase()}` : '',
};
},
+ dateURL() {
+ const from = this.timestamp;
+ const fromQuery = createDateQuery(from);
+ const until = new Date(this.timestamp.getTime() + (60 * 60 * 24 * 1000));
+ const untilQuery = createDateQuery(until);
+ return `/date?from=${fromQuery}&until=${untilQuery}`;
+ },
});
Template.feedItem.events({
diff --git a/imports/ui/templates/widgets/preview/preview.js b/imports/ui/templates/widgets/preview/preview.js
index 413359473..38ebfd91b 100644
--- a/imports/ui/templates/widgets/preview/preview.js
+++ b/imports/ui/templates/widgets/preview/preview.js
@@ -6,6 +6,7 @@ import { $ } from 'meteor/jquery';
import { stripHTMLfromText } from '/imports/ui/modules/utils';
import { Contracts } from '/imports/api/contracts/Contracts';
import { templetize, getImage } from '/imports/ui/templates/layout/templater';
+import { getProposalDescription } from '/imports/ui/templates/widgets/feed/feedItem';
import '/imports/ui/templates/widgets/preview/preview.html';
@@ -23,20 +24,40 @@ const _getCharLength = (id) => {
Template.preview.onCreated(function () {
Template.instance().feed = new ReactiveVar();
Template.instance().contract = new ReactiveVar();
+ Template.instance().proposal = new ReactiveVar();
const instance = this;
const _id = Template.currentData().contractId;
if (_id) {
const contract = Contracts.findOne({ _id });
- if (!contract) {
+ if (contract && contract.pollId) {
+ instance.contract.set(contract);
+ Meteor.call('getProposalContract', Template.currentData().contractId, function (error, result) {
+ if (result) {
+ instance.proposal.set(result);
+ } else if (error) {
+ console.log(error);
+ }
+ });
+ } else if (!contract) {
Meteor.call('getContractById', Template.currentData().contractId, function (error, result) {
if (result) {
instance.contract.set(result);
+
+ if (result.pollId) {
+ Meteor.call('getProposalContract', result._id, function (err, res) {
+ if (res) {
+ instance.proposal.set(res);
+ } else if (err) {
+ console.log(err);
+ }
+ });
+ }
} else if (error) {
console.log(error);
}
});
- } else {
+ } else if (contract) {
instance.contract.set(contract);
}
}
@@ -69,6 +90,10 @@ Template.preview.helpers({
return title;
},
fullTitle() {
+ const proposal = Template.instance().proposal.get();
+ if (proposal && proposal.title) {
+ return `${getProposalDescription(proposal.title, true).substring(0, 45)}...`;
+ }
return stripHTMLfromText(Template.instance().contract.get().title);
},
url() {
diff --git a/imports/ui/templates/widgets/search/search.jsx b/imports/ui/templates/widgets/search/search.jsx
index 763ab5abd..5b6d9262b 100644
--- a/imports/ui/templates/widgets/search/search.jsx
+++ b/imports/ui/templates/widgets/search/search.jsx
@@ -6,6 +6,7 @@ import { TAPi18n } from 'meteor/tap:i18n';
import { Contracts } from '/imports/api/contracts/Contracts';
import { Collectives } from '/imports/api/collectives/Collectives';
import { getProposalDescription } from '/imports/ui/templates/widgets/feed/feedItem';
+import { timeDateOnly } from '/imports/ui/modules/chronos';
import web3 from 'web3';
@@ -64,6 +65,13 @@ const _setTags = () => {
text: TAPi18n.__('search-contract').replace('{{searchTerm}}', _dynamicTitle(getProposalDescription(contract.title, true))),
},
];
+ } else if (params.query.from) {
+ query = [
+ {
+ id: Router.current().params.query.from,
+ text: TAPi18n.__('search-dates').replace('{{searchTerm}}', timeDateOnly(new Date(Router.current().params.query.from))),
+ },
+ ];
}
}
diff --git a/imports/ui/templates/widgets/tally/tally.js b/imports/ui/templates/widgets/tally/tally.js
index 8768e208b..ed355fc05 100644
--- a/imports/ui/templates/widgets/tally/tally.js
+++ b/imports/ui/templates/widgets/tally/tally.js
@@ -122,6 +122,7 @@ const _defaultTally = (view) => {
view === 'periodVotes' ||
view === 'transactionsToken' ||
view === 'transactionsPeer' ||
+ view === 'transactionsDate' ||
view === 'transactionsGeo' ||
view === 'transactionsDao' ||
view === 'transactionsSearch'
diff --git a/imports/ui/templates/widgets/transaction/transaction.html b/imports/ui/templates/widgets/transaction/transaction.html
index 338a11add..35e0b1774 100644
--- a/imports/ui/templates/widgets/transaction/transaction.html
+++ b/imports/ui/templates/widgets/transaction/transaction.html
@@ -160,7 +160,7 @@
{{_ 'pending'}}
{{else}}
-
{{sinceDate}}
+
{{sinceDate}}
{{/if}}
diff --git a/imports/ui/templates/widgets/transaction/transaction.js b/imports/ui/templates/widgets/transaction/transaction.js
index 796e93b43..8cf522daa 100644
--- a/imports/ui/templates/widgets/transaction/transaction.js
+++ b/imports/ui/templates/widgets/transaction/transaction.js
@@ -5,7 +5,7 @@ import { Session } from 'meteor/session';
import { Meteor } from 'meteor/meteor';
import { getVotes } from '/imports/api/transactions/transaction';
-import { timeCompressed } from '/imports/ui/modules/chronos';
+import { timeCompressed, timeDateOnly, hourOnly } from '/imports/ui/modules/chronos';
import { token } from '/lib/token';
import { Transactions } from '/imports/api/transactions/Transactions';
import { syncBlockchain } from '/imports/startup/both/modules/metamask';
@@ -91,6 +91,15 @@ const _getContractToken = (transaction) => {
return coin;
};
+
+/**
+* @summary creates string of date for URL
+* @return {string} uri
+*/
+const _createDateQuery = (date) => {
+ return `${date.getFullYear()}-${(date.getMonth() < 9) ? `0${parseInt(date.getMonth() + 1, 10)}` : parseInt(date.getMonth() + 1, 10)}-${(date.getDate() < 10) ? `0${date.getDate()}` : date.getDate()}`;
+};
+
Template.transaction.onCreated(function () {
Template.instance().totalVotes = new ReactiveVar(0);
Template.instance().loading = new ReactiveVar(false);
@@ -216,6 +225,16 @@ Template.transaction.helpers({
sinceDate() {
return `${timeCompressed(this.contract.timestamp, true)}`;
},
+ dateLink() {
+ const from = this.contract.timestamp;
+ const fromQuery = _createDateQuery(from);
+ const until = new Date(this.contract.timestamp.getTime() + (60 * 60 * 24 * 1000));
+ const untilQuery = _createDateQuery(until);
+ return `/date?from=${fromQuery}&until=${untilQuery}`;
+ },
+ dateDescription() {
+ return `${timeDateOnly(this.contract.timestamp)} ยท ${hourOnly(this.contract.timestamp)}`;
+ },
ragequit() {
return this.isRagequit;
},
@@ -321,9 +340,9 @@ Template.collectivePreview.helpers({
return Template.instance().collective.name;
},
url() {
- // console.log(Template.instance().collective);
return `/dao/${Template.instance().collective.uri}`;
},
});
export const getContractToken = _getContractToken;
+export const createDateQuery = _createDateQuery;
diff --git a/lib/views.js b/lib/views.js
index d110e0004..7ee89d36d 100644
--- a/lib/views.js
+++ b/lib/views.js
@@ -834,6 +834,48 @@ _views.contractVotes = (terms) => {
};
};
+/**
+* @summary gets contracts that belong to a given range of dates
+* @param {object} terms filters and limits
+* @return {object} query to use on collection
+*/
+_views.dateRange = (terms) => {
+ log(`{ view: 'dateRange', user: ${logUser()}, from: '${terms.from}', until: '${terms.until}' }`);
+ const from = new Date(`${terms.from}T00:00:00`);
+ const until = new Date(`${terms.until}T00:00:00`);
+ return {
+ find: { timestamp: { $gte: from, $lt: until }, stage: { $ne: 'DRAFT' }, kind: { $ne: 'DELEGATION' }, pollId: { $exists: false }, replyId: { $exists: false } },
+ options: { sort: { timestamp: -1 }, limit: terms.limit, skip: terms.skip },
+ };
+};
+
+
+/**
+* @summary returns transactions based on a range of dates
+* @param {object} terms filters and limits
+* @return {object} query to use on collection
+*/
+_views.transactionsDate = (terms) => {
+ log(`{ view: 'transactionsDate', user: ${logUser()}, from: '${terms.from}', until: '${terms.until}' }`);
+ const from = new Date(`${terms.from}T00:00:00`);
+ const until = new Date(`${terms.until}T00:00:00`);
+ const contracts = Contracts.find({ timestamp: { $gte: from, $lt: until }, stage: { $ne: 'DRAFT' }, kind: { $ne: 'DELEGATION' }, pollId: { $exists: false }, replyId: { $exists: false }, period: { $ne: 'RAGEQUIT' } }).fetch();
+ if (contracts.length > 0) {
+ const contractIds = _.uniq(_.pluck(contracts, '_id'));
+ const query = [];
+ for (const id of contractIds) {
+ query.push({
+ contractId: id,
+ });
+ }
+ return {
+ find: { $or: query },
+ };
+ }
+ return {};
+};
+
+
/**
* @summary all the daos listed in this server
* @param {object} terms filters and limits