initial commit
commit
c09e4fead9
@ -0,0 +1,4 @@
|
|||||||
|
Roundcube Webmail Swipe
|
||||||
|
=======================
|
||||||
|
|
||||||
|
* Created plugin
|
@ -0,0 +1,66 @@
|
|||||||
|
Roundcube Webmail Swipe
|
||||||
|
=======================
|
||||||
|
This plugin adds left/right/down swipe actions to entries in the the message
|
||||||
|
list on touch devices (tables/phones) with browsers that support touch events.
|
||||||
|
|
||||||
|
ATTENTION
|
||||||
|
---------
|
||||||
|
This is just a snapshot from the GIT repository and is **NOT A STABLE version
|
||||||
|
of Swipe**. It is Intended for use with the **GIT-master** version of
|
||||||
|
Roundcube and it may not be compatible with older versions. Stable versions of
|
||||||
|
Swipe are available from the [Roundcube plugin repository][rcplugrepo]
|
||||||
|
(for 1.4 and above) or the [releases section][releases] of the GitHub
|
||||||
|
repository.
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
This plugin is released under the [GNU General Public License Version 3+][gpl].
|
||||||
|
|
||||||
|
Even if skins might contain some programming work, they are not considered
|
||||||
|
as a linked part of the plugin and therefore skins DO NOT fall under the
|
||||||
|
provisions of the GPL license. See the README file located in the core skins
|
||||||
|
folder for details on the skin license.
|
||||||
|
|
||||||
|
Install
|
||||||
|
-------
|
||||||
|
* Place this plugin folder into plugins directory of Roundcube
|
||||||
|
* Add swipe to $config['plugins'] in your Roundcube config
|
||||||
|
|
||||||
|
**NB:** When downloading the plugin from GitHub you will need to create a
|
||||||
|
directory called skin and place the files in there, ignoring the root
|
||||||
|
directory in the downloaded archive.
|
||||||
|
|
||||||
|
Supported skins
|
||||||
|
---------------
|
||||||
|
* Elastic
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
To set the default actions add `$config['swipe_left']`, `$config['swipe_right']`
|
||||||
|
and `$config['swipe_down']` to your Roundcube config file. For example:
|
||||||
|
`$config['swipe_left'] = 'delete';`. Users can configure the actions, overriding
|
||||||
|
the defaults, from the List options menu.
|
||||||
|
|
||||||
|
Supported actions
|
||||||
|
-----------------
|
||||||
|
The following actions are available for left/right swipe:
|
||||||
|
|
||||||
|
* `archive` - Archive the message (Requires the Roundcube Archive plugin)
|
||||||
|
* `delete` - Delete the message
|
||||||
|
* `flagged` - Mark the message as flagged/unflagged
|
||||||
|
* `forward` - Forward the message
|
||||||
|
* `move` - Move the message to a chosen folder
|
||||||
|
* `read` - Mark the message as read/unread
|
||||||
|
* `reply` - Reply to the message
|
||||||
|
* `replyall` - Reply all to the message
|
||||||
|
* `select` - Select/deselect the message
|
||||||
|
* `none` - Swipe disabled
|
||||||
|
|
||||||
|
The following actions are available for down swipe:
|
||||||
|
|
||||||
|
* `checkmail` - Check for new messages in the current folder
|
||||||
|
* `none` - Swipe disabled
|
||||||
|
|
||||||
|
[rcplugrepo]: https://plugins.roundcube.net/packages/johndoh/swipe
|
||||||
|
[releases]: https://github.com/johndoh/roundcube-swipe/releases
|
||||||
|
[gpl]: https://www.gnu.org/licenses/gpl.html
|
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "johndoh/swipe",
|
||||||
|
"description": "Adds swipe actions to the message list of Roundcube",
|
||||||
|
"keywords": ["swipe","gesture"],
|
||||||
|
"homepage": "https://github.com/johndoh/roundcube-swipe/",
|
||||||
|
"license": "GPL-3.0",
|
||||||
|
"type": "roundcube-plugin",
|
||||||
|
"version": "0.1-git",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Philip Weir",
|
||||||
|
"email": "roundcube@tehinterweb.co.uk",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "composer",
|
||||||
|
"url": "https://plugins.roundcube.net"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.2.1",
|
||||||
|
"roundcube/plugin-installer": ">=0.1.2"
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"roundcube": {
|
||||||
|
"min-version": "1.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
/* Author: Philip Weir */
|
||||||
|
|
||||||
|
$labels = array();
|
||||||
|
$labels['swipeactions'] = 'Swipe actions';
|
||||||
|
$labels['swipeleft'] = 'Swipe left';
|
||||||
|
$labels['swiperight'] = 'Swipe right';
|
||||||
|
$labels['swipedown'] = 'Swipe down';
|
||||||
|
$labels['markasread'] = 'Mark as read';
|
||||||
|
$labels['markasunread'] = 'Mark as unread';
|
||||||
|
$labels['markasflagged'] = 'Mark as flagged';
|
||||||
|
$labels['markasunflagged'] = 'Mark as unflagged';
|
||||||
|
$labels['deselect'] = 'Deselect';
|
||||||
|
|
||||||
|
$messages = array();
|
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
/* Author: Philip Weir */
|
||||||
|
|
||||||
|
$labels = array();
|
||||||
|
$labels['swipeactions'] = 'Swipe actions';
|
||||||
|
$labels['swipeleft'] = 'Swipe left';
|
||||||
|
$labels['swiperight'] = 'Swipe right';
|
||||||
|
$labels['swipedown'] = 'Swipe down';
|
||||||
|
$labels['markasread'] = 'Mark as read';
|
||||||
|
$labels['markasunread'] = 'Mark as unread';
|
||||||
|
$labels['markasflagged'] = 'Mark as flagged';
|
||||||
|
$labels['markasunflagged'] = 'Mark as unflagged';
|
||||||
|
$labels['deselect'] = 'Deselect';
|
||||||
|
|
||||||
|
$messages = array();
|
@ -0,0 +1,54 @@
|
|||||||
|
<div id="swipeoptions-menu" class="popupmenu propform" role="dialog" aria-labelledby="aria-label-listoptions" data-options-menuname="messagelistmenu" data-options-menuid="listoptions-menu">
|
||||||
|
<fieldset class="swipe">
|
||||||
|
<legend><roundcube:label name="swipe.swipeactions" /></legend>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="swipeoptions-left" class="col-form-label col-sm-4"><roundcube:label name="swipe.swipeleft" /></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select id="swipeoptions-left" name="swipe_left" class="form-control">
|
||||||
|
<option value="none"><roundcube:label name="none" /></option>
|
||||||
|
<option value="read"><roundcube:label name="swipe.markasread" /></option>
|
||||||
|
<option value="flagged"><roundcube:label name="swipe.markasflagged" /></option>
|
||||||
|
<option value="delete"><roundcube:label name="delete" /></option>
|
||||||
|
<option value="forward"><roundcube:label name="forward" /></option>
|
||||||
|
<option value="reply"><roundcube:label name="reply" /></option>
|
||||||
|
<option value="replyall"><roundcube:label name="replyall" /></option>
|
||||||
|
<option value="move"><roundcube:label name="moveto" /></option>
|
||||||
|
<option value="select"><roundcube:label name="select" /></option>
|
||||||
|
<roundcube:if condition="env:archive_folder" />
|
||||||
|
<option value="archive"><roundcube:label name="archive.buttontext" /></option>
|
||||||
|
<roundcube:endif />
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="swipeoptions-right" class="col-form-label col-sm-4"><roundcube:label name="swipe.swiperight" /></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select id="swipeoptions-right" name="swipe_right" class="form-control">
|
||||||
|
<option value="none"><roundcube:label name="none" /></option>
|
||||||
|
<option value="read"><roundcube:label name="swipe.markasread" /></option>
|
||||||
|
<option value="flagged"><roundcube:label name="swipe.markasflagged" /></option>
|
||||||
|
<option value="delete"><roundcube:label name="delete" /></option>
|
||||||
|
<option value="forward"><roundcube:label name="forward" /></option>
|
||||||
|
<option value="reply"><roundcube:label name="reply" /></option>
|
||||||
|
<option value="replyall"><roundcube:label name="replyall" /></option>
|
||||||
|
<option value="move"><roundcube:label name="moveto" /></option>
|
||||||
|
<option value="select"><roundcube:label name="select" /></option>
|
||||||
|
<roundcube:if condition="env:archive_folder" />
|
||||||
|
<option value="archive"><roundcube:label name="archive.buttontext" /></option>
|
||||||
|
<roundcube:endif />
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="swipeoptions-down" class="col-form-label col-sm-4"><roundcube:label name="swipe.swipedown" /></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select id="swipeoptions-down" name="swipe_down" class="form-control">
|
||||||
|
<option value="none"><roundcube:label name="none" /></option>
|
||||||
|
<option value="checkmail"><roundcube:label name="checkmail" /></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
@ -0,0 +1,141 @@
|
|||||||
|
/**
|
||||||
|
* Swipe plugin styles
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import (reference) "../../../../skins/elastic/styles/variables";
|
||||||
|
@import (reference) "../../../../skins/elastic/styles/mixins";
|
||||||
|
|
||||||
|
#swipe-action {
|
||||||
|
position: absolute;
|
||||||
|
background-color: @color-black-shade-bg;
|
||||||
|
color: @color-black;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-collapse: collapse;
|
||||||
|
|
||||||
|
&.checkmail,
|
||||||
|
&.select,
|
||||||
|
&.deselect {
|
||||||
|
background-color: @color-btn-secondary-background;
|
||||||
|
color: @color-btn-secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.delete {
|
||||||
|
background-color: @color-btn-danger-background;
|
||||||
|
color: @color-btn-danger;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.flagged,
|
||||||
|
&.unflagged,
|
||||||
|
&.read,
|
||||||
|
&.unread {
|
||||||
|
background-color: @color-btn-primary-background;
|
||||||
|
color: @color-btn-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.forward,
|
||||||
|
&.reply,
|
||||||
|
&.replyall {
|
||||||
|
background-color: @color-success;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.move,
|
||||||
|
&.archive {
|
||||||
|
background-color: @color-warning;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
&.left {
|
||||||
|
position: absolute;
|
||||||
|
right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.down {
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
> span::before {
|
||||||
|
width: auto;
|
||||||
|
float: none;
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 0.2em;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> span {
|
||||||
|
line-height: 100%;
|
||||||
|
font-size: 1.2em;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
.font-icon-class;
|
||||||
|
padding: 0 1.25em 0 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.checkmail::before {
|
||||||
|
content: @fa-var-sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.delete::before {
|
||||||
|
content: @fa-var-trash-alt;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.flagged::before {
|
||||||
|
content: @fa-var-flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.forward::before {
|
||||||
|
content: @fa-var-share;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.unflagged::before {
|
||||||
|
.font-icon-regular(@fa-var-flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.move::before {
|
||||||
|
content: @fa-var-arrows-alt;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.read::before {
|
||||||
|
.font-icon-regular(@fa-var-star);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.unread::before {
|
||||||
|
content: @fa-var-star;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.reply::before {
|
||||||
|
content: @fa-var-reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.replyall::before {
|
||||||
|
content: @fa-var-reply-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.select::before {
|
||||||
|
.font-icon-regular(@fa-var-check-square);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.deselect::before {
|
||||||
|
.font-icon-regular(@fa-var-square);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.archive::before {
|
||||||
|
content: @fa-var-archive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.swipe-active {
|
||||||
|
background-color:@color-layout-list-background;
|
||||||
|
}
|
||||||
|
|
||||||
|
#messagelist.swipe-active {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swipe-noscroll {
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
#swipe-action{position:absolute;background-color:#f1f3f4;color:#161b1d;display:flex;align-items:center;border-collapse:collapse}#swipe-action.checkmail,#swipe-action.select,#swipe-action.deselect{background-color:#8b9fa7;color:#fff}#swipe-action.delete{background-color:#ff5552;color:#fff}#swipe-action.flagged,#swipe-action.unflagged,#swipe-action.read,#swipe-action.unread{background-color:#37beff;color:#fff}#swipe-action.forward,#swipe-action.reply,#swipe-action.replyall{background-color:#41b849;color:#fff}#swipe-action.move,#swipe-action.archive{background-color:#ffd452;color:#fff}#swipe-action>div.left{position:absolute;right:.5em}#swipe-action>div.down{margin:0 auto}#swipe-action>div.down>span::before{width:auto;float:none;margin:0;margin-bottom:.2em;padding:0}#swipe-action>div>span{line-height:100%;font-size:1.2em}#swipe-action>div>span::before{font-size:1.25em;display:block;float:left;margin:0 .25rem 0 0;width:1.18em;height:1em;font-family:'Icons';font-style:normal;font-weight:900;text-decoration:inherit;text-align:center;speak:none;font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;padding:0 1.25em 0 .5em}#swipe-action>div>span.checkmail::before{content:"\f021"}#swipe-action>div>span.delete::before{content:"\f2ed"}#swipe-action>div>span.flagged::before{content:"\f024"}#swipe-action>div>span.forward::before{content:"\f064"}#swipe-action>div>span.unflagged::before{content:"\f024";font-weight:400}#swipe-action>div>span.move::before{content:"\f0b2"}#swipe-action>div>span.read::before{content:"\f005";font-weight:400}#swipe-action>div>span.unread::before{content:"\f005"}#swipe-action>div>span.reply::before{content:"\f3e5"}#swipe-action>div>span.replyall::before{content:"\f122"}#swipe-action>div>span.select::before{content:"\f14a";font-weight:400}#swipe-action>div>span.deselect::before{content:"\f0c8";font-weight:400}#swipe-action>div>span.archive::before{content:"\f187"}.swipe-active{background-color:#fff}#messagelist.swipe-active{height:100%}.swipe-noscroll{overflow:hidden !important}
|
@ -0,0 +1,419 @@
|
|||||||
|
/**
|
||||||
|
* Swipe plugin script
|
||||||
|
*
|
||||||
|
* @licstart The following is the entire license notice for the
|
||||||
|
* JavaScript code in this file.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Philip Weir
|
||||||
|
*
|
||||||
|
* The JavaScript code in this page is free software: you can redistribute it
|
||||||
|
* and/or modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @licend The above is the entire license notice
|
||||||
|
* for the JavaScript code in this file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
rcube_webmail.prototype.swipe_position_target = function(obj, pos, vertical) {
|
||||||
|
var translate = '';
|
||||||
|
|
||||||
|
if (pos)
|
||||||
|
translate = (vertical ? 'translatey' : 'translatex') + '('+ pos +'px)';
|
||||||
|
|
||||||
|
$(obj).css({
|
||||||
|
'-webkit-transform': translate,
|
||||||
|
'-ms-transform': translate,
|
||||||
|
'transform': translate
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
rcube_webmail.prototype.swipe_list_selection = function(uid, show, prev_sel) {
|
||||||
|
// make the system think no preview pane exists while we do some fake message selects
|
||||||
|
// to enable/disable relevant commands for current selection
|
||||||
|
var prev_contentframe = rcmail.env.contentframe, i;
|
||||||
|
rcmail.env.contentframe = null;
|
||||||
|
|
||||||
|
if (show) {
|
||||||
|
if (rcmail.message_list.selection.length == 0 || !rcmail.message_list.in_selection(uid)) {
|
||||||
|
prev_sel = prev_sel ? prev_sel : rcmail.message_list.get_selection();
|
||||||
|
rcmail.message_list.clear_selection();
|
||||||
|
rcmail.message_list.highlight_row(uid, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (prev_sel) {
|
||||||
|
rcmail.message_list.clear_selection();
|
||||||
|
|
||||||
|
for (i in prev_sel)
|
||||||
|
rcmail[this.list_object].highlight_row(prev_sel[i], true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rcmail.message_list.clear_selection();
|
||||||
|
}
|
||||||
|
|
||||||
|
rcmail.env.contentframe = prev_contentframe;
|
||||||
|
|
||||||
|
return prev_sel;
|
||||||
|
};
|
||||||
|
|
||||||
|
rcube_webmail.prototype.swipe_select_action = function(direction, obj) {
|
||||||
|
var action = {
|
||||||
|
'class': '',
|
||||||
|
'text': '',
|
||||||
|
'callback': null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (rcmail.env.swipe_actions[direction] == 'checkmail') {
|
||||||
|
action.class = 'checkmail';
|
||||||
|
action.text = 'refresh';
|
||||||
|
action.callback = function(uid, obj, e) { rcmail.command('checkmail'); };
|
||||||
|
}
|
||||||
|
else if (rcmail.env.swipe_actions[direction] == 'delete') {
|
||||||
|
action.class = 'delete';
|
||||||
|
action.text = 'delete';
|
||||||
|
action.callback = function(uid, obj, e) {
|
||||||
|
if (!uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var prev_sel = rcmail.swipe_list_selection(uid, true);
|
||||||
|
|
||||||
|
// enable command
|
||||||
|
var prev_command = rcmail.commands['delete'];
|
||||||
|
rcmail.enable_command('delete', true);
|
||||||
|
var result = rcmail.command('delete', '', obj, e);
|
||||||
|
|
||||||
|
rcmail.enable_command('delete', prev_command);
|
||||||
|
rcmail.swipe_list_selection(uid, false, prev_sel);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (rcmail.env.swipe_actions[direction] == 'flagged') {
|
||||||
|
if (obj.hasClass('flagged')) {
|
||||||
|
action.class = 'unflagged';
|
||||||
|
action.text = 'swipe.markasunflagged';
|
||||||
|
action.callback = function(uid, obj, e) {
|
||||||
|
if (!uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rcmail.mark_message('unflagged', uid);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
action.class = 'flagged';
|
||||||
|
action.text = 'swipe.markasflagged';
|
||||||
|
action.callback = function(uid, obj, e) {
|
||||||
|
if (!uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rcmail.mark_message('flagged', uid);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rcmail.env.swipe_actions[direction] == 'forward') {
|
||||||
|
action.class = 'forward';
|
||||||
|
action.text = 'forward';
|
||||||
|
action.callback = function(uid, obj, e) {
|
||||||
|
if (!uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rcmail.enable_command('forward', true);
|
||||||
|
rcmail.env.uid = uid;
|
||||||
|
rcmail.command('forward', '', obj, e);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (rcmail.env.swipe_actions[direction] == 'move') {
|
||||||
|
action.class = 'move';
|
||||||
|
action.text = 'moveto';
|
||||||
|
action.callback = function(uid, obj, e) {
|
||||||
|
if (!uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rcmail.enable_command('move', true);
|
||||||
|
$('#' + rcmail.buttons['move'][0].id).click();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (rcmail.env.swipe_actions[direction] == 'read') {
|
||||||
|
if (obj.hasClass('unread')) {
|
||||||
|
action.class = 'read';
|
||||||
|
action.text = 'swipe.markasread';
|
||||||
|
action.callback = function(uid, obj, e) {
|
||||||
|
if (!uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rcmail.mark_message('read', uid);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
action.class = 'unread';
|
||||||
|
action.text = 'swipe.markasunread';
|
||||||
|
action.callback = function(uid, obj, e) {
|
||||||
|
if (!uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rcmail.mark_message('unread', uid);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rcmail.env.swipe_actions[direction] == 'reply') {
|
||||||
|
action.class = 'reply';
|
||||||
|
action.text = 'reply';
|
||||||
|
action.callback = function(uid, obj, e) {
|
||||||
|
if (!uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rcmail.enable_command('reply', true);
|
||||||
|
rcmail.env.uid = uid;
|
||||||
|
rcmail.command('reply', '', obj, e);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (rcmail.env.swipe_actions[direction] == 'replyall') {
|
||||||
|
action.class = 'replyall';
|
||||||
|
action.text = 'replyall';
|
||||||
|
action.callback = function(uid, obj, e) {
|
||||||
|
if (!uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rcmail.enable_command('reply-all', true);
|
||||||
|
rcmail.env.uid = uid;
|
||||||
|
rcmail.command('reply-all', '', obj, e);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (rcmail.env.swipe_actions[direction] == 'select') {
|
||||||
|
if (obj.hasClass('selected')) {
|
||||||
|
action.class = 'deselect';
|
||||||
|
action.text = 'swipe.deselect';
|
||||||
|
action.callback = function(uid, obj, e) {
|
||||||
|
if (!uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rcmail.message_list.highlight_row(uid, true);
|
||||||
|
|
||||||
|
if (rcmail.message_list.get_selection().length == 0)
|
||||||
|
$(rcmail.gui_objects.messagelist).removeClass('withselection');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
action.class = 'select';
|
||||||
|
action.text = 'select';
|
||||||
|
action.callback = function(uid, obj, e) {
|
||||||
|
if (!uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$(rcmail.gui_objects.messagelist).addClass('withselection');
|
||||||
|
rcmail.message_list.highlight_row(uid, true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rcmail.env.swipe_actions[direction] == 'archive' && rcmail.env.archive_folder) {
|
||||||
|
action.class = 'archive';
|
||||||
|
action.text = 'archive.buttontext';
|
||||||
|
action.callback = function(uid, obj, e) {
|
||||||
|
if (!uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var prev_sel = rcmail.swipe_list_selection(uid, true);
|
||||||
|
|
||||||
|
// enable command
|
||||||
|
var prev_command = rcmail.commands['plugin.archive'];
|
||||||
|
rcmail.enable_command('plugin.archive', true);
|
||||||
|
var result = rcmail.command('plugin.archive', '', obj, e);
|
||||||
|
|
||||||
|
rcmail.enable_command('plugin.archive', prev_command);
|
||||||
|
rcmail.swipe_list_selection(uid, false, prev_sel);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return action;
|
||||||
|
};
|
||||||
|
|
||||||
|
rcube_webmail.prototype.swipe_event = function(opts) {
|
||||||
|
var touchstart = {};
|
||||||
|
|
||||||
|
// swipe down on message list container
|
||||||
|
opts.source_obj
|
||||||
|
.on('touchstart', function(e) {
|
||||||
|
touchstart.x = e.originalEvent.targetTouches[0].pageX;
|
||||||
|
touchstart.y = e.originalEvent.targetTouches[0].pageY;
|
||||||
|
})
|
||||||
|
.on('touchmove', function(e) {
|
||||||
|
// make sure no other swipes are active
|
||||||
|
if (rcmail.env.swipe_active && rcmail.env.swipe_active != opts.axis)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var changeX = e.originalEvent.targetTouches[0].pageX - touchstart.x;
|
||||||
|
var changeY = e.originalEvent.targetTouches[0].pageY - touchstart.y;
|
||||||
|
|
||||||
|
// stop the message row from sliding off the screen completely
|
||||||
|
if (opts.axis == 'vertical') {
|
||||||
|
changeY = Math.min(opts.maxmove, changeY);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
changeX = changeX < 0 ? Math.max(opts.maxmove * -1, changeX) : Math.min(opts.maxmove, changeX);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((opts.axis == 'vertical' && (((changeX < 5 && changeX > -5) && opts.source_obj.scrollTop() == 0) || opts.target_obj.hasClass('swipe-active'))) ||
|
||||||
|
((opts.axis == 'horizontal' && ((changeY < 5 && changeY > -5) || opts.target_obj.hasClass('swipe-active'))))) {
|
||||||
|
// do not allow swipe up
|
||||||
|
if (opts.axis == 'vertical' && changeY < 0)
|
||||||
|
return
|
||||||
|
|
||||||
|
var direction = (opts.axis == 'vertical' ? 'down' : (changeX < 0 ? 'left' : 'right'));
|
||||||
|
var action = rcmail.swipe_select_action(direction, opts.source_obj);
|
||||||
|
|
||||||
|
// skip if there is no event
|
||||||
|
if (!action.callback)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$('#swipe-action')
|
||||||
|
.data('callback', action.callback)
|
||||||
|
.children('div')
|
||||||
|
.removeClass()
|
||||||
|
.addClass(direction)
|
||||||
|
.children('span')
|
||||||
|
.removeClass()
|
||||||
|
.addClass(action.class)
|
||||||
|
.text(rcmail.gettext(action.text));
|
||||||
|
|
||||||
|
if (!opts.target_obj.hasClass('swipe-active')) {
|
||||||
|
var action_style = opts.action_sytle(opts.target_obj);
|
||||||
|
$('#swipe-action').css({
|
||||||
|
'top': action_style.top,
|
||||||
|
'left': action_style.left,
|
||||||
|
'width': action_style.width,
|
||||||
|
'height': action_style.height
|
||||||
|
}).show();
|
||||||
|
opts.target_obj.addClass('swipe-active');
|
||||||
|
opts.source_obj.addClass('swipe-noscroll');
|
||||||
|
rcmail.env.swipe_active = opts.axis; // set the active swipe
|
||||||
|
}
|
||||||
|
|
||||||
|
// the user must swipe a certain about before the action is activated, try to prevent accidental actions
|
||||||
|
if ((opts.axis == 'vertical' && changeY > opts.minmove) ||
|
||||||
|
(opts.axis == 'horizontal' && (changeX < (opts.minmove * -1) || changeX > opts.minmove))) {
|
||||||
|
$('#swipe-action').addClass(action.class);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// reset the swipe if the user takes the row back to the start
|
||||||
|
$('#swipe-action').removeClass();
|
||||||
|
$('#swipe-action').data('callback', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
rcmail.swipe_position_target(opts.target_obj, opts.axis == 'vertical' ? changeY : changeX, opts.axis == 'vertical');
|
||||||
|
|
||||||
|
if (opts.parent_obj)
|
||||||
|
opts.parent_obj.on('touchmove', rcube_event.cancel);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on('touchend', function(e) {
|
||||||
|
if (rcmail.env.swipe_active && rcmail.env.swipe_active == opts.axis && opts.target_obj.hasClass('swipe-active')) {
|
||||||
|
rcmail.swipe_position_target(opts.target_obj, 0, opts.axis == 'vertical');
|
||||||
|
|
||||||
|
var callback = null;
|
||||||
|
if (callback = $('#swipe-action').data('callback'))
|
||||||
|
callback(opts.uid, opts.target_obj, e);
|
||||||
|
|
||||||
|
$('#swipe-action').removeClass().hide();
|
||||||
|
opts.target_obj.removeClass('swipe-active');
|
||||||
|
opts.source_obj.removeClass('swipe-noscroll');
|
||||||
|
rcmail.env.swipe_active = null;
|
||||||
|
|
||||||
|
if (opts.parent_obj)
|
||||||
|
opts.parent_obj.off('touchmove', rcube_event.cancel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
if (window.rcmail && bw.touch && !((bw.ie || bw.edge) && bw.pointer)) {
|
||||||
|
rcmail.addEventListener('init', function() {
|
||||||
|
var messagelist_container = $(rcmail.gui_objects.messagelist).parent();
|
||||||
|
if (rcmail.message_list.draggable || !messagelist_container[0].addEventListener)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rcmail.env.swipe_parent = messagelist_container;
|
||||||
|
rcmail.env.swipe_parent.prepend($('<div>').attr('id', 'swipe-action').html($('<div>').append($('<span>'))).hide());
|
||||||
|
|
||||||
|
// down swipe on message list container
|
||||||
|
var swipe_config = {
|
||||||
|
'source_obj': rcmail.env.swipe_parent,
|
||||||
|
'axis': 'vertical',
|
||||||
|
'minmove': $(window).height() * 0.1,
|
||||||
|
'maxmove': $(window).height() * 0.2,
|
||||||
|
'action_sytle': function(o) {
|
||||||
|
return {
|
||||||
|
'top': o.children('tbody').position().top,
|
||||||
|
'left': o.children('tbody').position().left,
|
||||||
|
'width': o.children('tbody').width() + 'px',
|
||||||
|
'height': $(window).height() * 0.2 + 'px'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
'target_obj': $(rcmail.gui_objects.messagelist),
|
||||||
|
'uid': null,
|
||||||
|
'parent_obj': rcmail.env.swipe_parent.parent()
|
||||||
|
};
|
||||||
|
|
||||||
|
rcmail.swipe_event(swipe_config);
|
||||||
|
});
|
||||||
|
|
||||||
|
// right/left swipe on message list
|
||||||
|
rcmail.addEventListener('insertrow', function(props) {
|
||||||
|
if (rcmail.message_list.draggable || !$('#' + props.row.id)[0].addEventListener)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var swipe_config = {
|
||||||
|
'source_obj': $('#' + props.row.id),
|
||||||
|
'axis': 'horizontal',
|
||||||
|
'minmove': $('#' + props.row.id).width() * 0.25,
|
||||||
|
'maxmove': $('#' + props.row.id).width() * 0.6,
|
||||||
|
'action_sytle': function(o) {
|
||||||
|
return {
|
||||||
|
'top': o.position().top,
|
||||||
|
'left': o.position().left,
|
||||||
|
'width': o.width() + 'px',
|
||||||
|
'height': (o.height() - 2) + 'px' // subtract the border
|
||||||
|
};
|
||||||
|
},
|
||||||
|
'target_obj': $('#' + props.row.id),
|
||||||
|
'uid': props.uid,
|
||||||
|
'parent_obj': rcmail.env.swipe_parent
|
||||||
|
};
|
||||||
|
|
||||||
|
rcmail.swipe_event(swipe_config);
|
||||||
|
});
|
||||||
|
|
||||||
|
// add swipe options to list options menu
|
||||||
|
rcmail.addEventListener('menu-open', function(p) {
|
||||||
|
if (p.name == $('#swipeoptions-menu').data('options-menuname')) {
|
||||||
|
if (!rcmail.message_list.draggable) {
|
||||||
|
// set form values
|
||||||
|
$.each(['left', 'right', 'down'], function() {
|
||||||
|
$('select[name="swipe_' + this + '"]:visible').val(rcmail.env.swipe_actions[this]);
|
||||||
|
});
|
||||||
|
$('fieldset.swipe').show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('fieldset.swipe').hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// save swipe options
|
||||||
|
rcmail.set_list_options_core = rcmail.set_list_options;
|
||||||
|
rcmail.set_list_options = function(cols, sort_col, sort_order, threads, layout)
|
||||||
|
{
|
||||||
|
var post = {};
|
||||||
|
$.each(['left', 'right', 'down'], function() {
|
||||||
|
if ($('select[name="swipe_' + this + '"]:visible').val() != rcmail.env.swipe_actions[this]) {
|
||||||
|
rcmail.env.swipe_actions[this] = $('select[name="swipe_' + this + '"]:visible').val();
|
||||||
|
post['swipe_' + this] = rcmail.env.swipe_actions[this];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!$.isEmptyObject(post))
|
||||||
|
rcmail.http_post('plugin.swipe.save_settings', post);
|
||||||
|
|
||||||
|
rcmail.set_list_options_core(cols, sort_col, sort_order, threads, layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#swipeoptions-menu > fieldset').appendTo('#' + $('#swipeoptions-menu').data('options-menuid'));
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swipe
|
||||||
|
*
|
||||||
|
* Pplugin to add swipe actions to the mesasge list on touch devices
|
||||||
|
*
|
||||||
|
* @author Philip Weir
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Philip Weir
|
||||||
|
*
|
||||||
|
* This program is a Roundcube (https://roundcube.net) plugin.
|
||||||
|
* For more information see README.md.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Roundcube. If not, see https://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
class swipe extends rcube_plugin
|
||||||
|
{
|
||||||
|
public $task = 'mail';
|
||||||
|
private $menu_file = '';
|
||||||
|
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$rcmail = rcube::get_instance();
|
||||||
|
$this->menu_file = '/' . $this->local_skin_path() . '/menu.html';
|
||||||
|
|
||||||
|
if (is_file(slashify($this->home) . $this->menu_file)) {
|
||||||
|
if ($rcmail->output->type == 'html') {
|
||||||
|
$this->api->output->set_env('swipe_actions', array(
|
||||||
|
'left' => $rcmail->config->get('swipe_left', 'none'),
|
||||||
|
'right' => $rcmail->config->get('swipe_right', 'none'),
|
||||||
|
'down' => $rcmail->config->get('swipe_down', 'none')
|
||||||
|
));
|
||||||
|
$this->add_texts('localization/', true);
|
||||||
|
$this->api->output->add_label('none', 'refresh', 'moveto', 'reply', 'replyall', 'forward', 'select');
|
||||||
|
$this->include_stylesheet($this->local_skin_path() . '/swipe.css');
|
||||||
|
$this->include_script('swipe.js');
|
||||||
|
$this->add_hook('render_page', array($this, 'options_menu'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->register_action('plugin.swipe.save_settings', array($this, 'save_settings'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function options_menu($args)
|
||||||
|
{
|
||||||
|
// Other plugins may use template parsing method, this causes more than one render_page execution.
|
||||||
|
// We have to make sure the menu is added only once (when content is going to be written to client).
|
||||||
|
if (!$args['write']) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add additional menus from skins folder
|
||||||
|
$html = $this->api->output->just_parse("<roundcube:include file=\"$this->menu_file\" skinpath=\"plugins/swipe\" />");
|
||||||
|
$this->api->output->add_footer($html);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save_settings()
|
||||||
|
{
|
||||||
|
$config = array();
|
||||||
|
foreach (array('left', 'right', 'down') as $direction) {
|
||||||
|
if ($prop = rcube_utils::get_input_value('swipe_' . $direction, rcube_utils::INPUT_POST)) {
|
||||||
|
$config['swipe_' . $direction] = $prop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($config) > 0) {
|
||||||
|
rcube::get_instance()->user->save_prefs($config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue