forked from MapComplete/MapComplete
Replace userbadge with panel access to user information, add more information to user profile
This commit is contained in:
parent
bc85c9bbe7
commit
6f018a2fd8
19 changed files with 398 additions and 174 deletions
|
@ -16,7 +16,7 @@ import Title from "./Title"
|
|||
*
|
||||
*/
|
||||
export default class ScrollableFullScreen {
|
||||
private static readonly empty = new FixedUiElement("")
|
||||
private static readonly empty = ScrollableFullScreen.initEmpty()
|
||||
private static _currentlyOpen: ScrollableFullScreen
|
||||
public isShown: UIEventSource<boolean>
|
||||
private hashToShow: string
|
||||
|
@ -61,6 +61,7 @@ export default class ScrollableFullScreen {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
isShown.addCallback((isShown) => {
|
||||
if (isShown) {
|
||||
// We first must set the hash, then activate the panel
|
||||
|
@ -68,8 +69,13 @@ export default class ScrollableFullScreen {
|
|||
if (setHash) {
|
||||
Hash.hash.setData(hashToShow)
|
||||
}
|
||||
ScrollableFullScreen._currentlyOpen = self
|
||||
self.Activate()
|
||||
|
||||
} else {
|
||||
if(self.hashToShow !== undefined){
|
||||
Hash.hash.setData(undefined)
|
||||
}
|
||||
// Some cleanup...
|
||||
ScrollableFullScreen.collapse()
|
||||
|
||||
|
@ -77,6 +83,18 @@ export default class ScrollableFullScreen {
|
|||
})
|
||||
}
|
||||
|
||||
private static initEmpty(): FixedUiElement{
|
||||
|
||||
document.addEventListener("keyup", function (event) {
|
||||
if (event.code === "Escape") {
|
||||
ScrollableFullScreen.collapse()
|
||||
event.preventDefault()
|
||||
}
|
||||
})
|
||||
|
||||
return new FixedUiElement("")
|
||||
|
||||
}
|
||||
public static collapse(){
|
||||
const fs = document.getElementById("fullscreen")
|
||||
if (fs !== null) {
|
||||
|
@ -84,7 +102,10 @@ export default class ScrollableFullScreen {
|
|||
fs.classList.add("hidden")
|
||||
}
|
||||
|
||||
ScrollableFullScreen._currentlyOpen?.isShown?.setData(false)
|
||||
const opened = ScrollableFullScreen._currentlyOpen
|
||||
if( opened !== undefined){
|
||||
opened?.isShown?.setData(false)
|
||||
}
|
||||
}
|
||||
|
||||
Destroy() {
|
||||
|
|
|
@ -42,11 +42,11 @@ export default class ExtraLinkButton extends UIElement {
|
|||
|
||||
const isIframe = window !== window.top
|
||||
|
||||
if (c.requirements.has("iframe") && !isIframe) {
|
||||
if (c.requirements?.has("iframe") && !isIframe) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (c.requirements.has("no-iframe") && isIframe) {
|
||||
if (c.requirements?.has("no-iframe") && isIframe) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
@ -82,11 +82,11 @@ export default class ExtraLinkButton extends UIElement {
|
|||
newTab: c.newTab,
|
||||
})
|
||||
|
||||
if (c.requirements.has("no-welcome-message")) {
|
||||
if (c.requirements?.has("no-welcome-message")) {
|
||||
link = new Toggle(undefined, link, this.state.featureSwitchWelcomeMessage)
|
||||
}
|
||||
|
||||
if (c.requirements.has("welcome-message")) {
|
||||
if (c.requirements?.has("welcome-message")) {
|
||||
link = new Toggle(link, undefined, this.state.featureSwitchWelcomeMessage)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import { VariableUiElement } from "../Base/VariableUIElement"
|
|||
import FeatureInfoBox from "../Popup/FeatureInfoBox"
|
||||
import CopyrightPanel from "./CopyrightPanel"
|
||||
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
|
||||
export default class LeftControls extends Combine {
|
||||
constructor(
|
||||
|
|
|
@ -18,7 +18,7 @@ export default class RightControls extends Combine {
|
|||
const geolocationButton = new Toggle(
|
||||
new MapControlButton(geolocatioHandler, {
|
||||
dontStyle: true,
|
||||
}),
|
||||
}).SetClass("p-1"),
|
||||
undefined,
|
||||
state.featureSwitchGeolocation
|
||||
)
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||
import Svg from "../../Svg"
|
||||
import Combine from "../Base/Combine"
|
||||
import { FixedUiElement } from "../Base/FixedUiElement"
|
||||
import LanguagePicker from "../LanguagePicker"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Link from "../Base/Link"
|
||||
import Toggle from "../Input/Toggle"
|
||||
import Img from "../Base/Img"
|
||||
import MapState from "../../Logic/State/MapState"
|
||||
import { LoginToggle } from "../Popup/LoginButton"
|
||||
|
||||
export default class UserBadge extends LoginToggle {
|
||||
constructor(state: MapState) {
|
||||
const userDetails = state.osmConnection.userDetails
|
||||
const logout = Svg.logout_svg().onClick(() => {
|
||||
state.osmConnection.LogOut()
|
||||
})
|
||||
|
||||
const userBadge = new VariableUiElement(
|
||||
userDetails.map((user) => {
|
||||
{
|
||||
const homeButton = new VariableUiElement(
|
||||
userDetails.map((userinfo) => {
|
||||
if (userinfo.home) {
|
||||
return Svg.home_svg()
|
||||
}
|
||||
return " "
|
||||
})
|
||||
).onClick(() => {
|
||||
const home = state.osmConnection.userDetails.data?.home
|
||||
if (home === undefined) {
|
||||
return
|
||||
}
|
||||
state.leafletMap.data?.setView([home.lat, home.lon], 16)
|
||||
})
|
||||
|
||||
const linkStyle = "flex items-baseline"
|
||||
const languagePicker = (
|
||||
new LanguagePicker(state.layoutToUse.language, "") ?? new FixedUiElement("")
|
||||
).SetStyle("width:min-content;")
|
||||
|
||||
let messageSpan = new Link(
|
||||
new Combine([Svg.envelope, "" + user.totalMessages]).SetClass(linkStyle),
|
||||
`${user.backend}/messages/inbox`,
|
||||
true
|
||||
)
|
||||
|
||||
const csCount = new Link(
|
||||
new Combine([Svg.star, "" + user.csCount]).SetClass(linkStyle),
|
||||
`${user.backend}/user/${user.name}/history`,
|
||||
true
|
||||
)
|
||||
|
||||
if (user.unreadMessages > 0) {
|
||||
messageSpan = new Link(
|
||||
new Combine([Svg.envelope, "" + user.unreadMessages]),
|
||||
`${user.backend}/messages/inbox`,
|
||||
true
|
||||
).SetClass("alert")
|
||||
}
|
||||
|
||||
let dryrun = new Toggle(
|
||||
new FixedUiElement("TESTING").SetClass("alert font-xs p-0 max-h-4"),
|
||||
undefined,
|
||||
state.featureSwitchIsTesting
|
||||
)
|
||||
|
||||
const settings = new Link(
|
||||
Svg.gear,
|
||||
`${user.backend}/user/${encodeURIComponent(user.name)}/account`,
|
||||
true
|
||||
)
|
||||
|
||||
const userName = new Link(
|
||||
new FixedUiElement(user.name),
|
||||
`${user.backend}/user/${user.name}`,
|
||||
true
|
||||
)
|
||||
|
||||
const userStats = new Combine([
|
||||
homeButton,
|
||||
settings,
|
||||
messageSpan,
|
||||
csCount,
|
||||
languagePicker,
|
||||
logout,
|
||||
]).SetClass("userstats")
|
||||
|
||||
const usertext = new Combine([
|
||||
new Combine([userName, dryrun]).SetClass("flex justify-end w-full"),
|
||||
userStats,
|
||||
]).SetClass("flex flex-col sm:w-auto sm:pl-2 overflow-hidden w-0")
|
||||
const userIcon = (
|
||||
user.img === undefined ? Svg.osm_logo_ui() : new Img(user.img)
|
||||
)
|
||||
.SetClass(
|
||||
"rounded-full opacity-0 m-0 p-0 duration-500 w-16 min-width-16 h16 float-left"
|
||||
)
|
||||
.onClick(() => {
|
||||
if (usertext.HasClass("w-0")) {
|
||||
usertext.RemoveClass("w-0")
|
||||
usertext.SetClass("w-min pl-2")
|
||||
} else {
|
||||
usertext.RemoveClass("w-min")
|
||||
usertext.RemoveClass("pl-2")
|
||||
usertext.SetClass("w-0")
|
||||
}
|
||||
})
|
||||
|
||||
return new Combine([usertext, userIcon]).SetClass("h-16 flex bg-white")
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
super(
|
||||
new Combine([
|
||||
userBadge.SetClass("inline-block m-0 w-full").SetStyle("pointer-events: all"),
|
||||
]).SetClass("shadow rounded-full h-min overflow-hidden block w-full md:w-max"),
|
||||
Translations.t.general.loginWithOpenStreetMap,
|
||||
state
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
import ScrollableFullScreen from "../Base/ScrollableFullScreen";
|
||||
import Translations from "../i18n/Translations";
|
||||
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
|
||||
import Combine from "../Base/Combine";
|
||||
import {SubtleButton} from "../Base/SubtleButton";
|
||||
import Svg from "../../Svg";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import Img from "../Base/Img";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import Link from "../Base/Link";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import Loc from "../../Models/Loc";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import Showdown from "showdown"
|
||||
import LanguagePicker from "../LanguagePicker";
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||
|
||||
class UserInformationMainPanel extends Combine {
|
||||
constructor(osmConnection: OsmConnection, locationControl: UIEventSource<Loc>, layout: LayoutConfig) {
|
||||
const t = Translations.t.userinfo;
|
||||
const imgSize = "h-6 w-6"
|
||||
const ud = osmConnection.userDetails;
|
||||
super([
|
||||
|
||||
new VariableUiElement(ud.map(ud => {
|
||||
|
||||
if (!ud?.loggedIn) {
|
||||
// Not logged in
|
||||
return new SubtleButton(
|
||||
Svg.login_svg(), "Login", {imgSize}
|
||||
).onClick(osmConnection.AttemptLogin)
|
||||
}
|
||||
|
||||
let img: Img = Svg.person_svg();
|
||||
if (ud.img !== undefined) {
|
||||
img = new Img(ud.img)
|
||||
}
|
||||
img.SetClass("rounded-full h-12 w-12 m-4")
|
||||
|
||||
let description: BaseUIElement = undefined
|
||||
if (ud.description) {
|
||||
const editButton = new Link(
|
||||
Svg.pencil_svg().SetClass("h-4 w-4"),
|
||||
"https://www.openstreetmap.org/profile/edit",
|
||||
true
|
||||
).SetClass("absolute block bg-subtle rounded-full p-2 bottom-2 right-2 w-min self-end")
|
||||
|
||||
description = new Combine([
|
||||
new FixedUiElement(new Showdown.Converter().makeHtml(ud.description)).SetClass("link-underline"),
|
||||
editButton
|
||||
]).SetClass("relative w-full m-2")
|
||||
|
||||
} else {
|
||||
description = new Combine([
|
||||
t.noDescription, new SubtleButton(Svg.pencil_svg(), t.noDescriptionCallToAction, {imgSize})
|
||||
]).SetClass("w-full m-2")
|
||||
}
|
||||
|
||||
let panToHome: BaseUIElement;
|
||||
if (ud.home) {
|
||||
panToHome = new SubtleButton(Svg.home_svg(), t.moveToHome, {imgSize})
|
||||
.onClick(() => {
|
||||
const home = ud?.home
|
||||
if (home === undefined) {
|
||||
return
|
||||
}
|
||||
locationControl.setData({...home, zoom: 16})
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return new Combine([
|
||||
new Combine([img, description]).SetClass("flex border border-black rounded-md"),
|
||||
new LanguagePicker(layout.language, Translations.t.general.pickLanguage.Clone()),
|
||||
|
||||
new SubtleButton(Svg.envelope_svg(), new Combine([t.gotoInbox,
|
||||
ud.unreadMessages == 0 ? undefined : t.newMessages.SetClass("alert block")
|
||||
]),
|
||||
{imgSize, url: `${ud.backend}/messages/inbox`, newTab: true}),
|
||||
new SubtleButton(Svg.gear_svg(), t.gotoSettings,
|
||||
{imgSize, url: `${ud.backend}/user/${encodeURIComponent(ud.name)}/account`, newTab: true}),
|
||||
panToHome,
|
||||
new SubtleButton(Svg.logout_svg(), Translations.t.general.logout, {imgSize}).onClick(osmConnection.LogOut)
|
||||
|
||||
])
|
||||
}
|
||||
)).SetClass("flex flex-col"),
|
||||
|
||||
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
export default class UserInformationPanel extends ScrollableFullScreen {
|
||||
constructor(state: {
|
||||
layoutToUse: LayoutConfig;
|
||||
osmConnection: OsmConnection, locationControl: UIEventSource<Loc> }) {
|
||||
const t = Translations.t.general;
|
||||
super(
|
||||
() => {
|
||||
return new VariableUiElement(state.osmConnection.userDetails.map(ud => "Welcome " + ud.name))
|
||||
},
|
||||
() => {
|
||||
return new UserInformationMainPanel(state.osmConnection, state.locationControl, state.layoutToUse)
|
||||
},
|
||||
"userinfo"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import FullWelcomePaneWithTabs from "./BigComponents/FullWelcomePaneWithTabs"
|
|||
import MapControlButton from "./MapControlButton"
|
||||
import Svg from "../Svg"
|
||||
import Toggle from "./Input/Toggle"
|
||||
import UserBadge from "./BigComponents/UserBadge"
|
||||
import SearchAndGo from "./BigComponents/SearchAndGo"
|
||||
import BaseUIElement from "./BaseUIElement"
|
||||
import LeftControls from "./BigComponents/LeftControls"
|
||||
|
@ -25,6 +24,11 @@ import Combine from "./Base/Combine"
|
|||
import AddNewMarker from "./BigComponents/AddNewMarker"
|
||||
import FilteredLayer from "../Models/FilteredLayer"
|
||||
import ExtraLinkButton from "./BigComponents/ExtraLinkButton"
|
||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||
import Img from "./Base/Img";
|
||||
import UserInformationPanel from "./BigComponents/UserInformation";
|
||||
import {LoginToggle} from "./Popup/LoginButton";
|
||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
||||
|
||||
/**
|
||||
* The default MapComplete GUI initializer
|
||||
|
@ -180,14 +184,38 @@ export default class DefaultGUI {
|
|||
|
||||
const self = this
|
||||
new Combine([
|
||||
Toggle.If(state.featureSwitchUserbadge, () => new UserBadge(state)),
|
||||
Toggle.If(state.featureSwitchUserbadge, () => {
|
||||
|
||||
const userInfo = new UserInformationPanel(state)
|
||||
|
||||
const mapControl = new MapControlButton(
|
||||
new VariableUiElement(state.osmConnection.userDetails.map(ud => {
|
||||
if (ud?.img === undefined) {
|
||||
return Svg.person_ui().SetClass("mt-1 block")
|
||||
}
|
||||
return new Img(ud?.img);
|
||||
})).SetClass("block rounded-full overflow-hidden"),
|
||||
{
|
||||
dontStyle: true
|
||||
}
|
||||
).onClick(() => userInfo.Activate());
|
||||
|
||||
return new LoginToggle(
|
||||
mapControl, Translations.t.general.loginWithOpenStreetMap, state
|
||||
)
|
||||
|
||||
|
||||
|
||||
}),
|
||||
Toggle.If(
|
||||
state.featureSwitchExtraLinkEnabled,
|
||||
() => new ExtraLinkButton(state, state.layoutToUse.extraLink)
|
||||
),
|
||||
Toggle.If(state.featureSwitchWelcomeMessage, () => self.InitWelcomeMessage()),
|
||||
Toggle.If(state.featureSwitchIsTesting, () => new FixedUiElement("TESTING").SetClass("alert m-2 border-2 border-black"))
|
||||
])
|
||||
.SetClass("flex flex-col")
|
||||
.AttachTo("userbadge")
|
||||
.AttachTo("top-left")
|
||||
|
||||
new Combine([
|
||||
new ExtraLinkButton(state, {
|
||||
|
@ -201,11 +229,8 @@ export default class DefaultGUI {
|
|||
.SetClass("flex items-center justify-center normal-background h-full")
|
||||
.AttachTo("on-small-screen")
|
||||
|
||||
Toggle.If(state.featureSwitchSearch, () => new SearchAndGo(state)).AttachTo("searchbox")
|
||||
Toggle.If(state.featureSwitchSearch, () => new SearchAndGo(state).SetClass("shadow rounded-full h-min w-full overflow-hidden sm:max-w-sm pointer-events-auto")).AttachTo("top-right")
|
||||
|
||||
Toggle.If(state.featureSwitchWelcomeMessage, () => self.InitWelcomeMessage()).AttachTo(
|
||||
"messagesbox"
|
||||
)
|
||||
|
||||
new LeftControls(state, guiState).AttachTo("bottom-left")
|
||||
new RightControls(state).AttachTo("bottom-right")
|
||||
|
|
|
@ -14,9 +14,10 @@ export default class MapControlButton extends Combine {
|
|||
super([contents])
|
||||
if (!options?.dontStyle) {
|
||||
contents.SetClass("mapcontrol p-1")
|
||||
this.SetClass("p-1")
|
||||
}
|
||||
this.SetClass(
|
||||
"relative block rounded-full w-10 h-10 p-1 pointer-events-auto z-above-map subtle-background m-0.5 md:m-1"
|
||||
"relative block rounded-full w-10 h-10 pointer-events-auto z-above-map subtle-background m-0.5 md:m-1"
|
||||
)
|
||||
this.SetStyle("box-shadow: 0 0 10px var(--shadow-color);")
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class LoginButton extends SubtleButton {
|
|||
|
||||
export class LoginToggle extends VariableUiElement {
|
||||
constructor(
|
||||
el,
|
||||
el: BaseUIElement,
|
||||
text: BaseUIElement | string,
|
||||
state: {
|
||||
osmConnection: OsmConnection
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue