github.com/wtfutil/wtf@v0.43.0/modules/pocket/widget.go (about)

     1  package pocket
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  
     7  	"github.com/rivo/tview"
     8  	"github.com/wtfutil/wtf/cfg"
     9  	"github.com/wtfutil/wtf/logger"
    10  	"github.com/wtfutil/wtf/utils"
    11  	"github.com/wtfutil/wtf/view"
    12  	"gopkg.in/yaml.v2"
    13  )
    14  
    15  type Widget struct {
    16  	view.ScrollableWidget
    17  
    18  	settings     *Settings
    19  	client       *Client
    20  	items        []Item
    21  	archivedView bool
    22  }
    23  
    24  func NewWidget(tviewApp *tview.Application, redrawChan chan bool, _ *tview.Pages, settings *Settings) *Widget {
    25  	widget := Widget{
    26  		ScrollableWidget: view.NewScrollableWidget(tviewApp, redrawChan, nil, settings.Common),
    27  		settings:         settings,
    28  		client:           NewClient(settings.consumerKey, "http://localhost"),
    29  		archivedView:     false,
    30  	}
    31  
    32  	widget.CommonSettings()
    33  	widget.SetRenderFunction(widget.Render)
    34  	widget.View.SetScrollable(true)
    35  	widget.View.SetRegions(true)
    36  	widget.initializeKeyboardControls()
    37  	widget.Selected = -1
    38  	widget.SetItemCount(0)
    39  	return &widget
    40  }
    41  
    42  /* -------------------- Exported Functions -------------------- */
    43  
    44  func (widget *Widget) Render() {
    45  
    46  	widget.Redraw(widget.content)
    47  }
    48  
    49  func (widget *Widget) Refresh() {
    50  	if widget.client.accessToken == nil {
    51  		metaData, err := readMetaDataFromDisk()
    52  		if err != nil || metaData.AccessToken == nil {
    53  			widget.Redraw(widget.authorizeWorkFlow)
    54  			return
    55  		}
    56  		widget.client.accessToken = metaData.AccessToken
    57  	}
    58  
    59  	state := Unread
    60  	if widget.archivedView {
    61  		state = Read
    62  	}
    63  	response, err := widget.client.GetLinks(state)
    64  	if err != nil {
    65  		widget.SetItemCount(0)
    66  	}
    67  
    68  	widget.items = orderItemResponseByKey(response)
    69  	widget.SetItemCount(len(widget.items))
    70  	widget.Redraw(widget.content)
    71  }
    72  
    73  /* -------------------- Unexported Functions -------------------- */
    74  
    75  type pocketMetaData struct {
    76  	AccessToken *string
    77  }
    78  
    79  func writeMetaDataToDisk(metaData pocketMetaData) error {
    80  
    81  	fileData, err := yaml.Marshal(metaData)
    82  	if err != nil {
    83  		return fmt.Errorf("could not write token to disk %w", err)
    84  	}
    85  
    86  	wtfConfigDir, err := cfg.WtfConfigDir()
    87  
    88  	if err != nil {
    89  		return nil
    90  	}
    91  
    92  	filePath := fmt.Sprintf("%s/%s", wtfConfigDir, "pocket.data")
    93  	err = os.WriteFile(filePath, fileData, 0644)
    94  
    95  	return err
    96  }
    97  
    98  func readMetaDataFromDisk() (pocketMetaData, error) {
    99  	wtfConfigDir, err := cfg.WtfConfigDir()
   100  	var metaData pocketMetaData
   101  	if err != nil {
   102  		return metaData, err
   103  	}
   104  	filePath := fmt.Sprintf("%s/%s", wtfConfigDir, "pocket.data")
   105  	fileData, err := utils.ReadFileBytes(filePath)
   106  
   107  	if err != nil {
   108  		return metaData, err
   109  	}
   110  
   111  	err = yaml.Unmarshal(fileData, &metaData)
   112  
   113  	return metaData, err
   114  
   115  }
   116  
   117  /*
   118  Authorization workflow is documented at https://getpocket.com/developer/docs/authentication
   119  broken to 4 steps :
   120  
   121  	1- Obtain a platform consumer key from http://getpocket.com/developer/apps/new.
   122  	2- Obtain a request token
   123  	3- Redirect user to Pocket to continue authorization
   124  	4- Receive the callback from Pocket, this wont be used
   125  	5- Convert a request token into a Pocket access token
   126  */
   127  func (widget *Widget) authorizeWorkFlow() (string, string, bool) {
   128  	title := widget.CommonSettings().Title
   129  
   130  	if widget.settings.requestKey == nil {
   131  		requestToken, err := widget.client.ObtainRequestToken()
   132  
   133  		if err != nil {
   134  			logger.Log(err.Error())
   135  			return title, err.Error(), true
   136  		}
   137  		widget.settings.requestKey = &requestToken
   138  		redirectURL := widget.client.CreateAuthLink(requestToken)
   139  		content := fmt.Sprintf("Please click on %s to Authorize the app", redirectURL)
   140  		return title, content, true
   141  	}
   142  
   143  	if widget.settings.accessToken == nil {
   144  		accessToken, err := widget.client.GetAccessToken(*widget.settings.requestKey)
   145  		if err != nil {
   146  			logger.Log(err.Error())
   147  			redirectURL := widget.client.CreateAuthLink(*widget.settings.requestKey)
   148  			content := fmt.Sprintf("Please click on %s to Authorize the app", redirectURL)
   149  			return title, content, true
   150  		}
   151  		content := "Authorized"
   152  		widget.settings.accessToken = &accessToken
   153  
   154  		metaData := pocketMetaData{
   155  			AccessToken: &accessToken,
   156  		}
   157  
   158  		err = writeMetaDataToDisk(metaData)
   159  		if err != nil {
   160  			content = err.Error()
   161  		}
   162  
   163  		return title, content, true
   164  	}
   165  
   166  	content := "Authorized"
   167  	return title, content, true
   168  
   169  }
   170  
   171  func (widget *Widget) toggleView() {
   172  	widget.archivedView = !widget.archivedView
   173  	widget.Refresh()
   174  }
   175  
   176  func (widget *Widget) openLink() {
   177  	sel := widget.GetSelected()
   178  	if sel >= 0 && widget.items != nil && sel < len(widget.items) {
   179  		item := &widget.items[sel]
   180  		utils.OpenFile(item.GivenURL)
   181  	}
   182  }
   183  
   184  func (widget *Widget) toggleLink() {
   185  	sel := widget.GetSelected()
   186  	action := Archive
   187  	if widget.archivedView {
   188  		action = ReAdd
   189  	}
   190  
   191  	if sel >= 0 && widget.items != nil && sel < len(widget.items) {
   192  		item := &widget.items[sel]
   193  		_, err := widget.client.ModifyLink(action, item.ItemID)
   194  		if err != nil {
   195  			logger.Log(err.Error())
   196  		}
   197  	}
   198  
   199  	widget.Refresh()
   200  }
   201  
   202  func (widget *Widget) formatItem(item Item, isSelected bool) string {
   203  	foreColor, backColor := widget.settings.Colors.RowTheme.EvenForeground, widget.settings.Colors.RowTheme.EvenBackground
   204  	text := item.ResolvedTitle
   205  	if isSelected {
   206  		foreColor = widget.settings.Colors.RowTheme.HighlightedForeground
   207  		backColor = widget.settings.Colors.RowTheme.HighlightedBackground
   208  
   209  	}
   210  
   211  	return fmt.Sprintf("[%s:%s]%s[white]", foreColor, backColor, tview.Escape(text))
   212  }
   213  
   214  func (widget *Widget) content() (string, string, bool) {
   215  	title := widget.CommonSettings().Title
   216  	currentViewTitle := "Reading List"
   217  	if widget.archivedView {
   218  		currentViewTitle = "Archived list"
   219  	}
   220  
   221  	title = fmt.Sprintf("%s-%s", title, currentViewTitle)
   222  	content := ""
   223  
   224  	for i, v := range widget.items {
   225  		content += widget.formatItem(v, i == widget.Selected) + "\n"
   226  	}
   227  
   228  	return title, content, false
   229  }