Skip to content

Commit

Permalink
feat: new icons + dispatch based on the theme
Browse files Browse the repository at this point in the history
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
  • Loading branch information
Akos Kitta committed Mar 27, 2023
1 parent 2e7f2d9 commit c8e447c
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { NativeImage } from '@theia/core/electron-shared/electron';
import { ThemeService } from '@theia/core/lib/browser/theming';
import {
Disposable,
DisposableCollection,
Expand All @@ -9,7 +10,11 @@ import { inject, injectable } from '@theia/core/shared/inversify';
import { SketchesError } from '../../common/protocol';
import { ConfigServiceClient } from '../config/config-service-client';
import { ArduinoMenus } from '../menu/arduino-menus';
import { NativeImageCache } from '../native-image-cache';
import {
isThemeNativeImage,
NativeImageCache,
ThemeNativeImage,
} from '../native-image-cache';
import { NotificationCenter } from '../notification-center';
import { CloudSketchContribution } from './cloud-contribution';
import { CommandRegistry, MenuModelRegistry, Sketch } from './contribution';
Expand All @@ -27,21 +32,34 @@ export class OpenRecentSketch extends CloudSketchContribution {
private readonly imageCache: NativeImageCache;
@inject(ConfigServiceClient)
private readonly configServiceClient: ConfigServiceClient;
@inject(ThemeService)
private readonly themeService: ThemeService;

private readonly toDispose = new DisposableCollection();
private cloudImage: NativeImage | undefined;
private readonly toDisposeBeforeRegister = new DisposableCollection();
private readonly toDispose = new DisposableCollection(
this.toDisposeBeforeRegister
);
private cloudImage: NativeImage | ThemeNativeImage;

override onStart(): void {
this.notificationCenter.onRecentSketchesDidChange(({ sketches }) =>
this.refreshMenu(sketches)
);
this.imageCache
.getImage('cloud')
.then((image) => (this.cloudImage = image));
this.toDispose.pushAll([
this.notificationCenter.onRecentSketchesDidChange(({ sketches }) =>
this.refreshMenu(sketches)
),
this.themeService.onDidColorThemeChange(() => this.update()),
]);
}

onStop(): void {
this.toDispose.dispose();
}

override async onReady(): Promise<void> {
this.update();
this.imageCache.getImage('cloud').then((image) => {
this.cloudImage = image;
this.update();
});
}

override registerMenus(registry: MenuModelRegistry): void {
Expand All @@ -65,7 +83,7 @@ export class OpenRecentSketch extends CloudSketchContribution {

private register(sketches: Sketch[]): void {
const order = 0;
this.toDispose.dispose();
this.toDisposeBeforeRegister.dispose();
for (const sketch of sketches) {
const { uri } = sketch;
const command = { id: `arduino-open-recent--${uri}` };
Expand Down Expand Up @@ -95,7 +113,7 @@ export class OpenRecentSketch extends CloudSketchContribution {
ArduinoMenus.FILE__OPEN_RECENT_SUBMENU,
menuAction
);
this.toDispose.pushAll([
this.toDisposeBeforeRegister.pushAll([
new DisposableCollection(
Disposable.create(() =>
this.commandRegistry.unregisterCommand(command)
Expand All @@ -109,13 +127,23 @@ export class OpenRecentSketch extends CloudSketchContribution {
}

private assignImage(sketch: Sketch, menuAction: MenuAction): MenuAction {
if (this.cloudImage) {
const image = this.nativeImageForTheme();
if (image) {
const dataDirUri = this.configServiceClient.tryGetDataDirUri();
const isCloud = this.createFeatures.isCloud(sketch, dataDirUri);
if (isCloud) {
Object.assign(menuAction, { nativeImage: this.cloudImage });
Object.assign(menuAction, { nativeImage: image });
}
}
return menuAction;
}

private nativeImageForTheme(): NativeImage | undefined {
const image = this.cloudImage;
if (isThemeNativeImage(image)) {
const themeType = this.themeService.getCurrentTheme().type;
return themeType === 'light' ? image.light : image.dark;
}
return image;
}
}
55 changes: 40 additions & 15 deletions arduino-ide-extension/src/browser/native-image-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,54 @@ import fetch from 'cross-fetch';
const nativeImageIdentifierLiterals = ['cloud'] as const;
export type NativeImageIdentifier =
typeof nativeImageIdentifierLiterals[number];
export const nativeImages: Record<NativeImageIdentifier, string> = {
cloud: 'cloud.png',
export const nativeImages: Record<
NativeImageIdentifier,
string | { light: string; dark: string }
> = {
cloud: { light: 'cloud-light.png', dark: 'cloud-dark.png' },
};

export interface ThemeNativeImage {
readonly light: NativeImage;
readonly dark: NativeImage;
}

export function isThemeNativeImage(arg: unknown): arg is ThemeNativeImage {
return (
typeof arg === 'object' &&
(<ThemeNativeImage>arg).light !== undefined &&
(<ThemeNativeImage>arg).dark !== undefined
);
}

type Image = NativeImage | ThemeNativeImage;

@injectable()
export class NativeImageCache implements FrontendApplicationContribution {
private readonly cache = new Map<NativeImageIdentifier, NativeImage>();
private readonly loading = new Map<
NativeImageIdentifier,
Promise<NativeImage>
>();
private readonly cache = new Map<NativeImageIdentifier, Image>();
private readonly loading = new Map<NativeImageIdentifier, Promise<Image>>();

onStart(): void {
Object.keys(nativeImages).forEach((identifier: NativeImageIdentifier) =>
this.getImage(identifier)
);
}

tryGetImage(identifier: NativeImageIdentifier): NativeImage | undefined {
tryGetImage(identifier: NativeImageIdentifier): Image | undefined {
return this.cache.get(identifier);
}

async getImage(identifier: NativeImageIdentifier): Promise<NativeImage> {
async getImage(identifier: NativeImageIdentifier): Promise<Image> {
const image = this.cache.get(identifier);
if (image) {
return image;
}
let loading = this.loading.get(identifier);
if (!loading) {
const deferred = new Deferred<NativeImage>();
const deferred = new Deferred<Image>();
loading = deferred.promise;
this.loading.set(identifier, loading);
this.fetchIconData(identifier).then(
this.fetchImage(identifier).then(
(image) => {
if (!this.cache.has(identifier)) {
this.cache.set(identifier, image);
Expand All @@ -61,10 +76,20 @@ export class NativeImageCache implements FrontendApplicationContribution {
return loading;
}

private async fetchIconData(
identifier: NativeImageIdentifier
): Promise<NativeImage> {
const path = `nativeImage/${nativeImages[identifier]}`;
private async fetchImage(identifier: NativeImageIdentifier): Promise<Image> {
const value = nativeImages[identifier];
if (typeof value === 'string') {
return this.fetchIconData(value);
}
const [light, dark] = await Promise.all([
this.fetchIconData(value.light),
this.fetchIconData(value.dark),
]);
return { light, dark };
}

private async fetchIconData(filename: string): Promise<NativeImage> {
const path = `nativeImage/${filename}`;
const endpoint = new Endpoint({ path }).getRestUrl().toString();
const response = await fetch(endpoint);
const arrayBuffer = await response.arrayBuffer();
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.

0 comments on commit c8e447c

Please sign in to comment.