Skip to content

Commit

Permalink
Debug
Browse files Browse the repository at this point in the history
  • Loading branch information
corrideat committed Sep 22, 2024
2 parents 9b89d48 + 9fd5e16 commit bce3c5c
Show file tree
Hide file tree
Showing 31 changed files with 233 additions and 141 deletions.
2 changes: 0 additions & 2 deletions frontend/common/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
// Doing otherwise defeats the purpose of this file and could lead to bugs and conflicts!
// You may *add* behavior, but never modify or remove it.

export { default as Vue } from 'vue'
export { default as L } from './translations.js'
export * from './translations.js'
export * from './errors.js'
export * as Errors from './errors.js'
77 changes: 8 additions & 69 deletions frontend/common/translations.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,12 @@

// since this file is loaded by common.js, we avoid circular imports and directly import
import sbp from '@sbp/sbp'
import Vue from 'vue'
import dompurify from 'dompurify'
import { defaultConfig as defaultDompurifyConfig } from './vSafeHtml.js'
import template from './stringTemplate.js'

Vue.prototype.L = L
Vue.prototype.LTags = LTags

const defaultLanguage = 'en-US'
const defaultLanguageCode = 'en'
const defaultTranslationTable: { [string]: string } = {}

/**
* Allow 'href' and 'target' attributes to avoid breaking our hyperlinks,
* but keep sanitizing their values.
* See https://github.com/cure53/DOMPurify#can-i-configure-dompurify
*/
const dompurifyConfig = {
...defaultDompurifyConfig,
ALLOWED_ATTR: ['class', 'href', 'rel', 'target'],
ALLOWED_TAGS: ['a', 'b', 'br', 'button', 'em', 'i', 'p', 'small', 'span', 'strong', 'sub', 'sup', 'u'],
RETURN_DOM_FRAGMENT: false
}

