Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This component is a wrapper of [Sharee](https://github.com/parsagholipour/sharee
- Default social media drivers to share links easily: Copy, Twitter, Facebook, Linkedin, Whatsapp, Telegram.
- Modes to display the Easy Share menu: Fixed, Normal, Hover, Text, Dropdown.
- By defaut, locale and keys are in English but custom locales and keys can be defined.
- Listen to share actions to track which driver was clicked.

## Supported versions

Expand Down Expand Up @@ -114,6 +115,20 @@ NormalShareEasy.create().withNoTitle(true).forComponent(div);
add(div);
```

## Listening to share actions

Register a listener to be notified when one of the share drivers is clicked. The event exposes the
clicked driver (both as the raw name and, for default drivers, as a `Driver` enum value) and the
share link:

```java
Div div = new Div();
NormalShareEasy.create()
.withShareListener(event -> Notification.show("Shared via " + event.getDriverName()))
.forComponent(div);
add(div);
```

## Special configuration when using Spring

By default, Vaadin Flow only includes ```com/vaadin/flow/component``` to be always scanned for UI components and views. For this reason, the addon might need to be whitelisted in order to display correctly.
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>org.vaadin.addons.flowingcode</groupId>
<artifactId>share-easy-addon</artifactId>
<version>2.1.1-SNAPSHOT</version>
<version>2.2.0-SNAPSHOT</version>
<name>Share Easy Add-on</name>
<description>Share Eady Add-on for Vaadin Flow</description>

Expand Down Expand Up @@ -148,7 +148,7 @@
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>5.1.1</version>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dependency.NpmPackage;
import elemental.json.JsonObject;
import elemental.json.JsonString;

/**
* BaseSharee represents the base class for creating Sharee instances.
Expand All @@ -49,9 +50,11 @@ class BaseShareEasy<T extends BaseShareEasy<T>> {
private Options options = new Options();

Map<String, CustomDriverOptions> customDrivers = new HashMap<>();

protected boolean withDefaultDriversListFirst = true;

private ShareEasyClickListener shareListener;

/**
* Sets the share link for the shared content.
*
Expand Down Expand Up @@ -121,6 +124,17 @@ public T withDrivers(ShareEasyDriver... drivers) {
return (T) this;
}

/**
* Sets the listener that is notified when one of the share drivers is clicked.
*
* @param shareListener the listener to notify on driver clicks
* @return The current instance of the BaseSharee class
*/
public T withShareListener(ShareEasyClickListener shareListener) {
this.shareListener = shareListener;
return (T) this;
}

/**
* Initializes the Sharee component with the specified Vaadin component.
*
Expand All @@ -141,6 +155,7 @@ protected JsonObject getJsonObjectOptions() {
}

private void createSharee() {
registerShareListener();
String shareeOptions = getJsonObjectOptions().toJson();
if(customDrivers.isEmpty()) {
this.create(shareeOptions);
Expand All @@ -162,6 +177,22 @@ private void create(String shareeOptions) {
component.getElement().executeJs("fcShareeConnector.create(this, $0)", shareeOptions);
}

private void registerShareListener() {
if (shareListener == null) {
return;
}
component.getElement().addEventListener("driver-clicked", event -> {
JsonObject detail = event.getEventData();
String driverName = getStringOrNull(detail, "event.detail.name");
String link = getStringOrNull(detail, "event.detail.link");
shareListener.onShare(new ShareEasyClickEvent(component, driverName, link));
}).addEventData("event.detail.name").addEventData("event.detail.link");
}

private static String getStringOrNull(JsonObject data, String key) {
return data.hasKey(key) && data.get(key) instanceof JsonString ? data.getString(key) : null;
}

/**
* Sets custom language keys for the default locale.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*-
* #%L
* Share Easy Add-on
* %%
* Copyright (C) 2023 - 2026 Flowing Code
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package com.flowingcode.vaadin.addons.shareeasy;

import java.io.Serializable;
import java.util.Objects;
import java.util.Optional;
import com.flowingcode.vaadin.addons.shareeasy.enums.Driver;
import com.vaadin.flow.component.Component;

/**
* Event fired when one of the share drivers is clicked.
*
* @author Paola De Bartolo / Flowing Code
*/
@SuppressWarnings("serial")
public class ShareEasyClickEvent implements Serializable {

private final Component source;

private final String driverName;

private final String link;

/**
* Creates a new share click event.
*
* @param source the component the Share Easy instance is attached to
* @param driverName the name of the clicked driver, e.g. "telegram"
* @param link the share link associated with the driver (the copied URL for the copy driver); may
* be {@code null} for custom drivers without a link
*/
public ShareEasyClickEvent(Component source, String driverName, String link) {
this.source = Objects.requireNonNull(source, "source must not be null");
this.driverName = Objects.requireNonNull(driverName, "driverName must not be null");
this.link = link;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Gets the component the Share Easy instance is attached to.
*
* @return the source component
*/
public Component getSource() {
return source;
}

/**
* Gets the name of the clicked driver.
*
* @return the driver name, e.g. "telegram"
*/
public String getDriverName() {
return driverName;
}

/**
* Gets the clicked driver as a {@link Driver default driver}, if it corresponds to one.
*
* @return the matching default {@link Driver}, or an empty {@link Optional} if the clicked driver
* is a custom driver
*/
public Optional<Driver> getDriver() {
return Driver.fromName(driverName);
}

/**
* Gets the share link associated with the clicked driver. For the copy driver this is the URL that
* was copied to the clipboard.
*
* @return the share link, or {@code null} for custom drivers without a link
*/
public String getLink() {
return link;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*-
* #%L
* Share Easy Add-on
* %%
* Copyright (C) 2023 - 2026 Flowing Code
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package com.flowingcode.vaadin.addons.shareeasy;

import com.vaadin.flow.function.SerializableConsumer;

/**
* Listener notified when one of the share drivers is clicked.
*
* @author Paola De Bartolo / Flowing Code
*/
@FunctionalInterface
public interface ShareEasyClickListener extends SerializableConsumer<ShareEasyClickEvent> {

/**
* Invoked when a share driver is clicked.
*
* @param event the share click event
*/
void onShare(ShareEasyClickEvent event);

@Override
default void accept(ShareEasyClickEvent event) {
onShare(event);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/
package com.flowingcode.vaadin.addons.shareeasy.enums;

import java.util.Optional;
import com.flowingcode.vaadin.addons.shareeasy.ShareEasyDriver;

/**
Expand All @@ -40,4 +41,21 @@ private Driver(String name) {
public String getName() {
return name;
}

/**
* Resolves a default driver from its name (the name carried by the share event), if it matches one
* of the default drivers.
*
* @param name the driver name, e.g. "telegram"
* @return the matching {@link Driver}, or an empty {@link Optional} if the name does not correspond
* to a default driver (for instance, a custom driver)
*/
public static Optional<Driver> fromName(String name) {
for (Driver driver : values()) {
if (driver.name.equals(name)) {
return Optional.of(driver);
}
}
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ window.fcShareeConnector = {
create: function (container, optionsJson) {
this._updateXButtonLabel();
this._updateCopyDriverOnClick();
this._patchDriverNames();
let parsedOptions = JSON.parse(optionsJson);
const sharee = new Sharee(container, parsedOptions);
},

createWithCustomDrivers: function (container, optionsJson) {
this._updateXButtonLabel();
this._updateCopyDriverOnClick();
this._patchDriverNames();
let parsedOptions = JSON.parse(optionsJson);
// add the custom drivers to the list of drivers
let drivers = parsedOptions.drivers.concat(container.customDrivers);
Expand Down Expand Up @@ -95,14 +97,33 @@ window.fcShareeConnector = {
*/
_updateCopyDriverOnClick() {
Sharee.drivers['copy'].prototype.onClick = function(e) {
// The copy driver copies a URL instead of navigating, so the shared
// "link" is the text that gets copied (the base CopyDriver's getLink()
// is undefined). Dispatch the same "driver-clicked" event the base
// driver emits via super.onClick (lost when onClick is fully replaced
// here), carrying the actual copied URL.
const copyText = this.options?.shareLink || window.location.href
const event = new CustomEvent('driver-clicked', {
detail: {
name: this.getName(),
link: copyText,
originalEvent: e,
},
bubbles: true,
composed: true,
});
this.options?.targetElement.dispatchEvent(event);
// Honor preventDefault as the base CopyDriver does after super.onClick.
if (e.defaultPrevented) {
return
}
const successText = this.lang['CopiedSuccessfully']
const el = e.currentTarget;
const textEl = el.querySelector('div:nth-child(2)')
if (textEl.innerHTML === successText) {
textEl.innerHTML = this.getButtonText()
return
}
let copyText = this.options?.shareLink || window.location.href
navigator.clipboard.writeText(copyText).then(() => {
textEl.innerHTML = successText
textEl.style.transition = '300ms all'
Expand All @@ -125,5 +146,20 @@ window.fcShareeConnector = {
Sharee.drivers['x'].prototype.getButtonText = function () {
return "X";
}
}
},

/**
* Overrides getName on each default driver so that it returns the driver's
* registered key (e.g. "telegram", "copy") instead of the bundled (and
* unstable) class name. This makes the name carried by the "driver-clicked"
* event reliable on the server side. Custom drivers already define their own
* getName in _addDriver, so they are not affected.
*/
_patchDriverNames() {
Object.keys(Sharee.drivers).forEach(function (name) {
Sharee.drivers[name].prototype.getName = function () {
return name;
};
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.flowingcode.vaadin.addons.shareeasy.util.CustomDriverOptions;
import com.flowingcode.vaadin.addons.shareeasy.util.LanguageKeys;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;

Expand Down Expand Up @@ -127,12 +128,26 @@
customDrivers.put("Trello", new TrelloDriverOptions());
NormalShareEasy.create().withCustomDrivers(customDrivers).forComponent(normalDiv7);
// #if vaadin eq 0
Div example7 = createContainerDiv("With custom driver for extra social: Trello", normalDiv7);
Div example7 = createContainerDiv("With custom driver for extra social: Trello", normalDiv7);
SourceCodeViewer.highlightOnHover(example7, "example7");
add(example7);
addSeparator();
// #endif
// show-source add(normalDiv7);
// end-block

// begin-block example8
Div normalDiv8 = new Div();
NormalShareEasy.create()
.withShareListener(event -> Notification.show("Shared via " + event.getDriverName()))
.forComponent(normalDiv8);
// #if vaadin eq 0
Div example8 = createContainerDiv("With share listener", normalDiv8);
SourceCodeViewer.highlightOnHover(example8, "example8");
add(example8);
// #endif
// show-source add(normalDiv8);

Check warning on line 149 in src/test/java/com/flowingcode/vaadin/addons/shareeasy/NormalModeDemo.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This block of commented-out lines of code should be removed.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_ShareEasy&issues=AZ6UGSugKiggci1pzHi6&open=AZ6UGSugKiggci1pzHi6&pullRequest=24
// end-block
}

}
Loading
Loading