UBER-325/349/352/342 (#3320)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2023-06-01 17:17:54 +07:00 committed by GitHub
parent 2e9a0f44b4
commit f40750ec95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 118 additions and 85 deletions

View File

@ -98,6 +98,7 @@ services:
- MINIO_ENDPOINT=minio - MINIO_ENDPOINT=minio
- MINIO_ACCESS_KEY=minioadmin - MINIO_ACCESS_KEY=minioadmin
- MINIO_SECRET_KEY=minioadmin - MINIO_SECRET_KEY=minioadmin
- TITLE=DevPlatform
restart: unless-stopped restart: unless-stopped
# tracker-front: # tracker-front:
# image: hardcoreeng/tracker-front # image: hardcoreeng/tracker-front

View File

@ -87,6 +87,7 @@ interface Config {
REKONI_URL: string REKONI_URL: string
TELEGRAM_URL: string TELEGRAM_URL: string
GMAIL_URL: string GMAIL_URL: string
TITLE?: string
} }
const devConfig = process.env.CLIENT_TYPE === 'dev-production' const devConfig = process.env.CLIENT_TYPE === 'dev-production'
@ -161,5 +162,5 @@ export async function configurePlatform() {
// Disable for now, since it causes performance issues on linux/docker/kubernetes boxes for now. // Disable for now, since it causes performance issues on linux/docker/kubernetes boxes for now.
setMetadata(client.metadata.UseProtocolCompression, true) setMetadata(client.metadata.UseProtocolCompression, true)
setMetadata(workbench.metadata.PlatformTitle, 'Platform') setMetadata(workbench.metadata.PlatformTitle, config.TITLE ?? 'Platform')
} }

View File