let currentLanguage = defaultLanguage
let currentLanguageCode = defaultLanguage.split('-')[0]
let currentTranslationTable = defaultTranslationTable
Expand All @@ -40,7 +22,7 @@ let currentTranslationTable = defaultTranslationTable
*
* @see https://tools.ietf.org/rfc/bcp/bcp47.txt
*/
sbp('sbp/selectors/register', {
export default (sbp('sbp/selectors/register', {
'translations/init': async function init (language: string): Promise<void> {
// A language code is usually the first part of a language tag.
const [languageCode] = language.toLowerCase().split('-')
Expand Down Expand Up @@ -69,7 +51,7 @@ sbp('sbp/selectors/register', {
console.error(error)
}
}
})
}): string[])

/*
Examples:
Expand Down Expand Up @@ -122,19 +104,22 @@ export function LTags (...tags: string[]): {|br_: string|} {
return o
}

export default function L (
export function L (
key: string,
args: Array<*> | Object | void
): string {
return template(currentTranslationTable[key] || key, args)
// Avoid inopportune linebreaks before certain punctuations.
.replace(/\s(?=[;:?!])/g, '&nbsp;')
// '\u00a0' is a non-breaking space
// The character is used instead of `&nbsp;` or `&#160;` for conciseness
// and compatibility.
.replace(/\s(?=[;:?!])/g, '\u00a0')
}

export function LError (error: Error, toGithub?: boolean): {|reportError: any|} {
let url = 'https://github.com/okTurtles/group-income/issues'
if (!toGithub && sbp('state/vuex/state').loggedIn) {
const baseRoute = document.location.origin + sbp('controller/router').options.base
const baseRoute = sbp('controller/router').options.base
url = `${baseRoute}?modal=UserSettingsModal&tab=application-logs&errorMsg=${encodeURIComponent(error.message)}`
}
return {
Expand All @@ -145,49 +130,3 @@ export function LError (error: Error, toGithub?: boolean): {|reportError: any|}
})
}
}

function sanitize (inputString) {
return dompurify.sanitize(inputString, dompurifyConfig)
}

Vue.component('i18n', {
functional: true,
props: {
args: [Object, Array],
tag: {
type: String,
default: 'span'
},
compile: Boolean
},
render: function (h, context) {
const text = context.children[0].text
const translation = L(text, context.props.args || {})
if (!translation) {
console.warn('The following i18n text was not translated correctly:', text)
return h(context.props.tag, context.data, text)
}
// Prevent reverse tabnabbing by including `rel="noopener noreferrer"` when rendering as an outbound hyperlink.
if (context.props.tag === 'a' && context.data.attrs.target === '_blank') {
context.data.attrs.rel = 'noopener noreferrer'
}
if (context.props.compile) {
const result = Vue.compile('<wrap>' + sanitize(translation) + '</wrap>')
// console.log('TRANSLATED RENDERED TEXT:', context, result.render.toString())
return result.render.call({
_c: (tag, ...args) => {
if (tag === 'wrap') {
return h(context.props.tag, context.data, ...args)
} else {
return h(tag, ...args)
}
},
_v: x => x
})
} else {
if (!context.data.domProps) context.data.domProps = {}
context.data.domProps.innerHTML = sanitize(translation)
return h(context.props.tag, context.data)
}
}
})
17 changes: 16 additions & 1 deletion frontend/controller/app/chatroom.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,22 @@ sbp('okTurtles.events/on', JOINED_CHATROOM, ({ identityContractID, groupContract
const rootState = sbp('state/vuex/state')
if (rootState.loggedIn?.identityContractID !== identityContractID) return
if (!rootState.chatroom.currentChatRoomIDs[groupContractID] || rootState.chatroom.pendingChatRoomIDs[groupContractID] === chatRoomID) {
sbp('state/vuex/commit', 'setCurrentChatRoomId', { groupID: groupContractID, chatRoomID })
let attemptCount = 0
// Sometimes, the state may not be ready (it needs to be copied from the SW
// to Vuex). In this case, we try again after a short delay.
// TODO: Figure out a better way of doing this that doesn't require a timeout
const setCurrentChatRoomId = () => {
if (!rootState[chatRoomID]?.members?.[identityContractID]) {
if (++attemptCount > 5) {
console.warn('[JOINED_CHATROOM] Given up on setCurrentChatRoomId after 5 attempts', { identityContractID, groupContractID, chatRoomID })
return
}
setTimeout(setCurrentChatRoomId, 5 + 5 * attemptCount)
} else {
sbp('state/vuex/commit', 'setCurrentChatRoomId', { groupID: groupContractID, chatRoomID })
}
}
setCurrentChatRoomId()
}
})
sbp('okTurtles.events/on', LEFT_CHATROOM, switchCurrentChatRoomHandler)
Expand Down
4 changes: 1 addition & 3 deletions frontend/controller/app/identity.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
'use strict'

import * as Common from '@common/common.js'
import { GIErrorUIRuntimeError, L, LError, LTags } from '@common/common.js'
import { cloneDeep } from '@model/contracts/shared/giLodash.js'
import sbp from '@sbp/sbp'
import Vue from 'vue'
import { LOGIN, LOGIN_COMPLETE, LOGIN_ERROR } from '~/frontend/utils/events.js'
import { Secret } from '~/shared/domains/chelonia/Secret.js'
import { boxKeyPair, buildRegisterSaltRequest, computeCAndHc, decryptContractSalt, hash, hashPassword, randomNonce } from '~/shared/zkpp.js'
// Using relative path to crypto.js instead of ~-path to workaround some esbuild bug
import { CURVE25519XSALSA20POLY1305, EDWARDS25519SHA512BATCH, deriveKeyFromPassword, serializeKey } from '../../../shared/domains/chelonia/crypto.js'
import { handleFetchResult } from '../utils/misc.js'

const { Vue } = Common

const loadState = async (identityContractID: string, password: ?string) => {
if (password) {
const stateKeyEncryptionKeyFn = (stateEncryptionKeyId, salt) => {
Expand Down
3 changes: 2 additions & 1 deletion frontend/controller/router.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
'use strict'

import sbp from '@sbp/sbp'
import { Vue, L } from '@common/common.js'
import { L } from '@common/common.js'
import Router from 'vue-router'
import store from '~/frontend/model/state.js'
import Home from '@pages/Home.vue'
import Join from '@pages/Join.vue'
import { lazyPage } from '@utils/lazyLoadedView.js'
import Vue from 'vue'

/*
* Lazy load all the pages that are not necessary at initial loading of the app.
Expand Down
55 changes: 37 additions & 18 deletions frontend/controller/service-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,35 +26,50 @@ window.addEventListener('beforeinstallprompt', e => {

sbp('sbp/selectors/register', {
'service-workers/setup': async function () {
console.error('@@@SW SETUP')
// setup service worker
// TODO: move ahead with encryption stuff ignoring this service worker stuff for now
// TODO: improve updating the sw: https://stackoverflow.com/a/49748437
// NOTE: user should still be able to use app even if all the SW stuff fails
if (!('serviceWorker' in navigator)) { return }
if (!('serviceWorker' in navigator)) {
throw new Error('Service worker APIs missing')
}

try {
const swRegistration = await navigator.serviceWorker.register('/assets/js/sw-primary.js', { scope: '/' })

if (swRegistration) {
swRegistration.active?.postMessage({ type: 'store-client-id' })
console.error('@@@SW swRegistration')

// if an active service-worker exists, checks for the updates immediately first and then repeats it every 1hr
await swRegistration.update()
setInterval(() => sbp('service-worker/update'), HOURS_MILLIS)

// Keep the service worker alive while the window is open
// The default idle timeout on Chrome and Firefox is 30 seconds. We send
// a ping message every 5 seconds to ensure that the worker remains
// active.
// The downside of this is that there are messges going back and forth
// between the service worker and each tab, the number of which is
// proportional to the number of tabs open.
// The upside of this is that the service worker remains active while
// there are open tabs, which makes it faster and smoother to interact
// with contracts than if the service worker had to be restarted.
setInterval(() => navigator.serviceWorker.controller?.postMessage({ type: 'ping' }), 5000)
if (swRegistration.active) {
console.error('@@@SW swRegistration active')
swRegistration.active?.postMessage({ type: 'store-client-id' })
} else {
console.error('@@@SW swRegistration handling')
const handler = () => {
navigator.serviceWorker.removeEventListener('controllerchange', handler, false)
navigator.serviceWorker.controller.postMessage({ type: 'store-client-id' })
}
navigator.serviceWorker.addEventListener('controllerchange', handler, false)
}

// if an active service-worker exists, checks for the updates immediately first and then repeats it every 1hr
await swRegistration.update()
setInterval(() => sbp('service-worker/update'), HOURS_MILLIS)

// Keep the service worker alive while the window is open
// The default idle timeout on Chrome and Firefox is 30 seconds. We send
// a ping message every 5 seconds to ensure that the worker remains
// active.
// The downside of this is that there are messges going back and forth
// between the service worker and each tab, the number of which is
// proportional to the number of tabs open.
// The upside of this is that the service worker remains active while
// there are open tabs, which makes it faster and smoother to interact
// with contracts than if the service worker had to be restarted.
setInterval(() => navigator.serviceWorker.controller?.postMessage({ type: 'ping' }), 5000)

console.error('@@@SW swRegistration 70')

navigator.serviceWorker.addEventListener('message', event => {
const data = event.data

Expand All @@ -78,6 +93,8 @@ sbp('sbp/selectors/register', {
}
}
})

console.error('@@@SW DONE, returning')
} catch (e) {
console.error('error setting up service worker:', e)
}
Expand All @@ -94,6 +111,8 @@ sbp('sbp/selectors/register', {
}

const pubsub = sbp('okTurtles.data/get', PUBSUB_INSTANCE)
if (!pubsub) return // TODO: This needs to be moved into the service worker
// proper. pubsub will be undefined in this context.
const existingSubscription = await registration.pushManager.getSubscription()
const messageToPushServerIfSocketConnected = (msgPayload) => {
// make sure the websocket client is not in the state of CLOSING, CLOSED before sending a message.
Expand Down
45 changes: 45 additions & 0 deletions frontend/controller/serviceworkers/sw-primary.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,51 @@ sbp('sbp/selectors/register', {
},
'state/vuex/commit': () => {
console.error('[sw] CALLED state/vuex/commit WHICH IS UNDEFINED')
},
'state/vuex/getters': () => {
return {
chatRoomUnreadMessages () {
return []
},
getChatroomNameById () {
return ''
},
groupIdFromChatRoomId () {
return ''
},
isGroupDirectMessage () {
return false
},
notificationsByGroup () {
return []
},
ourContactProfilesByUsername: {},
ourIdentityContractId: '',
userDisplayNameFromID () {
return ''
},
usernameFromID () {
return ''
}
}
}
})

sbp('sbp/selectors/register', {
'controller/router': () => {
return { options: { base: '/app/' } }
}
})

sbp('sbp/selectors/register', {
'gi.ui/seriousErrorBanner': (...args) => {
console.error('### SERIOUS ERROR ###', ...args)
}
})

sbp('sbp/selectors/register', {
'gi.notifications/emit': (...args) => {
console.error('### notification ###', ...args)
}
})

Expand Down
9 changes: 5 additions & 4 deletions frontend/main.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict'

// import SBP stuff before anything else so that domains register themselves before called
import * as Common from '@common/common.js'
import { L, LError } from '@common/common.js'
import '@model/captureLogs.js'
import '@sbp/okturtles.data'
import '@sbp/okturtles.eventqueue'
Expand Down Expand Up @@ -29,13 +29,15 @@ import Modal from './views/components/modal/Modal.vue'
import BackgroundSounds from './views/components/sounds/Background.vue'
import Navigation from './views/containers/navigation/Navigation.vue'
import './views/utils/avatar.js'
import './views/utils/i18n.js'
import './views/utils/ui.js'
import './views/utils/vError.js'
import './views/utils/vFocus.js'
// import './views/utils/vSafeHtml.js' // this gets imported by translations, which is part of common.js
import { GIMessage } from '~/shared/domains/chelonia/GIMessage.js'
import { Secret } from '~/shared/domains/chelonia/Secret.js'
import { deserializer, serializer } from '~/shared/serdes/index.js'
import Vue from 'vue'
import notificationsMixin from './model/notifications/mainNotificationsMixin.js'
import './model/notifications/periodicNotifications.js'
import FaviconBadge from './utils/faviconBadge.js'
Expand All @@ -46,8 +48,6 @@ import './views/utils/vStyle.js'
deserializer.register(GIMessage)
deserializer.register(Secret)

const { Vue, L, LError } = Common

console.info('GI_VERSION:', process.env.GI_VERSION)
console.info('CONTRACTS_VERSION:', process.env.CONTRACTS_VERSION)
console.info('LIGHTWEIGHT_CLIENT:', process.env.LIGHTWEIGHT_CLIENT)
Expand Down Expand Up @@ -150,7 +150,7 @@ async function startApp () {
new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('Timed out setting up service worker'))
}, 8e3)
}, 16e3)
})]
).catch(e => {
console.error('[main] Error setting up service worker', e)
Expand Down Expand Up @@ -369,6 +369,7 @@ async function startApp () {
})
}).finally(() => {
// Wait for SW to be ready
console.debug('[app] Waiting for SW to be ready')
navigator.serviceWorker.ready.then(() => {
const onready = () => {
this.ephemeral.ready = true
Expand Down
3 changes: 2 additions & 1 deletion frontend/model/chatroom/vuexModule.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
'use strict'

import sbp from '@sbp/sbp'
import { Vue } from '@common/common.js'
import { merge, cloneDeep, union } from '@model/contracts/shared/giLodash.js'
import { MESSAGE_NOTIFY_SETTINGS, CHATROOM_PRIVACY_LEVEL } from '@model/contracts/shared/constants.js'
import Vue from 'vue'

const defaultState = {
currentChatRoomIDs: {}, // { [groupId]: currentChatRoomId }
pendingChatRoomIDs: {}, // { [groupId]: currentChatRoomId }
Expand Down
Loading

0 comments on commit bce3c5c

Please sign in to comment.