From fa9d975c4c796c93e15524c5da024f535cb7ebc0 Mon Sep 17 00:00:00 2001 From: Ilia Naryzhny Date: Tue, 1 Jun 2021 21:37:31 -0700 Subject: [PATCH] Add vuecket-extensions (issue #1). Markdown, fullcalendar, chat --- pom.xml | 8 +- vuecket-demo/pom.xml | 5 + .../org/orienteer/vuecket/demo/HomePage.java | 1 + vuecket-extensions/pom.xml | 106 ++++++++ .../vuecket/extensions/FullCalendar.java | 112 +++++++++ .../vuecket/extensions/VueAdvancedChat.java | 236 ++++++++++++++++++ .../vuecket/extensions}/VueMarkdown.java | 2 +- .../vuecket/extensions/package-info.java | 4 + .../orienteer/vuecket/extensions/AppTest.java | 38 +++ vuecket/pom.xml | 2 - 10 files changed, 506 insertions(+), 8 deletions(-) create mode 100644 vuecket-extensions/pom.xml create mode 100644 vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/FullCalendar.java create mode 100644 vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/VueAdvancedChat.java rename {vuecket-demo/src/main/java/org/orienteer/vuecket/demo => vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions}/VueMarkdown.java (92%) create mode 100644 vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/package-info.java create mode 100644 vuecket-extensions/src/test/java/org/orienteer/vuecket/extensions/AppTest.java diff --git a/pom.xml b/pom.xml index f8435de..95204ab 100644 --- a/pom.xml +++ b/pom.xml @@ -14,10 +14,7 @@ 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. ---> - +--> 4.0.0 org.orienteer.vuecket @@ -251,5 +248,6 @@ vuecket vuecket-demo - + vuecket-extensions + diff --git a/vuecket-demo/pom.xml b/vuecket-demo/pom.xml index cf8dcf4..383c96e 100644 --- a/vuecket-demo/pom.xml +++ b/vuecket-demo/pom.xml @@ -20,6 +20,11 @@ vuecket ${project.version} + + org.orienteer.vuecket + vuecket-extensions + ${project.version} + org.slf4j diff --git a/vuecket-demo/src/main/java/org/orienteer/vuecket/demo/HomePage.java b/vuecket-demo/src/main/java/org/orienteer/vuecket/demo/HomePage.java index 2a7b66a..77cefe2 100644 --- a/vuecket-demo/src/main/java/org/orienteer/vuecket/demo/HomePage.java +++ b/vuecket-demo/src/main/java/org/orienteer/vuecket/demo/HomePage.java @@ -3,6 +3,7 @@ import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.request.resource.PackageResourceReference; import org.orienteer.vuecket.VueComponent; +import org.orienteer.vuecket.extensions.VueMarkdown; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.WebPage; diff --git a/vuecket-extensions/pom.xml b/vuecket-extensions/pom.xml new file mode 100644 index 0000000..1ead2a4 --- /dev/null +++ b/vuecket-extensions/pom.xml @@ -0,0 +1,106 @@ + + + 4.0.0 + + vuecket-parent + org.orienteer.vuecket + 1.0-SNAPSHOT + + + vuecket-extensions + + vuecket-extensions + + + + org.orienteer.vuecket + vuecket + ${project.version} + + + junit + junit + test + + + + + + + org.eclipse.jetty + jetty-maven-plugin + ${jetty9.version} + + + + maven.project.build.directory.test-classes + ${project.build.directory}/test-classes + + + true + true + + jar + + ${project.basedir}/../vuecket-demo/src/test/jetty/jetty.xml,${project.basedir}/../vuecket-demo/src/test/jetty/jetty-http.xml + + ${project.basedir}/src/test/jetty/web.xml + / + src/test/resources + ^$ + ^$ + + + + + + + + maven-clean-plugin + 3.1.0 + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + + + + + + maven-project-info-reports-plugin + + + + diff --git a/vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/FullCalendar.java b/vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/FullCalendar.java new file mode 100644 index 0000000..3988d61 --- /dev/null +++ b/vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/FullCalendar.java @@ -0,0 +1,112 @@ +package org.orienteer.vuecket.extensions; + +import java.util.Date; +import java.util.ArrayList; +import java.util.List; + +import org.apache.wicket.markup.head.CssHeaderItem; +import org.apache.wicket.markup.head.IHeaderResponse; +import org.apache.wicket.markup.head.JavaScriptHeaderItem; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.util.io.IClusterable; +import org.orienteer.vuecket.VueComponent; +import org.orienteer.vuecket.VueSettings; +import org.orienteer.vuecket.VueSettings.INPMPackageProvider; +import org.orienteer.vuecket.descriptor.VueNpm; +import org.orienteer.vuecket.method.IVuecketMethod.Context; +import org.orienteer.vuecket.method.JsFunction; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import org.orienteer.vuecket.method.VueMethod; +import org.orienteer.vuecket.util.VuecketUtils; + +/** + * Vuecket component for FullCalendar js library + * https://fullcalendar.io/docs/vue + */ +@VueNpm(packageName = "@fullcalendar/vue", path = "dist/main.global.js", enablement = "") +@Slf4j +public class FullCalendar extends VueComponent { + + /** + * Class for passing infor about events requests + */ + @Data + public static class EventsRequestInfo implements IClusterable { + private Date start; + private Date end; + private String startStr; + private String endStr; + private String timeZone; + } + + /** + * Container for events data + */ + @Data + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class Event implements IClusterable { + private String id; + private String groupId; + private Boolean allDay; + private Date start; + private Date end; + private String title; + private String url; + } + + /** + * Options for FullCalendar + */ + @Data + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class Options implements IClusterable { + private Object events; + private Object headerToolbar = VuecketUtils.toJsonNode("{ center: 'dayGridMonth,timeGridWeek' }"); + + public Options(Object events) { + this.events = events; + } + + public Options(FullCalendar calendar) { + this(JsFunction.call(calendar, "lookupEvents")); + } + } + + public FullCalendar(String id) { + super(id); + setDefaultModel(Model.of(new Options(this))); + } + + public FullCalendar(String id, IModel optionsModel) { + super(id, optionsModel); + } + + @Override + protected void onInitialize() { + super.onInitialize(); + dataFiberBuilder("options").bindToProperty().init().build(); + } + + @Override + public void renderHead(IHeaderResponse response) { + INPMPackageProvider provider = VueSettings.get().getNpmPackageProvider(); + response.render(CssHeaderItem.forReference(provider.provide("fullcalendar", "main.css"))); + response.render(JavaScriptHeaderItem.forReference(provider.provide("fullcalendar", "main.min.js"), "fullcalendar")); + super.renderHead(response); + } + + @VueMethod("lookupEvents") + public final List lookupEvents(Context ctx, EventsRequestInfo eventsRequestInfo) { + return lookupEvents(eventsRequestInfo.getStart(), eventsRequestInfo.getEnd()); + } + + public List lookupEvents(Date start, Date end) { + return new ArrayList(); + } +} diff --git a/vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/VueAdvancedChat.java b/vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/VueAdvancedChat.java new file mode 100644 index 0000000..d39a35e --- /dev/null +++ b/vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/VueAdvancedChat.java @@ -0,0 +1,236 @@ +package org.orienteer.vuecket.extensions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import org.apache.wicket.markup.ComponentTag; +import org.apache.wicket.markup.head.CssHeaderItem; +import org.apache.wicket.markup.head.IHeaderResponse; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.util.io.IClusterable; +import org.orienteer.vuecket.DataFiber; +import org.orienteer.vuecket.VueComponent; +import org.orienteer.vuecket.VueSettings; +import org.orienteer.vuecket.descriptor.VueNpm; +import org.orienteer.vuecket.method.IVuecketMethod; +import org.orienteer.vuecket.method.VueOn; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; +import lombok.Getter; +import lombok.experimental.Accessors; +import lombok.extern.slf4j.Slf4j; + +/** + * Vuecket component to integrate with vue-advanced-chat + * https://github.com/antoine92190/vue-advanced-chat + */ +@VueNpm(packageName = "vue-advanced-chat", path = "dist/vue-advanced-chat.umd.js", + enablement = "Vue.component('ChatWindow', window['vue-advanced-chat'].default)") +@Slf4j +public abstract class VueAdvancedChat extends VueComponent { + + /** + * Data class to provide information about Room + */ + @Data + @Accessors(chain = true) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Room implements IClusterable { + private Object roomId; + private String roomName; + private List users; + private List messages = new ArrayList(); + private ChatMessage lastMessage; + private String avatar; + + public Room addMessage(ChatMessage message) { + messages.add(message); + return this; + } + } + + /** + * Data class to provide information about available Actions + */ + @Data + @Accessors(chain = true) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Action implements IClusterable { + private String name; + private String title; + } + + /** + * Data class to provide information about Message which has been sent + */ + @Data + @Accessors(chain = true) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class SendMessage implements IClusterable { + private Object roomId; + private String content; + } + + /** + * Data class to provide information about a User in chat room + */ + @Data + @Accessors(chain = true) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class ChatUser implements IClusterable { + @JsonProperty("_id") + private String id; + private String username; + private ChatUserStatus status = new ChatUserStatus(); + } + + /** + * Data class to provide information about status of a User in chat room + */ + @Data + @Accessors(chain = true) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class ChatUserStatus implements IClusterable { + private String state = "online"; + private Date lastChanged; + } + + /** + * Data class to provide information about a message in a chat room + */ + @Data + @Accessors(chain = true) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class ChatMessage implements IClusterable { + @Getter(onMethod=@__({@JsonProperty("_id")})) + private Object id; + private String content; + private Object senderId; + private String username; + private boolean system=false; + private String date; + private String timestamp; + private Boolean saved; + private Boolean distributed; + private Boolean seen; + } + + /** + * Data class to reflect event to fetch messages from a room + */ + @Data + @Accessors(chain = true) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class FetchMessages implements IClusterable { + private Room room; + private Object options; + } + + /** + * Data class to reflect event out of action selected by user to be executed + */ + @Data + @Accessors(chain = true) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class ActionHandlerEvent implements IClusterable { + private Object roomId; + private Action action; + } + + protected IModel roomsLoaded = Model.of(true); + protected IModel messagesLoaded = Model.of(true); + protected IModel> roomActions = Model.ofList(new ArrayList()); + protected IModel> menuActions = Model.ofList(new ArrayList()); + + public VueAdvancedChat(String id, IModel> rooms, IModel> messages) { + + super(id); + dataFiberBuilder(rooms, "rooms").bindToProperty(new ArrayList()).init().update().bind(); + dataFiberBuilder(messages, "messages") + .bindToProperty(getDefaultMessages()).init().update().bind(); + dataFiberBuilder(messagesLoaded, "messages-loaded").bindToProperty(false).bind(); + dataFiberBuilder(roomsLoaded, "rooms-loaded").bindToProperty(false).init().bind(); + dataFiberBuilder(roomActions, "room-actions").bindToProperty().init().bind(); + dataFiberBuilder(roomActions, "menu-actions").bindToProperty().init().bind(); + } + + private static IModel> getDefaultMessages() { + return Model.ofList(Arrays.asList(new ChatMessage() + .setId("-1") + .setSystem(true) + .setContent("Chat") + .setSenderId("System") + )); + } + + @Override + public void renderHead(IHeaderResponse response) { + super.renderHead(response); + response.render(CssHeaderItem.forReference( + VueSettings.get().getNpmPackageProvider() + .provide("vue-advanced-chat", "dist/vue-advanced-chat.css"))); + } + + @Override + protected void onComponentTag(ComponentTag tag) { + super.onComponentTag(tag); + tag.setName("chat-window"); + tag.put("current-user-id", getCurrentUserId()); +// tag.put("room-id", DAO.asDocument(getModelObject()).getIdentity().toString()); + tag.put(":show-add-room", false); + tag.put(":show-audio", false); + List rooms = ((DataFiber>)getDataFibers().getDataFiberByName("rooms").get()).getValueInternal(); + tag.put(":single-room", rooms==null || rooms.size()<=1); + } + + public VueAdvancedChat addRoomAction(String key) { + roomActions.getObject().add(new Action().setName(key).setTitle(getLocalizer().getString("room.action."+key, this))); + return this; + } + + public VueAdvancedChat addRoomActions(String... keys) { + for (String key : keys) { + addRoomAction(key); + } + return this; + } + + public VueAdvancedChat addMenuAction(String key) { + menuActions.getObject().add(new Action().setName(key).setTitle(getLocalizer().getString("menu.action."+key, this))); + return this; + } + + public VueAdvancedChat addMenuActions(String... keys) { + for (String key : keys) { + addMenuAction(key); + } + return this; + } + + public abstract String getCurrentUserId(); + + @VueOn("send-message") + public abstract void onSendMessage(IVuecketMethod.Context ctx, SendMessage message); + + @VueOn("fetch-messages") + public void onFetchMessage(IVuecketMethod.Context ctx, FetchMessages fetchMessages) { + log.info("Fetch: "+ctx); + } + + @VueOn("room-action-handler") + public void onRoomActionHandler(IVuecketMethod.Context ctx, ActionHandlerEvent event) { + log.info("onRoomActionHandler: "+ctx); + } + + @VueOn("menu-action-handler") + public void onMenuActionHandler(IVuecketMethod.Context ctx, ActionHandlerEvent event) { + log.info("onMenuActionHandler: "+ctx); + } + +} diff --git a/vuecket-demo/src/main/java/org/orienteer/vuecket/demo/VueMarkdown.java b/vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/VueMarkdown.java similarity index 92% rename from vuecket-demo/src/main/java/org/orienteer/vuecket/demo/VueMarkdown.java rename to vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/VueMarkdown.java index 61edc04..e824449 100644 --- a/vuecket-demo/src/main/java/org/orienteer/vuecket/demo/VueMarkdown.java +++ b/vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/VueMarkdown.java @@ -1,4 +1,4 @@ -package org.orienteer.vuecket.demo; +package org.orienteer.vuecket.extensions; import java.io.Serializable; diff --git a/vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/package-info.java b/vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/package-info.java new file mode 100644 index 0000000..37527e6 --- /dev/null +++ b/vuecket-extensions/src/main/java/org/orienteer/vuecket/extensions/package-info.java @@ -0,0 +1,4 @@ +/** + * Package pre-integrated VueJs libraries into Wicket by Vuecket + */ +package org.orienteer.vuecket.extensions; \ No newline at end of file diff --git a/vuecket-extensions/src/test/java/org/orienteer/vuecket/extensions/AppTest.java b/vuecket-extensions/src/test/java/org/orienteer/vuecket/extensions/AppTest.java new file mode 100644 index 0000000..765c28e --- /dev/null +++ b/vuecket-extensions/src/test/java/org/orienteer/vuecket/extensions/AppTest.java @@ -0,0 +1,38 @@ +package org.orienteer.vuecket.extensions; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/vuecket/pom.xml b/vuecket/pom.xml index f470a34..a74ef79 100644 --- a/vuecket/pom.xml +++ b/vuecket/pom.xml @@ -40,12 +40,10 @@ org.eclipse.jetty jetty-webapp test - ${jetty9.version} org.eclipse.jetty jetty-jmx - ${jetty9.version} test