@ -268,34 +268,34 @@ export function createModel (builder: Builder): void {
// }, // },
// setting.ids.Support // setting.ids.Support
// ) // )
builder.createDoc( // builder.createDoc(
setting.class.SettingsCategory, // setting.class.SettingsCategory,
core.space.Model, // core.space.Model,
{ // {
name: 'privacy', // name: 'privacy',
label: setting.string.Privacy, // label: setting.string.Privacy,
icon: setting.icon.Privacy, // icon: setting.icon.Privacy,
component: setting.component.Privacy, // component: setting.component.Privacy,
group: 'main', // group: 'main',
secured: false, // secured: false,
order: 6000 // order: 6000
}, // },
setting.ids.Privacy // setting.ids.Privacy
) // )
builder.createDoc( // builder.createDoc(
setting.class.SettingsCategory, // setting.class.SettingsCategory,
core.space.Model, // core.space.Model,
{ // {
name: 'terms', // name: 'terms',
label: setting.string.Terms, // label: setting.string.Terms,
icon: setting.icon.Terms, // icon: setting.icon.Terms,
component: setting.component.Terms, // component: setting.component.Terms,
group: 'main', // group: 'main',
secured: false, // secured: false,
order: 10000 // order: 10000
}, // },
setting.ids.Terms // setting.ids.Terms
) // )
builder.createDoc( builder.createDoc(
workbench.class.Application, workbench.class.Application,

View File

@ -236,52 +236,52 @@ export const CadetGreyColor = '#95A2B3'
/** /**
* @public * @public
*/ */
export function getPlatformColor (hash: number, blackTheme: boolean): string { export function getPlatformColor (hash: number, darkTheme: boolean): string {
const palette = blackTheme ? darkPalette : whitePalette const palette = darkTheme ? darkPalette : whitePalette
return (palette[Math.abs(hash) % palette.length] ?? palette[0]).color return (palette[Math.abs(hash) % palette.length] ?? palette[0]).color
} }
/** /**
* @public * @public
*/ */
export function getPlatformColorDef (hash: number, blackTheme: boolean): ColorDefinition { export function getPlatformColorDef (hash: number, darkTheme: boolean): ColorDefinition {
const palette = blackTheme ? darkPalette : whitePalette const palette = darkTheme ? darkPalette : whitePalette
return palette[Math.abs(hash) % palette.length] ?? palette[0] return palette[Math.abs(hash) % palette.length] ?? palette[0]
} }
/** /**
* @public * @public
*/ */
export function getPlatformAvatarColorDef (hash: number, blackTheme: boolean): ColorDefinition { export function getPlatformAvatarColorDef (hash: number, darkTheme: boolean): ColorDefinition {
const palette = blackTheme ? avatarDarkColors : avatarWhiteColors const palette = darkTheme ? avatarDarkColors : avatarWhiteColors
return palette[Math.abs(hash) % palette.length] ?? palette[0] return palette[Math.abs(hash) % palette.length] ?? palette[0]
} }
/** /**
* @public * @public
*/ */
export function getPlatformAvatarColorForTextDef (text: string, blackTheme: boolean): ColorDefinition { export function getPlatformAvatarColorForTextDef (text: string, darkTheme: boolean): ColorDefinition {
return getPlatformAvatarColorDef(hashCode(text), blackTheme) return getPlatformAvatarColorDef(hashCode(text), darkTheme)
} }
/** /**
* @public * @public
*/ */
export function getPlatformColorForText (text: string, blackTheme: boolean): string { export function getPlatformColorForText (text: string, darkTheme: boolean): string {
return getPlatformColor(hashCode(text), blackTheme) return getPlatformColor(hashCode(text), darkTheme)
} }
/** /**
* @public * @public
*/ */
export function getPlatformColorForTextDef (text: string, blackTheme: boolean): ColorDefinition { export function getPlatformColorForTextDef (text: string, darkTheme: boolean): ColorDefinition {
return getPlatformColorDef(hashCode(text), blackTheme) return getPlatformColorDef(hashCode(text), darkTheme)
} }
/** /**
* @public * @public
*/ */
export function getPlatformColors (blackTheme: boolean): readonly ColorDefinition[] { export function getPlatformColors (darkTheme: boolean): readonly ColorDefinition[] {
return blackTheme ? darkPalette : whitePalette return darkTheme ? darkPalette : whitePalette
} }
function hashCode (str: string): number { function hashCode (str: string): number {

View File

@ -62,13 +62,15 @@
} }
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const sortedElemenets = sortFilterValues($channelProviders, (v) => isSelected(v, selected))
</script> </script>
<div class="selectPopup" use:resizeObserver={() => dispatch('changeContent')}> <div class="selectPopup" use:resizeObserver={() => dispatch('changeContent')}>
<div class="menu-space" /> <div class="menu-space" />
<div class="scroll"> <div class="scroll">
<div class="box"> <div class="box">
{#each sortFilterValues($channelProviders, (v) => isSelected(v, selected)) as element} {#each sortedElemenets as element}
<button <button
class="menu-item no-focus flex-row-center content-pointer-events-none" class="menu-item no-focus flex-row-center content-pointer-events-none"
on:click={() => { on:click={() => {

View File

@ -94,6 +94,7 @@
}) })
} }
} }
values = sortFilterValues(values, (v) => isSelected(v, filter.value))
objectsPromise = undefined objectsPromise = undefined
} }
@ -146,7 +147,7 @@
{#if objectsPromise} {#if objectsPromise}
<Loading /> <Loading />
{:else} {:else}
{#each sortFilterValues(values, (v) => isSelected(v, filter.value)) as value} {#each values as value}
<button <button
class="menu-item no-focus flex-row-center" class="menu-item no-focus flex-row-center"
on:click={() => { on:click={() => {

View File

@ -157,11 +157,7 @@
margin-right: 0.5rem; margin-right: 0.5rem;
display: flex; display: flex;
flex-grow: 1; flex-grow: 1;
padding: 0.5rem 0; padding: 0.5rem 0;
&.read {
opacity: 0.6;
}
} }
} }

View File

@ -55,28 +55,45 @@
let categories: TagCategory[] = [] let categories: TagCategory[] = []
let objects: TagElement[] = [] let objects: TagElement[] = []
let catsSorted = false
client.findAll(tags.class.TagCategory, { targetClass: _class }).then((res) => { client.findAll(tags.class.TagCategory, { targetClass: _class }).then((res) => {
categories = res categories = res
}) })
let objectsPromise: Promise<FindResult<TagElement>> | undefined let objectsPromise: Promise<FindResult<TagElement>> | undefined
let queryId = 0
async function getValues (search: string): Promise<void> { async function getValues (search: string): Promise<void> {
if (objectsPromise) { const qid = ++queryId
await objectsPromise
}
const resultQuery = const resultQuery =
search !== '' search !== ''
? { ? {
title: { $like: '%' + search + '%' }, title: { $like: search.replace('*', '%') + '%' },
targetClass: _class targetClass: _class
} }
: { targetClass: _class } : { targetClass: _class }
objectsPromise = client.findAll(tags.class.TagElement, resultQuery) objectsPromise = client.findAll(tags.class.TagElement, resultQuery)
objects = await objectsPromise const _objects = await objectsPromise
if (qid !== queryId) {
return
}
objects = _objects
objectsPromise = undefined objectsPromise = undefined
} }
$: if (!catsSorted && categories.length > 0 && objects.length > 0 && selected.length > 0) {
categories.sort((a, b) => {
const alen = objects.filter((el) => el.category === a._id && isSelected(el))
const blen = objects.filter((el) => el.category === b._id && isSelected(el))
console.log(alen.length, blen.length, selected.length)
return blen.length - alen.length
})
categories = categories
catsSorted = true
}
let search: string = '' let search: string = ''
const toggleGroup = (ev: MouseEvent): void => { const toggleGroup = (ev: MouseEvent): void => {
@ -105,16 +122,16 @@
objects = objects objects = objects
categories = categories categories = categories
updateFilter(selected) updateFilter(selected, level)
} }
function updateFilter (newValues: Ref<TagElement>[]) { function updateFilter (newValues: Ref<TagElement>[], newLevel: number) {
clearTimeout(filterUpdateTimeout) clearTimeout(filterUpdateTimeout)
filterUpdateTimeout = setTimeout(() => { filterUpdateTimeout = setTimeout(() => {
filter.value = [...newValues] filter.value = [...newValues]
// Replace last one with value with level // Replace last one with value with level
filter.props = { level } filter.props = { level: newLevel }
onChange(filter) onChange(filter)
}, FILTER_DEBOUNCE_MS) }, FILTER_DEBOUNCE_MS)
} }
@ -150,6 +167,7 @@
if (Number.isFinite(res) && res >= 0 && res <= 8) { if (Number.isFinite(res) && res >= 0 && res <= 8) {
if (res != null) { if (res != null) {
level = res level = res
updateFilter(selected, level)
} }
} }
}) })
@ -164,7 +182,10 @@
<Loading /> <Loading />
{:else} {:else}
{#each categories as cat, i} {#each categories as cat, i}
{@const values = objects.filter((el) => el.category === cat._id)} {@const values = sortFilterValues(
objects.filter((el) => el.category === cat._id),
isSelected
)}
{#if values.length > 0} {#if values.length > 0}
{#if i > 0}<div class="menu-separator" />{/if} {#if i > 0}<div class="menu-separator" />{/if}
<div class="sticky-wrapper"> <div class="sticky-wrapper">
@ -190,7 +211,7 @@
{/if} {/if}
</button> </button>
<div class="menu-group"> <div class="menu-group">
{#each sortFilterValues(values, isSelected) as element} {#each values as element}
{@const color = getPlatformColorDef(element.color, $themeStore.dark)} {@const color = getPlatformColorDef(element.color, $themeStore.dark)}
<button <button
class="menu-item no-focus flex-row-center" class="menu-item no-focus flex-row-center"

View File

@ -105,7 +105,9 @@
} }
function getStatusItem (status: MilestoneStatus, docs: Milestone[]): Milestone[] { function getStatusItem (status: MilestoneStatus, docs: Milestone[]): Milestone[] {
return docs.filter((p) => p.status === status) let vals = docs.filter((p) => p.status === status)
vals = sortFilterValues(vals, (v) => isSelected(v._id, selectedValues))
return vals
} }
function getStatuses (): MilestoneStatus[] { function getStatuses (): MilestoneStatus[] {
@ -161,7 +163,7 @@
<span class="overflow-label"><Label label={status.label} /></span> <span class="overflow-label"><Label label={status.label} /></span>
</div> </div>
</div> </div>
{#each sortFilterValues(items, (v) => isSelected(v._id, selectedValues)) as doc} {#each items as doc}
<button <button
class="menu-item no-focus flex-row-center" class="menu-item no-focus flex-row-center"
on:click={() => { on:click={() => {

View File

@ -125,6 +125,7 @@
}) })
} }
} }
values = sortFilterValues(values, (v) => isSelected(v, filter.value))
objectsPromise = undefined objectsPromise = undefined
} }
@ -190,7 +191,7 @@
{#if objectsPromise} {#if objectsPromise}
<Loading /> <Loading />
{:else} {:else}
{#each sortFilterValues(values, (v) => isSelected(v, filter.value)) as value} {#each values as value}
<button <button
class="menu-item no-focus content-pointer-events-none" class="menu-item no-focus content-pointer-events-none"
on:click={() => { on:click={() => {

View File

@ -46,6 +46,7 @@
const promise = getPresenter(client, filter.key._class, key, key) const promise = getPresenter(client, filter.key._class, key, key)
let values = new Set<any>() let values = new Set<any>()
let sortedValues: any[] = []
let selectedValues: Set<any> = new Set<any>(filter.value.map((p) => p[0])) let selectedValues: Set<any> = new Set<any>(filter.value.map((p) => p[0]))
const realValues = new Map<any, Set<any>>() const realValues = new Map<any, Set<any>>()
@ -95,6 +96,7 @@
values.add(object) values.add(object)
} }
values = values values = values
sortedValues = sortFilterValues([...values.keys()], (v) => isSelected(v, selectedValues))
objectsPromise = undefined objectsPromise = undefined
} }
@ -165,7 +167,7 @@
{#if objectsPromise} {#if objectsPromise}
<Loading /> <Loading />
{:else} {:else}
{#each sortFilterValues([...values.keys()], (v) => isSelected(v, selectedValues)) as value} {#each sortedValues as value}
{@const realValue = [...(realValues.get(value) ?? [])][0]} {@const realValue = [...(realValues.get(value) ?? [])][0]}
<button <button
class="menu-item no-focus content-pointer-events-none" class="menu-item no-focus content-pointer-events-none"

View File

@ -340,7 +340,7 @@ export function getFilterKey (_class: Ref<Class<Doc>> | undefined): string {
* @param {(value: T) => boolean} checkIsSelected * @param {(value: T) => boolean} checkIsSelected
* @returns {readonly T[]} * @returns {readonly T[]}
*/ */
export function sortFilterValues<T> (values: T[], checkIsSelected: (value: T) => boolean): readonly T[] { export function sortFilterValues<T> (values: T[], checkIsSelected: (value: T) => boolean): T[] {
const selectedValues: T[] = [] const selectedValues: T[] = []
const notSelectedValues: T[] = [] const notSelectedValues: T[] = []

View File

@ -14,11 +14,9 @@
--> -->
<script lang="ts"> <script lang="ts">
import type { Ref } from '@hcengineering/core' import type { Ref } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation'
import { Scroller } from '@hcengineering/ui' import { Scroller } from '@hcengineering/ui'
import { NavLink } from '@hcengineering/view-resources' import { NavLink } from '@hcengineering/view-resources'
import type { Application } from '@hcengineering/workbench' import type { Application } from '@hcengineering/workbench'
import workbench from '@hcengineering/workbench'
import { hideApplication, showApplication } from '../utils' import { hideApplication, showApplication } from '../utils'
import App from './App.svelte' import App from './App.svelte'
@ -27,13 +25,13 @@
export let direction: 'vertical' | 'horizontal' = 'vertical' export let direction: 'vertical' | 'horizontal' = 'vertical'
export let shown: boolean = false export let shown: boolean = false
let loaded: boolean = false const loaded: boolean = true
let hiddenAppsIds: Ref<Application>[] = [] const hiddenAppsIds: Ref<Application>[] = []
const hiddenAppsIdsQuery = createQuery() // const hiddenAppsIdsQuery = createQuery()
hiddenAppsIdsQuery.query(workbench.class.HiddenApplication, {}, (res) => { // hiddenAppsIdsQuery.query(workbench.class.HiddenApplication, {}, (res) => {
hiddenAppsIds = res.map((r) => r.attachedTo) // hiddenAppsIds = res.map((r) => r.attachedTo)
loaded = true // loaded = true
}) // })
</script> </script>
<div class="flex-{direction === 'horizontal' ? 'row-center' : 'col-center'} clear-mins apps-{direction} relative"> <div class="flex-{direction === 'horizontal' ? 'row-center' : 'col-center'} clear-mins apps-{direction} relative">

View File

@ -14,9 +14,10 @@
--> -->
<script lang="ts"> <script lang="ts">
export let mini: boolean = false export let mini: boolean = false
export let workspace: string
</script> </script>
<div class="antiLogo red" class:mini>P</div> <div class="antiLogo red" class:mini>{workspace.toUpperCase()[0]}</div>
<style lang="scss"> <style lang="scss">
.antiLogo { .antiLogo {
@ -30,6 +31,9 @@
outline: none; outline: none;
cursor: pointer; cursor: pointer;
&:hover {
opacity: 0.8;
}
&:not(.mini) { &:not(.mini) {
width: 2rem; width: 2rem;
height: 2rem; height: 2rem;
@ -39,7 +43,7 @@
height: 1.5rem; height: 1.5rem;
} }
&.red { &.red {
background-color: #c93030; background-color: rgb(246, 105, 77);
} }
&.blue { &.blue {
background-color: #2b5190; background-color: #2b5190;

View File

@ -20,7 +20,8 @@
import notification, { notificationId } from '@hcengineering/notification' import notification, { notificationId } from '@hcengineering/notification'
import { BrowserNotificatator, NotificationClientImpl } from '@hcengineering/notification-resources' import { BrowserNotificatator, NotificationClientImpl } from '@hcengineering/notification-resources'
import { IntlString, getMetadata, getResource } from '@hcengineering/platform' import { IntlString, getMetadata, getResource } from '@hcengineering/platform'
import { configurationStore, createQuery, getClient, ActionContext } from '@hcengineering/presentation' import { ActionContext, configurationStore, createQuery, getClient } from '@hcengineering/presentation'
import { resolvedLocationStore } from '@hcengineering/ui'
import { import {
AnyComponent, AnyComponent,
CompAndProps, CompAndProps,
@ -49,7 +50,6 @@
showPopup showPopup
} from '@hcengineering/ui' } from '@hcengineering/ui'
import view from '@hcengineering/view' import view from '@hcengineering/view'
import setting from '@hcengineering/setting'
import { import {
ActionHandler, ActionHandler,
ListSelectionProvider, ListSelectionProvider,
@ -69,9 +69,8 @@
import Logo from './Logo.svelte' import Logo from './Logo.svelte'
import NavHeader from './NavHeader.svelte' import NavHeader from './NavHeader.svelte'
import Navigator from './Navigator.svelte' import Navigator from './Navigator.svelte'
import SpaceView from './SpaceView.svelte'
import SelectWorkspaceMenu from './SelectWorkspaceMenu.svelte' import SelectWorkspaceMenu from './SelectWorkspaceMenu.svelte'
import Settings from './icons/Settings.svelte' import SpaceView from './SpaceView.svelte'
import TopMenu from './icons/TopMenu.svelte' import TopMenu from './icons/TopMenu.svelte'
let contentPanel: HTMLElement let contentPanel: HTMLElement
@ -574,7 +573,7 @@
showPopup(SelectWorkspaceMenu, {}, popupSpacePosition) showPopup(SelectWorkspaceMenu, {}, popupSpacePosition)
}} }}
> >
<Logo mini={appsMini} /> <Logo mini={appsMini} workspace={$resolvedLocationStore.path[1]} />
</div> </div>
<div class="topmenu-container clear-mins flex-no-shrink" class:mini={appsMini}> <div class="topmenu-container clear-mins flex-no-shrink" class:mini={appsMini}>
<AppItem <AppItem
@ -622,13 +621,13 @@
/> />
</div> </div>
<div class="info-box {appsDirection}" class:vertical-mobile={appsDirection === 'vertical'} class:mini={appsMini}> <div class="info-box {appsDirection}" class:vertical-mobile={appsDirection === 'vertical'} class:mini={appsMini}>
<AppItem <!-- <AppItem
icon={Settings} icon={Settings}
label={setting.string.Settings} label={setting.string.Settings}
selected={shownMenu} selected={shownMenu}
size={appsMini ? 'small' : 'large'} size={appsMini ? 'small' : 'large'}
on:click={() => (shownMenu = !shownMenu)} on:click={() => (shownMenu = !shownMenu)}
/> /> -->
<div class="flex-center" class:mt-3={appsDirection === 'vertical'} class:ml-2={appsDirection === 'horizontal'}> <div class="flex-center" class:mt-3={appsDirection === 'vertical'} class:ml-2={appsDirection === 'horizontal'}>
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<div <div

View File

@ -107,6 +107,8 @@ if (serverSecret === undefined) {
process.exit(1) process.exit(1)
} }
const title = process.env.TITLE
setMetadata(serverToken.metadata.Secret, serverSecret) setMetadata(serverToken.metadata.Secret, serverSecret)
const config = { const config = {
@ -119,7 +121,8 @@ const config = {
collaboratorUrl, collaboratorUrl,
gmailUrl, gmailUrl,
telegramUrl, telegramUrl,
rekoniUrl rekoniUrl,
title
} }
console.log('Starting Front service with', config) console.log('Starting Front service with', config)
const shutdown = start(config, SERVER_PORT) const shutdown = start(config, SERVER_PORT)

View File

@ -140,6 +140,7 @@ export function start (
rekoniUrl: string rekoniUrl: string
telegramUrl: string telegramUrl: string
gmailUrl: string gmailUrl: string
title?: string
}, },
port: number port: number
): () => void { ): () => void {
@ -175,7 +176,8 @@ export function start (
COLLABORATOR_URL: config.collaboratorUrl, COLLABORATOR_URL: config.collaboratorUrl,
REKONI_URL: config.rekoniUrl, REKONI_URL: config.rekoniUrl,
TELEGRAM_URL: config.telegramUrl, TELEGRAM_URL: config.telegramUrl,
GMAIL_URL: config.gmailUrl GMAIL_URL: config.gmailUrl,
TITLE: config.title
}) })
}) })