Skip to content

Commit

Permalink
Invite expires. TODO: Update exisiting invites
Browse files Browse the repository at this point in the history
  • Loading branch information
corrideat committed Sep 30, 2024
1 parent b31dd3e commit eed98aa
Show file tree
Hide file tree
Showing 10 changed files with 54 additions and 61 deletions.
8 changes: 4 additions & 4 deletions frontend/controller/actions/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,10 @@ export default (sbp('sbp/selectors/register', {
permissions: [GIMessage.OP_KEY_REQUEST],
meta: {
quantity: 60,
expires: Math.min(
Date.now() + 1 * 60 * 1000,
Date.now() + DAYS_MILLIS * INVITE_EXPIRES_IN_DAYS.ON_BOARDING
), // TODO: revert this after development
...(INVITE_EXPIRES_IN_DAYS.ON_BOARDING && {
expires:
await sbp('chelonia/time') + DAYS_MILLIS * INVITE_EXPIRES_IN_DAYS.ON_BOARDING
}),
private: {
content: inviteKeyS
}
Expand Down
13 changes: 1 addition & 12 deletions frontend/controller/app/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
import { L } from '@common/common.js'
import {
INVITE_INITIAL_CREATOR,
MAX_GROUP_MEMBER_COUNT,
INVITE_EXPIRES_IN_DAYS
MAX_GROUP_MEMBER_COUNT
} from '@model/contracts/shared/constants.js'
import { DAYS_MILLIS } from '@model/contracts/shared/time.js'
import sbp from '@sbp/sbp'
import { JOINED_GROUP, LEFT_GROUP, OPEN_MODAL, REPLACE_MODAL, SWITCH_GROUP } from '@utils/events.js'
import ALLOWED_URLS from '@view-utils/allowedUrls.js'
Expand Down Expand Up @@ -107,14 +105,5 @@ export default (sbp('sbp/selectors/register', {
} else {
sbp('okTurtles.events/emit', OPEN_MODAL, 'AddMembers')
}
},
'gi.app/group/fixAnyoneCanJoinLink': async function () {
await sbp('gi.actions/group/updateGroupInviteExpiry', {
contractID: sbp('state/vuex/state').currentGroupId,
data: {
inviteKeyId: sbp('state/vuex/getters').currentWelcomeInvite.inviteId,
expires: Date.now() + DAYS_MILLIS * INVITE_EXPIRES_IN_DAYS.ON_BOARDING
}
})
}
}): string[])
12 changes: 0 additions & 12 deletions frontend/model/contracts/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -1379,18 +1379,6 @@ sbp('chelonia/defineContract', {
}
}
},
'gi.contracts/group/updateGroupInviteExpiry': {
validate: actionRequireActiveMember(objectOf({
inviteKeyId: stringMax(MAX_HASH_LEN),
expires: numberRange(0, Number.MAX_SAFE_INTEGER)
})),
process ({ data }, { state }) {
const invite = state._vm.invites[data.inviteKeyId]
if (invite) {
invite.expires = data.expires
}
}
},
'gi.contracts/group/upgradeFrom1.0.7': {
validate: actionRequireActiveMember(optional),
process ({ height }, { state }) {
Expand Down
2 changes: 1 addition & 1 deletion frontend/model/contracts/shared/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export const MESSAGE_TYPES = {
}

export const INVITE_EXPIRES_IN_DAYS = {
ON_BOARDING: 365 * 20, // 20years - we shouldn't let the anyone-can-join invite expire. (reference: https://github.com/okTurtles/group-income/issues/2341)
ON_BOARDING: null, // No expiration
PROPOSAL: 7
}

Expand Down
39 changes: 38 additions & 1 deletion frontend/model/contracts/shared/time.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ export function humanDate (
: ((navigator.languages: any): string[]) ?? navigator.language ?? fallback
// NOTE: `.toLocaleDateString()` automatically takes local timezone differences into account.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString
return new Date(date).toLocaleDateString(locale, options)
const dateObj = new Date(date)
if (!isNaN(dateObj.valueOf())) return dateObj.toLocaleDateString(locale, options)
}

export function isPeriodStamp (arg: string): boolean {
Expand Down Expand Up @@ -258,3 +259,39 @@ export function cycleAtDate (atDate: string | Date): number {
const partialCycles = now.getDate() / lastDayOfMonth(now).getDate()
return partialCycles
}

export function timeLeft (expiryTime: number) {
const now = new Date()
const expiry = new Date(expiryTime)

if (expiry < now) {
return {}
}

let years = expiry.getFullYear() - now.getFullYear()
let months = expiry.getMonth() - now.getMonth()
let days = expiry.getDate() - now.getDate()
let hours = expiry.getHours() - now.getHours()
let minutes = expiry.getMinutes() - now.getMinutes()

// Adjust for negative values
if (minutes < 0) {
minutes += 60
hours--
}
if (hours < 0) {
hours += 24
days--
}
if (days < 0) {
const lastMonth = new Date(expiry.getFullYear(), expiry.getMonth(), 0)
days += lastMonth.getDate()
months--
}
if (months < 0) {
months += 12
years--
}

return { years, months, days, hours, minutes }
}
2 changes: 1 addition & 1 deletion frontend/model/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ const getters = {
// $FlowFixMe[method-unbinding]
return [groupMembersPending, getters.groupProfiles].flatMap(Object.keys)
.filter(memberID => getters.groupProfiles[memberID] ||
getters.groupMembersPending[memberID].expires >= Date.now())
!(getters.groupMembersPending[memberID].expires < Date.now()))
.map(memberID => {
const { contractID, displayName, username } = getters.globalProfile(memberID) || groupMembersPending[memberID] || (getters.groupProfiles[memberID] ? { contractID: memberID } : {})
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ modal-template(ref='modal' :a11yTitle='L("Add new members")')
i18n.is-title-4(tag='h3') Share this link to grant access to your group.
i18n.has-text-1(tag='p') After the onboarding period has ended, everyone will be asked to vote on whether or not a new member should be added. But for now, enjoy 60 free passes!
link-to-copy.c-link(:link='link')
i18n.has-text-1(tag='p' :args='{ expireDate }') This invite link expires on {expireDate}.
i18n.has-text-1(v-if='expireDate' tag='p' :args='{ expireDate }') This invite link expires on {expireDate}.
i18n.has-text-1(v-else tag='p') This invite link doesn't expire
i18n.is-outlined.c-cta(tag='button' @click.prevent='close') Awesome
.c-broken(v-else)
svg-broken-link.c-svg
Expand Down
32 changes: 5 additions & 27 deletions frontend/views/containers/group-settings/InvitationsTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,7 @@ import { INVITE_INITIAL_CREATOR } from '@model/contracts/shared/constants.js'
import { OPEN_MODAL } from '@utils/events.js'
import { mapGetters, mapState } from 'vuex'
import { L } from '@common/common.js'
import {
MINS_MILLIS,
HOURS_MILLIS,
DAYS_MILLIS,
MONTHS_MILLIS,
YEARS_MILLIS
} from '@model/contracts/shared/time.js'
import { timeLeft } from '@model/contracts/shared/time.js'
export default ({
name: 'InvitationsTable',
Expand Down Expand Up @@ -244,27 +238,11 @@ export default ({
else return isInviteExpired || isInviteRevoked ? L('Not used') : L('Not used yet')
},
readableExpiryInfo (expiryTime) {
const timeLeft = expiryTime - Date.now()
let remainder
const years = Math.floor(timeLeft / YEARS_MILLIS)
remainder = years // ratio of months left where 12 as the 100% eg) 10 months and half => 10.5
? (timeLeft % YEARS_MILLIS) / YEARS_MILLIS
: timeLeft / YEARS_MILLIS
const months = Math.floor(remainder * 12)
remainder = months
? (remainder % 1) * MONTHS_MILLIS // eg) when given 10.5 months, take 0.5 from it and turn it into 15days.
: timeLeft
const days = Math.floor(remainder / DAYS_MILLIS)
const daysCeil = Math.ceil(remainder / DAYS_MILLIS) // to be used for when expiryTime > '1 month'
remainder = remainder % DAYS_MILLIS
const hours = Math.floor(remainder / HOURS_MILLIS)
remainder = remainder % HOURS_MILLIS
const minutes = Math.ceil(remainder / MINS_MILLIS)
if (expiryTime == null) return
const { years, months, days, hours, minutes } = timeLeft(expiryTime)
// In the cases when displaying years/months, count the remainer hours/mins as +1 day eg) 3days 15hrs 25mins -> 4days.
if (years) return L('{years}yr {months}mth {days}d left', { years, months, days: daysCeil })
if (months) return L('{months}mth {days}d left', { months, days: daysCeil })
if (years) return L('{years}yr {months}mth {days}d left', { years, months, days })
if (months) return L('{months}mth {days}d left', { months, days })
if (days) return L('{days}d {hours}h {minutes}m left', { days, hours, minutes })
if (hours) return L('{hours}h {minutes}m left', { hours, minutes })
Expand Down
2 changes: 2 additions & 0 deletions frontend/views/containers/proposals/ProposalItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ export default ({
this.currentGroupState._vm.authorizedKeys[inviteKeyId]._notAfterHeight !== undefined ||
// If the expiration date is less than the current date, it means that
// the invite can no longer be used
// Note: Using negative logic to allow for undefined expiry, which means
// it never expires
this.currentGroupState._vm.invites[inviteKeyId].expires < Date.now()
) {
return true
Expand Down
2 changes: 0 additions & 2 deletions frontend/views/pages/Join.vue
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,6 @@ export default ({
const state = await sbp('chelonia/latestContractState', groupId)
const publicKeyId = keyId(secret)
const invite = state._vm.invites[publicKeyId]
console.log('!@# invites: ', state._vm.invites)
console.log('!@# found invite: ', invite)
if (invite?.expires < Date.now()) {
console.log('Join.vue error: Link is already expired.')
this.ephemeral.errorMsg = messageToAskAnother
Expand Down

0 comments on commit eed98aa

Please sign in to comment.